Comparison

plugins/mod_auth_ldap.lua @ 11837:a37bf4497280

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