Comparison

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
comparison
equal deleted inserted replaced
4982:8a4b17e2e984 4983:7c77058a1ac5
1 -- Export a module:may() that works on Prosody 0.12 and earlier
2 -- (i.e. backed by is_admin).
3
4 -- This API is safe because Prosody 0.12 and earlier do not support
5 -- per-session roles - all authorization is based on JID alone. It is not
6 -- safe on versions that support per-session authorization.
7
8 module:set_global();
9
10 local moduleapi = require "core.moduleapi";
11
12 -- If module.may already exists, abort
13 if moduleapi.may then return; end
14
15 local jid_split = require "util.jid".split;
16 local um_is_admin = require "core.usermanager".is_admin;
17
18 local function get_jid_role_name(jid, host)
19 if um_is_admin(jid, "*") then
20 return "prosody:operator";
21 elseif um_is_admin(jid, host) then
22 return "prosody:admin";
23 end
24 return nil;
25 end
26
27 local function get_user_role_name(username, host)
28 return get_jid_role_name(username.."@"..host, host);
29 end
30
31 -- permissions[host][permission_name] = permitted_role_name
32 local permissions = {};
33
34 local function role_may(role_name, permission)
35 local role_permissions = permissions[role_name];
36 if not role_permissions then
37 return false;
38 end
39 return not not permissions[role_name][permission];
40 end
41
42 function moduleapi.may(self, action, context)
43 if action:byte(1) == 58 then -- action begins with ':'
44 action = self.name..action; -- prepend module name
45 end
46 if type(context) == "string" then -- check JID permissions
47 local role;
48 local node, host = jid_split(context);
49 if host == self.host then
50 role = get_user_role_name(node, self.host);
51 else
52 role = get_jid_role_name(context, self.host);
53 end
54 if not role then
55 self:log("debug", "Access denied: JID <%s> may not %s (no role found)", context, action);
56 return false;
57 end
58
59 local permit = role_may(role, action);
60 if not permit then
61 self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", context, action, role.name);
62 end
63 return permit;
64 end
65
66 local session = context.origin or context.session;
67 if type(session) ~= "table" then
68 error("Unable to identify actor session from context");
69 end
70 if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then
71 local actor_jid = context.stanza.attr.from;
72 local role_name = get_jid_role_name(actor_jid);
73 if not role_name then
74 self:log("debug", "Access denied: JID <%s> may not %s (no role found)", actor_jid, action);
75 return false;
76 end
77 local permit = role_may(role_name, action, context);
78 if not permit then
79 self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", actor_jid, action, role_name);
80 end
81 return permit;
82 end
83 end
84
85 function moduleapi.default_permission(self, role_name, permission)
86 local r = permissions[self.host][role_name];
87 if not r then
88 r = {};
89 permissions[self.host][role_name] = r;
90 end
91 r[permission] = true;
92 end
93
94 function moduleapi.default_permissions(self, role_name, permission_list)
95 for _, permission in ipairs(permission_list) do
96 self:default_permission(role_name, permission);
97 end
98 end
99
100 function module.add_host(host_module)
101 permissions[host_module.host] = {};
102 function host_module.unload()
103 permissions[host_module.host] = nil;
104 end
105 end