Software /
code /
prosody
File
util/pubsub.lua @ 5915:e6fed1d80116
Back out 1b0ac7950129, as SSLv3 appears to still be in moderate use on the network. Also, although obsolete, SSLv3 isn't documented to have any weaknesses that TLS 1.0 (the most common version used today) doesn't also have. Get your act together clients!
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Tue, 12 Nov 2013 02:13:01 +0000 |
parent | 5675:e29ece65e3b0 |
child | 5776:bd0ff8ae98a8 |
line wrap: on
line source
local events = require "util.events"; module("pubsub", package.seeall); local service = {}; local service_mt = { __index = service }; local default_config = { broadcaster = function () end; get_affiliation = function () end; capabilities = {}; }; function new(config) config = config or {}; return setmetatable({ config = setmetatable(config, { __index = default_config }); affiliations = {}; subscriptions = {}; nodes = {}; events = events.new(); }, service_mt); end function service:jids_equal(jid1, jid2) local normalize = self.config.normalize_jid; return normalize(jid1) == normalize(jid2); end 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"; -- Check if node allows/forbids it local node_capabilities = node_obj and node_obj.capabilities; 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 service_capabilities = self.config.capabilities; 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.affiliations[jid] = affiliation; local _, jid_sub = self:get_subscription(node, true, jid); if not jid_sub and not self:may(node, jid, "be_unsubscribed") then local ok, err = self:add_subscription(node, true, 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, true, 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 actor == true or jid == actor or self: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, true); if not ok then return ok, err; end node_obj = self.nodes[node]; end end node_obj.subscribers[jid] = options or true; local normal_jid = self.config.normalize_jid(jid); local subs = self.subscriptions[normal_jid]; if subs then if not subs[jid] then subs[jid] = { [node] = true }; else subs[jid][node] = true; end else self.subscriptions[normal_jid] = { [jid] = { [node] = true } }; end self.events.fire_event("subscription-added", { node = node, jid = jid, normalized_jid = normal_jid, options = options }); return true; end function service:remove_subscription(node, actor, jid) -- Access checking local cap; if actor == true or jid == actor or self: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"; end if not node_obj.subscribers[jid] then return false, "not-subscribed"; end node_obj.subscribers[jid] = nil; local normal_jid = self.config.normalize_jid(jid); local subs = self.subscriptions[normal_jid]; if subs then local jid_subs = subs[jid]; if jid_subs then jid_subs[node] = nil; if next(jid_subs) == nil then subs[jid] = nil; end end if next(subs) == nil then self.subscriptions[normal_jid] = nil; end end self.events.fire_event("subscription-removed", { node = node, jid = jid, normalized_jid = normal_jid }); return true; end function service:remove_all_subscriptions(actor, jid) local normal_jid = self.config.normalize_jid(jid); local subs = self.subscriptions[normal_jid] subs = subs and subs[jid]; if subs then for node in pairs(subs) do self:remove_subscription(node, true, jid); end end return true; end function service:get_subscription(node, actor, jid) -- Access checking local cap; if actor == true or jid == actor or self: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 not node_obj then return false, "item-not-found"; end return true, node_obj.subscribers[jid]; end function service:create(node, actor) -- Access checking if not self:may(node, actor, "create") then return false, "forbidden"; end -- 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:delete(node, actor) -- Access checking if not self:may(node, actor, "delete") then return false, "forbidden"; end -- local node_obj = self.nodes[node]; if not node_obj then return false, "item-not-found"; end self.nodes[node] = nil; self.config.broadcaster("delete", node, node_obj.subscribers); return true; 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 if not self.config.autocreate_on_publish then return false, "item-not-found"; end local ok, err = self:create(node, true); if not ok then return ok, err; end node_obj = self.nodes[node]; end node_obj.data[id] = item; self.events.fire_event("item-published", { node = node, actor = actor, id = id, item = item }); self.config.broadcaster("items", 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.config.broadcaster("items", node, node_obj.subscribers, retract); end return true end function service:purge(node, actor, notify) -- Access checking if not self:may(node, actor, "retract") then return false, "forbidden"; end -- local node_obj = self.nodes[node]; if not node_obj then return false, "item-not-found"; end node_obj.data = {}; -- Purge if notify then self.config.broadcaster("purge", node, node_obj.subscribers); end return true end 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 not node_obj then return false, "item-not-found"; end if id then -- Restrict results to a single specific item return true, { [id] = node_obj.data[id] }; else return true, node_obj.data; end end function service:get_nodes(actor) -- Access checking if not self:may(nil, actor, "get_nodes") then return false, "forbidden"; end -- return true, self.nodes; end function service:get_subscriptions(node, actor, jid) -- Access checking local cap; if actor == true or jid == actor or self:jids_equal(actor, jid) then cap = "get_subscriptions"; else cap = "get_subscriptions_other"; end if not self:may(node, actor, cap) then return false, "forbidden"; end -- local node_obj; if node then node_obj = self.nodes[node]; if not node_obj then return false, "item-not-found"; end end local normal_jid = self.config.normalize_jid(jid); local subs = self.subscriptions[normal_jid]; -- We return the subscription object from the node to save -- a get_subscription() call for each node. local ret = {}; if subs then for jid, subscribed_nodes in pairs(subs) do if node then -- Return only subscriptions to this node if subscribed_nodes[node] then ret[#ret+1] = { node = node; jid = jid; subscription = node_obj.subscribers[jid]; }; end else -- Return subscriptions to all nodes local nodes = self.nodes; for subscribed_node in pairs(subscribed_nodes) do ret[#ret+1] = { node = subscribed_node; jid = jid; subscription = nodes[subscribed_node].subscribers[jid]; }; end end end end return true, ret; end -- Access models only affect 'none' affiliation caps, service/default access level... function service:set_node_capabilities(node, actor, capabilities) -- Access checking if not self:may(node, actor, "configure") then return false, "forbidden"; end -- local node_obj = self.nodes[node]; if not node_obj then return false, "item-not-found"; end node_obj.capabilities = capabilities; return true; end return _M;