# HG changeset patch # User Goffi # Date 1428004025 -7200 # Node ID d1072db4db44f92b96d2b39de82d96d8e03b4352 # Parent 495a093798eb9220ef58cf2f846d413300a10e9e mod_privilege: implemented roster set privilege diff -r 495a093798eb -r d1072db4db44 mod_privilege/mod_privilege.lua --- a/mod_privilege/mod_privilege.lua Thu Apr 02 21:47:05 2015 +0200 +++ b/mod_privilege/mod_privilege.lua Thu Apr 02 21:47:05 2015 +0200 @@ -3,12 +3,15 @@ -- -- This module is MIT/X11 licensed. Please see the -- COPYING file in the source package for more information. +-- +-- Some parts come from mod_remote_roster (module by Waqas Hussain and Kim Alvefur, see https://code.google.com/p/prosody-modules/) local jid = require("util/jid") local set = require("util/set") local st = require("util/stanza") local roster_manager = require("core/rostermanager") +local user_manager = require("core/usermanager") local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'}) local _ROSTER_GET_PERM = set.new({'get', 'both'}) @@ -30,20 +33,20 @@ -- as expained in section 4.2 local message = st.message({to=to_jid}) :tag("privilege", {xmlns=_PRIV_ENT_NS}) - + for _, perm in pairs({'roster', 'message', 'presence'}) do if perms[perm] then message:tag("perm", {access=perm, type=perms[perm]}):up() end end - + module:send(message) end function on_auth(event) -- Check if entity is privileged according to configuration, -- and set session.privileges accordingly - + local session = event.session local bare_jid = jid.join(session.username, session.host) @@ -89,19 +92,20 @@ --> roster permission <-- +-- get module:hook("iq-get/bare/jabber:iq:roster:query", function(event) local session, stanza = event.origin, event.stanza; if not stanza.attr.to then -- we don't want stanzas addressed to /self return; end - + if session.privileges and _ROSTER_GET_PERM:contains(session.privileges.roster) then module:log("debug", "Roster get from allowed privileged entity received") -- following code is adapted from mod_remote_roster local node, host = jid.split(stanza.attr.to); local roster = roster_manager.load_roster(node, host); - + local reply = st.reply(stanza):query("jabber:iq:roster"); for entity_jid, item in pairs(roster) do if entity_jid and entity_jid ~= "pending" then @@ -118,12 +122,104 @@ reply:up(); -- move out from item end end + -- end of code adapted from mod_remote_roster session.send(reply); else module:log("warn", "Entity "..tostring(session.full_jid).." try to get roster without permission") session.send(st.error_reply(stanza, 'auth', 'forbidden')) end - + return true +end); +-- set +module:hook("iq-set/bare/jabber:iq:roster:query", function(event) + local session, stanza = event.origin, event.stanza; + if not stanza.attr.to then + -- we don't want stanzas addressed to /self + return; + end + + if session.privileges and _ROSTER_SET_PERM:contains(session.privileges.roster) then + module:log("debug", "Roster set from allowed privileged entity received") + -- following code is adapted from mod_remote_roster + local from_node, from_host = jid.split(stanza.attr.to); + if not(user_manager.user_exists(from_node, from_host)) then return; end + local roster = roster_manager.load_roster(from_node, from_host); + if not(roster) then return; end + + local query = stanza.tags[1]; + for i_, item in ipairs(query.tags) do + if item.name == "item" + and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid + -- Protection against overwriting roster.pending, until we move it + and item.attr.jid ~= "pending" then + + local item_jid = jid.prep(item.attr.jid); + local node, host, resource = jid.split(item_jid); + if not resource then + if item_jid ~= stanza.attr.to then -- not self-item_jid + if item.attr.subscription == "remove" then + local r_item = roster[item_jid]; + if r_item then + local to_bare = node and (node.."@"..host) or host; -- bare jid + roster[item_jid] = nil; + if roster_manager.save_roster(from_node, from_host, roster) then + session.send(st.reply(stanza)); + roster_manager.roster_push(from_node, from_host, item_jid); + else + roster[item_jid] = item; + session.send(st.error_reply(stanza, "wait", "internal-server-error", "Unable to save roster")); + end + else + session.send(st.error_reply(stanza, "modify", "item-not-found")); + end + else + local subscription = item.attr.subscription; + if subscription ~= "both" and subscription ~= "to" and subscription ~= "from" and subscription ~= "none" then -- TODO error on invalid + subscription = roster[item_jid] and roster[item_jid].subscription or "none"; + end + local r_item = {name = item.attr.name, groups = {}}; + if r_item.name == "" then r_item.name = nil; end + r_item.subscription = subscription; + if subscription ~= "both" and subscription ~= "to" then + r_item.ask = roster[item_jid] and roster[item_jid].ask; + end + for _, child in ipairs(item) do + if child.name == "group" then + local text = table.concat(child); + if text and text ~= "" then + r_item.groups[text] = true; + end + end + end + local olditem = roster[item_jid]; + roster[item_jid] = r_item; + if roster_manager.save_roster(from_node, from_host, roster) then -- Ok, send success + session.send(st.reply(stanza)); + -- and push change to all resources + roster_manager.roster_push(from_node, from_host, item_jid); + else -- Adding to roster failed + roster[item_jid] = olditem; + session.send(st.error_reply(stanza, "wait", "internal-server-error", "Unable to save roster")); + end + end + else -- Trying to add self to roster + session.send(st.error_reply(stanza, "cancel", "not-allowed")); + end + else -- Invalid JID added to roster + module:log("warn", "resource: %s , host: %s", tostring(resource), tostring(host)) + session.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME what's the correct error? + end + else -- Roster set didn't include a single item, or its name wasn't 'item' + session.send(st.error_reply(stanza, "modify", "bad-request")); + end + end -- for loop end + -- end of code adapted from mod_remote_roster + else -- The permission is not granted + module:log("warn", "Entity "..tostring(session.full_jid).." try to set roster without permission") + session.send(st.error_reply(stanza, 'auth', 'forbidden')) + end + + return true end);