comparison sat_pubsub/privilege.py @ 343:ff8aff4c9b79

backend, psql: implemented notifications for auto subscribers in PEP: people with presence permission from node owner and who request notification (+notify) from node will receive notifications according to XEP-0163 §4. This make SàT Pubsub a nearly fully compliant PEP service. Following features are still missing: - presence access model, should be implemented very soon as everything is already there - deleting resource on unavailable presence, will be implemented really soon - XEP-0115 hash check - and most importantly, rosters updates. XEP-0356 needs to be updated for this.
author Goffi <goffi@goffi.org>
date Sun, 20 Aug 2017 18:41:21 +0200
parents 28c9579901d3
children d1f63ae1eaf4
comparison
equal deleted inserted replaced
342:28c9579901d3 343:ff8aff4c9b79
66 # FIXME: we use a hack supposing that our privilege come from hostname 66 # FIXME: we use a hack supposing that our privilege come from hostname
67 # and we are a component named [name].hostname 67 # and we are a component named [name].hostname
68 # but we need to manage properly server 68 # but we need to manage properly server
69 # TODO: do proper server handling 69 # TODO: do proper server handling
70 self.server_jid = jid.JID(service_jid.host.split('.', 1)[1]) 70 self.server_jid = jid.JID(service_jid.host.split('.', 1)[1])
71 self.caps_map = {} # key: full jid, value: caps_hash 71 self.caps_map = {} # key: bare jid, value: dict of resources with caps hash
72 self.hash_map = {} # key: (hash,version), value: DiscoInfo instance 72 self.hash_map = {} # key: (hash,version), value: dict with DiscoInfo instance (infos) and nodes to notify (notify)
73 self.roster_cache = {} # key: jid, value: dict with "timestamp" and "roster" 73 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") 74 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 75 self.server = None
76 76
77 @property 77 @property
121 Retrieve contact list. 121 Retrieve contact list.
122 122
123 @return: Roster as a mapping from L{JID} to L{RosterItem}. 123 @return: Roster as a mapping from L{JID} to L{RosterItem}.
124 @rtype: L{twisted.internet.defer.Deferred} 124 @rtype: L{twisted.internet.defer.Deferred}
125 """ 125 """
126 # TODO: cache results
126 if self._permissions[PERM_ROSTER] not in ('get', 'both'): 127 if self._permissions[PERM_ROSTER] not in ('get', 'both'):
127 log.msg("WARNING: permission not allowed to get roster") 128 log.msg("WARNING: permission not allowed to get roster")
128 raise failure.Failure(NotAllowedError('roster get is not allowed')) 129 raise failure.Failure(NotAllowedError('roster get is not allowed'))
129 130
130 def processRoster(result): 131 def processRoster(result):
235 # no capabilities, we don't go further 236 # no capabilities, we don't go further
236 return 237 return
237 238
238 # FIXME: hash is not checked (cf. XEP-0115) 239 # FIXME: hash is not checked (cf. XEP-0115)
239 disco_tuple = (hash_, ver) 240 disco_tuple = (hash_, ver)
240 if from_jid not in self.caps_map: 241 jid_caps = self.caps_map.setdefault(from_jid_bare, {})
241 self.caps_map[from_jid] = disco_tuple 242 if from_jid.resource not in jid_caps:
243 jid_caps[from_jid.resource] = disco_tuple
242 244
243 if disco_tuple not in self.hash_map: 245 if disco_tuple not in self.hash_map:
244 # first time we se this hash, what is behind it? 246 # first time we se this hash, what is behind it?
245 infos = yield self.requestInfo(from_jid) 247 infos = yield self.requestInfo(from_jid)
246 self.hash_map[disco_tuple] = { 248 self.hash_map[disco_tuple] = {
253 if not nodes: 255 if not nodes:
254 return 256 return
255 # publishers are entities which have granted presence access to our user + user itself 257 # publishers are entities which have granted presence access to our user + user itself
256 publishers = tuple(self.presence_map.get(from_jid_bare, ())) + (from_jid_bare,) 258 publishers = tuple(self.presence_map.get(from_jid_bare, ())) + (from_jid_bare,)
257 259
260 # FIXME: add "presence" access_model (for node) for getLastItems
258 last_items = yield self._backend.storage.getLastItems(publishers, nodes, ('open',), ('open',), True) 261 last_items = yield self._backend.storage.getLastItems(publishers, nodes, ('open',), ('open',), True)
259 # we send message with last item, as required by https://xmpp.org/extensions/xep-0163.html#notify-last 262 # we send message with last item, as required by https://xmpp.org/extensions/xep-0163.html#notify-last
260 for pep_jid, node, item, item_access_model in last_items: 263 for pep_jid, node, item, item_access_model in last_items:
261 self.notifyPublish(pep_jid, node, [(from_jid, None, [item])]) 264 self.notifyPublish(pep_jid, node, [(from_jid, None, [item])])
265
266 ## misc ##
267
268 @defer.inlineCallbacks
269 def getAutoSubscribers(self, recipient, nodeIdentifier, explicit_subscribers):
270 """get automatic subscribers, i.e. subscribers with presence subscription and +notify for this node
271
272 @param recipient(jid.JID): jid of the PEP owner of this node
273 @param nodeIdentifier(unicode): node
274 @param explicit_subscribers(set(jid.JID}: jids of people which have an explicit subscription
275 @return (list[jid.JID]): full jid of automatically subscribed entities
276 """
277 auto_subscribers = []
278 roster = yield self.getRoster(recipient)
279 for roster_jid, roster_item in roster.iteritems():
280 if roster_jid in explicit_subscribers:
281 continue
282 if roster_item.subscriptionFrom:
283 try:
284 online_resources = self.caps_map[roster_jid]
285 except KeyError:
286 continue
287 for res, hash_ in online_resources.iteritems():
288 notify = self.hash_map[hash_]['notify']
289 if nodeIdentifier in notify:
290 full_jid = jid.JID(tuple=(roster_jid.user, roster_jid.host, res))
291 auto_subscribers.append(full_jid)
292 defer.returnValue(auto_subscribers)