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 |