diff -r a661292d074a -r f299e570a0fe plugins/mod_authz_internal.lua --- 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