Mercurial > libervia-pubsub
diff sat_pubsub/backend.py @ 250:eb14b8d30cba
fine tuning per-item permissions
author | Goffi <goffi@goffi.org> |
---|---|
date | Sun, 24 Jun 2012 19:35:49 +0200 |
parents | 50f6ee966da8 |
children | 0a7d43b3dad6 |
line wrap: on
line diff
--- a/sat_pubsub/backend.py Fri Jun 08 18:34:45 2012 +0200 +++ b/sat_pubsub/backend.py Sun Jun 24 19:35:49 2012 +0200 @@ -73,13 +73,14 @@ from twisted.words.protocols.jabber.jid import JID, InvalidFormat from twisted.words.xish import utility -from wokkel import disco +from wokkel import disco, data_form from wokkel.iwokkel import IPubSubResource from wokkel.pubsub import PubSubResource, PubSubError -from sat_pubsub import error, iidavoll +from sat_pubsub import error, iidavoll, const from sat_pubsub.iidavoll import IBackendService, ILeafNode + def _getAffiliation(node, entity): d = node.getAffiliation(entity) d.addCallback(lambda affiliation: (node, affiliation)) @@ -141,6 +142,10 @@ return True + def supportsGroupBlog(self): + return True + + def supportsOutcastAffiliation(self): return True @@ -188,10 +193,33 @@ d.addCallback(check, node) return d + def parseItemConfig(self, item): + """Get and remove item configuration information + @param item: + """ + item_config = None + access_model = const.VAL_DEFAULT + for i in range(len(item.children)): + elt = item.children[i] + if not (elt.uri,elt.name)==(data_form.NS_X_DATA,'x'): + continue + form = data_form.Form.fromElement(elt) + if (form.formNamespace == const.NS_ITEM_CONFIG): + item_config = form + del item.children[i] #we need to remove the config from item + break + + if item_config: + access_model = item_config.get(const.OPT_ACCESS_MODEL, const.VAL_DEFAULT) + + return (access_model, item_config) + def publish(self, nodeIdentifier, items, requestor): d = self.storage.getNode(nodeIdentifier) d.addCallback(self._checkAuth, requestor) + #FIXME: owner and publisher are not necessarly the same. So far we use only owner to get roster. + #FIXME: in addition, there can be several owners: that is not managed yet d.addCallback(self._doPublish, items, requestor) return d @@ -209,29 +237,32 @@ elif not items and (persistItems or deliverPayloads): raise error.ItemRequired() - if persistItems or deliverPayloads: - for item in items: + parsed_items = [] + for item in items: + if persistItems or deliverPayloads: item.uri = None item.defaultUri = None if not item.getAttribute("id"): item["id"] = str(uuid.uuid4()) + access_model, item_config = self.parseItemConfig(item) + parsed_items.append((access_model, item_config, item)) if persistItems: - d = node.storeItems(items, requestor) + d = node.storeItems(parsed_items, requestor) else: d = defer.succeed(None) - d.addCallback(self._doNotify, node.nodeIdentifier, items, + d.addCallback(self._doNotify, node, parsed_items, deliverPayloads) return d - def _doNotify(self, result, nodeIdentifier, items, deliverPayloads): + def _doNotify(self, result, node, items, deliverPayloads): if items and not deliverPayloads: - for item in items: + for access_model, item_config, item in items: item.children = [] - self.dispatch({'items': items, 'nodeIdentifier': nodeIdentifier}, + self.dispatch({'items': items, 'node': node}, '//event/pubsub/notify') @@ -284,6 +315,7 @@ def _doSubscribe(self, result, subscriber): node, affiliation = result + #FIXME: must check node's access_model before subscribing if affiliation == 'outcast': raise error.Forbidden() @@ -353,8 +385,9 @@ nodeIdentifier = 'generic/%s' % uuid.uuid4() if self.supportsCreatorCheck(): + groupblog = nodeIdentifier.startswith(const.NS_GROUPBLOG_PREFIX) try: - nodeIdentifierJID = JID(nodeIdentifier) + nodeIdentifierJID = JID(nodeIdentifier[len(const.NS_GROUPBLOG_PREFIX):] if groupblog else nodeIdentifier) except InvalidFormat: is_user_jid = False else: @@ -667,20 +700,60 @@ if self.backend.supportsPublisherAffiliation(): self.features.append("publisher-affiliation") + if self.backend.supportsGroupBlog(): + self.features.append("groupblog") + def _notify(self, data): items = data['items'] - nodeIdentifier = data['nodeIdentifier'] + node = data['node'] + + def _notifyAllowed(result): + """Check access of subscriber for each item, + and notify only allowed ones""" + notifications, roster = result + + #we filter items not allowed for the subscribers + notifications_filtered = [] + + for subscriber, subscriptions, items in notifications: + allowed_items = [] #we keep only item which subscriber can access + + for access_model, item_config, item in items: + if access_model == 'open': + allowed_items.append(item) + elif access_model == 'roster': + _subscriber = subscriber.userhost() + if not _subscriber in roster: + continue + #the subscriber is known, is he in the right group ? + authorized_groups = item_config[const.OPT_ROSTER_GROUPS_ALLOWED] + if roster[_subscriber].groups.intersection(authorized_groups): + allowed_items.append(item) + + else: #unknown access_model + raise NotImplementedError + + notifications_filtered.append((subscriber, subscriptions, allowed_items)) + + return self.pubsubService.notifyPublish( + self.serviceJID, + node.nodeIdentifier, + notifications_filtered) + + if 'subscription' not in data: - d = self.backend.getNotifications(nodeIdentifier, items) + d1 = self.backend.getNotifications(node.nodeIdentifier, items) else: subscription = data['subscription'] - d = defer.succeed([(subscription.subscriber, [subscription], + d1 = defer.succeed([(subscription.subscriber, [subscription], items)]) - d.addCallback(lambda notifications: self.pubsubService.notifyPublish( - self.serviceJID, - nodeIdentifier, - notifications)) + + d2 = node.getNodeOwner() + d2.addCallback(self.backend.roster.getRoster) + + d = defer.gatherResults([d1, d2]) + d.addCallback(_notifyAllowed) def _preDelete(self, data):