1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
-- mod_auth_ldap
local new_sasl = require "prosody.util.sasl".new;
local lualdap = require "lualdap";
local function ldap_filter_escape(s)
return (s:gsub("[*()\\%z]", function(c) return ("\\%02x"):format(c:byte()) end));
end
-- Config options
local ldap_server = module:get_option_string("ldap_server", "localhost");
local ldap_rootdn = module:get_option_string("ldap_rootdn", "");
local ldap_password = module:get_option_string("ldap_password", "");
local ldap_tls = module:get_option_boolean("ldap_tls");
local ldap_scope = module:get_option_enum("ldap_scope", "subtree", "base", "onelevel");
local ldap_filter = module:get_option_string("ldap_filter", "(uid=$user)"):gsub("%%s", "$user", 1);
local ldap_base = assert(module:get_option_string("ldap_base"), "ldap_base is a required option for ldap");
local ldap_mode = module:get_option_enum("ldap_mode", "bind", "getpasswd");
local ldap_admins = module:get_option_string("ldap_admin_filter",
module:get_option_string("ldap_admins")); -- COMPAT with mistake in documentation
local host = ldap_filter_escape(module:get_option_string("realm", module.host));
if ldap_admins then
module:log("error", "The 'ldap_admin_filter' option has been deprecated, "..
"and will be ignored. Equivalent functionality may be added in "..
"the future if there is demand."
);
end
-- Initiate connection
local ld = nil;
module.unload = function() if ld then pcall(ld, ld.close); end end
function ldap_do_once(method, ...)
if ld == nil then
local err;
ld, err = lualdap.open_simple(ldap_server, ldap_rootdn, ldap_password, ldap_tls);
if not ld then return nil, err, "reconnect"; end
end
-- luacheck: ignore 411/success
local success, iterator, invariant, initial = pcall(ld[method], ld, ...);
if not success then ld = nil; return nil, iterator, "search"; end
local success, dn, attr = pcall(iterator, invariant, initial);
if not success then ld = nil; return success, dn, "iter"; end
return dn, attr, "return";
end
function ldap_do(method, retry_count, ...)
local dn, attr, where;
for _=1,1+retry_count do
dn, attr, where = ldap_do_once(method, ...);
if dn or not(attr) then break; end -- nothing or something found
module:log("warn", "LDAP: %s %s (in %s)", tostring(dn), tostring(attr), where);
-- otherwise retry
end
if not dn and attr then
module:log("error", "LDAP: %s", tostring(attr));
end
return dn, attr;
end
function get_user(username)
module:log("debug", "get_user(%q)", username);
return ldap_do("search", 2, {
base = ldap_base;
scope = ldap_scope;
sizelimit = 1;
filter = ldap_filter:gsub("%$(%a+)", {
user = ldap_filter_escape(username);
host = host;
});
});
end
local provider = {};
function provider.create_user(username, password) -- luacheck: ignore 212
return nil, "Account creation not available with LDAP.";
end
function provider.user_exists(username)
return not not get_user(username);
end
function provider.set_password(username, password)
local dn, attr = get_user(username);
if not dn then return nil, attr end
if attr.userPassword == password then return true end
return ldap_do("modify", 2, dn, { '=', userPassword = password });
end
if ldap_mode == "getpasswd" then
function provider.get_password(username)
local dn, attr = get_user(username);
if dn and attr then
return attr.userPassword;
end
end
function provider.test_password(username, password)
return provider.get_password(username) == password;
end
function provider.get_sasl_handler()
return new_sasl(module.host, {
plain = function(sasl, username) -- luacheck: ignore 212/sasl
local password = provider.get_password(username);
if not password then return "", nil; end
return password, true;
end
});
end
elseif ldap_mode == "bind" then
local function test_password(userdn, password)
local ok, err = lualdap.open_simple(ldap_server, userdn, password, ldap_tls);
if not ok then
module:log("debug", "ldap open_simple error: %s", err);
end
return not not ok;
end
function provider.test_password(username, password)
local dn = get_user(username);
if not dn then return end
return test_password(dn, password)
end
function provider.get_sasl_handler()
return new_sasl(module.host, {
plain_test = function(sasl, username, password) -- luacheck: ignore 212/sasl
return provider.test_password(username, password), true;
end
});
end
else
module:log("error", "Unsupported ldap_mode %s", tostring(ldap_mode));
end
module:provides("auth", provider);
|