Changeset

13831:bf4cd327966f

Merge 13.0->trunk
author Matthew Wild <mwild1@gmail.com>
date Wed, 09 Apr 2025 10:59:28 +0100
parents 13826:53eb500b19a4 (current diff) 13830:145b71d1283c (diff)
children 13837:0b63c2c3b563
files
diffstat 5 files changed, 133 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/core/modulemanager.lua	Thu Apr 03 20:37:03 2025 +0100
+++ b/core/modulemanager.lua	Wed Apr 09 10:59:28 2025 +0100
@@ -54,50 +54,58 @@
 local _ENV = nil;
 -- luacheck: std none
 
-local loader = pluginloader.init({
-	load_filter_cb = function (path, content)
-		local metadata = {};
-		for line in content:gmatch("([^\r\n]+)\r?\n") do
-			local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$");
-			if key then
-				value = value:gsub("%s+$", "");
-				metadata[key] = value;
+local function plugin_load_filter_cb(path, content)
+	local metadata = {};
+	for line in content:gmatch("([^\r\n]+)\r?\n") do
+		local key, value = line:match("^%-%-%% *([%w_]+): *(.+)$");
+		if key then
+			value = value:gsub("%s+$", "");
+			metadata[key] = value;
+		end
+	end
+
+	if metadata.lua then
+		local supported = false;
+		for supported_lua_version in metadata.lua:gmatch("[^, ]+") do
+			if supported_lua_version == lua_version then
+				supported = true;
+				break;
 			end
 		end
-
-		if metadata.lua then
-			local supported = false;
-			for supported_lua_version in metadata.lua:gmatch("[^, ]+") do
-				if supported_lua_version == lua_version then
-					supported = true;
-					break;
-				end
+		if not supported then
+			if prosody.process_type ~= "prosodyctl" then
+				log("warn", "Not loading module, we have Lua %s but the module requires one of (%s): %s", lua_version, metadata.lua, path);
 			end
-			if not supported then
-				log("warn", "Not loading module, we have Lua %s but the module requires one of (%s): %s", lua_version, metadata.lua, path);
-				return; -- Don't load this module
-			end
+			return nil, "incompatible with Lua "..lua_version; -- Don't load this module
 		end
+	end
 
-		if metadata.conflicts then
-			local conflicts_features = set.new(array.collect(metadata.conflicts:gmatch("[^, ]+")));
-			local conflicted_features = set.intersection(conflicts_features, core_features);
-			if not conflicted_features:empty() then
-				log("warn", "Not loading module, due to conflicting features '%s': %s", conflicted_features, path);
-				return; -- Don't load this module
+	if metadata.conflicts then
+		local conflicts_features = set.new(array.collect(metadata.conflicts:gmatch("[^, ]+")));
+		local conflicted_features = set.intersection(conflicts_features, core_features);
+		if not conflicted_features:empty() then
+			if prosody.process_type ~= "prosodyctl" then
+				log("warn", "Not loading module, due to conflict with built-in features '%s': %s", conflicted_features, path);
 			end
+			return nil, "conflict with built-in feature"; -- Don't load this module
 		end
-		if metadata.requires then
-			local required_features = set.new(array.collect(metadata.requires:gmatch("[^, ]+")));
-			local missing_features = required_features - core_features;
-			if not missing_features:empty() then
+	end
+	if metadata.requires then
+		local required_features = set.new(array.collect(metadata.requires:gmatch("[^, ]+")));
+		local missing_features = required_features - core_features;
+		if not missing_features:empty() then
+			if prosody.process_type ~= "prosodyctl" then
 				log("warn", "Not loading module, due to missing features '%s': %s", missing_features, path);
-				return; -- Don't load this module
 			end
+			return nil, "Prosody version missing required feature"; -- Don't load this module
 		end
+	end
 
-		return path, content, metadata;
-	end;
+	return path, content, metadata;
+end;
+
+local loader = pluginloader.init({
+	load_filter_cb = plugin_load_filter_cb;
 });
 
 local load_modules_for_host, load, unload, reload, get_module, get_items;
--- a/plugins/mod_admin_shell.lua	Thu Apr 03 20:37:03 2025 +0100
+++ b/plugins/mod_admin_shell.lua	Wed Apr 09 10:59:28 2025 +0100
@@ -139,6 +139,8 @@
   prosody:admin      - Host administrator
   prosody:operator   - Server administrator
 
+To view roles and policies, see the commands in 'help role'.
+
 Roles can be assigned using the user management commands (see 'help user').
 ]];
 
@@ -2458,6 +2460,64 @@
 	return true, ("Showing %d certificates in %s"):format(c, path);
 end
 
