comparison mod_privilege/mod_privilege.lua @ 1725:d85d5b0bf977

Merge with Goffi
author Kim Alvefur <zash@zash.se>
date Thu, 07 May 2015 23:39:54 +0200
parents ad7afcf86131
children 0d78bb31348e
comparison
equal deleted inserted replaced
1706:e4867211cddb 1725:d85d5b0bf977
16 local hosts = prosody.hosts 16 local hosts = prosody.hosts
17 local full_sessions = prosody.full_sessions; 17 local full_sessions = prosody.full_sessions;
18 18
19 local priv_session = module:shared("/*/privilege/session") 19 local priv_session = module:shared("/*/privilege/session")
20 20
21 if priv_session.connected_cb == nil then
22 -- set used to have connected event listeners
23 -- which allows a host to react on events from
24 -- other hosts
25 priv_session.connected_cb = set.new()
26 end
27 local connected_cb = priv_session.connected_cb
28
21 -- the folowing sets are used to forward presence stanza 29 -- the folowing sets are used to forward presence stanza
22 if not priv_session.presence_man_ent then 30 -- the folowing sets are used to forward presence stanza
23 priv_session.presence_man_ent = set.new() 31 local presence_man_ent = set.new()
24 end 32 local presence_roster = set.new()
25 local presence_man_ent = priv_session.presence_man_ent
26 if not priv_session.presence_roster then
27 priv_session.presence_roster = set.new()
28 end
29 local presence_roster = priv_session.presence_roster
30 33
31 local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'}) 34 local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'})
32 local _ROSTER_GET_PERM = set.new({'get', 'both'}) 35 local _ROSTER_GET_PERM = set.new({'get', 'both'})
33 local _ROSTER_SET_PERM = set.new({'set', 'both'}) 36 local _ROSTER_SET_PERM = set.new({'set', 'both'})
34 local _ALLOWED_MESSAGE = set.new({'none', 'outgoing'}) 37 local _ALLOWED_MESSAGE = set.new({'none', 'outgoing'})
42 module:log("debug", "Loading privileged entity module "); 45 module:log("debug", "Loading privileged entity module ");
43 46
44 47
45 --> Permissions management <-- 48 --> Permissions management <--
46 49
47 privileges = module:get_option("privileged_entities", {}) 50 local privileges = module:get_option("privileged_entities", {})
48 51
49 function advertise_perm(session, to_jid, perms) 52 local function advertise_perm(session, to_jid, perms)
50 -- send <message/> stanza to advertise permissions 53 -- send <message/> stanza to advertise permissions
51 -- as expained in § 4.2 54 -- as expained in § 4.2
52 local message = st.message({to=to_jid}) 55 local message = st.message({from=module.host, to=to_jid})
53 :tag("privilege", {xmlns=_PRIV_ENT_NS}) 56 :tag("privilege", {xmlns=_PRIV_ENT_NS})
54 57
55 for _, perm in pairs({'roster', 'message', 'presence'}) do 58 for _, perm in pairs({'roster', 'message', 'presence'}) do
56 if perms[perm] then 59 if perms[perm] then
57 message:tag("perm", {access=perm, type=perms[perm]}):up() 60 message:tag("perm", {access=perm, type=perms[perm]}):up()
58 end 61 end
59 end 62 end
60 session.send(message) 63 session.send(message)
61 end 64 end
62 65
63 function set_presence_perm_set(to_jid, perms) 66 local function set_presence_perm_set(to_jid, perms)
64 -- fill the global presence sets according to perms 67 -- fill the presence sets according to perms
65 if _PRESENCE_MANAGED:contains(perms.presence) then 68 if _PRESENCE_MANAGED:contains(perms.presence) then
66 presence_man_ent:add(to_jid) 69 presence_man_ent:add(to_jid)
67 end 70 end
68 if perms.presence == 'roster' then 71 if perms.presence == 'roster' then
69 presence_roster:add(to_jid) 72 presence_roster:add(to_jid)
70 end 73 end
71 end 74 end
72 75
73 function advertise_presences(session, to_jid, perms) 76 local function advertise_presences(session, to_jid, perms)
74 -- send presence status for already conencted entities 77 -- send presence status for already conencted entities
75 -- as explained in § 7.1 78 -- as explained in § 7.1
76 -- people in roster are probed only for active sessions 79 -- people in roster are probed only for active sessions
77 -- TODO: manage roster load for inactive sessions 80 -- TODO: manage roster load for inactive sessions
78 if not perms.presence then return; end 81 if not perms.presence then return; end
90 93
91 if user_session.roster then 94 if user_session.roster then
92 local bare_jid = jid.bare(user_session.full_jid) 95 local bare_jid = jid.bare(user_session.full_jid)
93 for entity, item in pairs(user_session.roster) do 96 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 97 if entity~=false and entity~="pending" and (item.subscription=="both" or item.subscription=="to") then
95 _, host = jid.split(entity) 98 local _, host = jid.split(entity)
96 if not hosts[host] then -- we don't probe jid from hosts we manage 99 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 100 -- using a table with entity as key avoid probing several time the same one
98 to_probe[entity] = bare_jid 101 to_probe[entity] = bare_jid
99 end 102 end
100 end 103 end
102 end 105 end
103 end 106 end
104 end 107 end
105 108
106 -- now we probe peoples for "roster" presence permission 109 -- now we probe peoples for "roster" presence permission
107 for to_jid, from_jid in pairs(to_probe) do 110 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)) 111 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"}) 112 local probe = st.presence({from=probe_from, to=probe_to, type="probe"})
110 prosody.core_route_stanza(nil, probe) 113 prosody.core_route_stanza(nil, probe)
111 end 114 end
112 end 115 end
113 116
114 function on_auth(event) 117 local function on_auth(event)
115 -- Check if entity is privileged according to configuration, 118 -- Check if entity is privileged according to configuration,
116 -- and set session.privileges accordingly 119 -- and set session.privileges accordingly
117 120
118 local session = event.session 121 local session = event.session
119 local bare_jid = jid.join(session.username, session.host) 122 local bare_jid = jid.join(session.username, session.host)
136 end 139 end
137 -- extra checks for presence permission 140 -- extra checks for presence permission
138 if ent_priv.permission == 'roster' and not _ROSTER_GET_PERM:contains(session.privileges.roster) then 141 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") 142 module:log("warn", "Can't allow roster presence privilege without roster \"get\" privilege")
140 module:log("warn", "Setting presence permission to none") 143 module:log("warn", "Setting presence permission to none")
141 end_priv.permission = nil 144 ent_priv.permission = nil
142 end 145 end
143 146
144 if session.type == "component" then 147 if session.type == "component" then
145 -- we send the message stanza only for component 148 -- we send the message stanza only for component
146 -- it will be sent at first <presence/> for other entities 149 -- it will be sent at first <presence/> for other entities
151 end 154 end
152 155
153 session.privileges = ent_priv 156 session.privileges = ent_priv
154 end 157 end
155 158
156 function on_presence(event) 159 local function on_presence(event)
157 -- Permission are already checked at this point, 160 -- Permission are already checked at this point,
158 -- we only advertise them to the entity 161 -- we only advertise them to the entity
159 local session, stanza = event.origin, event.stanza; 162 local session = event.origin
160 if session.privileges then 163 if session.privileges then
161 advertise_perm(session, session.full_jid, session.privileges) 164 advertise_perm(session, session.full_jid, session.privileges)
162 set_presence_perm_set(session.full_jid, session.privileges) 165 set_presence_perm_set(session.full_jid, session.privileges)
163 advertise_presences(session, session.full_jid, session.privileges) 166 advertise_presences(session, session.full_jid, session.privileges)
164 end 167 end
165 end 168 end
166 169
170 local function on_component_auth(event)
171 -- react to component-authenticated event from this host
172 -- and call the on_auth methods from all other hosts
173 -- needed for the component to get delegations advertising
174 for callback in connected_cb:items() do
175 callback(event)
176 end
177 end
178
179 connected_cb:add(on_auth)
167 module:hook('authentication-success', on_auth) 180 module:hook('authentication-success', on_auth)
168 module:hook('component-authenticated', on_auth) 181 module:hook('component-authenticated', on_component_auth)
169 module:hook('presence/initial', on_presence) 182 module:hook('presence/initial', on_presence)
170 183
171 184
172 --> roster permission <-- 185 --> roster permission <--
173 186
186 local roster = roster_manager.load_roster(node, host); 199 local roster = roster_manager.load_roster(node, host);
187 200
188 local reply = st.reply(stanza):query("jabber:iq:roster"); 201 local reply = st.reply(stanza):query("jabber:iq:roster");
189 for entity_jid, item in pairs(roster) do 202 for entity_jid, item in pairs(roster) do
190 if entity_jid and entity_jid ~= "pending" then 203 if entity_jid and entity_jid ~= "pending" then
191 local node, host = jid.split(entity_jid); 204 reply:tag("item", {
192 reply:tag("item", { 205 jid = entity_jid,
193 jid = entity_jid, 206 subscription = item.subscription,
194 subscription = item.subscription, 207 ask = item.ask,
195 ask = item.ask, 208 name = item.name,
196 name = item.name, 209 });
197 }); 210 for group in pairs(item.groups) do
198 for group in pairs(item.groups) do 211 reply:tag("group"):text(group):up();
199 reply:tag("group"):text(group):up(); 212 end
200 end 213 reply:up(); -- move out from item
201 reply:up(); -- move out from item
202 end 214 end
203 end 215 end
204 -- end of code adapted from mod_remote_roster 216 -- end of code adapted from mod_remote_roster
205 session.send(reply); 217 session.send(reply);
206 else 218 else
226 if not(user_manager.user_exists(from_node, from_host)) then return; end 238 if not(user_manager.user_exists(from_node, from_host)) then return; end
227 local roster = roster_manager.load_roster(from_node, from_host); 239 local roster = roster_manager.load_roster(from_node, from_host);
228 if not(roster) then return; end 240 if not(roster) then return; end
229 241
230 local query = stanza.tags[1]; 242 local query = stanza.tags[1];
231 for i_, item in ipairs(query.tags) do 243 for _, item in ipairs(query.tags) do
232 if item.name == "item" 244 if item.name == "item"
233 and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid 245 and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid
234 -- Protection against overwriting roster.pending, until we move it 246 -- Protection against overwriting roster.pending, until we move it
235 and item.attr.jid ~= "pending" then 247 and item.attr.jid ~= "pending" then
236 248
237 local item_jid = jid.prep(item.attr.jid); 249 local item_jid = jid.prep(item.attr.jid);
238 local node, host, resource = jid.split(item_jid); 250 local _, host, resource = jid.split(item_jid);
239 if not resource then 251 if not resource then
240 if item_jid ~= stanza.attr.to then -- not self-item_jid 252 if item_jid ~= stanza.attr.to then -- not self-item_jid
241 if item.attr.subscription == "remove" then 253 if item.attr.subscription == "remove" then
242 local r_item = roster[item_jid]; 254 local r_item = roster[item_jid];
243 if r_item then 255 if r_item then
244 local to_bare = node and (node.."@"..host) or host; -- bare jid
245 roster[item_jid] = nil; 256 roster[item_jid] = nil;
246 if roster_manager.save_roster(from_node, from_host, roster) then 257 if roster_manager.save_roster(from_node, from_host, roster) then
247 session.send(st.reply(stanza)); 258 session.send(st.reply(stanza));
248 roster_manager.roster_push(from_node, from_host, item_jid); 259 roster_manager.roster_push(from_node, from_host, item_jid);
249 else 260 else
313 if session.privileges and session.privileges.message=="outgoing" then 324 if session.privileges and session.privileges.message=="outgoing" then
314 if #privilege_elt.tags==1 and privilege_elt.tags[1].name == "forwarded" 325 if #privilege_elt.tags==1 and privilege_elt.tags[1].name == "forwarded"
315 and privilege_elt.tags[1].attr.xmlns==_FORWARDED_NS then 326 and privilege_elt.tags[1].attr.xmlns==_FORWARDED_NS then
316 local message_elt = privilege_elt.tags[1]:get_child('message', 'jabber:client') 327 local message_elt = privilege_elt.tags[1]:get_child('message', 'jabber:client')
317 if message_elt ~= nil then 328 if message_elt ~= nil then
318 local from_node, from_host, from_resource = jid.split(message_elt.attr.from) 329 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 330 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 331 -- at this point everything should be alright, we can send the message
321 prosody.core_route_stanza(nil, message_elt) 332 prosody.core_route_stanza(nil, message_elt)
322 else -- trying to send a message from a forbidden entity 333 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)) 334 module:log("warn", "Entity "..tostring(session.full_jid).." try to send a message from "..tostring(message_elt.attr.from))
338 end); 349 end);
339 350
340 351
341 --> presence permission <-- 352 --> presence permission <--
342 353
343 function same_tags(tag1, tag2) 354 local function same_tags(tag1, tag2)
344 -- check if two tags are equivalent 355 -- check if two tags are equivalent
345 356
346 if tag1.name ~= tag2.name then return false; end 357 if tag1.name ~= tag2.name then return false; end
347 358
348 if #tag1 ~= #tag2 then return false; end 359 if #tag1 ~= #tag2 then return false; end
360 end 371 end
361 372
362 return true 373 return true
363 end 374 end
364 375
365 function same_presences(presence1, presence2) 376 local function same_presences(presence1, presence2)
366 -- check that 2 <presence/> stanzas are equivalent (except for "to" attribute) 377 -- 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 378 -- /!\ if the id change but everything else is equivalent, this method return false
368 -- this behaviour may change in the future 379 -- this behaviour may change in the future
369 if presence1.attr.from ~= presence2.attr.from or presence1.attr.id ~= presence2.attr.id 380 if presence1.attr.from ~= presence2.attr.from or presence1.attr.id ~= presence2.attr.id
370 or presence1.attr.type ~= presence2.attr.type then 381 or presence1.attr.type ~= presence2.attr.type then
384 end 395 end
385 396
386 return true 397 return true
387 end 398 end
388 399
389 function forward_presence(presence, to_jid) 400 local function forward_presence(presence, to_jid)
390 presence_fwd = st.clone(presence) 401 local presence_fwd = st.clone(presence)
391 presence_fwd.attr.to = to_jid 402 presence_fwd.attr.to = to_jid
392 module:log("debug", "presence forwarded to "..to_jid..": "..tostring(presence_fwd)) 403 module:log("debug", "presence forwarded to "..to_jid..": "..tostring(presence_fwd))
393 module:send(presence_fwd) 404 module:send(presence_fwd)
394 -- cache used to avoid to send several times the same stanza 405 -- cache used to avoid to send several times the same stanza
395 priv_session.last_presence = presence 406 priv_session.last_presence = presence
396 end 407 end
397 408
398 module:hook("presence/bare", function(event) 409 module:hook("presence/bare", function(event)
399 if presence_man_ent:empty() and presence_roster:empty() then return; end 410 if presence_man_ent:empty() and presence_roster:empty() then return; end
400 411
401 local session, stanza = event.origin, event.stanza; 412 local stanza = event.stanza
402 if stanza.attr.type == nil or stanza.attr.type == "unavailable" then 413 if stanza.attr.type == nil or stanza.attr.type == "unavailable" then
403 if not stanza.attr.to then 414 if not stanza.attr.to then
404 for entity in presence_man_ent:items() do 415 for entity in presence_man_ent:items() do
405 if stanza.attr.from ~= entity then forward_presence(stanza, entity); end 416 if stanza.attr.from ~= entity then forward_presence(stanza, entity); end
406 end 417 end
407 else -- directed presence 418 else -- directed presence
408 -- we ignore directed presences from our own host, as we already have them 419 -- we ignore directed presences from our own host, as we already have them
409 _, from_host = jid.split(stanza.attr.from) 420 local _, from_host = jid.split(stanza.attr.from)
410 if hosts[from_host] then return; end 421 if hosts[from_host] then return; end
411 422
412 -- we don't send several time the same presence, as recommended in §7 #2 423 -- 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 424 if priv_session.last_presence and same_presences(priv_session.last_presence, stanza) then
414 return 425 return