comparison src/pubsub_admin.py @ 382:77b52dbda89a

pubsub_admin: Pubsub Admin experimental protocol first draft: This protocol allows an admin to publish items where the publisher is specified.
author Goffi <goffi@goffi.org>
date Sun, 13 Jan 2019 18:56:19 +0100
parents
children 1d2222a91e6b
comparison
equal deleted inserted replaced
381:7c490bb60847 382:77b52dbda89a
1 #!/usr/bin/python
2 #-*- coding: utf-8 -*-
3
4 # Copyright (c) 2019 Jérôme Poisson
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15 #
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 """
20 Pubsub Admin experimental protocol implementation
21
22 """
23
24 from zope.interface import implements
25 from twisted.python import log
26 from twisted.internet import defer
27 from twisted.words.protocols.jabber import jid, error, xmlstream
28 from wokkel.subprotocols import XMPPHandler
29 from wokkel import disco, iwokkel, pubsub
30
31 NS_PUBSUB_ADMIN = u"https://salut-a-toi.org/spec/pubsub_admin:0"
32 ADMIN_REQUEST = '/iq[@type="set"]/admin[@xmlns="{}"]'.format(NS_PUBSUB_ADMIN)
33
34
35 class PubsubAdminHandler(XMPPHandler):
36 implements(iwokkel.IDisco)
37
38 def __init__(self, backend):
39 super(PubsubAdminHandler, self).__init__()
40 self.backend = backend
41
42 def connectionInitialized(self):
43 self.xmlstream.addObserver(ADMIN_REQUEST, self.onAdminRequest)
44
45 def sendError(self, iq_elt, condition=u'bad-request'):
46 stanza_error = error.StanzaError(u'bad-request')
47 iq_error = stanza_error.toResponse(iq_elt)
48 self.parent.xmlstream.send(iq_error)
49
50 @defer.inlineCallbacks
51 def onAdminRequest(self, iq_elt):
52 """Pubsub Admin request received"""
53 iq_elt.handled = True
54 try:
55 pep = bool(iq_elt.delegated)
56 except AttributeError:
57 pep = False
58
59 # is the sender really an admin?
60 admins = self.backend.config[u'admins_jids_list']
61 from_jid = jid.JID(iq_elt[u'from'])
62 if from_jid.userhostJID() not in admins:
63 log.msg("WARNING: admin request done by non admin entity {from_jid}"
64 .format(from_jid=from_jid.full()))
65 self.sendError(iq_elt, u'forbidden')
66 return
67
68 # alright, we can proceed
69 recipient = jid.JID(iq_elt[u'to'])
70 admin_elt = iq_elt.admin
71 try:
72 pubsub_elt = next(admin_elt.elements(pubsub.NS_PUBSUB, u'pubsub'))
73 publish_elt = next(pubsub_elt.elements(pubsub.NS_PUBSUB, u'publish'))
74 except StopIteration:
75 self.sendError(iq_elt)
76 return
77 try:
78 node = publish_elt[u'node']
79 except KeyError:
80 self.sendError(iq_elt)
81 return
82
83 # we prepare the result IQ request, we will fill it with item ids
84 iq_result_elt = xmlstream.toResponse(iq_elt, u'result')
85 result_admin_elt = iq_result_elt.addElement((NS_PUBSUB_ADMIN, u'admin'))
86 result_pubsub_elt = result_admin_elt.addElement((pubsub.NS_PUBSUB, u'pubsub'))
87 result_publish_elt = result_pubsub_elt.addElement(u'publish')
88 result_publish_elt[u'node'] = node
89
90 # now we can send the items
91 for item in publish_elt.elements(pubsub.NS_PUBSUB, u'item'):
92 try:
93 requestor = jid.JID(item.attributes.pop(u'publisher'))
94 except Exception as e:
95 log.msg(u"WARNING: invalid jid in publisher ({requestor}): {msg}"
96 .format(requestor=requestor, msg=e))
97 self.sendError(iq_elt)
98 return
99 except KeyError:
100 requestor = from_jid
101
102 # we don't use a DeferredList because we want to be sure that
103 # each request is done in order
104 payload = yield self.backend.publish(
105 nodeIdentifier=node,
106 items=[item],
107 requestor=requestor,
108 pep=pep,
109 recipient=recipient)
110
111 result_item_elt = result_publish_elt.addElement(u'item')
112 # either the id was given and it is available in item
113 # either it's a new item, and we can retrieve it from return payload
114 try:
115 result_item_elt[u'id'] = item[u'id']
116 except KeyError:
117 result_item_elt = payload.publish.item[u'id']
118
119 self.xmlstream.send(iq_result_elt)
120
121 def getDiscoInfo(self, requestor, service, nodeIdentifier=''):
122 return [disco.DiscoFeature(NS_PUBSUB_ADMIN)]
123
124 def getDiscoItems(self, requestor, service, nodeIdentifier=''):
125 return []