Software /
code /
prosody
Comparison
plugins/mod_authz_internal.lua @ 12648:f299e570a0fe
mod_authz_internal: Use util.roles, some API changes and config support
This commit was too awkward to split (hg record didn't like it), so:
- Switch to the new util.roles lib to provide a consistent representation of
a role object.
- Change API method from get_role_info() to get_role_by_name() (touches
sessionmanager and usermanager)
- Change get_roles() to get_user_roles(), take a username instead of a JID
This is more consistent with all other usermanager API methods.
- Support configuration of custom roles and permissions via the config file
(to be documented).
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Tue, 19 Jul 2022 18:02:02 +0100 |
parent | 12642:9061f9621330 |
child | 12662:07424992d7fc |
comparison
equal
deleted
inserted
replaced
12647:a661292d074a | 12648:f299e570a0fe |
---|---|
1 local array = require "util.array"; | 1 local array = require "util.array"; |
2 local it = require "util.iterators"; | 2 local it = require "util.iterators"; |
3 local set = require "util.set"; | 3 local set = require "util.set"; |
4 local jid_split, jid_bare = require "util.jid".split, require "util.jid".bare; | 4 local jid_split, jid_bare = require "util.jid".split, require "util.jid".bare; |
5 local normalize = require "util.jid".prep; | 5 local normalize = require "util.jid".prep; |
6 local roles = require "util.roles"; | |
7 | |
6 local config_global_admin_jids = module:context("*"):get_option_set("admins", {}) / normalize; | 8 local config_global_admin_jids = module:context("*"):get_option_set("admins", {}) / normalize; |
7 local config_admin_jids = module:get_option_inherited_set("admins", {}) / normalize; | 9 local config_admin_jids = module:get_option_inherited_set("admins", {}) / normalize; |
8 local host = module.host; | 10 local host = module.host; |
9 local role_store = module:open_store("roles"); | 11 local role_store = module:open_store("roles"); |
10 local role_map_store = module:open_store("roles", "map"); | 12 local role_map_store = module:open_store("roles", "map"); |
11 | 13 |
12 local role_methods = {}; | 14 local role_registry = {}; |
13 local role_mt = { __index = role_methods }; | 15 |
14 | 16 function register_role(role) |
15 local role_registry = { | 17 if role_registry[role.name] ~= nil then |
16 ["prosody:operator"] = { | 18 return error("A role '"..role.name.."' is already registered"); |
17 default = true; | 19 end |
18 priority = 75; | 20 if not roles.is_role(role) then |
19 includes = { "prosody:admin" }; | 21 -- Convert table syntax to real role object |
20 }; | 22 for i, inherited_role in ipairs(role.inherits or {}) do |
21 ["prosody:admin"] = { | 23 if type(inherited_role) == "string" then |
22 default = true; | 24 role.inherits[i] = assert(role_registry[inherited_role], "The named role '"..inherited_role.."' is not registered"); |
23 priority = 50; | 25 end |
24 includes = { "prosody:user" }; | 26 end |
25 }; | 27 if not role.permissions then role.permissions = {}; end |
26 ["prosody:user"] = { | 28 for _, allow_permission in ipairs(role.allow or {}) do |
27 default = true; | 29 role.permissions[allow_permission] = true; |
28 priority = 25; | 30 end |
29 includes = { "prosody:restricted" }; | 31 for _, deny_permission in ipairs(role.deny or {}) do |
30 }; | 32 role.permissions[deny_permission] = false; |
31 ["prosody:restricted"] = { | 33 end |
32 default = true; | 34 role = roles.new(role); |
33 priority = 15; | 35 end |
34 }; | 36 role_registry[role.name] = role; |
35 }; | 37 end |
36 | 38 |
37 -- Some processing on the role registry | 39 -- Default roles |
38 for role_name, role_info in pairs(role_registry) do | 40 register_role { |
39 role_info.name = role_name; | 41 name = "prosody:restricted"; |
40 role_info.includes = set.new(role_info.includes) / function (included_role_name) | 42 priority = 15; |
41 return role_registry[included_role_name]; | 43 }; |
42 end; | 44 |
43 if not role_info.permissions then | 45 register_role { |
44 role_info.permissions = {}; | 46 name = "prosody:user"; |
45 end | 47 priority = 25; |
46 setmetatable(role_info, role_mt); | 48 inherits = { "prosody:restricted" }; |
47 end | 49 }; |
48 | 50 |
49 function role_methods:may(action, context) | 51 register_role { |
50 local policy = self.permissions[action]; | 52 name = "prosody:admin"; |
51 if policy ~= nil then | 53 priority = 50; |
52 return policy; | 54 inherits = { "prosody:user" }; |
53 end | 55 }; |
54 for inherited_role in self.includes do | 56 |
55 module:log("debug", "Checking included role '%s' for %s", inherited_role.name, action); | 57 register_role { |
56 policy = inherited_role:may(action, context); | 58 name = "prosody:operator"; |
57 if policy ~= nil then | 59 priority = 75; |
58 return policy; | 60 inherits = { "prosody:admin" }; |
59 end | 61 }; |
60 end | 62 |
61 return false; | 63 |
64 -- Process custom roles from config | |
65 | |
66 local custom_roles = module:get_option("custom_roles", {}); | |
67 for n, role_config in ipairs(custom_roles) do | |
68 local ok, err = pcall(register_role, role_config); | |
69 if not ok then | |
70 module:log("error", "Error registering custom role %s: %s", role_config.name or tostring(n), err); | |
71 end | |
72 end | |
73 | |
74 -- Process custom permissions from config | |
75 | |
76 local config_add_perms = module:get_option("add_permissions", {}); | |
77 local config_remove_perms = module:get_option("remove_permissions", {}); | |
78 | |
79 for role_name, added_permissions in pairs(config_add_perms) do | |
80 if not role_registry[role_name] then | |
81 module:log("error", "Cannot add permissions to unknown role '%s'", role_name); | |
82 else | |
83 for _, permission in ipairs(added_permissions) do | |
84 role_registry[role_name]:set_permission(permission, true, true); | |
85 end | |
86 end | |
87 end | |
88 | |
89 for role_name, removed_permissions in pairs(config_remove_perms) do | |
90 if not role_registry[role_name] then | |
91 module:log("error", "Cannot remove permissions from unknown role '%s'", role_name); | |
92 else | |
93 for _, permission in ipairs(removed_permissions) do | |
94 role_registry[role_name]:set_permission(permission, false, true); | |
95 end | |
96 end | |
62 end | 97 end |
63 | 98 |
64 -- Public API | 99 -- Public API |
65 | 100 |
66 local config_operator_role_set = { | 101 local config_operator_role_set = { |
67 ["prosody:operator"] = role_registry["prosody:operator"]; | 102 ["prosody:operator"] = role_registry["prosody:operator"]; |
68 }; | 103 }; |
69 local config_admin_role_set = { | 104 local config_admin_role_set = { |
70 ["prosody:admin"] = role_registry["prosody:admin"]; | 105 ["prosody:admin"] = role_registry["prosody:admin"]; |
106 }; | |
107 local default_role_set = { | |
108 ["prosody:user"] = role_registry["prosody:user"]; | |
71 }; | 109 }; |
72 | 110 |
73 function get_user_roles(user) | 111 function get_user_roles(user) |
74 local bare_jid = user.."@"..host; | 112 local bare_jid = user.."@"..host; |
75 if config_global_admin_jids:contains(bare_jid) then | 113 if config_global_admin_jids:contains(bare_jid) then |
76 return config_operator_role_set; | 114 return config_operator_role_set; |
77 elseif config_admin_jids:contains(bare_jid) then | 115 elseif config_admin_jids:contains(bare_jid) then |
78 return config_admin_role_set; | 116 return config_admin_role_set; |
79 end | 117 end |
80 local role_names = role_store:get(user); | 118 local role_names = role_store:get(user); |
81 if not role_names then return {}; end | 119 if not role_names then return default_role_set; end |
82 local roles = {}; | 120 local user_roles = {}; |
83 for role_name in pairs(role_names) do | 121 for role_name in pairs(role_names) do |
84 roles[role_name] = role_registry[role_name]; | 122 user_roles[role_name] = role_registry[role_name]; |
85 end | 123 end |
86 return roles; | 124 return user_roles; |
87 end | 125 end |
88 | 126 |
89 function set_user_roles(user, roles) | 127 function set_user_roles(user, user_roles) |
90 role_store:set(user, roles) | 128 role_store:set(user, user_roles) |
91 return true; | 129 return true; |
92 end | 130 end |
93 | 131 |
94 function get_user_default_role(user) | 132 function get_user_default_role(user) |
95 local roles = get_user_roles(user); | 133 local user_roles = get_user_roles(user); |
96 if not roles then return nil; end | 134 if not user_roles then return nil; end |
97 local default_role; | 135 local default_role; |
98 for role_name, role_info in pairs(roles) do --luacheck: ignore 213/role_name | 136 for role_name, role_info in pairs(user_roles) do --luacheck: ignore 213/role_name |
99 if role_info.default and (not default_role or role_info.priority > default_role.priority) then | 137 if role_info.default ~= false and (not default_role or role_info.priority > default_role.priority) then |
100 default_role = role_info; | 138 default_role = role_info; |
101 end | 139 end |
102 end | 140 end |
103 if not default_role then return nil; end | 141 if not default_role then return nil; end |
104 return default_role; | 142 return default_role; |
132 return role_registry["prosody:admin"]; | 170 return role_registry["prosody:admin"]; |
133 end | 171 end |
134 return nil; | 172 return nil; |
135 end | 173 end |
136 | 174 |
137 function set_jid_role(jid) -- luacheck: ignore 212 | 175 function set_jid_role(jid, role_name) -- luacheck: ignore 212 |
138 return false; | 176 return false; |
139 end | 177 end |
140 | 178 |
141 function get_jids_with_role(role_name) | 179 function get_jids_with_role(role_name) |
142 -- Fetch role users from storage | 180 -- Fetch role users from storage |
155 local role = role_registry[role_name]; | 193 local role = role_registry[role_name]; |
156 if not role then | 194 if not role then |
157 module:log("warn", "Attempt to add default permission for unknown role: %s", role_name); | 195 module:log("warn", "Attempt to add default permission for unknown role: %s", role_name); |
158 return nil, "no-such-role"; | 196 return nil, "no-such-role"; |
159 end | 197 end |
160 if role.permissions[action] == nil then | 198 if policy == nil then policy = true; end |
161 if policy == nil then | 199 module:log("debug", "Adding policy %s for permission %s on role %s", policy, action, role_name); |
162 policy = true; | 200 return role:set_permission(action, policy); |
163 end | 201 end |
164 module:log("debug", "Adding permission, role '%s' may '%s': %s", role_name, action, policy and "allow" or "deny"); | 202 |
165 role.permissions[action] = policy; | 203 function get_role_by_name(role_name) |
166 end | 204 return assert(role_registry[role_name], role_name); |
167 return true; | 205 end |
168 end | |
169 | |
170 function get_role_info(role_name) | |
171 return role_registry[role_name]; | |
172 end |