+def_env.role = new_section("Role and access management");
+
+describe_command [[role:list(host) - List known roles]]
+function def_env.role:list(host)
+	if not host then
+		return nil, "Specify which host to list roles for";
+	end
+	local role_list = {};
+	for _, role in it.sorted_pairs(um.get_all_roles(host)) do
+		table.insert(role_list, role);
+	end
+	table.sort(role_list, function (a, b)
+		if a.priority ~= b.priority then
+			return (a.priority or 0) > (b.priority or 0);
+		end
+		return a.name < b.name;
+	end);
+	for _, role in ipairs(role_list) do
+		self.session.print(role.name);
+	end
+	return true, ("Showing %d roles on %s"):format(#role_list, host);
+end
+
+describe_command [[role:show(host, role_name) - Show information about a role]]
+function def_env.role:show(host, role_name)
+	if not host or not role_name then
+		return nil, "Specify the host and role to show";
+	end
+
+	local print = self.session.print;
+	local role = um.get_role_by_name(role_name, host);
+
+	if not role then
+		return nil, ("Unable to find role %s on host %s"):format(role_name, host);
+	end
+
+	local inherits = {};
+	for _, inherited_role in ipairs(role.inherits or {}) do
+		table.insert(inherits, inherited_role.name);
+	end
+
+	local permissions = {};
+	for permission, is_allowed in role:policies() do
+		permissions[permission] = is_allowed and "allowed" or "denied";
+	end
+
+	print("Name:    ", role.name);
+	print("Inherits:", table.concat(inherits, ", "));
+	print("Policies:");
+	local c = 0;
+	for permission, policy in it.sorted_pairs(permissions) do
+		c = c + 1;
+		print("  ["..(policy == "allowed" and "+" or " ").."] " .. permission);
+	end
+	print("");
+	return true, ("Showing role %s with %d policies"):format(role.name, c);
+end
+
 def_env.stats = new_section("Commands to show internal statistics");
 
 local short_units = {
--- a/plugins/mod_authz_internal.lua	Thu Apr 03 20:37:03 2025 +0100
+++ b/plugins/mod_authz_internal.lua	Wed Apr 09 10:59:28 2025 +0100
@@ -298,7 +298,11 @@
 end
 
 function get_role_by_name(role_name)
-	return assert(role_registry[role_name], role_name);
+	local role = role_registry[role_name];
+	if not role then
+		return error("Unknown role: "..role_name);
+	end
+	return role, role_name;
 end
 
 function get_all_roles()
--- a/util/pluginloader.lua	Thu Apr 03 20:37:03 2025 +0100
+++ b/util/pluginloader.lua	Wed Apr 09 10:59:28 2025 +0100
@@ -25,6 +25,7 @@
 function pluginloader_methods:load_file(names)
 	local file, err, path;
 	local load_filter_cb = self._options.load_filter_cb;
+	local last_filter_path, last_filter_err;
 	for i=1,#plugin_dir do
 		for j=1,#names do
 			path = plugin_dir[i]..names[j];
@@ -36,12 +37,18 @@
 				if load_filter_cb then
 					path, content, metadata = load_filter_cb(path, content);
 				end
-				if content and path then
+				if path and content then
 					return content, path, metadata;
+				else
+					last_filter_path = plugin_dir[i]..names[j];
+					last_filter_err = content or "skipped";
 				end
 			end
 		end
 	end
+	if last_filter_err then
+		return nil, err..(" (%s skipped because of %s)"):format(last_filter_path, last_filter_err);
+	end
 	return file, err;
 end
 
--- a/util/prosodyctl/check.lua	Thu Apr 03 20:37:03 2025 +0100
+++ b/util/prosodyctl/check.lua	Wed Apr 09 10:59:28 2025 +0100
@@ -644,6 +644,25 @@
 			print("    been deprecated. You can safely remove it.");
 		end
 
+		local load_failures = {};
+		for mod_name in all_modules do
+			local mod, err = modulemanager.loader:load_resource(mod_name, nil);
+			if not mod then
+				load_failures[mod_name] = err;
+			end
+		end
+
+		if next(load_failures) ~= nil then
+			print("");
+			print("    The following modules failed to load:");
+			print("");
+			for mod_name, err in it.sorted_pairs(load_failures) do
+				print(("        mod_%s: %s"):format(mod_name, err));
+			end
+			print("")
+			print("    Check for typos and remove any obsolete/incompatible modules from your config.");
+		end
+
 		for host, host_config in pairs(config) do --luacheck: ignore 213/host
 			if type(rawget(host_config, "storage")) == "string" and rawget(host_config, "default_storage") then
 				print("");