Software /
code /
prosody
Changeset
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 | 12647:a661292d074a |
children | 12649:86e1187f6274 |
files | core/sessionmanager.lua core/usermanager.lua plugins/mod_authz_internal.lua |
diffstat | 3 files changed, 147 insertions(+), 96 deletions(-) [+] |
line wrap: on
line diff
--- a/core/sessionmanager.lua Tue Jul 19 17:44:26 2022 +0100 +++ b/core/sessionmanager.lua Tue Jul 19 18:02:02 2022 +0100 @@ -133,7 +133,7 @@ local role; if role_name then - role = hosts[session.host].authz.get_role_info(role_name); + role = hosts[session.host].authz.get_role_by_name(role_name); else role = hosts[session.host].authz.get_user_default_role(username); end
--- a/core/usermanager.lua Tue Jul 19 17:44:26 2022 +0100 +++ b/core/usermanager.lua Tue Jul 19 18:02:02 2022 +0100 @@ -10,8 +10,6 @@ local log = require "util.logger".init("usermanager"); local type = type; local it = require "util.iterators"; -local jid_bare = require "util.jid".bare; -local jid_split = require "util.jid".split; local jid_prep = require "util.jid".prep; local config = require "core.configmanager"; local sasl_new = require "util.sasl".new; @@ -150,48 +148,54 @@ return hosts[host].users; end -local function get_roles(jid, host) +-- Returns a map of { [role_name] = role, ... } that a user is allowed to assume +local function get_user_roles(user, host) if host and not hosts[host] then return false; end - if type(jid) ~= "string" then return false; end + if type(user) ~= "string" then return false; end - jid = jid_bare(jid); host = host or "*"; - local actor_user, actor_host = jid_split(jid); - local roles; + local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + return authz_provider.get_user_roles(user); +end + +local function get_user_default_role(user, host) + if host and not hosts[host] then return false; end + if type(user) ~= "string" then return false; end + + host = host or "*"; local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; - - if actor_user and actor_host == host then -- Local user - roles = authz_provider.get_user_roles(actor_user); - else -- Remote user/JID - roles = authz_provider.get_jid_roles(jid); - end - - return roles; + return authz_provider.get_user_default_role(user); end -local function set_roles(jid, host, roles) +-- Accepts a set of role names which the user is allowed to assume +local function set_user_roles(user, host, roles) if host and not hosts[host] then return false; end - if type(jid) ~= "string" then return false; end + if type(user) ~= "string" then return false; end - jid = jid_bare(jid); host = host or "*"; - local actor_user, actor_host = jid_split(jid); - local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; - if actor_user and actor_host == host then -- Local user - local ok, err = authz_provider.set_user_roles(actor_user, roles); - if ok then - prosody.events.fire_event("user-roles-changed", { - username = actor_user, host = actor_host - }); - end - return ok, err; - else -- Remote entity - return authz_provider.set_jid_roles(jid, roles) + local ok, err = authz_provider.set_user_roles(user, roles); + if ok then + prosody.events.fire_event("user-roles-changed", { + username = user, host = host + }); end + return ok, err; +end + +local function get_jid_role(jid, host) + host = host or "*"; + local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + return authz_provider.get_jid_role(jid); +end + +local function set_jid_role(jid, host, role_name) + host = host or "*"; + local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + return authz_provider.set_jid_role(jid, role_name) end local function get_users_with_role(role, host) @@ -211,6 +215,16 @@ return authz_provider.get_jids_with_role(role); end +local function get_role_by_name(role_name, host) + if host and not hosts[host] then return false; end + if type(role_name) ~= "string" then return false; end + + host = host or "*"; + + local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + return authz_provider.get_role_by_name(role_name); +end + return { new_null_provider = new_null_provider; initialize_host = initialize_host; @@ -224,8 +238,12 @@ users = users; get_sasl_handler = get_sasl_handler; get_provider = get_provider; - get_roles = get_roles; - set_roles = set_roles; + get_user_default_role = get_user_default_role; + get_user_roles = get_user_roles; + set_user_roles = set_user_roles; get_users_with_role = get_users_with_role; + get_jid_role = get_jid_role; + set_jid_role = set_jid_role; get_jids_with_role = get_jids_with_role; + get_role_by_name = get_role_by_name; };
--- a/plugins/mod_authz_internal.lua Tue Jul 19 17:44:26 2022 +0100 +++ b/plugins/mod_authz_internal.lua Tue Jul 19 18:02:02 2022 +0100 @@ -3,62 +3,97 @@ local set = require "util.set"; local jid_split, jid_bare = require "util.jid".split, require "util.jid".bare; local normalize = require "util.jid".prep; +local roles = require "util.roles"; + local config_global_admin_jids = module:context("*"):get_option_set("admins", {}) / normalize; local config_admin_jids = module:get_option_inherited_set("admins", {}) / normalize; local host = module.host; local role_store = module:open_store("roles"); local role_map_store = module:open_store("roles", "map"); -local role_methods = {}; -local role_mt = { __index = role_methods }; +local role_registry = {}; -local role_registry = { - ["prosody:operator"] = { - default = true; - priority = 75; - includes = { "prosody:admin" }; - }; - ["prosody:admin"] = { - default = true; - priority = 50; - includes = { "prosody:user" }; - }; - ["prosody:user"] = { - default = true; - priority = 25; - includes = { "prosody:restricted" }; - }; - ["prosody:restricted"] = { - default = true; - priority = 15; - }; +function register_role(role) + if role_registry[role.name] ~= nil then + return error("A role '"..role.name.."' is already registered"); + end + if not roles.is_role(role) then + -- Convert table syntax to real role object + for i, inherited_role in ipairs(role.inherits or {}) do + if type(inherited_role) == "string" then + role.inherits[i] = assert(role_registry[inherited_role], "The named role '"..inherited_role.."' is not registered"); + end + end + if not role.permissions then role.permissions = {}; end + for _, allow_permission in ipairs(role.allow or {}) do + role.permissions[allow_permission] = true; + end + for _, deny_permission in ipairs(role.deny or {}) do + role.permissions[deny_permission] = false; + end + role = roles.new(role); + end + role_registry[role.name] = role; +end + +-- Default roles +register_role { + name = "prosody:restricted"; + priority = 15; +}; + +register_role { + name = "prosody:user"; + priority = 25; + inherits = { "prosody:restricted" }; }; --- Some processing on the role registry -for role_name, role_info in pairs(role_registry) do - role_info.name = role_name; - role_info.includes = set.new(role_info.includes) / function (included_role_name) - return role_registry[included_role_name]; - end; - if not role_info.permissions then - role_info.permissions = {}; +register_role { + name = "prosody:admin"; + priority = 50; + inherits = { "prosody:user" }; +}; + +register_role { + name = "prosody:operator"; + priority = 75; + inherits = { "prosody:admin" }; +}; + + +-- Process custom roles from config + +local custom_roles = module:get_option("custom_roles", {}); +for n, role_config in ipairs(custom_roles) do + local ok, err = pcall(register_role, role_config); + if not ok then + module:log("error", "Error registering custom role %s: %s", role_config.name or tostring(n), err); end - setmetatable(role_info, role_mt); end -function role_methods:may(action, context) - local policy = self.permissions[action]; - if policy ~= nil then - return policy; - end - for inherited_role in self.includes do - module:log("debug", "Checking included role '%s' for %s", inherited_role.name, action); - policy = inherited_role:may(action, context); - if policy ~= nil then - return policy; +-- Process custom permissions from config + +local config_add_perms = module:get_option("add_permissions", {}); +local config_remove_perms = module:get_option("remove_permissions", {}); + +for role_name, added_permissions in pairs(config_add_perms) do + if not role_registry[role_name] then + module:log("error", "Cannot add permissions to unknown role '%s'", role_name); + else + for _, permission in ipairs(added_permissions) do + role_registry[role_name]:set_permission(permission, true, true); end end - return false; +end + +for role_name, removed_permissions in pairs(config_remove_perms) do + if not role_registry[role_name] then + module:log("error", "Cannot remove permissions from unknown role '%s'", role_name); + else + for _, permission in ipairs(removed_permissions) do + role_registry[role_name]:set_permission(permission, false, true); + end + end end -- Public API @@ -69,6 +104,9 @@ local config_admin_role_set = { ["prosody:admin"] = role_registry["prosody:admin"]; }; +local default_role_set = { + ["prosody:user"] = role_registry["prosody:user"]; +}; function get_user_roles(user) local bare_jid = user.."@"..host; @@ -78,25 +116,25 @@ return config_admin_role_set; end local role_names = role_store:get(user); - if not role_names then return {}; end - local roles = {}; + if not role_names then return default_role_set; end + local user_roles = {}; for role_name in pairs(role_names) do - roles[role_name] = role_registry[role_name]; + user_roles[role_name] = role_registry[role_name]; end - return roles; + return user_roles; end -function set_user_roles(user, roles) - role_store:set(user, roles) +function set_user_roles(user, user_roles) + role_store:set(user, user_roles) return true; end function get_user_default_role(user) - local roles = get_user_roles(user); - if not roles then return nil; end + local user_roles = get_user_roles(user); + if not user_roles then return nil; end local default_role; - for role_name, role_info in pairs(roles) do --luacheck: ignore 213/role_name - if role_info.default and (not default_role or role_info.priority > default_role.priority) then + for role_name, role_info in pairs(user_roles) do --luacheck: ignore 213/role_name + if role_info.default ~= false and (not default_role or role_info.priority > default_role.priority) then default_role = role_info; end end @@ -134,7 +172,7 @@ return nil; end -function set_jid_role(jid) -- luacheck: ignore 212 +function set_jid_role(jid, role_name) -- luacheck: ignore 212 return false; end @@ -157,16 +195,11 @@ module:log("warn", "Attempt to add default permission for unknown role: %s", role_name); return nil, "no-such-role"; end - if role.permissions[action] == nil then - if policy == nil then - policy = true; - end - module:log("debug", "Adding permission, role '%s' may '%s': %s", role_name, action, policy and "allow" or "deny"); - role.permissions[action] = policy; - end - return true; + if policy == nil then policy = true; end + module:log("debug", "Adding policy %s for permission %s on role %s", policy, action, role_name); + return role:set_permission(action, policy); end -function get_role_info(role_name) - return role_registry[role_name]; +function get_role_by_name(role_name) + return assert(role_registry[role_name], role_name); end