diff mod_password_policy/mod_password_policy.lua @ 4832:bfd4af4caddc

mod_password_policy: Support for additional policies provided by other modules E.g. check a password contains an uppercase character: module:provides("password-policy", { name = "contains_uppercase"; check_password = function (password, policy) return (policy ~= true) or (not not password:match("%u")); end; }) Config: password_policy = { contains_uppercase = true; }
author Matthew Wild <mwild1@gmail.com>
date Wed, 22 Dec 2021 14:48:46 +0000
parents 5a42cb84c8ee
children d3b69859553a
line wrap: on
line diff
--- a/mod_password_policy/mod_password_policy.lua	Wed Dec 22 14:43:53 2021 +0000
+++ b/mod_password_policy/mod_password_policy.lua	Wed Dec 22 14:48:46 2021 +0000
@@ -9,6 +9,10 @@
 --    }
 
 
+local it = require "util.iterators";
+local set = require "util.set";
+local st = require "util.stanza";
+
 local options = module:get_option("password_policy");
 
 options = options or {};
@@ -17,7 +21,21 @@
 	options.exclude_username = true;
 end
 
-local st = require "util.stanza";
+local builtin_policies = set.new({ "length", "exclude_username" });
+local extra_policies = set.new(it.to_array(it.keys(options))) - builtin_policies;
+
+local extra_policy_handlers = {};
+
+module:handle_items("password-policy-provider", function (event)
+	-- Password policy handler added
+	local item = event.item;
+	module:log("error", "Adding password policy handler '%s'", item.name);
+	extra_policy_handlers[item.name] = item.check_password;
+end, function (event)
+	-- Password policy handler removed
+	local item = event.item;
+	extra_policy_handlers[item.name] = nil;
+end);
 
 function check_password(password, additional_info)
 	if not password or password == "" then
@@ -34,6 +52,19 @@
 			return nil, "Password must not include your username", "username";
 		end
 	end
+
+	for policy in extra_policies do
+		local handler = extra_policy_handlers[policy];
+		if not handler then
+			module:log("error", "No policy handler found for '%s' (typo, or module not loaded?)", policy);
+			return nil, "Internal error while verifying password", "internal";
+		end
+		local ok, reason_text, reason_name = handler(password, options[policy], additional_info);
+		if ok ~= true then
+			return nil, reason_text or ("Password failed %s check"):format(policy), reason_name or policy;
+		end
+	end
+
 	return true;
 end