comparison sat_pubsub/pubsub_admin.py @ 405:c56a728412f1

file organisation + setup refactoring: - `/src` has been renamed to `/sat_pubsub`, this is the recommended naming convention - revamped `setup.py` on the basis of SàT's `setup.py` - added a `VERSION` which is the unique place where version number will now be set - use same trick as in SàT to specify dev version (`D` at the end) - use setuptools_scm to retrieve Mercurial hash when in dev version
author Goffi <goffi@goffi.org>
date Fri, 16 Aug 2019 12:00:02 +0200
parents src/pubsub_admin.py@1d2222a91e6b
children ccb2a22ea0fc
comparison
equal deleted inserted replaced
404:105a0772eedd 405:c56a728412f1
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 as jabber_error, xmlstream
28 from sat_pubsub import error
29 from wokkel.subprotocols import XMPPHandler
30 from wokkel import disco, iwokkel, pubsub
31
32 NS_PUBSUB_ADMIN = u"https://salut-a-toi.org/spec/pubsub_admin:0"
33 ADMIN_REQUEST = '/iq[@type="set"]/admin[@xmlns="{}"]'.format(NS_PUBSUB_ADMIN)
34
35
36 class PubsubAdminHandler(XMPPHandler):
37 implements(iwokkel.IDisco)
38
39 def __init__(self, backend):
40 super(PubsubAdminHandler, self).__init__()
41 self.backend = backend
42
43 def connectionInitialized(self):
44 self.xmlstream.addObserver(ADMIN_REQUEST, self.onAdminRequest)
45
46 def sendError(self, iq_elt, condition=u'bad-request'):
47 stanza_error = jabber_error.StanzaError(condition)
48 iq_error = stanza_error.toResponse(iq_elt)
49 self.parent.xmlstream.send(iq_error)
50
51 @defer.inlineCallbacks
52 def onAdminRequest(self, iq_elt):
53 """Pubsub Admin request received"""
54 iq_elt.handled = True
55 try:
56 pep = bool(iq_elt.delegated)
57 except AttributeError:
58 pep = False
59
60 # is the sender really an admin?
61 admins = self.backend.config[u'admins_jids_list']
62 from_jid = jid.JID(iq_elt[u'from'])
63 if from_jid.userhostJID() not in admins:
64 log.msg("WARNING: admin request done by non admin entity {from_jid}"
65 .format(from_jid=from_jid.full()))
66 self.sendError(iq_elt, u'forbidden')
67 return
68
69 # alright, we can proceed
70 recipient = jid.JID(iq_elt[u'to'])
71 admin_elt = iq_elt.admin
72 try:
73 pubsub_elt = next(admin_elt.elements(pubsub.NS_PUBSUB, u'pubsub'))
74 publish_elt = next(pubsub_elt.elements(pubsub.NS_PUBSUB, u'publish'))
75 except StopIteration:
76 self.sendError(iq_elt)
77 return
78 try:
79 node = publish_elt[u'node']
80 except KeyError:
81 self.sendError(iq_elt)
82 return
83
84 # we prepare the result IQ request, we will fill it with item ids
85 iq_result_elt = xmlstream.toResponse(iq_elt, u'result')
86 result_admin_elt = iq_result_elt.addElement((NS_PUBSUB_ADMIN, u'admin'))
87 result_pubsub_elt = result_admin_elt.addElement((pubsub.NS_PUBSUB, u'pubsub'))
88 result_publish_elt = result_pubsub_elt.addElement(u'publish')
89 result_publish_elt[u'node'] = node
90
91 # now we can send the items
92 for item in publish_elt.elements(pubsub.NS_PUBSUB, u'item'):
93 try:
94 requestor = jid.JID(item.attributes.pop(u'publisher'))
95 except Exception as e:
96 log.msg(u"WARNING: invalid jid in publisher ({requestor}): {msg}"
97 .format(requestor=requestor, msg=e))
98 self.sendError(iq_elt)
99 return
100 except KeyError:
101 requestor = from_jid
102
103 # we don't use a DeferredList because we want to be sure that
104 # each request is done in order
105 try:
106 payload = yield self.backend.publish(
107 nodeIdentifier=node,
108 items=[item],
109 requestor=requestor,
110 pep=pep,
111 recipient=recipient)
112 except (error.Forbidden, error.ItemForbidden):
113 __import__('pudb').set_trace()
114 self.sendError(iq_elt, u"forbidden")
115 return
116 except Exception as e:
117 self.sendError(iq_elt, u"internal-server-error")
118 log.msg(u"INTERNAL ERROR: {msg}".format(msg=e))
119 return
120
121 result_item_elt = result_publish_elt.addElement(u'item')
122 # either the id was given and it is available in item
123 # either it's a new item, and we can retrieve it from return payload
124 try:
125 result_item_elt[u'id'] = item[u'id']
126 except KeyError:
127 result_item_elt = payload.publish.item[u'id']
128
129 self.xmlstream.send(iq_result_elt)
130
131 def getDiscoInfo(self, requestor, service, nodeIdentifier=''):
132 return [disco.DiscoFeature(NS_PUBSUB_ADMIN)]
133
134 def getDiscoItems(self, requestor, service, nodeIdentifier=''):
135 return []