util.pubsub: Too many changes to list or split sensibly. Added access control to...
authorMatthew Wild <mwild1@gmail.com>
Tue, 21 Dec 2010 03:26:06 +0000 (03:26 +0000)
committerMatthew Wild <mwild1@gmail.com>
Tue, 21 Dec 2010 03:26:06 +0000 (03:26 +0000)
util/pubsub.lua

index f667295e36599a919ca79d6b9648b18f9b89e9c7..b12e52460e08ef03ab51e282903a4f70ec23e98d 100644 (file)
@@ -18,16 +18,112 @@ function new(config)
        }, service_mt);
 end
 
-function service:add_subscription(node, actor, jid)
+function service:may(node, actor, action)
+       if actor == true then return true; end
+       
+       
+       local node_obj = self.nodes[node];
+       local node_aff = node_obj and node_obj.affiliations[actor];
+       local service_aff = self.affiliations[actor]
+                        or self.config.get_affiliation(actor, node, action)
+                        or "none";
+       
+       local node_capabilities = node_obj and node_obj.capabilities;
+       local service_capabilities = self.config.capabilities;
+       
+       -- Check if node allows/forbids it      
+       if node_capabilities then
+               local caps = node_capabilities[node_aff or service_aff];
+               if caps then
+                       local can = caps[action];
+                       if can ~= nil then
+                               return can;
+                       end
+               end
+       end
+       -- Check service-wide capabilities instead
+       local caps = service_capabilities[node_aff or service_aff];
+       if caps then
+               local can = caps[action];
+               if can ~= nil then
+                       return can;
+               end
+       end
+       
+       return false;
+end
+
+function service:set_affiliation(node, actor, jid, affiliation)
+       -- Access checking
+       if not self:may(node, actor, "set_affiliation") then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
        if not node_obj then
                return false, "item-not-found";
        end
-       node_obj.subscribers[jid] = true;
+       node_obj.affiliations[jid] = affiliation;
+       local _, jid_sub = self:get_subscription(node, nil, jid);
+       if not jid_sub and not self:may(node, jid, "be_unsubscribed") then
+               local ok, err = self:add_subscription(node, nil, jid);
+               if not ok then
+                       return ok, err;
+               end
+       elseif jid_sub and not self:may(node, jid, "be_subscribed") then
+               local ok, err = self:add_subscription(node, nil, jid);
+               if not ok then
+                       return ok, err;
+               end
+       end
+       return true;
+end
+
+function service:add_subscription(node, actor, jid, options)
+       -- Access checking
+       local cap;
+       if jid == actor or self.config.jids_equal(actor, jid) then
+               cap = "subscribe";
+       else
+               cap = "subscribe_other";
+       end
+       if not self:may(node, actor, cap) then
+               return false, "forbidden";
+       end
+       if not self:may(node, jid, "be_subscribed") then
+               return false, "forbidden";
+       end
+       --
+       local node_obj = self.nodes[node];
+       if not node_obj then
+               if not self.config.autocreate_on_subscribe then
+                       return false, "item-not-found";
+               else
+                       local ok, err = self:create(node, actor);
+                       if not ok then
+                               return ok, err;
+                       end
+               end
+       end
+       node_obj.subscribers[jid] = options or true;
        return true;
 end
 
 function service:remove_subscription(node, actor, jid)
+       -- Access checking
+       local cap;
+       if jid == actor or self.config.jids_equal(actor, jid) then
+               cap = "unsubscribe";
+       else
+               cap = "unsubscribe_other";
+       end
+       if not self:may(node, actor, cap) then
+               return false, "forbidden";
+       end
+       if not self:may(node, jid, "be_unsubscribed") then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
        if not node_obj then
                return false, "item-not-found";
@@ -40,55 +136,109 @@ function service:remove_subscription(node, actor, jid)
 end
 
 function service:get_subscription(node, actor, jid)
+       -- Access checking
+       local cap;
+       if jid == actor or self.config.jids_equal(actor, jid) then
+               cap = "get_subscription";
+       else
+               cap = "get_subscription_other";
+       end
+       if not self:may(node, actor, cap) then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
        if node_obj then
-               return node_obj.subscribers[jid];
+               return true, node_obj.subscribers[jid];
        end
 end
 
 function service:create(node, actor)
-       if not self.nodes[node] then
-               self.nodes[node] = { name = node, subscribers = {}, config = {}, data = {} };
-               return true;
+       -- Access checking
+       if not self:may(node, actor, "create") then
+               return false, "forbidden";
        end
-       return false, "conflict";
+       --
+       if self.nodes[node] then
+               return false, "conflict";
+       end
+       
+       self.nodes[node] = {
+               name = node;
+               subscribers = {};
+               config = {};
+               data = {};
+               affiliations = {};
+       };
+       local ok, err = self:set_affiliation(node, true, actor, "owner");
+       if not ok then
+               self.nodes[node] = nil;
+       end
+       return ok, err;
 end
 
 function service:publish(node, actor, id, item)
+       -- Access checking
+       if not self:may(node, actor, "publish") then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
        if not node_obj then
-               node_obj = { name = node, subscribers = {}, config = {}, data = {} };
-               self.nodes[node] = node_obj;
+               if not self.config.autocreate_on_publish then
+                       return false, "item-not-found";
+               end
+               local ok, err = self:create(node, actor);
+               if not ok then
+                       return ok, err;
+               end
+               node_obj = self.nodes[node];
        end
        node_obj.data[id] = item;
-       self.cb.broadcaster(node, node_obj.subscribers, item);
+       self.config.broadcaster(node, node_obj.subscribers, item);
        return true;
 end
 
 function service:retract(node, actor, id, retract)
+       -- Access checking
+       if not self:may(node, actor, "retract") then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
        if (not node_obj) or (not node_obj.data[id]) then
                return false, "item-not-found";
        end
        node_obj.data[id] = nil;
        if retract then
-               self.cb.broadcaster(node, node_obj.subscribers, retract);
+               self.config.broadcaster(node, node_obj.subscribers, retract);
        end
        return true
 end
 
-function service:get(node, actor, id)
+function service:get_items(node, actor, id)
+       -- Access checking
+       if not self:may(node, actor, "get_items") then
+               return false, "forbidden";
+       end
+       --
        local node_obj = self.nodes[node];
-       if node_obj then
-               if id then
-                       return { node_obj.data[id] };
-               else
-                       return node_obj.data;
-               end
+       if not node_obj then
+               return false, "item-not-found";
+       end
+       if id then -- Restrict results to a single specific item
+               return true, { node_obj.data[id] };
+       else
+               return true, node_obj.data;
        end
 end
 
 function service:get_nodes(actor)
+       -- Access checking
+       if not self:may(node, actor, "get_nodes") then
+               return false, "forbidden";
+       end
+       --
        return true, self.nodes;
 end