changeset 1660:d1072db4db44

mod_privilege: implemented roster set privilege
author Goffi <goffi@goffi.org>
date Thu, 02 Apr 2015 21:47:05 +0200
parents 495a093798eb
children 69aa2b54ba8a
files mod_privilege/mod_privilege.lua
diffstat 1 files changed, 102 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- 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);