changeset 259:6fe7da6b4b32

node "roster" access model management
author Goffi <goffi@goffi.org>
date Mon, 06 May 2013 00:11:44 +0200
parents e5b83fbb0219
children f0cd02c032b3
files sat_pubsub/backend.py sat_pubsub/const.py sat_pubsub/pgsql_storage.py
diffstat 3 files changed, 63 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/sat_pubsub/backend.py	Sun Apr 28 19:29:58 2013 +0200
+++ b/sat_pubsub/backend.py	Mon May 06 00:11:44 2013 +0200
@@ -116,6 +116,19 @@
                      "never": "Never",
                      "on_sub": "When a new subscription is processed"}
                 },
+            const.OPT_ACCESS_MODEL:
+                {"type": "list-single",
+                 "label": "Who can subscribe to this node",
+                 "options": {
+                     const.VAL_AMODEL_OPEN: "Public node",
+                     const.VAL_AMODEL_ROSTER: "Node restricted to some roster groups",
+                     const.VAL_AMODEL_JID: "Node restricted to some jids",
+                     }
+                },
+            const.OPT_ROSTER_GROUPS_ALLOWED:
+                {"type": "list-multi",
+                 "label": "Groups of the roster allowed to access the node",
+                },
             }
 
     subscriptionOptions = {
@@ -199,7 +212,7 @@
         @param item:
         """
         item_config = None
-        access_model = const.VAL_DEFAULT
+        access_model = const.VAL_AMODEL_DEFAULT
         for i in range(len(item.children)):
             elt = item.children[i]
             if not (elt.uri,elt.name)==(data_form.NS_X_DATA,'x'):
@@ -211,7 +224,7 @@
                 break
 
         if item_config:
-            access_model = item_config.get(const.OPT_ACCESS_MODEL, const.VAL_DEFAULT)
+            access_model = item_config.get(const.OPT_ACCESS_MODEL, const.VAL_AMODEL_DEFAULT)
 
         return (access_model, item_config)
 
@@ -381,10 +394,13 @@
         return True
 
 
-    def createNode(self, nodeIdentifier, requestor):
+    def createNode(self, nodeIdentifier, requestor, options = None):
         if not nodeIdentifier:
             nodeIdentifier = 'generic/%s' % uuid.uuid4()
 
+        if not options:
+            options = {}
+
         if self.supportsCreatorCheck():
             groupblog = nodeIdentifier.startswith(const.NS_GROUPBLOG_PREFIX)
             try:
@@ -402,6 +418,7 @@
         nodeType = 'leaf'
         config = self.storage.getDefaultConfiguration(nodeType)
         config['pubsub#node_type'] = nodeType
+        config.update(options)
 
         d = self.storage.createNode(nodeIdentifier, requestor, config)
         d.addCallback(lambda _: nodeIdentifier)
@@ -454,20 +471,20 @@
         return d
 
     def checkGroup(self, roster_groups, entity):
-        """Check that requester is in roster
+        """Check that entity is authorized and 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"""
+        @return: (True, roster) if entity is in roster and authorized
+                 (False, roster) if entity is in roster but not authorized
+        @raise: error.NotInRoster if entity is not in roster"""
         roster, authorized_groups = roster_groups
         _entity = entity.userhostJID()
         
         if not _entity in roster:
             raise error.NotInRoster
-        if roster[_entity].groups.intersection(authorized_groups):
-            return (True, roster)
-        raise error.NotInRoster
+        return (roster[_entity].groups.intersection(authorized_groups), roster)
 
     def _getNodeGroups(self, roster, nodeIdentifier):
         d = self.storage.getNodeGroups(nodeIdentifier)
@@ -481,16 +498,16 @@
             ret = []
             for data in items_data:
                 item, access_model, access_list = data
-                if access_model == const.VAL_OPEN:
+                if access_model == const.VAL_AMODEL_OPEN:
                     pass
-                elif access_model == const.VAL_ROSTER: 
+                elif access_model == const.VAL_AMODEL_ROSTER:
                     form = data_form.Form('submit', formNamespace=const.NS_ITEM_CONFIG)
-                    access = data_form.Field(None, const.OPT_ACCESS_MODEL, value=const.VAL_ROSTER)
+                    access = data_form.Field(None, const.OPT_ACCESS_MODEL, value=const.VAL_AMODEL_ROSTER)
                     allowed = data_form.Field(None, const.OPT_ROSTER_GROUPS_ALLOWED, values=access_list)
                     form.addField(access)
                     form.addField(allowed)
                     item.addChild(form.toElement())
-                elif access_model == const.VAL_JID:
+                elif access_model == const.VAL_AMODEL_JID:
                     #FIXME: manage jid
                     raise NotImplementedError
                 else:
@@ -528,7 +545,7 @@
         d.addCallback(self.roster.getRoster)
         
         if access_model == 'open' or affiliation == 'owner':
-            d.addCallback(lambda roster: (True,roster))
+            d.addCallback(lambda roster: (True, roster))
             d.addCallback(access_checked)
         elif access_model == 'roster':
             d.addCallback(self._getNodeGroups,node.nodeIdentifier)
@@ -919,7 +936,7 @@
 
     def create(self, request):
         d = self.backend.createNode(request.nodeIdentifier,
-                                    request.sender)
+                                    request.sender, request.options)
         return d.addErrback(self._mapErrors)
 
 
--- a/sat_pubsub/const.py	Sun Apr 28 19:29:58 2013 +0200
+++ b/sat_pubsub/const.py	Mon May 06 00:11:44 2013 +0200
@@ -57,7 +57,7 @@
 NS_ITEM_CONFIG = "http://jabber.org/protocol/pubsub#item-config"
 OPT_ACCESS_MODEL = 'pubsub#access_model'
 OPT_ROSTER_GROUPS_ALLOWED = 'pubsub#roster_groups_allowed'
-VAL_OPEN = 'open'
-VAL_ROSTER = 'roster'
-VAL_JID = 'jid'
-VAL_DEFAULT = VAL_OPEN
+VAL_AMODEL_OPEN = 'open'
+VAL_AMODEL_ROSTER = 'roster'
+VAL_AMODEL_JID = 'jid'
+VAL_AMODEL_DEFAULT = VAL_AMODEL_OPEN
--- a/sat_pubsub/pgsql_storage.py	Sun Apr 28 19:29:58 2013 +0200
+++ b/sat_pubsub/pgsql_storage.py	Mon May 06 00:11:44 2013 +0200
@@ -73,12 +73,12 @@
                 "pubsub#persist_items": True,
                 "pubsub#deliver_payloads": True,
                 "pubsub#send_last_published_item": 'on_sub',
-                "pubsub#access_model": 'open',
+                const.OPT_ACCESS_MODEL: const.VAL_AMODEL_DEFAULT,
             },
             'collection': {
                 "pubsub#deliver_payloads": True,
                 "pubsub#send_last_published_item": 'on_sub',
-                "pubsub#access_model": 'open',
+                const.OPT_ACCESS_MODEL: const.VAL_AMODEL_DEFAULT,
             }
     }
 
@@ -95,7 +95,7 @@
                                  persist_items,
                                  deliver_payloads,
                                  send_last_published_item,
-                                 access_model
+                                 access_model,
                           FROM nodes
                           WHERE node=%s""",
                        (nodeIdentifier,))
