changeset 243:42048e37699e

added experimental roster access_model (use remote_roster)
author Goffi <goffi@goffi.org>
date Sun, 27 May 2012 15:55:25 +0200
parents a6170637690d
children 3ecc94407e36
files db/pubsub.sql sat_pubsub/backend.py sat_pubsub/error.py sat_pubsub/pgsql_storage.py
diffstat 4 files changed, 81 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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,
--- 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',
--- 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
 
--- 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