Mercurial > prosody-modules
comparison mod_privilege/mod_privilege.lua @ 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 |
comparison
equal
deleted
inserted
replaced
1659:495a093798eb | 1660:d1072db4db44 |
---|---|
1 -- XEP-0356 (Privileged Entity) | 1 -- XEP-0356 (Privileged Entity) |
2 -- Copyright (C) 2015 Jérôme Poisson | 2 -- Copyright (C) 2015 Jérôme Poisson |
3 -- | 3 -- |
4 -- This module is MIT/X11 licensed. Please see the | 4 -- This module is MIT/X11 licensed. Please see the |
5 -- COPYING file in the source package for more information. | 5 -- COPYING file in the source package for more information. |
6 -- | |
7 -- Some parts come from mod_remote_roster (module by Waqas Hussain and Kim Alvefur, see https://code.google.com/p/prosody-modules/) | |
6 | 8 |
7 | 9 |
8 local jid = require("util/jid") | 10 local jid = require("util/jid") |
9 local set = require("util/set") | 11 local set = require("util/set") |
10 local st = require("util/stanza") | 12 local st = require("util/stanza") |
11 local roster_manager = require("core/rostermanager") | 13 local roster_manager = require("core/rostermanager") |
14 local user_manager = require("core/usermanager") | |
12 | 15 |
13 local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'}) | 16 local _ALLOWED_ROSTER = set.new({'none', 'get', 'set', 'both'}) |
14 local _ROSTER_GET_PERM = set.new({'get', 'both'}) | 17 local _ROSTER_GET_PERM = set.new({'get', 'both'}) |
15 local _ROSTER_SET_PERM = set.new({'set', 'both'}) | 18 local _ROSTER_SET_PERM = set.new({'set', 'both'}) |
16 local _ALLOWED_MESSAGE = set.new({'none', 'outgoing'}) | 19 local _ALLOWED_MESSAGE = set.new({'none', 'outgoing'}) |
28 function advertise_perm(to_jid, perms) | 31 function advertise_perm(to_jid, perms) |
29 -- send <message/> stanza to advertise permissions | 32 -- send <message/> stanza to advertise permissions |
30 -- as expained in section 4.2 | 33 -- as expained in section 4.2 |
31 local message = st.message({to=to_jid}) | 34 local message = st.message({to=to_jid}) |
32 :tag("privilege", {xmlns=_PRIV_ENT_NS}) | 35 :tag("privilege", {xmlns=_PRIV_ENT_NS}) |
33 | 36 |
34 for _, perm in pairs({'roster', 'message', 'presence'}) do | 37 for _, perm in pairs({'roster', 'message', 'presence'}) do |
35 if perms[perm] then | 38 if perms[perm] then |
36 message:tag("perm", {access=perm, type=perms[perm]}):up() | 39 message:tag("perm", {access=perm, type=perms[perm]}):up() |
37 end | 40 end |
38 end | 41 end |
39 | 42 |
40 module:send(message) | 43 module:send(message) |
41 end | 44 end |
42 | 45 |
43 function on_auth(event) | 46 function on_auth(event) |
44 -- Check if entity is privileged according to configuration, | 47 -- Check if entity is privileged according to configuration, |
45 -- and set session.privileges accordingly | 48 -- and set session.privileges accordingly |
46 | 49 |
47 local session = event.session | 50 local session = event.session |
48 local bare_jid = jid.join(session.username, session.host) | 51 local bare_jid = jid.join(session.username, session.host) |
49 | 52 |
50 local ent_priv = privileges[bare_jid] | 53 local ent_priv = privileges[bare_jid] |
51 if ent_priv ~= nil then | 54 if ent_priv ~= nil then |
87 module:hook('presence/initial', on_presence) | 90 module:hook('presence/initial', on_presence) |
88 | 91 |
89 | 92 |
90 --> roster permission <-- | 93 --> roster permission <-- |
91 | 94 |
95 -- get | |
92 module:hook("iq-get/bare/jabber:iq:roster:query", function(event) | 96 module:hook("iq-get/bare/jabber:iq:roster:query", function(event) |
93 local session, stanza = event.origin, event.stanza; | 97 local session, stanza = event.origin, event.stanza; |
94 if not stanza.attr.to then | 98 if not stanza.attr.to then |
95 -- we don't want stanzas addressed to /self | 99 -- we don't want stanzas addressed to /self |
96 return; | 100 return; |
97 end | 101 end |
98 | 102 |
99 if session.privileges and _ROSTER_GET_PERM:contains(session.privileges.roster) then | 103 if session.privileges and _ROSTER_GET_PERM:contains(session.privileges.roster) then |
100 module:log("debug", "Roster get from allowed privileged entity received") | 104 module:log("debug", "Roster get from allowed privileged entity received") |
101 -- following code is adapted from mod_remote_roster | 105 -- following code is adapted from mod_remote_roster |
102 local node, host = jid.split(stanza.attr.to); | 106 local node, host = jid.split(stanza.attr.to); |
103 local roster = roster_manager.load_roster(node, host); | 107 local roster = roster_manager.load_roster(node, host); |
104 | 108 |
105 local reply = st.reply(stanza):query("jabber:iq:roster"); | 109 local reply = st.reply(stanza):query("jabber:iq:roster"); |
106 for entity_jid, item in pairs(roster) do | 110 for entity_jid, item in pairs(roster) do |
107 if entity_jid and entity_jid ~= "pending" then | 111 if entity_jid and entity_jid ~= "pending" then |
108 local node, host = jid.split(entity_jid); | 112 local node, host = jid.split(entity_jid); |
109 reply:tag("item", { | 113 reply:tag("item", { |
116 reply:tag("group"):text(group):up(); | 120 reply:tag("group"):text(group):up(); |
117 end | 121 end |
118 reply:up(); -- move out from item | 122 reply:up(); -- move out from item |
119 end | 123 end |
120 end | 124 end |
125 -- end of code adapted from mod_remote_roster | |
121 session.send(reply); | 126 session.send(reply); |
122 else | 127 else |
123 module:log("warn", "Entity "..tostring(session.full_jid).." try to get roster without permission") | 128 module:log("warn", "Entity "..tostring(session.full_jid).." try to get roster without permission") |
124 session.send(st.error_reply(stanza, 'auth', 'forbidden')) | 129 session.send(st.error_reply(stanza, 'auth', 'forbidden')) |
125 end | 130 end |
126 | 131 |
127 return true | 132 return true |
128 | |
129 end); | 133 end); |
134 | |
135 -- set | |
136 module:hook("iq-set/bare/jabber:iq:roster:query", function(event) | |
137 local session, stanza = event.origin, event.stanza; | |
138 if not stanza.attr.to then | |
139 -- we don't want stanzas addressed to /self | |
140 return; | |
141 end | |
142 | |
143 if session.privileges and _ROSTER_SET_PERM:contains(session.privileges.roster) then | |
144 module:log("debug", "Roster set from allowed privileged entity received") | |
145 -- following code is adapted from mod_remote_roster | |
146 local from_node, from_host = jid.split(stanza.attr.to); | |
147 if not(user_manager.user_exists(from_node, from_host)) then return; end | |
148 local roster = roster_manager.load_roster(from_node, from_host); | |
149 if not(roster) then return; end | |
150 | |
151 local query = stanza.tags[1]; | |
152 for i_, item in ipairs(query.tags) do | |
153 if item.name == "item" | |
154 and item.attr.xmlns == "jabber:iq:roster" and item.attr.jid | |
155 -- Protection against overwriting roster.pending, until we move it | |
156 and item.attr.jid ~= "pending" then | |
157 | |
158 local item_jid = jid.prep(item.attr.jid); | |
159 local node, host, resource = jid.split(item_jid); | |
160 if not resource then | |
161 if item_jid ~= stanza.attr.to then -- not self-item_jid | |
162 if item.attr.subscription == "remove" then | |
163 local r_item = roster[item_jid]; | |
164 if r_item then | |
165 local to_bare = node and (node.."@"..host) or host; -- bare jid | |
166 roster[item_jid] = nil; | |
167 if roster_manager.save_roster(from_node, from_host, roster) then | |
168 session.send(st.reply(stanza)); | |
169 roster_manager.roster_push(from_node, from_host, item_jid); | |
170 else | |
171 roster[item_jid] = item; | |
172 session.send(st.error_reply(stanza, "wait", "internal-server-error", "Unable to save roster")); | |
173 end | |
174 else | |
175 session.send(st.error_reply(stanza, "modify", "item-not-found")); | |
176 end | |
177 else | |
178 local subscription = item.attr.subscription; | |
179 if subscription ~= "both" and subscription ~= "to" and subscription ~= "from" and subscription ~= "none" then -- TODO error on invalid | |
180 subscription = roster[item_jid] and roster[item_jid].subscription or "none"; | |
181 end | |
182 local r_item = {name = item.attr.name, groups = {}}; | |
183 if r_item.name == "" then r_item.name = nil; end | |
184 r_item.subscription = subscription; | |
185 if subscription ~= "both" and subscription ~= "to" then | |
186 r_item.ask = roster[item_jid] and roster[item_jid].ask; | |
187 end | |
188 for _, child in ipairs(item) do | |
189 if child.name == "group" then | |
190 local text = table.concat(child); | |
191 if text and text ~= "" then | |
192 r_item.groups[text] = true; | |
193 end | |
194 end | |
195 end | |
196 local olditem = roster[item_jid]; | |
197 roster[item_jid] = r_item; | |
198 if roster_manager.save_roster(from_node, from_host, roster) then -- Ok, send success | |
199 session.send(st.reply(stanza)); | |
200 -- and push change to all resources | |
201 roster_manager.roster_push(from_node, from_host, item_jid); | |
202 else -- Adding to roster failed | |
203 roster[item_jid] = olditem; | |
204 session.send(st.error_reply(stanza, "wait", "internal-server-error", "Unable to save roster")); | |
205 end | |
206 end | |
207 else -- Trying to add self to roster | |
208 session.send(st.error_reply(stanza, "cancel", "not-allowed")); | |
209 end | |
210 else -- Invalid JID added to roster | |
211 module:log("warn", "resource: %s , host: %s", tostring(resource), tostring(host)) | |
212 session.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME what's the correct error? | |
213 end | |
214 else -- Roster set didn't include a single item, or its name wasn't 'item' | |
215 session.send(st.error_reply(stanza, "modify", "bad-request")); | |
216 end | |
217 end -- for loop end | |
218 -- end of code adapted from mod_remote_roster | |
219 else -- The permission is not granted | |
220 module:log("warn", "Entity "..tostring(session.full_jid).." try to set roster without permission") | |
221 session.send(st.error_reply(stanza, 'auth', 'forbidden')) | |
222 end | |
223 | |
224 return true | |
225 end); |