@@ -108,18 +108,18 @@
             configuration = {
                     'pubsub#persist_items': row[1],
                     'pubsub#deliver_payloads': row[2],
-                    'pubsub#send_last_published_item':
-                        row[3],
-                    'pubsub#access_model':row[4]}
+                    'pubsub#send_last_published_item': row[3],
+                    const.OPT_ACCESS_MODEL:row[4],
+                    }
             node = LeafNode(nodeIdentifier, configuration)
             node.dbpool = self.dbpool
             return node
         elif row[0] == 'collection':
             configuration = {
                     'pubsub#deliver_payloads': row[2],
-                    'pubsub#send_last_published_item':
-                        row[3],
-                    'pubsub#access_model':row[4]}
+                    'pubsub#send_last_published_item': row[3],
+                    const.OPT_ACCESS_MODEL: row[4],
+                    }
             node = CollectionNode(nodeIdentifier, configuration)
             node.dbpool = self.dbpool
             return node
@@ -152,11 +152,15 @@
                             config['pubsub#persist_items'],
                             config['pubsub#deliver_payloads'],
                             config['pubsub#send_last_published_item'],
-                            config['pubsub#access_model'])
+                            config[const.OPT_ACCESS_MODEL],
+                            )
                            )
         except cursor._pool.dbapi.IntegrityError:
             raise error.NodeExists()
 
+        cursor.execute("""SELECT node_id FROM nodes WHERE node=%s""", (nodeIdentifier,));
+        node_id = cursor.fetchone()[0]
+
         cursor.execute("""SELECT 1 from entities where jid=%s""",
                        (owner,))
 
@@ -166,12 +170,21 @@
 
         cursor.execute("""INSERT INTO affiliations
                           (node_id, entity_id, affiliation)
-                          SELECT node_id, entity_id, 'owner' FROM
-                          (SELECT node_id FROM nodes WHERE node=%s) as n
-                          CROSS JOIN
+                          SELECT %s, entity_id, 'owner' FROM
                           (SELECT entity_id FROM entities
                                             WHERE jid=%s) as e""",
-                       (nodeIdentifier, owner))
+                       (node_id, owner))
+
+        #TODO: manage JID access
+        if config[const.OPT_ACCESS_MODEL] == const.VAL_AMODEL_ROSTER:
+            if const.OPT_ROSTER_GROUPS_ALLOWED in config:
+                allowed_groups = config[const.OPT_ROSTER_GROUPS_ALLOWED]
+            else:
+                allowed_groups = []
+            for group in allowed_groups:
+                #TODO: check that group are actually in roster
+                cursor.execute("""INSERT INTO node_groups_authorized (node_id, groupname)
+                                  VALUES (%s,%s)""" , (node_id, group))
 
 
     def deleteNode(self, nodeIdentifier):
@@ -534,7 +547,7 @@
                         access_model,
                         self.nodeIdentifier))
 
-        if access_model == const.VAL_ROSTER:
+        if access_model == const.VAL_AMODEL_ROSTER:
             item_id = cursor.fetchone()[0];
             if const.OPT_ROSTER_GROUPS_ALLOWED in item_config:
                 item_config.fields[const.OPT_ROSTER_GROUPS_ALLOWED].fieldType='list-multi' #XXX: needed to force list if there is only one value