mod_privilege/mod_privilege.lua
changeset 1711 64b3d1eb0cfe
parent 1671 c81a981479d4
child 1712 ad7afcf86131
equal deleted inserted replaced
1674:5f5ff061b316 1711:64b3d1eb0cfe
    42 module:log("debug", "Loading privileged entity module ");
    42 module:log("debug", "Loading privileged entity module ");
    43 
    43 
    44 
    44 
    45 --> Permissions management <--
    45 --> Permissions management <--
    46 
    46 
    47 privileges = module:get_option("privileged_entities", {})
    47 local privileges = module:get_option("privileged_entities", {})
    48 
    48 
    49 function advertise_perm(session, to_jid, perms)
    49 local function advertise_perm(session, to_jid, perms)
    50 	-- send <message/> stanza to advertise permissions
    50 	-- send <message/> stanza to advertise permissions
    51 	-- as expained in § 4.2
    51 	-- as expained in § 4.2
    52 	local message = st.message({to=to_jid})
    52 	local message = st.message({to=to_jid})
    53 					  :tag("privilege", {xmlns=_PRIV_ENT_NS})
    53 					  :tag("privilege", {xmlns=_PRIV_ENT_NS})
    54 
    54 
    58 		end
    58 		end
    59 	end
    59 	end
    60 	session.send(message)
    60 	session.send(message)
    61 end
    61 end
    62 
    62 
    63 function set_presence_perm_set(to_jid, perms)
    63 local function set_presence_perm_set(to_jid, perms)
    64 	-- fill the global presence sets according to perms
    64 	-- fill the global presence sets according to perms
    65 	if _PRESENCE_MANAGED:contains(perms.presence) then
    65 	if _PRESENCE_MANAGED:contains(perms.presence) then
    66 		presence_man_ent:add(to_jid)
    66 		presence_man_ent:add(to_jid)
    67 	end
    67 	end
    68 	if perms.presence == 'roster' then
    68 	if perms.presence == 'roster' then
    69 		presence_roster:add(to_jid)
    69 		presence_roster:add(to_jid)
    70 	end
    70 	end
    71 end
    71 end
    72 
    72 
    73 function advertise_presences(session, to_jid, perms)
    73 local function advertise_presences(session, to_jid, perms)
    74 	-- send presence status for already conencted entities
    74 	-- send presence status for already conencted entities
    75 	-- as explained in § 7.1
    75 	-- as explained in § 7.1
    76 	-- people in roster are probed only for active sessions
    76 	-- people in roster are probed only for active sessions
    77 	-- TODO: manage roster load for inactive sessions
    77 	-- TODO: manage roster load for inactive sessions
    78 	if not perms.presence then return; end
    78 	if not perms.presence then return; end
    90 
    90 
    91 			if user_session.roster then
    91 			if user_session.roster then
    92 				local bare_jid = jid.bare(user_session.full_jid)
    92 				local bare_jid = jid.bare(user_session.full_jid)
    93 				for entity, item in pairs(user_session.roster) do
    93 				for entity, item in pairs(user_session.roster) do
    94 					if entity~=false and entity~="pending" and (item.subscription=="both" or item.subscription=="to") then
    94 					if entity~=false and entity~="pending" and (item.subscription=="both" or item.subscription=="to") then
    95 						_, host = jid.split(entity)
    95 						local _, host = jid.split(entity)
    96 						if not hosts[host] then -- we don't probe jid from hosts we manage
    96 						if not hosts[host] then -- we don't probe jid from hosts we manage
    97 							-- using a table with entity as key avoid probing several time the same one
    97 							-- using a table with entity as key avoid probing several time the same one
    98 							to_probe[entity] = bare_jid
    98 							to_probe[entity] = bare_jid
    99 						end
    99 						end
   100 					end
   100 					end
   102 			end
   102 			end
   103 		end
   103 		end
   104 	end
   104 	end
   105 
   105 
   106 	-- now we probe peoples for "roster" presence permission
   106 	-- now we probe peoples for "roster" presence permission
   107 	for to_jid, from_jid in pairs(to_probe) do
   107 	for probe_to, probe_from in pairs(to_probe) do
   108 		module:log("debug", "probing presence for %s (on behalf of %s)", tostring(to_jid), tostring(from_jid))
   108 		module:log("debug", "probing presence for %s (on behalf of %s)", tostring(probe_to), tostring(probe_from))
   109 		local probe = st.presence({from=from_jid, to=to_jid, type="probe"})
   109 		local probe = st.presence({from=probe_from, to=probe_to, type="probe"})
   110 		prosody.core_route_stanza(nil, probe)
   110 		prosody.core_route_stanza(nil, probe)
   111 	end
   111 	end
   112 end
   112 end
   113 
   113 
   114 function on_auth(event)
   114 local function on_auth(event)
   115 	-- Check if entity is privileged according to configuration,
   115 	-- Check if entity is privileged according to configuration,
   116 	-- and set session.privileges accordingly
   116 	-- and set session.privileges accordingly
   117 
   117 
   118 	local session = event.session
   118 	local session = event.session
   119 	local bare_jid = jid.join(session.username, session.host)
   119 	local bare_jid = jid.join(session.username, session.host)
   136 		end
   136 		end
   137 		-- extra checks for presence permission
   137 		-- extra checks for presence permission
   138 		if ent_priv.permission == 'roster' and not _ROSTER_GET_PERM:contains(session.privileges.roster) then
   138 		if ent_priv.permission == 'roster' and not _ROSTER_GET_PERM:contains(session.privileges.roster) then
   139 			module:log("warn", "Can't allow roster presence privilege without roster \"get\" privilege")
   139 			module:log("warn", "Can't allow roster presence privilege without roster \"get\" privilege")
   140 			module:log("warn", "Setting presence permission to none")
   140 			module:log("warn", "Setting presence permission to none")
   141 			end_priv.permission = nil
   141 			ent_priv.permission = nil
   142 		end
   142 		end
   143 
   143 
   144 		if session.type == "component" then
   144 		if session.type == "component" then
   145 			-- we send the message stanza only for component
   145 			-- we send the message stanza only for component
   146 			-- it will be sent at first <presence/> for other entities
   146 			-- it will be sent at first <presence/> for other entities
   151 	end
   151 	end
   152 
   152 
   153 	session.privileges = ent_priv
   153 	session.privileges = ent_priv
   154 end
   154 end
   155 
   155 
   156 function on_presence(event)
   156 local function on_presence(event)
   157 	-- Permission are already checked at this point,
   157 	-- Permission are already checked at this point,
   158 	-- we only advertise them to the entity
   158 	-- we only advertise them to the entity
   159 	local session, stanza = event.origin, event.stanza;
   159 	local session = event.origin
   160 	if session.privileges then
   160 	if session.privileges then
   161 		advertise_perm(session, session.full_jid, session.privileges)
   161 		advertise_perm(session, session.full_jid, session.privileges)
   162 		set_presence_perm_set(session.full_jid, session.privileges)
   162 		set_presence_perm_set(session.full_jid, session.privileges)
   163 		advertise_presences(session, session.full_jid, session.privileges)
   163 		advertise_presences(session, session.full_jid, session.privileges)
   164 	end
   164 	end
   186 		local roster = roster_manager.load_roster(node, host);
   186 		local roster = roster_manager.load_roster(node, host);
   187 
   187 
   188 		local reply = st.reply(stanza):query("jabber:iq:roster");
   188 		local reply = st.reply(stanza):query("jabber:iq:roster");
   189 		for entity_jid, item in pairs(roster) do
   189 		for entity_jid, item in pairs(roster) do
   190 			if entity_jid and entity_jid ~= "pending" then
   190 			if entity_jid and entity_jid ~= "pending" then
   191 				local node, host = jid.split(entity_jid);
   191 				reply:tag("item", {
   192 					reply:tag("item", {
   192 					jid = entity_jid,
   193 						jid = entity_jid,
   193 					subscription = item.subscription,
   194 						subscription = item.subscription,
   194 					ask = item.ask,
   195 						ask = item.ask,
   195 					name = item.name,
   196 						name = item.name,
   196 				});
   197 					});
   197 				for group in pairs(item.groups) do
   198 					for group in pairs(item.groups) do
   198 					reply:tag("group"):text(group):up();
   199 						reply:tag("group"):text(group):up();
   199 				end
   200 					end
   200 				reply:up(); -- move out from item
   201 					reply:up(); -- move out from item
       
   202 			end
   201 			end
   203 		end
   202 		end
   204 		-- end of code adapted from mod_remote_roster
   203 		-- end of code adapted from mod_remote_roster
   205 		session.send(reply);
   204 		session.send(reply);
   206 	else
   205 	else
   226 		if not(user_manager.user_exists(from_node, from_host)) then return; end
   225 		if not(user_manager.user_exists(from_node, from_host)) then return; end
   227 		local roster = roster_manager.load_roster(from_node, from_host);
   226 		local roster = roster_manager.load_roster(from_node, from_host);
   228 		if not(roster) then return; end
   227 		if not(roster) then return; end
   229 
   228 
   230 		local query = stanza.tags[1];
   229 		local query = stanza.tags[1];
   231 		for i_, item in ipairs(query.tags) do
   230 		for _, item in ipairs(query.tags) do
   232 			if item.name == "item"
   231 			if item.name == "item"
   233 				and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid
   232 				and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid
   234 					-- Protection against overwriting roster.pending, until we move it
   233 					-- Protection against overwriting roster.pending, until we move it
   235 				and item.attr.jid ~= "pending" then
   234 				and item.attr.jid ~= "pending" then
   236 
   235 
   237 				local item_jid = jid.prep(item.attr.jid);
   236 				local item_jid = jid.prep(item.attr.jid);
   238 				local node, host, resource = jid.split(item_jid);
   237 				local _, host, resource = jid.split(item_jid);
   239 				if not resource then
   238 				if not resource then
   240 					if item_jid ~= stanza.attr.to then -- not self-item_jid
   239 					if item_jid ~= stanza.attr.to then -- not self-item_jid
   241 						if item.attr.subscription == "remove" then
   240 						if item.attr.subscription == "remove" then
   242 							local r_item = roster[item_jid];
   241 							local r_item = roster[item_jid];
   243 							if r_item then
   242 							if r_item then
   244 								local to_bare = node and (node.."@"..host) or host; -- bare jid
       
   245 								roster[item_jid] = nil;
   243 								roster[item_jid] = nil;
   246 								if roster_manager.save_roster(from_node, from_host, roster) then
   244 								if roster_manager.save_roster(from_node, from_host, roster) then
   247 									session.send(st.reply(stanza));
   245 									session.send(st.reply(stanza));
   248 									roster_manager.roster_push(from_node, from_host, item_jid);
   246 									roster_manager.roster_push(from_node, from_host, item_jid);
   249 								else
   247 								else
   313 	if session.privileges and session.privileges.message=="outgoing" then
   311 	if session.privileges and session.privileges.message=="outgoing" then
   314 		if #privilege_elt.tags==1 and privilege_elt.tags[1].name == "forwarded"
   312 		if #privilege_elt.tags==1 and privilege_elt.tags[1].name == "forwarded"
   315 			and privilege_elt.tags[1].attr.xmlns==_FORWARDED_NS then
   313 			and privilege_elt.tags[1].attr.xmlns==_FORWARDED_NS then
   316 			local message_elt = privilege_elt.tags[1]:get_child('message', 'jabber:client')
   314 			local message_elt = privilege_elt.tags[1]:get_child('message', 'jabber:client')
   317 			if message_elt ~= nil then
   315 			if message_elt ~= nil then
   318 				local from_node, from_host, from_resource = jid.split(message_elt.attr.from)
   316 				local _, from_host, from_resource = jid.split(message_elt.attr.from)
   319 				if from_resource == nil and hosts[from_host] then -- we only accept bare jids from one of the server hosts
   317 				if from_resource == nil and hosts[from_host] then -- we only accept bare jids from one of the server hosts
   320 					-- at this point everything should be alright, we can send the message
   318 					-- at this point everything should be alright, we can send the message
   321 					prosody.core_route_stanza(nil, message_elt)
   319 					prosody.core_route_stanza(nil, message_elt)
   322 				else -- trying to send a message from a forbidden entity
   320 				else -- trying to send a message from a forbidden entity
   323 	    			module:log("warn", "Entity "..tostring(session.full_jid).." try to send a message from "..tostring(message_elt.attr.from))
   321 	    			module:log("warn", "Entity "..tostring(session.full_jid).." try to send a message from "..tostring(message_elt.attr.from))
   338 end);
   336 end);
   339 
   337 
   340 
   338 
   341 --> presence permission <--
   339 --> presence permission <--
   342 
   340 
   343 function same_tags(tag1, tag2)
   341 local function same_tags(tag1, tag2)
   344 	-- check if two tags are equivalent
   342 	-- check if two tags are equivalent
   345 
   343 
   346     if tag1.name ~= tag2.name then return false; end
   344     if tag1.name ~= tag2.name then return false; end
   347 
   345 
   348 	if #tag1 ~= #tag2 then return false; end
   346 	if #tag1 ~= #tag2 then return false; end
   360 	end
   358 	end
   361 
   359 
   362 	return true
   360 	return true
   363 end
   361 end
   364 
   362 
   365 function same_presences(presence1, presence2)
   363 local function same_presences(presence1, presence2)
   366 	-- check that 2 <presence/> stanzas are equivalent (except for "to" attribute)
   364 	-- check that 2 <presence/> stanzas are equivalent (except for "to" attribute)
   367 	-- /!\ if the id change but everything else is equivalent, this method return false
   365 	-- /!\ if the id change but everything else is equivalent, this method return false
   368 	-- this behaviour may change in the future
   366 	-- this behaviour may change in the future
   369 	if presence1.attr.from ~= presence2.attr.from or presence1.attr.id ~= presence2.attr.id
   367 	if presence1.attr.from ~= presence2.attr.from or presence1.attr.id ~= presence2.attr.id
   370 		or presence1.attr.type ~= presence2.attr.type then
   368 		or presence1.attr.type ~= presence2.attr.type then
   384 	end
   382 	end
   385 
   383 
   386 	return true
   384 	return true
   387 end
   385 end
   388 
   386 
   389 function forward_presence(presence, to_jid)
   387 local function forward_presence(presence, to_jid)
   390 	presence_fwd = st.clone(presence)
   388 	local presence_fwd = st.clone(presence)
   391 	presence_fwd.attr.to = to_jid
   389 	presence_fwd.attr.to = to_jid
   392 	module:log("debug", "presence forwarded to "..to_jid..": "..tostring(presence_fwd))
   390 	module:log("debug", "presence forwarded to "..to_jid..": "..tostring(presence_fwd))
   393 	module:send(presence_fwd)
   391 	module:send(presence_fwd)
   394 	-- cache used to avoid to send several times the same stanza
   392 	-- cache used to avoid to send several times the same stanza
   395 	priv_session.last_presence = presence
   393 	priv_session.last_presence = presence
   396 end
   394 end
   397 
   395 
   398 module:hook("presence/bare", function(event)
   396 module:hook("presence/bare", function(event)
   399 	if presence_man_ent:empty() and presence_roster:empty() then return; end
   397 	if presence_man_ent:empty() and presence_roster:empty() then return; end
   400 
   398 
   401 	local session, stanza = event.origin, event.stanza;
   399 	local stanza = event.stanza
   402 	if stanza.attr.type == nil or stanza.attr.type == "unavailable" then
   400 	if stanza.attr.type == nil or stanza.attr.type == "unavailable" then
   403 		if not stanza.attr.to then
   401 		if not stanza.attr.to then
   404 			for entity in presence_man_ent:items() do
   402 			for entity in presence_man_ent:items() do
   405 				if stanza.attr.from ~= entity then forward_presence(stanza, entity); end
   403 				if stanza.attr.from ~= entity then forward_presence(stanza, entity); end
   406 			end
   404 			end
   407 		else -- directed presence
   405 		else -- directed presence
   408 			-- we ignore directed presences from our own host, as we already have them
   406 			-- we ignore directed presences from our own host, as we already have them
   409 			_, from_host = jid.split(stanza.attr.from)
   407 			local _, from_host = jid.split(stanza.attr.from)
   410 			if hosts[from_host] then return; end
   408 			if hosts[from_host] then return; end
   411 
   409 
   412 			-- we don't send several time the same presence, as recommended in §7 #2
   410 			-- we don't send several time the same presence, as recommended in §7 #2
   413 			if priv_session.last_presence and same_presences(priv_session.last_presence, stanza) then
   411 			if priv_session.last_presence and same_presences(priv_session.last_presence, stanza) then
   414 			   return
   412 			   return