Software /
code /
prosody-modules
Diff
mod_compat_roles/mod_compat_roles.lua @ 4983:7c77058a1ac5
mod_compat_roles: New module providing compat shim for trunk's new role API
The new role API is translated to is_admin() calls on older versions. On newer
versions (which have the role API) this module does nothing.
It allows modules to drop their use of is_admin() (which is not available in
trunk) and switch to the new role API, while remaining compatible with
previous Prosody versions.
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Thu, 11 Aug 2022 17:49:33 +0100 |
child | 5097:d414fa8b37dc |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_compat_roles/mod_compat_roles.lua Thu Aug 11 17:49:33 2022 +0100 @@ -0,0 +1,105 @@ +-- Export a module:may() that works on Prosody 0.12 and earlier +-- (i.e. backed by is_admin). + +-- This API is safe because Prosody 0.12 and earlier do not support +-- per-session roles - all authorization is based on JID alone. It is not +-- safe on versions that support per-session authorization. + +module:set_global(); + +local moduleapi = require "core.moduleapi"; + +-- If module.may already exists, abort +if moduleapi.may then return; end + +local jid_split = require "util.jid".split; +local um_is_admin = require "core.usermanager".is_admin; + +local function get_jid_role_name(jid, host) + if um_is_admin(jid, "*") then + return "prosody:operator"; + elseif um_is_admin(jid, host) then + return "prosody:admin"; + end + return nil; +end + +local function get_user_role_name(username, host) + return get_jid_role_name(username.."@"..host, host); +end + +-- permissions[host][permission_name] = permitted_role_name +local permissions = {}; + +local function role_may(role_name, permission) + local role_permissions = permissions[role_name]; + if not role_permissions then + return false; + end + return not not permissions[role_name][permission]; +end + +function moduleapi.may(self, action, context) + if action:byte(1) == 58 then -- action begins with ':' + action = self.name..action; -- prepend module name + end + if type(context) == "string" then -- check JID permissions + local role; + local node, host = jid_split(context); + if host == self.host then + role = get_user_role_name(node, self.host); + else + role = get_jid_role_name(context, self.host); + end + if not role then + self:log("debug", "Access denied: JID <%s> may not %s (no role found)", context, action); + return false; + end + + local permit = role_may(role, action); + if not permit then + self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", context, action, role.name); + end + return permit; + end + + local session = context.origin or context.session; + if type(session) ~= "table" then + error("Unable to identify actor session from context"); + end + if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then + local actor_jid = context.stanza.attr.from; + local role_name = get_jid_role_name(actor_jid); + if not role_name then + self:log("debug", "Access denied: JID <%s> may not %s (no role found)", actor_jid, action); + return false; + end + local permit = role_may(role_name, action, context); + if not permit then + self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", actor_jid, action, role_name); + end + return permit; + end +end + +function moduleapi.default_permission(self, role_name, permission) + local r = permissions[self.host][role_name]; + if not r then + r = {}; + permissions[self.host][role_name] = r; + end + r[permission] = true; +end + +function moduleapi.default_permissions(self, role_name, permission_list) + for _, permission in ipairs(permission_list) do + self:default_permission(role_name, permission); + end +end + +function module.add_host(host_module) + permissions[host_module.host] = {}; + function host_module.unload() + permissions[host_module.host] = nil; + end +end