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); |
