# HG changeset patch # User Goffi # Date 1338126925 -7200 # Node ID 42048e37699e7157cf48459a71b20068fbb83de5 # Parent a6170637690d114bf3fc913e32252aa15eda20a0 added experimental roster access_model (use remote_roster) diff -r a6170637690d -r 42048e37699e db/pubsub.sql --- a/db/pubsub.sql Sun May 27 15:36:06 2012 +0200 +++ b/db/pubsub.sql Sun May 27 15:55:25 2012 +0200 @@ -27,6 +27,13 @@ UNIQUE (entity_id, node_id) ); +CREATE TABLE node_groups_authorized ( + node_groups_authorized_id serial PRIMARY KEY, + node_id integer NOT NULL references nodes ON DELETE CASCADE, + groupname text NOT NULL, + UNIQUE (node_id,groupname) +); + CREATE TABLE subscriptions ( subscription_id serial PRIMARY KEY, entity_id integer NOT NULL REFERENCES entities ON DELETE CASCADE, diff -r a6170637690d -r 42048e37699e sat_pubsub/backend.py --- a/sat_pubsub/backend.py Sun May 27 15:36:06 2012 +0200 +++ b/sat_pubsub/backend.py Sun May 27 15:55:25 2012 +0200 @@ -416,24 +416,62 @@ itemIdentifiers=None): d = self.storage.getNode(nodeIdentifier) d.addCallback(_getAffiliation, requestor) - d.addCallback(self._doGetItems, maxItems, itemIdentifiers) + d.addCallback(self._doGetItems, requestor, maxItems, itemIdentifiers) return d + def checkGroup(self, roster_groups, entity): + """Check that requester is in roster + @param roster_group: tuple which 2 items: + - roster: mapping of jid to RosterItem as given by self.roster.getRoster + - groups: list of authorized groups + @param entity: entity which must be in group + @return: True if requestor is in roster""" + roster, authorized_groups = roster_groups + _entity = entity.userhost() + + if not _entity in roster: + raise error.NotInRoster + if roster[_entity].groups.intersection(authorized_groups): + return True + raise error.NotInRoster - def _doGetItems(self, result, maxItems, itemIdentifiers): + def _getNodeGroups(self, roster, nodeIdentifier): + d = self.storage.getNodeGroups(nodeIdentifier) + d.addCallback(lambda groups: (roster, groups)) + return d + + def _doGetItems(self, result, requestor, maxItems, itemIdentifiers): node, affiliation = result + def access_checked(authorized): + if not authorized: + raise error.NotAuthorized() + + if itemIdentifiers: + return node.getItemsById(itemIdentifiers) + else: + return node.getItems(maxItems) + + if not ILeafNode.providedBy(node): return [] if affiliation == 'outcast': raise error.Forbidden() - if itemIdentifiers: - return node.getItemsById(itemIdentifiers) - else: - return node.getItems(maxItems) + access_model = node.getConfiguration()["pubsub#access_model"] + + if access_model == 'open' or affiliation == 'owner': + d = defer.succeed(True) + d.addCallback(access_checked) + elif access_model == 'roster': + d = node.getNodeOwner() + d.addCallback(self.roster.getRoster) + d.addCallback(self._getNodeGroups,node.nodeIdentifier) + d.addCallback(self.checkGroup, requestor) + d.addCallback(access_checked) + return d def retractItem(self, nodeIdentifier, itemIdentifiers, requestor): d = self.storage.getNode(nodeIdentifier) @@ -575,6 +613,8 @@ error.NodeNotFound: ('item-not-found', None, None), error.NodeExists: ('conflict', None, None), error.Forbidden: ('forbidden', None, None), + error.NotAuthorized: ('not-authorized', None, None), + error.NotInRoster: ('not-authorized', 'not-in-roster-group', None), error.ItemForbidden: ('bad-request', 'item-forbidden', None), error.ItemRequired: ('bad-request', 'item-required', None), error.NoInstantNodes: ('not-acceptable', diff -r a6170637690d -r 42048e37699e sat_pubsub/error.py --- a/sat_pubsub/error.py Sun May 27 15:36:06 2012 +0200 +++ b/sat_pubsub/error.py Sun May 27 15:55:25 2012 +0200 @@ -93,6 +93,16 @@ +class NotAuthorized(Error): + pass + + + +class NotInRoster(Error): + pass + + + class ItemForbidden(Error): pass diff -r a6170637690d -r 42048e37699e sat_pubsub/pgsql_storage.py --- a/sat_pubsub/pgsql_storage.py Sun May 27 15:36:06 2012 +0200 +++ b/sat_pubsub/pgsql_storage.py Sun May 27 15:55:25 2012 +0200 @@ -56,7 +56,7 @@ from zope.interface import implements -from twisted.enterprise import adbapi +from twisted.internet import defer from twisted.words.protocols.jabber import jid from wokkel.generic import parseXml, stripNamespace @@ -185,6 +185,15 @@ if cursor.rowcount != 1: raise error.NodeNotFound() + def getNodeGroups(self, nodeIdentifier): + return self.dbpool.runInteraction(self._getNodeGroups, nodeIdentifier) + + def _getNodeGroups(self, cursor, nodeIdentifier): + cursor.execute("SELECT groupname FROM node_groups_authorized NATURAL JOIN nodes WHERE node=%s", + (nodeIdentifier,)) + rows = cursor.fetchall() + + return [row[0] for row in rows] def getAffiliations(self, entity): d = self.dbpool.runQuery("""SELECT node, affiliation FROM entities @@ -228,6 +237,7 @@ def __init__(self, nodeIdentifier, config): self.nodeIdentifier = nodeIdentifier self._config = config + self.owner = None; def _checkNodeExists(self, cursor): @@ -240,6 +250,13 @@ def getType(self): return self.nodeType + def getNodeOwner(self): + if self.owner: + return defer.succeed(self.owner) + d = self.dbpool.runQuery("""SELECT jid FROM nodes NATURAL JOIN affiliations NATURAL JOIN entities WHERE node=%s""", (self.nodeIdentifier,)) + d.addCallback(lambda result: jid.JID(result[0][0])) + return d + def getConfiguration(self): return self._config