Mercurial > prosody-modules
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 |