Mercurial > prosody-hg
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 |
| parents | 9061f9621330 |
| children | 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 |
