From 38346dd6f1dcd963e17722bf175445465d7683f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Wed, 27 Apr 2022 17:44:14 +0200 Subject: net: isolate LuaSec-specifics For this, various accessor functions are now provided directly on the sockets, which reach down into the LuaSec implementation to obtain the information. While this may seem of little gain at first, it hides the implementation detail of the LuaSec+LuaSocket combination that the actual socket and the TLS layer are separate objects. The net gain here is that an alternative implementation does not have to emulate that specific implementation detail and "only" has to expose LuaSec-compatible data structures on the new functions. --- core/certmanager.lua | 34 +--------------------------------- core/portmanager.lua | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 43 deletions(-) (limited to 'core') diff --git a/core/certmanager.lua b/core/certmanager.lua index c193e824..6013c633 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -9,7 +9,6 @@ local ssl = require "ssl"; local configmanager = require "core.configmanager"; local log = require "util.logger".init("certmanager"); -local ssl_context = ssl.context or require "ssl.context"; local ssl_newcontext = ssl.newcontext; local new_config = require"util.sslconfig".new; local stat = require "lfs".attributes; @@ -313,10 +312,6 @@ else core_defaults.curveslist = nil; end -local path_options = { -- These we pass through resolve_path() - key = true, certificate = true, cafile = true, capath = true, dhparam = true -} - local function create_context(host, mode, ...) local cfg = new_config(); cfg:apply(core_defaults); @@ -352,34 +347,7 @@ local function create_context(host, mode, ...) if user_ssl_config.certificate and not user_ssl_config.key then return nil, "No key present in SSL/TLS configuration for "..host; end end - for option in pairs(path_options) do - if type(user_ssl_config[option]) == "string" then - user_ssl_config[option] = resolve_path(config_path, user_ssl_config[option]); - else - user_ssl_config[option] = nil; - end - end - - -- LuaSec expects dhparam to be a callback that takes two arguments. - -- We ignore those because it is mostly used for having a separate - -- set of params for EXPORT ciphers, which we don't have by default. - if type(user_ssl_config.dhparam) == "string" then - local f, err = io_open(user_ssl_config.dhparam); - if not f then return nil, "Could not open DH parameters: "..err end - local dhparam = f:read("*a"); - f:close(); - user_ssl_config.dhparam = function() return dhparam; end - end - - local ctx, err = ssl_newcontext(user_ssl_config); - - -- COMPAT Older LuaSec ignores the cipher list from the config, so we have to take care - -- of it ourselves (W/A for #x) - if ctx and user_ssl_config.ciphers then - local success; - success, err = ssl_context.setcipher(ctx, user_ssl_config.ciphers); - if not success then ctx = nil; end - end + local ctx, err = cfg:build(); if not ctx then err = err or "invalid ssl config" diff --git a/core/portmanager.lua b/core/portmanager.lua index 38c74b66..8c7dfddb 100644 --- a/core/portmanager.lua +++ b/core/portmanager.lua @@ -240,21 +240,22 @@ local function add_sni_host(host, service) log("debug", "Gathering certificates for SNI for host %s, %s service", host, service or "default"); for name, interface, port, n, active_service --luacheck: ignore 213 in active_services:iter(service, nil, nil, nil) do - if active_service.server.hosts and active_service.tls_cfg then - local config_prefix = (active_service.config_prefix or name).."_"; - if config_prefix == "_" then config_prefix = ""; end - local prefix_ssl_config = config.get(host, config_prefix.."ssl"); + if active_service.server and active_service.tls_cfg then local alternate_host = name and config.get(host, name.."_host"); if not alternate_host and name == "https" then -- TODO should this be some generic thing? e.g. in the service definition alternate_host = config.get(host, "http_host"); end local autocert = certmanager.find_host_cert(alternate_host or host); - -- luacheck: ignore 211/cfg - local ssl, err, cfg = certmanager.create_context(host, "server", prefix_ssl_config, autocert, active_service.tls_cfg); - if ssl then - active_service.server.hosts[alternate_host or host] = ssl; - else + local manualcert = active_service.tls_cfg; + local certificate = (autocert and autocert.certificate) or manualcert.certificate; + local key = (autocert and autocert.key) or manualcert.key; + local ok, err = active_service.server:sslctx():set_sni_host( + host, + certificate, + key + ); + if not ok then log("error", "Error creating TLS context for SNI host %s: %s", host, err); end end @@ -277,7 +278,7 @@ prosody.events.add_handler("host-deactivated", function (host) for name, interface, port, n, active_service --luacheck: ignore 213 in active_services:iter(nil, nil, nil, nil) do if active_service.tls_cfg then - active_service.server.hosts[host] = nil; + active_service.server:sslctx():remove_sni_host(host) end end end); -- cgit v1.2.3 From 9f7c3b9ba6c2fde4431cd6f3a12072518b478d69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Sat, 2 Apr 2022 11:15:33 +0200 Subject: net: refactor sslconfig to not depend on LuaSec This now requires that the network backend exposes a tls_builder function, which essentially wraps the former util.sslconfig.new() function, passing a factory to create the eventual SSL context. That allows a net.server backend to pick whatever it likes as SSL context factory, as long as it understands the config table passed by the SSL config builder. Heck, a backend could even mock and replace the entire SSL config builder API. --- core/certmanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/certmanager.lua b/core/certmanager.lua index 6013c633..7958e8a9 100644 --- a/core/certmanager.lua +++ b/core/certmanager.lua @@ -10,7 +10,7 @@ local ssl = require "ssl"; local configmanager = require "core.configmanager"; local log = require "util.logger".init("certmanager"); local ssl_newcontext = ssl.newcontext; -local new_config = require"util.sslconfig".new; +local new_config = require"net.server".tls_builder; local stat = require "lfs".attributes; local x509 = require "util.x509"; -- cgit v1.2.3 From 67177ce287a7a35c93538d9b1c574e9ed6ca44d3 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sun, 29 May 2022 16:06:42 +0200 Subject: core.configmanager: Remove COMPAT for old config format from 2013 --- core/configmanager.lua | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'core') diff --git a/core/configmanager.lua b/core/configmanager.lua index 092b3946..4b8df96e 100644 --- a/core/configmanager.lua +++ b/core/configmanager.lua @@ -40,16 +40,10 @@ function _M.getconfig() return config; end -function _M.get(host, key, _oldkey) - if key == "core" then - key = _oldkey; -- COMPAT with code that still uses "core" - end +function _M.get(host, key) return config[host][key]; end -function _M.rawget(host, key, _oldkey) - if key == "core" then - key = _oldkey; -- COMPAT with code that still uses "core" - end +function _M.rawget(host, key) local hostconfig = rawget(config, host); if hostconfig then return rawget(hostconfig, key); @@ -68,10 +62,7 @@ local function set(config_table, host, key, value) return false; end -function _M.set(host, key, value, _oldvalue) - if key == "core" then - key, value = value, _oldvalue; --COMPAT with code that still uses "core" - end +function _M.set(host, key, value) return set(config, host, key, value); end -- cgit v1.2.3 From 5251c9b686fc7885c1213cc2580d66ebda2dda9b Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 Jul 2022 19:07:38 +0200 Subject: compat: Remove handling of Lua 5.1 location of 'unpack' function --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 870a6a50..98474b27 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -27,7 +27,7 @@ local ipairs, pairs, select = ipairs, pairs, select; local tonumber, tostring = tonumber, tostring; local require = require; local pack = table.pack or require "util.table".pack; -- table.pack is only in 5.2 -local unpack = table.unpack or unpack; --luacheck: ignore 113 -- renamed in 5.2 +local unpack = table.unpack; local prosody = prosody; local hosts = prosody.hosts; -- cgit v1.2.3 From f8e73eba98a73e5d3dd14f73d7ce66e5503efbb4 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 11 Jul 2022 19:15:24 +0200 Subject: compat: Use table.pack (there since Lua 5.2) over our util.table Added in d278a770eddc avoid having to deal with its absence in Lua 5.1. No longer needed when Lua 5.1 support is dropped. --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 98474b27..36d82193 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -26,7 +26,7 @@ local error, setmetatable, type = error, setmetatable, type; local ipairs, pairs, select = ipairs, pairs, select; local tonumber, tostring = tonumber, tostring; local require = require; -local pack = table.pack or require "util.table".pack; -- table.pack is only in 5.2 +local pack = table.pack; local unpack = table.unpack; local prosody = prosody; -- cgit v1.2.3 From d73714b4f426da4f9c79d5ddf0b8cb11d09e9f3f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 15 Jun 2022 12:15:01 +0100 Subject: Switch to a new role-based authorization framework, removing is_admin() We began moving away from simple "is this user an admin?" permission checks before 0.12, with the introduction of mod_authz_internal and the ability to dynamically change the roles of individual users. The approach in 0.12 still had various limitations however, and apart from the introduction of roles other than "admin" and the ability to pull that info from storage, not much actually changed. This new framework shakes things up a lot, though aims to maintain the same functionality and behaviour on the surface for a default Prosody configuration. That is, if you don't take advantage of any of the new features, you shouldn't notice any change. The biggest change visible to developers is that usermanager.is_admin() (and the auth provider is_admin() method) have been removed. Gone. Completely. Permission checks should now be performed using a new module API method: module:may(action_name, context) This method accepts an action name, followed by either a JID (string) or (preferably) a table containing 'origin'/'session' and 'stanza' fields (e.g. the standard object passed to most events). It will return true if the action should be permitted, or false/nil otherwise. Modules should no longer perform permission checks based on the role name. E.g. a lot of code previously checked if the user's role was prosody:admin before permitting some action. Since many roles might now exist with similar permissions, and the permissions of prosody:admin may be redefined dynamically, it is no longer suitable to use this method for permission checks. Use module:may(). If you start an action name with ':' (recommended) then the current module's name will automatically be used as a prefix. To define a new permission, use the new module API: module:default_permission(role_name, action_name) module:default_permissions(role_name, { action_name[, action_name...] }) This grants the specified role permission to execute the named action(s) by default. This may be overridden via other mechanisms external to your module. The built-in roles that developers should use are: - prosody:user (normal user) - prosody:admin (host admin) - prosody:operator (global admin) The new prosody:operator role is intended for server-wide actions (such as shutting down Prosody). Finally, all usage of is_admin() in modules has been fixed by this commit. Some of these changes were trickier than others, but no change is expected to break existing deployments. EXCEPT: mod_auth_ldap no longer supports the ldap_admin_filter option. It's very possible nobody is using this, but if someone is then we can later update it to pull roles from LDAP somehow. --- core/moduleapi.lua | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ core/sessionmanager.lua | 15 +++++++++--- core/usermanager.lua | 6 ----- 3 files changed, 75 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 36d82193..f0b412f3 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -19,6 +19,7 @@ local promise = require "util.promise"; local time_now = require "util.time".now; local format = require "util.format".format; local jid_node = require "util.jid".node; +local jid_split = require "util.jid".split; local jid_resource = require "util.jid".resource; local t_insert, t_remove, t_concat = table.insert, table.remove, table.concat; @@ -601,4 +602,66 @@ function api:get_status() return self.status_type, self.status_message, self.status_time; end +function api:default_permission(role_name, permission) + permission = permission:gsub("^:", self.name..":"); + hosts[self.host].authz.add_default_permission(role_name, permission); +end + +function api:default_permissions(role_name, permissions) + for _, permission in ipairs(permissions) do + permission = permission:gsub("^:", self.name..":"); + self:default_permission(role_name, permission); + end +end + +function api:may(action, context) + if type(context) == "string" then -- check JID permissions + local role; + local node, host = jid_split(context); + if host == self.host then + role = hosts[host].authz.get_user_role(node); + else + role = hosts[self.host].authz.get_jid_role(context); + end + if not role then + self:log("debug", "Access denied: JID <%s> may not %s (no role found)", context, action); + return false; + end + local permit = role:may(action); + if not permit then + self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", context, action, role.name); + end + return permit; + end + + local session = context.origin or context.session; + if not session then + error("Unable to identify actor session from context"); + end + if action:byte(1) == 58 then -- action begins with ':' + action = self.name..action; -- prepend module name + end + if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then + local actor_jid = context.stanza.attr.from; + local role = hosts[self.host].authz.get_jid_role(actor_jid); + if not role then + self:log("debug", "Access denied: JID <%s> may not %s (no role found)", actor_jid, action); + return false; + end + local permit = role:may(action, context); + if not permit then + self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", actor_jid, action, role.name); + end + return permit; + elseif session.role then + local permit = session.role:may(action, context); + if not permit then + self:log("debug", "Access denied: session %s (%s) may not %s (not permitted by role %s)", + session.id, session.full_jid, action, session.role.name + ); + end + return permit; + end +end + return api; diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 7f296ff1..426afa7b 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -123,15 +123,24 @@ local function destroy_session(session, err) retire_session(session); end -local function make_authenticated(session, username, scope) +local function make_authenticated(session, username, role_name) username = nodeprep(username); if not username or #username == 0 then return nil, "Invalid username"; end session.username = username; if session.type == "c2s_unauthed" then session.type = "c2s_unbound"; end - session.auth_scope = scope; - session.log("info", "Authenticated as %s@%s", username, session.host or "(unknown)"); + + local role; + if role_name then + role = hosts[session.host].authz.get_role_info(role_name); + else + role = hosts[session.host].authz.get_user_default_role(username); + end + if role then + sessionlib.set_role(session, role); + end + session.log("info", "Authenticated as %s@%s [%s]", username, session.host or "(unknown)", role and role.name or "no role"); return true; end diff --git a/core/usermanager.lua b/core/usermanager.lua index 45f104fa..970140ef 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -188,11 +188,6 @@ local function set_roles(jid, host, roles) end end -local function is_admin(jid, host) - local roles = get_roles(jid, host); - return roles and roles["prosody:admin"]; -end - local function get_users_with_role(role, host) if not hosts[host] then return false; end if type(role) ~= "string" then return false; end @@ -224,7 +219,6 @@ return { get_provider = get_provider; get_roles = get_roles; set_roles = set_roles; - is_admin = is_admin; get_users_with_role = get_users_with_role; get_jids_with_role = get_jids_with_role; }; -- cgit v1.2.3 From c776c71066e22eff9f8c5b0401946513bd22f103 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 15 Jun 2022 23:03:15 +0200 Subject: core.moduleapi: Fixup method name `get_user_role()` did not exist anywhere else. MattJ said `get_user_default_role()` was indented --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index f0b412f3..0652e032 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -619,7 +619,7 @@ function api:may(action, context) local role; local node, host = jid_split(context); if host == self.host then - role = hosts[host].authz.get_user_role(node); + role = hosts[host].authz.get_user_default_role(node); else role = hosts[self.host].authz.get_jid_role(context); end -- cgit v1.2.3 From af339f0e66480da6825fd655a5bf35e2824cfc00 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 15 Jun 2022 23:04:17 +0200 Subject: core.moduleapi: Expand permission name ':' prefix earlier Ensures it applies to the context as string case Somehow this fixes everything --- core/moduleapi.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 0652e032..fe248c20 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -615,6 +615,9 @@ function api:default_permissions(role_name, permissions) end function api:may(action, context) + if action:byte(1) == 58 then -- action begins with ':' + action = self.name..action; -- prepend module name + end if type(context) == "string" then -- check JID permissions local role; local node, host = jid_split(context); @@ -638,9 +641,6 @@ function api:may(action, context) if not session then error("Unable to identify actor session from context"); end - if action:byte(1) == 58 then -- action begins with ':' - action = self.name..action; -- prepend module name - end if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then local actor_jid = context.stanza.attr.from; local role = hosts[self.host].authz.get_jid_role(actor_jid); -- cgit v1.2.3 From 4db3d1572390ce5b615282cb1112358d9e3ba892 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 12 Jul 2022 13:14:47 +0100 Subject: usermanager, mod_auth_*: Add get_account_info() returning creation/update time This is useful for a number of things. For example, listing users that need to rotate their passwords after some event. It also provides a safer way for code to determine that a user password has changed without needing to set a handler for the password change event (which is a more fragile approach). --- core/usermanager.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 970140ef..23571fe7 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -116,6 +116,12 @@ local function set_password(username, password, host, resource) return ok, err; end +local function get_account_info(username, host) + local method = hosts[host].users.get_account_info; + if not method then return nil, "method-not-supported"; end + return method(username); +end + local function user_exists(username, host) if hosts[host].sessions[username] then return true; end return hosts[host].users.user_exists(username); @@ -211,6 +217,7 @@ return { test_password = test_password; get_password = get_password; set_password = set_password; + get_account_info = get_account_info; user_exists = user_exists; create_user = create_user; delete_user = delete_user; -- cgit v1.2.3 From c0b857e5fb2a670d0a7a6ef29977ee58528e842f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 19 Jul 2022 18:02:02 +0100 Subject: 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). --- core/sessionmanager.lua | 2 +- core/usermanager.lua | 80 ++++++++++++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 32 deletions(-) (limited to 'core') diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 426afa7b..924c4968 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -133,7 +133,7 @@ local function make_authenticated(session, username, role_name) 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 diff --git a/core/usermanager.lua b/core/usermanager.lua index 23571fe7..af82c940 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -10,8 +10,6 @@ local modulemanager = require "core.modulemanager"; 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 @@ local function get_provider(host) 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 - 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 +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 - return roles; + host = host or "*"; + + local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + 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 @@ local function get_jids_with_role(role, host) 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 @@ return { 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; }; -- cgit v1.2.3 From 5f8e4414499e33a05dc8eda7a224e69c70bdb7a2 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 20 Jul 2022 13:05:35 +0200 Subject: moduleapi: Distribute permissions set from global modules to all hosts Roles and permissions will always happen in the context of a host. Prevents error upon indexing since `hosts["*"] == nil` --- core/moduleapi.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index fe248c20..0ec96fb6 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -604,6 +604,14 @@ end function api:default_permission(role_name, permission) permission = permission:gsub("^:", self.name..":"); + if self.host == "*" then + for _, host in pairs(hosts) do + if host.authz then + host.authz.add_default_permission(role_name, permission); + end + end + return + end hosts[self.host].authz.add_default_permission(role_name, permission); end -- cgit v1.2.3 From 55378f128acb817baab9fbeb412768f1a748a969 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 20 Jul 2022 13:07:04 +0200 Subject: moduleapi: Remove redundant expansion of ':' prefix in permission names --- core/moduleapi.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 0ec96fb6..c1d8851e 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -617,7 +617,6 @@ end function api:default_permissions(role_name, permissions) for _, permission in ipairs(permissions) do - permission = permission:gsub("^:", self.name..":"); self:default_permission(role_name, permission); end end -- cgit v1.2.3 From cdd5608f4a42e7740536d09eeda14451924c9657 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 20 Jul 2022 13:08:07 +0200 Subject: moduleapi: Stricter type check for actor in permission check Non-table but truthy values would trigger "attempt to index a foo value" on the next line otherwise --- core/moduleapi.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index c1d8851e..8790a9d3 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -645,7 +645,7 @@ function api:may(action, context) end local session = context.origin or context.session; - if not session then + if type(session) ~= "table" then error("Unable to identify actor session from context"); end if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then -- cgit v1.2.3 From a88c982ae232f85bbf30401aa7690dbccc482866 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Wed, 20 Jul 2022 13:10:47 +0200 Subject: core.usermanager: Add missing stub authz methods to global authz provider Except, should we have a global authz provider at all? --- core/usermanager.lua | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index af82c940..ec782906 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -54,6 +54,13 @@ local global_authz_provider = { end; set_user_roles = function (user, roles) end; -- luacheck: ignore 212 set_jid_roles = function (jid, roles) end; -- luacheck: ignore 212 + + get_user_default_role = function (user) end; -- luacheck: ignore 212 + get_users_with_role = function (role_name) end; -- luacheck: ignore 212 + get_jid_role = function (jid) end; -- luacheck: ignore 212 + set_jid_role = function (jid) end; -- luacheck: ignore 212 + add_default_permission = function (role_name, action, policy) end; -- luacheck: ignore 212 + get_role_info = function (role_name) end; -- luacheck: ignore 212 }; local provider_mt = { __index = new_null_provider() }; -- cgit v1.2.3 From b1f0061da37e72d12a71eb067ae103fe2ae6ca91 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 1 Aug 2022 20:26:00 +0100 Subject: usermanager: Handle local JIDs being passed to get/set_jid_role() There is no reasonable fallback for set_jid_role() because users may have multiple roles, so that's an error. --- core/usermanager.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index ec782906..09beb6d4 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -10,7 +10,7 @@ local modulemanager = require "core.modulemanager"; local log = require "util.logger".init("usermanager"); local type = type; local it = require "util.iterators"; -local jid_prep = require "util.jid".prep; +local jid_prep, jid_split = require "util.jid".prep, require "util.jid".split; local config = require "core.configmanager"; local sasl_new = require "util.sasl".new; local storagemanager = require "core.storagemanager"; @@ -196,12 +196,20 @@ end local function get_jid_role(jid, host) host = host or "*"; local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; + local jid_node, jid_host = jid_split(jid); + if host == jid_host and jid_node then + return authz_provider.get_user_default_role(jid_node); + end 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; + local _, jid_host = jid_split(jid); + if host == jid_host then + return nil, "unexpected-local-jid"; + end return authz_provider.set_jid_role(jid, role_name) end -- cgit v1.2.3 From 341a5fd99fdee635257dd03d1f098a5a09338455 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Aug 2022 16:47:09 +0100 Subject: features: Add "permissions" feature for role-auth --- core/features.lua | 2 ++ 1 file changed, 2 insertions(+) (limited to 'core') diff --git a/core/features.lua b/core/features.lua index 7248f881..96023b09 100644 --- a/core/features.lua +++ b/core/features.lua @@ -4,5 +4,7 @@ return { available = set.new{ -- mod_bookmarks bundled "mod_bookmarks"; + -- Roles, module.may and per-session authz + "permissions"; }; }; -- cgit v1.2.3 From afbc47f8bafd599ab7cf7dc5dcac4931757b417a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 11 Aug 2022 16:56:59 +0100 Subject: usermanager: Remove obsolete function from global authz provider --- core/usermanager.lua | 5 ----- 1 file changed, 5 deletions(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 09beb6d4..9adf1f25 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -43,11 +43,6 @@ local global_admins = set.new(global_admins_config) / jid_prep; local admin_role = { ["prosody:admin"] = true }; local global_authz_provider = { get_user_roles = function (user) end; --luacheck: ignore 212/user - get_jid_roles = function (jid) - if global_admins:contains(jid) then - return admin_role; - end - end; get_jids_with_role = function (role) if role ~= "prosody:admin" then return {}; end return it.to_array(global_admins); -- cgit v1.2.3 From bd2b2af7b736d9ef13be1228432443592fbaf6cd Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 12 Aug 2022 11:58:25 +0100 Subject: usermanager: Fix method name of global authz provider (thanks Zash) --- core/usermanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 9adf1f25..a299f9d9 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -55,7 +55,7 @@ local global_authz_provider = { get_jid_role = function (jid) end; -- luacheck: ignore 212 set_jid_role = function (jid) end; -- luacheck: ignore 212 add_default_permission = function (role_name, action, policy) end; -- luacheck: ignore 212 - get_role_info = function (role_name) end; -- luacheck: ignore 212 + get_role_by_name = function (role_name) end; -- luacheck: ignore 212 }; local provider_mt = { __index = new_null_provider() }; -- cgit v1.2.3 From 7ccf41ebb5e7a1a21fdf5945c5dd157e40b7024c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 12 Aug 2022 16:21:57 +0100 Subject: usermanager: Remove concept of global authz provider Rationale: - Removes a bunch of code! - We don't have many cases where an actor is not bound to one of our hosts - A notable exception is the admin shell, but if we ever attempt to lock those sessions down, there is a load of other work that also has to be done. And it's not clear if we would need a global authz provider for that anyway. - Removes an extra edge case from the necessary mental model for operators - Sessions that aren't bound to a host generally are anonymous or have an alternative auth model (such as by IP addres). - With the encapsulation now provided by util.roles, ad-hoc "detached roles" can still be created anyway by code that needs them. --- core/usermanager.lua | 58 +++++++++++----------------------------------------- 1 file changed, 12 insertions(+), 46 deletions(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index a299f9d9..2311ce9e 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -9,12 +9,10 @@ local modulemanager = require "core.modulemanager"; local log = require "util.logger".init("usermanager"); local type = type; -local it = require "util.iterators"; -local jid_prep, jid_split = require "util.jid".prep, require "util.jid".split; +local jid_split = require "util.jid".split; local config = require "core.configmanager"; local sasl_new = require "util.sasl".new; local storagemanager = require "core.storagemanager"; -local set = require "util.set"; local prosody = _G.prosody; local hosts = prosody.hosts; @@ -34,19 +32,9 @@ local function new_null_provider() }); end -local global_admins_config = config.get("*", "admins"); -if type(global_admins_config) ~= "table" then - global_admins_config = nil; -- TODO: factor out moduleapi magic config handling and use it here -end -local global_admins = set.new(global_admins_config) / jid_prep; - -local admin_role = { ["prosody:admin"] = true }; -local global_authz_provider = { +local fallback_authz_provider = { get_user_roles = function (user) end; --luacheck: ignore 212/user - get_jids_with_role = function (role) - if role ~= "prosody:admin" then return {}; end - return it.to_array(global_admins); - end; + get_jids_with_role = function (role) end; --luacheck: ignore 212 set_user_roles = function (user, roles) end; -- luacheck: ignore 212 set_jid_roles = function (jid, roles) end; -- luacheck: ignore 212 @@ -66,7 +54,7 @@ local function initialize_host(host) local authz_provider_name = config.get(host, "authorization") or "internal"; local authz_mod = modulemanager.load(host, "authz_"..authz_provider_name); - host_session.authz = authz_mod or global_authz_provider; + host_session.authz = authz_mod or fallback_authz_provider; if host_session.type ~= "local" then return; end @@ -155,20 +143,14 @@ local function get_user_roles(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; - return authz_provider.get_user_roles(user); + return hosts[host].authz.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; - return authz_provider.get_user_default_role(user); + return hosts[host].authz.get_user_default_role(user); end -- Accepts a set of role names which the user is allowed to assume @@ -176,10 +158,7 @@ local function set_user_roles(user, host, roles) 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; - local ok, err = authz_provider.set_user_roles(user, roles); + local ok, err = hosts[host].authz.set_user_roles(user, roles); if ok then prosody.events.fire_event("user-roles-changed", { username = user, host = host @@ -189,50 +168,37 @@ local function set_user_roles(user, host, roles) end local function get_jid_role(jid, host) - host = host or "*"; - local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; local jid_node, jid_host = jid_split(jid); if host == jid_host and jid_node then - return authz_provider.get_user_default_role(jid_node); + return hosts[host].authz.get_user_default_role(jid_node); end - return authz_provider.get_jid_role(jid); + return hosts[host].authz.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; local _, jid_host = jid_split(jid); if host == jid_host then return nil, "unexpected-local-jid"; end - return authz_provider.set_jid_role(jid, role_name) + return hosts[host].authz.set_jid_role(jid, role_name) end local function get_users_with_role(role, host) if not hosts[host] then return false; end if type(role) ~= "string" then return false; end - return hosts[host].authz.get_users_with_role(role); end local function get_jids_with_role(role, host) if host and not hosts[host] then return false; end if type(role) ~= "string" then return false; end - - host = host or "*"; - - local authz_provider = (host ~= "*" and hosts[host].authz) or global_authz_provider; - return authz_provider.get_jids_with_role(role); + return hosts[host].authz.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); + return hosts[host].authz.get_role_by_name(role_name); end return { -- cgit v1.2.3 From ae3a89375d7dcb1df54c072ddebe2a60248c58b0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 15 Aug 2022 15:25:07 +0100 Subject: usermanager: Add back temporary is_admin to warn about deprecated API usage Goal: Introduce role-auth with minimal disruption is_admin() is unsafe in a system with per-session permissions, so it has been deprecated. Roll-out approach: 1) First, log a warning when is_admin() is used. It should continue to function normally, backed by the new role API. Nothing is really using per-session authz yet, so there is minimal security concern. The 'strict_deprecate_is_admin' global setting can be set to 'true' to force a hard failure of is_admin() attempts (it will log an error and always return false). 2) In some time (at least 1 week), but possibly longer depending on the number of affected deployments: switch 'strict_deprecate_is_admin' to 'true' by default. It can still be disabled for systems that need it. 3) Further in the future, before the next release, the option will be removed and is_admin() will be permanently disabled. --- core/usermanager.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 2311ce9e..0a2f5c4d 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -21,6 +21,8 @@ local setmetatable = setmetatable; local default_provider = "internal_hashed"; +local debug = debug; + local _ENV = nil; -- luacheck: std none @@ -183,6 +185,20 @@ local function set_jid_role(jid, host, role_name) return hosts[host].authz.set_jid_role(jid, role_name) end +local strict_deprecate_is_admin; +local legacy_admin_roles = { ["prosody:admin"] = true, ["prosody:operator"] = true }; +local function is_admin(jid, host) + if strict_deprecate_is_admin == nil then + strict_deprecate_is_admin = (config.get("*", "strict_deprecate_is_admin") == true); + end + if strict_deprecate_is_admin then + log("error", "Attempt to use deprecated is_admin() API: %s", debug.traceback()); + return false; + end + log("warn", "Usage of legacy is_admin() API, which will be disabled in a future build: %s", debug.traceback()); + return legacy_admin_roles[get_jid_role(jid, host)] or false; +end + local function get_users_with_role(role, host) if not hosts[host] then return false; end if type(role) ~= "string" then return false; end @@ -222,4 +238,7 @@ return { set_jid_role = set_jid_role; get_jids_with_role = get_jids_with_role; get_role_by_name = get_role_by_name; + + -- Deprecated + is_admin = is_admin; }; -- cgit v1.2.3 From f5768f63c993cee9f7f8e3c89db7e4e3080beab5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 17 Aug 2022 16:38:53 +0100 Subject: mod_authz_internal, and more: New iteration of role API These changes to the API (hopefully the last) introduce a cleaner separation between the user's primary (default) role, and their secondary (optional) roles. To keep the code sane and reduce complexity, a data migration is needed for people using stored roles in 0.12. This can be performed with prosodyctl mod_authz_internal migrate --- core/moduleapi.lua | 3 ++- core/sessionmanager.lua | 2 +- core/usermanager.lua | 68 +++++++++++++++++++++++++++++++++++-------------- 3 files changed, 52 insertions(+), 21 deletions(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 8790a9d3..73ce4911 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -538,6 +538,7 @@ function api:load_resource(path, mode) end function api:open_store(name, store_type) + if self.host == "*" then return nil, "global-storage-not-supported"; end return require"core.storagemanager".open(self.host, name or self.name, store_type); end @@ -629,7 +630,7 @@ function api:may(action, context) local role; local node, host = jid_split(context); if host == self.host then - role = hosts[host].authz.get_user_default_role(node); + role = hosts[host].authz.get_user_role(node); else role = hosts[self.host].authz.get_jid_role(context); end diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 924c4968..dec21674 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -135,7 +135,7 @@ local function make_authenticated(session, username, role_name) if role_name then role = hosts[session.host].authz.get_role_by_name(role_name); else - role = hosts[session.host].authz.get_user_default_role(username); + role = hosts[session.host].authz.get_user_role(username); end if role then sessionlib.set_role(session, role); diff --git a/core/usermanager.lua b/core/usermanager.lua index 0a2f5c4d..cf54fc31 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -37,13 +37,17 @@ end local fallback_authz_provider = { get_user_roles = function (user) end; --luacheck: ignore 212/user get_jids_with_role = function (role) end; --luacheck: ignore 212 - set_user_roles = function (user, roles) end; -- luacheck: ignore 212 - set_jid_roles = function (jid, roles) end; -- luacheck: ignore 212 - get_user_default_role = function (user) end; -- luacheck: ignore 212 - get_users_with_role = function (role_name) end; -- luacheck: ignore 212 + get_user_role = function (user) end; -- luacheck: ignore 212 + set_user_role = function (user, roles) end; -- luacheck: ignore 212 + + add_user_secondary_role = function (user, host, role_name) end; --luacheck: ignore 212 + remove_user_secondary_role = function (user, host, role_name) end; --luacheck: ignore 212 + get_jid_role = function (jid) end; -- luacheck: ignore 212 - set_jid_role = function (jid) end; -- luacheck: ignore 212 + set_jid_role = function (jid, role) end; -- luacheck: ignore 212 + + get_users_with_role = function (role_name) end; -- luacheck: ignore 212 add_default_permission = function (role_name, action, policy) end; -- luacheck: ignore 212 get_role_by_name = function (role_name) end; -- luacheck: ignore 212 }; @@ -140,39 +144,63 @@ local function get_provider(host) return hosts[host].users; end --- Returns a map of { [role_name] = role, ... } that a user is allowed to assume -local function get_user_roles(user, host) +local function get_user_role(user, host) if host and not hosts[host] then return false; end if type(user) ~= "string" then return false; end - return hosts[host].authz.get_user_roles(user); + return hosts[host].authz.get_user_role(user); end -local function get_user_default_role(user, host) +local function set_user_role(user, host, role_name) if host and not hosts[host] then return false; end if type(user) ~= "string" then return false; end - return hosts[host].authz.get_user_default_role(user); + local role, err = hosts[host].authz.set_user_role(user, role_name); + if role then + prosody.events.fire_event("user-role-changed", { + username = user, host = host, role = role; + }); + end + return role, err; end --- Accepts a set of role names which the user is allowed to assume -local function set_user_roles(user, host, roles) +local function add_user_secondary_role(user, host, role_name) if host and not hosts[host] then return false; end if type(user) ~= "string" then return false; end - local ok, err = hosts[host].authz.set_user_roles(user, roles); + local role, err = hosts[host].authz.add_user_secondary_role(user, role_name); + if role then + prosody.events.fire_event("user-role-added", { + username = user, host = host, role = role; + }); + end + return role, err; +end + +local function remove_user_secondary_role(user, host, role_name) + if host and not hosts[host] then return false; end + if type(user) ~= "string" then return false; end + + local ok, err = hosts[host].authz.remove_user_secondary_role(user, role_name); if ok then - prosody.events.fire_event("user-roles-changed", { - username = user, host = host + prosody.events.fire_event("user-role-removed", { + username = user, host = host, role_name = role_name; }); end return ok, err; end +local function get_user_secondary_roles(user, host) + if host and not hosts[host] then return false; end + if type(user) ~= "string" then return false; end + + return hosts[host].authz.get_user_secondary_roles(user); +end + local function get_jid_role(jid, host) local jid_node, jid_host = jid_split(jid); if host == jid_host and jid_node then - return hosts[host].authz.get_user_default_role(jid_node); + return hosts[host].authz.get_user_role(jid_node); end return hosts[host].authz.get_jid_role(jid); end @@ -230,9 +258,11 @@ return { users = users; get_sasl_handler = get_sasl_handler; get_provider = get_provider; - get_user_default_role = get_user_default_role; - get_user_roles = get_user_roles; - set_user_roles = set_user_roles; + get_user_role = get_user_role; + set_user_role = set_user_role; + add_user_secondary_role = add_user_secondary_role; + remove_user_secondary_role = remove_user_secondary_role; + get_user_secondary_roles = get_user_secondary_roles; get_users_with_role = get_users_with_role; get_jid_role = get_jid_role; set_jid_role = set_jid_role; -- cgit v1.2.3 From f75ac951b518b04fb6b5f425950cfb2a8c8bb67b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 18 Aug 2022 10:37:59 +0100 Subject: mod_authz_internal: Expose convenience method to test if user can assume role --- core/usermanager.lua | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index cf54fc31..4f15c302 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -164,6 +164,13 @@ local function set_user_role(user, host, role_name) return role, err; end +local function user_can_assume_role(user, host, role_name) + if host and not hosts[host] then return false; end + if type(user) ~= "string" then return false; end + + return hosts[host].authz.user_can_assume_role(user, role_name); +end + local function add_user_secondary_role(user, host, role_name) if host and not hosts[host] then return false; end if type(user) ~= "string" then return false; end @@ -260,6 +267,7 @@ return { get_provider = get_provider; get_user_role = get_user_role; set_user_role = set_user_role; + user_can_assume_role = user_can_assume_role; add_user_secondary_role = add_user_secondary_role; remove_user_secondary_role = remove_user_secondary_role; get_user_secondary_roles = get_user_secondary_roles; -- cgit v1.2.3 From 8bed557afd8266b7c5578608c35e973f42669772 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 18 Aug 2022 14:07:54 +0200 Subject: core.usermanager: Add scoped luacheck ignore rule to reduce clutter --- core/usermanager.lua | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 4f15c302..03b27327 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -35,21 +35,22 @@ local function new_null_provider() end local fallback_authz_provider = { - get_user_roles = function (user) end; --luacheck: ignore 212/user - get_jids_with_role = function (role) end; --luacheck: ignore 212 + -- luacheck: ignore 212 + get_user_roles = function (user) end; + get_jids_with_role = function (role) end; - get_user_role = function (user) end; -- luacheck: ignore 212 - set_user_role = function (user, roles) end; -- luacheck: ignore 212 + get_user_role = function (user) end; + set_user_role = function (user, roles) end; - add_user_secondary_role = function (user, host, role_name) end; --luacheck: ignore 212 - remove_user_secondary_role = function (user, host, role_name) end; --luacheck: ignore 212 + add_user_secondary_role = function (user, host, role_name) end; + remove_user_secondary_role = function (user, host, role_name) end; - get_jid_role = function (jid) end; -- luacheck: ignore 212 - set_jid_role = function (jid, role) end; -- luacheck: ignore 212 + get_jid_role = function (jid) end; + set_jid_role = function (jid, role) end; - get_users_with_role = function (role_name) end; -- luacheck: ignore 212 - add_default_permission = function (role_name, action, policy) end; -- luacheck: ignore 212 - get_role_by_name = function (role_name) end; -- luacheck: ignore 212 + get_users_with_role = function (role_name) end; + add_default_permission = function (role_name, action, policy) end; + get_role_by_name = function (role_name) end; }; local provider_mt = { __index = new_null_provider() }; -- cgit v1.2.3 From 51b17f1c20421231995d9aecc73e95d7d3e55948 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 18 Aug 2022 14:10:21 +0200 Subject: core.usermanager: Add missing methods to fallback authz provider --- core/usermanager.lua | 3 +++ 1 file changed, 3 insertions(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 03b27327..4bb63477 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -42,9 +42,12 @@ local fallback_authz_provider = { get_user_role = function (user) end; set_user_role = function (user, roles) end; + get_user_secondary_roles = function (user) end; add_user_secondary_role = function (user, host, role_name) end; remove_user_secondary_role = function (user, host, role_name) end; + user_can_assume_role = function(user, role_name) end; + get_jid_role = function (jid) end; set_jid_role = function (jid, role) end; -- cgit v1.2.3 From 1f4b94adabd686877059752453ecc4cb247b903d Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 18 Aug 2022 15:38:18 +0200 Subject: core.usermanager: Remove obsolete authz fallback method --- core/usermanager.lua | 1 - 1 file changed, 1 deletion(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 4bb63477..f8514bc4 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -36,7 +36,6 @@ end local fallback_authz_provider = { -- luacheck: ignore 212 - get_user_roles = function (user) end; get_jids_with_role = function (role) end; get_user_role = function (user) end; -- cgit v1.2.3 From b608af36ed2d57505ce4ba603dece395a68f3b17 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Thu, 18 Aug 2022 15:42:07 +0200 Subject: core.usermanager: Update argument name in authz fallback method It's not plural --- core/usermanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index f8514bc4..2c469269 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -39,7 +39,7 @@ local fallback_authz_provider = { get_jids_with_role = function (role) end; get_user_role = function (user) end; - set_user_role = function (user, roles) end; + set_user_role = function (user, role_name) end; get_user_secondary_roles = function (user) end; add_user_secondary_role = function (user, host, role_name) end; -- cgit v1.2.3 From 54fcc029c8fafd6e9fd9a38e279e5557177c1370 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 26 Aug 2022 17:04:15 +0100 Subject: mod_smacks: Long overdue cleanup of resumption code, fixes some old TODOs --- core/sessionmanager.lua | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) (limited to 'core') diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index dec21674..cdfd040f 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -10,7 +10,7 @@ local tostring, setmetatable = tostring, setmetatable; local pairs, next= pairs, next; -local hosts = prosody.hosts; +local prosody, hosts = prosody, prosody.hosts; local full_sessions = prosody.full_sessions; local bare_sessions = prosody.bare_sessions; @@ -92,6 +92,49 @@ local function retire_session(session) return setmetatable(session, resting_session); end +-- Update a session with a new one (transplanting connection, filters, etc.) +-- new_session should be discarded after this call returns +local function update_session(to_session, from_session) + to_session.log("debug", "Updating with parameters from session %s", from_session.id); + from_session.log("debug", "Session absorbed into %s", to_session.id); + + local replaced_conn = to_session.conn; + if replaced_conn then + to_session.log("debug", "closing a replaced connection for this session"); + replaced_conn:close(); + end + + to_session.ip = from_session.ip; + to_session.conn = from_session.conn; + to_session.rawsend = from_session.rawsend; + to_session.rawsend.session = to_session; + to_session.rawsend.conn = to_session.conn; + to_session.send = from_session.send; + to_session.send.session = to_session; + to_session.close = from_session.close; + to_session.filter = from_session.filter; + to_session.filter.session = to_session; + to_session.filters = from_session.filters; + to_session.send.filter = to_session.filter; + to_session.stream = from_session.stream; + to_session.secure = from_session.secure; + to_session.hibernating = nil; + to_session.resumption_counter = (to_session.resumption_counter or 0) + 1; + from_session.log = to_session.log; + from_session.type = to_session.type; + -- Inform xmppstream of the new session (passed to its callbacks) + to_session.stream:set_session(to_session); + + -- Retire the session we've pulled from, to avoid two sessions on the same connection + retire_session(from_session); + + prosody.events.fire_event("c2s-session-updated", { + session = to_session; + from_session = from_session; + replaced_conn = replaced_conn; + }); +end + local function destroy_session(session, err) (session.log or log)("debug", "Destroying session for %s (%s@%s)%s", session.full_jid or "(unknown)", session.username or "(unknown)", @@ -267,6 +310,7 @@ end return { new_session = new_session; retire_session = retire_session; + update_session = update_session; destroy_session = destroy_session; make_authenticated = make_authenticated; bind_resource = bind_resource; -- cgit v1.2.3 From a018497a27dea6d379d7772b36c4ba5593d67ecb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Fri, 26 Aug 2022 19:10:15 +0200 Subject: mod_s2s: Simplify conditionals since all sessions should have .host now --- core/stanza_router.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core') diff --git a/core/stanza_router.lua b/core/stanza_router.lua index b54ea1ab..89a02c02 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -127,7 +127,7 @@ function core_process_stanza(origin, stanza) end core_post_stanza(origin, stanza, origin.full_jid); else - local h = hosts[stanza.attr.to or origin.host or origin.to_host]; + local h = hosts[stanza.attr.to or origin.host]; if h then local event; if xmlns == nil then @@ -143,7 +143,7 @@ function core_process_stanza(origin, stanza) if h.events.fire_event(event, {origin = origin, stanza = stanza}) then return; end end if host and not hosts[host] then host = nil; end -- COMPAT: workaround for a Pidgin bug which sets 'to' to the SRV result - handle_unhandled_stanza(host or origin.host or origin.to_host, origin, stanza); + handle_unhandled_stanza(host or origin.host, origin, stanza); end end -- cgit v1.2.3 From 6a37ab635638f39f26359fcac9b50844a69dfdcb Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Sat, 27 Aug 2022 17:01:36 +0200 Subject: core.usermanager: Link to docs for new role API to make warning more actionable --- core/usermanager.lua | 1 + 1 file changed, 1 insertion(+) (limited to 'core') diff --git a/core/usermanager.lua b/core/usermanager.lua index 2c469269..fcb1fa9b 100644 --- a/core/usermanager.lua +++ b/core/usermanager.lua @@ -234,6 +234,7 @@ local function is_admin(jid, host) return false; end log("warn", "Usage of legacy is_admin() API, which will be disabled in a future build: %s", debug.traceback()); + log("warn", "See https://prosody.im/doc/developers/permissions about the new permissions API"); return legacy_admin_roles[get_jid_role(jid, host)] or false; end -- cgit v1.2.3 From 03b3b1b9ad4b6a0b749d0345b34591694bb0cd90 Mon Sep 17 00:00:00 2001 From: Kim Alvefur Date: Mon, 29 Aug 2022 11:47:31 +0200 Subject: core.moduleapi: Check for local role-aware sessions before e.g. s2s The condition checked for s2sin but not s2sout, so would have ignored bidi-enabled s2sout sessions. Components as well. --- core/moduleapi.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'core') diff --git a/core/moduleapi.lua b/core/moduleapi.lua index 73ce4911..fd54500d 100644 --- a/core/moduleapi.lua +++ b/core/moduleapi.lua @@ -649,7 +649,15 @@ function api:may(action, context) if type(session) ~= "table" then error("Unable to identify actor session from context"); end - if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then + if session.role and session.type == "c2s" and session.host == self.host then + local permit = session.role:may(action, context); + if not permit then + self:log("debug", "Access denied: session %s (%s) may not %s (not permitted by role %s)", + session.id, session.full_jid, action, session.role.name + ); + end + return permit; + else local actor_jid = context.stanza.attr.from; local role = hosts[self.host].authz.get_jid_role(actor_jid); if not role then @@ -661,14 +669,6 @@ function api:may(action, context) self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", actor_jid, action, role.name); end return permit; - elseif session.role then - local permit = session.role:may(action, context); - if not permit then - self:log("debug", "Access denied: session %s (%s) may not %s (not permitted by role %s)", - session.id, session.full_jid, action, session.role.name - ); - end - return permit; end end -- cgit v1.2.3