Software / code / prosody
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); |