changeset 5295:98d5acb93439

mod_authz_delegate: make resistant against startup order issues There is no guarantee that the target_host gets activated and initialized before the host this module is loaded on. As add_default_permission is called during load time by many modules, we need to be prepared to queue stuff.
author Jonas Schäfer <jonas@wielicki.name>
date Fri, 31 Mar 2023 16:56:42 +0200
parents 385346b6c81d
children 0f5657db1cfc
files mod_authz_delegate/mod_authz_delegate.lua
diffstat 1 files changed, 31 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/mod_authz_delegate/mod_authz_delegate.lua	Thu Mar 30 11:32:50 2023 +0100
+++ b/mod_authz_delegate/mod_authz_delegate.lua	Fri Mar 31 16:56:42 2023 +0200
@@ -1,6 +1,7 @@
 local target_host = assert(module:get_option("authz_delegate_to"));
 local this_host = module:get_host();
 
+local array = require"util.array";
 local jid_split = import("prosody.util.jid", "split");
 
 local hosts = prosody.hosts;
@@ -53,8 +54,23 @@
 	return nil, "cannot set jid role on delegation target"
 end
 
+local default_permission_queue = array{};
+
 function add_default_permission(role_name, action, policy)
-	return hosts[target_host].authz.add_default_permission(role_name, action, policy)
+	-- NOTE: we always record default permissions, because the delegated-to
+	-- host may be re-activated.
+	default_permission_queue:push({
+		role_name = role_name,
+		action = action,
+		policy = policy,
+	});
+	local target_host_object = hosts[target_host];
+	local authz = target_host_object and target_host_object.authz;
+	if not authz then
+		module:log("debug", "queueing add_default_permission call for later, %s is not active yet", target_host);
+		return;
+	end
+	return authz.add_default_permission(role_name, action, policy)
 end
 
 function get_role_by_name(role_name)
@@ -64,3 +80,17 @@
 function get_all_roles()
 	return hosts[target_host].authz.get_all_roles()
 end
+
+module:hook_global("host-activated", function(host)
+	if host == target_host then
+		local authz = hosts[target_host].authz;
+		module:log("debug", "replaying %d queued permission changes", #default_permission_queue);
+		assert(authz);
+		-- replay default permission changes, if any
+		for i, item in ipairs(default_permission_queue) do
+			authz.add_default_permission(item.role_name, item.action, item.policy);
+		end
+		-- NOTE: we do not clear that array here -- in case the target_host is
+		-- re-activated
+	end
+end, -10000)