comparison sat_pubsub/privilege.py @ 460:607616f9ef5b

backend: new `server_jid` option: Server domain must be known to validate requests, this can be done explicitely by using the `server_jid` option. If this option is not set, the server domain is found: - by using the `from` name of the initial delegation advertising message - or it fallbacks to using the part after initial `.` (`pubsub.example.org` would give `example.org`)
author Goffi <goffi@goffi.org>
date Fri, 15 Oct 2021 09:32:07 +0200
parents 0b5233981671
children a017af61a32b
comparison
equal deleted inserted replaced
459:cebcb7f56889 460:607616f9ef5b
1 #!/usr/bin/env python3 1 #!/usr/bin/env python3
2 #-*- coding: utf-8 -*-
3 # 2 #
4 # Copyright (c) 2015 Jérôme Poisson 3 # Copyright (c) 2015-2021 Jérôme Poisson
5 4
6 5
7 # This program is free software: you can redistribute it and/or modify 6 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by 7 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or 8 # the Free Software Foundation, either version 3 of the License, or
15 # GNU Affero General Public License for more details. 14 # GNU Affero General Public License for more details.
16 15
17 # You should have received a copy of the GNU Affero General Public License 16 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 18
20 # --- 19 "This module implements XEP-0356 (Privileged Entity) to manage rosters, messages and "
20 "presences"
21 21
22 # This module implements XEP-0356 (Privileged Entity) to manage rosters, messages and presences 22 # This module implements XEP-0356 (Privileged Entity) to manage rosters, messages and presences
23 23
24 from wokkel import xmppim 24 from wokkel import xmppim
25 from wokkel.compat import IQ 25 from wokkel.compat import IQ
56 #FIXME: need to manage updates, and database sync 56 #FIXME: need to manage updates, and database sync
57 #TODO: cache 57 #TODO: cache
58 58
59 def __init__(self, service_jid): 59 def __init__(self, service_jid):
60 super(PrivilegesHandler, self).__init__() 60 super(PrivilegesHandler, self).__init__()
61 self.backend = None
61 self._permissions = {PERM_ROSTER: 'none', 62 self._permissions = {PERM_ROSTER: 'none',
62 PERM_MESSAGE: 'none', 63 PERM_MESSAGE: 'none',
63 PERM_PRESENCE: 'none'} 64 PERM_PRESENCE: 'none'}
64 self._pubsub_service = None 65 self._pubsub_service = None
65 self._backend = None
66 # FIXME: we use a hack supposing that our privilege come from hostname
67 # and we are a component named [name].hostname
68 # but we need to manage properly server
69 # TODO: do proper server handling
70 self.server_jid = jid.JID(service_jid.host.split('.', 1)[1])
71 self.caps_map = {} # key: bare jid, value: dict of resources with caps hash 66 self.caps_map = {} # key: bare jid, value: dict of resources with caps hash
72 self.hash_map = {} # key: (hash,version), value: dict with DiscoInfo instance (infos) and nodes to notify (notify) 67 # key: (hash,version), value: dict with DiscoInfo instance (infos) and nodes to
68 # notify (notify)
69 self.hash_map = {}
73 self.roster_cache = {} # key: jid, value: dict with "timestamp" and "roster" 70 self.roster_cache = {} # key: jid, value: dict with "timestamp" and "roster"
74 self.presence_map = {} # inverted roster: key: jid, value: set of entities who has this jid in roster (with presence of "from" or "both") 71 self.presence_map = {} # inverted roster: key: jid, value: set of entities who has this jid in roster (with presence of "from" or "both")
75 self.server = None
76 72
77 @property 73 @property
78 def permissions(self): 74 def permissions(self):
79 return self._permissions 75 return self._permissions
80 76
81 def connectionInitialized(self): 77 def connectionInitialized(self):
82 for handler in self.parent.handlers: 78 for handler in self.parent.handlers:
83 if IPubSubService.providedBy(handler): 79 if IPubSubService.providedBy(handler):
84 self._pubsub_service = handler 80 self._pubsub_service = handler
85 break 81 break
86 self._backend = self.parent.parent.getServiceNamed('backend') 82 self.backend = self.parent.parent.getServiceNamed('backend')
87 self.xmlstream.addObserver(PRIV_ENT_ADV_XPATH, self.onAdvertise) 83 self.xmlstream.addObserver(PRIV_ENT_ADV_XPATH, self.onAdvertise)
88 self.xmlstream.addObserver('/presence', self.onPresence) 84 self.xmlstream.addObserver('/presence', self._onPresence)
89 85
90 def onAdvertise(self, message): 86 def onAdvertise(self, message):
91 """Managage the <message/> advertising privileges 87 """Managage the <message/> advertising privileges
92 88
93 self._permissions will be updated according to advertised privileged 89 self._permissions will be updated according to advertised privileged
173 log.msg("WARNING: permission not allowed to send privileged messages") 169 log.msg("WARNING: permission not allowed to send privileged messages")
174 raise failure.Failure(NotAllowedError('privileged messages are not allowed')) 170 raise failure.Failure(NotAllowedError('privileged messages are not allowed'))
175 171
176 main_message = domish.Element((None, "message")) 172 main_message = domish.Element((None, "message"))
177 if to_jid is None: 173 if to_jid is None:
178 to_jid = self.server_jid 174 to_jid = self.backend.server_jid
179 main_message['to'] = to_jid.full() 175 main_message['to'] = to_jid.full()
180 privilege_elt = main_message.addElement((PRIV_ENT_NS, 'privilege')) 176 privilege_elt = main_message.addElement((PRIV_ENT_NS, 'privilege'))
181 forwarded_elt = privilege_elt.addElement((FORWARDED_NS, 'forwarded')) 177 forwarded_elt = privilege_elt.addElement((FORWARDED_NS, 'forwarded'))
182 priv_message['xmlns'] = 'jabber:client' 178 priv_message['xmlns'] = 'jabber:client'
183 forwarded_elt.addChild(priv_message) 179 forwarded_elt.addChild(priv_message)
236 ) 232 )
237 self.sendMessage(message) 233 self.sendMessage(message)
238 234
239 ## presence ## 235 ## presence ##
240 236
241 @defer.inlineCallbacks 237 def _onPresence(self, presence_elt: domish.Element) -> None:
242 def onPresence(self, presence_elt): 238 defer.ensureDeferred(self.onPresence(presence_elt))
243 if self.server is None: 239
244 # FIXME: we use a hack supposing that our delegation come from hostname 240 async def onPresence(self, presence_elt: domish.Element) -> None:
245 # and we are a component named [name].hostname
246 # but we need to manage properly allowed servers
247 # TODO: do proper origin security check
248 _, self.server = presence_elt['to'].split('.', 1)
249 from_jid = jid.JID(presence_elt['from']) 241 from_jid = jid.JID(presence_elt['from'])
250 from_jid_bare = from_jid.userhostJID() 242 from_jid_bare = from_jid.userhostJID()
251 if from_jid.host == self.server and from_jid_bare not in self.roster_cache: 243 if ((jid.JID(from_jid.host) == self.backend.server_jid
252 roster = yield self.getRoster(from_jid_bare) 244 and from_jid_bare not in self.roster_cache)):
245 roster = await self.getRoster(from_jid_bare)
253 timestamp = time.time() 246 timestamp = time.time()
254 self.roster_cache[from_jid_bare] = {'timestamp': timestamp, 247 self.roster_cache[from_jid_bare] = {'timestamp': timestamp,
255 'roster': roster, 248 'roster': roster,
256 } 249 }
257 for roster_jid, roster_item in roster.items(): 250 for roster_jid, roster_item in roster.items():