1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : ejabberd_auth_ldap.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Authentification via LDAP |
5 |
|
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2013 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(ejabberd_auth_ldap). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
%% gen_server callbacks |
30 |
|
-behaviour(gen_server). |
31 |
|
-export([init/1, handle_info/2, handle_call/3, |
32 |
|
handle_cast/2, terminate/2, code_change/3]). |
33 |
|
|
34 |
|
%% External exports |
35 |
|
-behaviour(mongoose_gen_auth). |
36 |
|
|
37 |
|
-export([start/1, |
38 |
|
stop/1, |
39 |
|
config_spec/0, |
40 |
|
start_link/1, |
41 |
|
set_password/4, |
42 |
|
authorize/1, |
43 |
|
try_register/4, |
44 |
|
get_registered_users/3, |
45 |
|
get_registered_users_number/3, |
46 |
|
does_user_exist/3, |
47 |
|
remove_user/3, |
48 |
|
supports_sasl_module/2, |
49 |
|
supported_features/0 |
50 |
|
]). |
51 |
|
|
52 |
|
%% Internal |
53 |
|
-export([check_password/4, |
54 |
|
check_password/6]). |
55 |
|
|
56 |
|
-ignore_xref([start_link/1]). |
57 |
|
|
58 |
|
-include("mongoose_config_spec.hrl"). |
59 |
|
-include_lib("eldap/include/eldap.hrl"). |
60 |
|
|
61 |
|
-record(state, |
62 |
|
{host_type :: mongooseim:host_type(), |
63 |
|
eldap_id :: eldap_utils:eldap_id(), |
64 |
|
bind_eldap_id :: eldap_utils:eldap_id(), |
65 |
|
base = <<>> :: binary(), |
66 |
|
uids = [] :: [{binary()} | {binary(), binary()}], |
67 |
|
ufilter = <<>> :: binary(), |
68 |
|
sfilter = <<>> :: binary(), |
69 |
|
lfilter :: {any(), any()} | undefined, |
70 |
|
deref = neverDerefAliases :: eldap_utils:deref(), |
71 |
|
dn_filter :: eldap_utils:dn() | undefined, |
72 |
|
dn_filter_attrs = [] :: [binary()] |
73 |
|
}). |
74 |
|
-type state() :: #state{}. |
75 |
|
|
76 |
:-( |
handle_cast(_Request, State) -> {noreply, State}. |
77 |
|
|
78 |
:-( |
code_change(_OldVsn, State, _Extra) -> {ok, State}. |
79 |
|
|
80 |
:-( |
handle_info(_Info, State) -> {noreply, State}. |
81 |
|
|
82 |
|
-define(LDAP_SEARCH_TIMEOUT, 5). |
83 |
|
|
84 |
|
%%%---------------------------------------------------------------------- |
85 |
|
%%% API |
86 |
|
%%%---------------------------------------------------------------------- |
87 |
|
|
88 |
|
-spec start(HostType :: mongooseim:host_type()) -> ok. |
89 |
|
start(HostType) -> |
90 |
:-( |
Proc = gen_mod:get_module_proc(HostType, ?MODULE), |
91 |
:-( |
ChildSpec = {Proc, {?MODULE, start_link, [HostType]}, |
92 |
|
transient, 1000, worker, [?MODULE]}, |
93 |
:-( |
ejabberd_sup:start_child(ChildSpec), |
94 |
:-( |
ok. |
95 |
|
|
96 |
|
-spec stop(HostType :: mongooseim:host_type()) -> ok. |
97 |
|
stop(HostType) -> |
98 |
:-( |
Proc = gen_mod:get_module_proc(HostType, ?MODULE), |
99 |
:-( |
gen_server:call(Proc, stop), |
100 |
:-( |
ejabberd_sup:stop_child(Proc), |
101 |
:-( |
ok. |
102 |
|
|
103 |
|
-spec config_spec() -> mongoose_config_spec:config_section(). |
104 |
|
config_spec() -> |
105 |
200 |
CommonLDAPSpec = mongoose_ldap_config:spec(), |
106 |
200 |
Items = #{<<"bind_pool_tag">> => #option{type = atom, |
107 |
|
validate = non_empty}, |
108 |
|
<<"uids">> => #list{items = mongoose_ldap_config:uids()}, |
109 |
|
<<"dn_filter">> => mongoose_ldap_config:dn_filter(), |
110 |
|
<<"local_filter">> => mongoose_ldap_config:local_filter()}, |
111 |
200 |
Defaults = #{<<"bind_pool_tag">> => bind, |
112 |
|
<<"base">> => <<>>, |
113 |
|
<<"uids">> => [{<<"uid">>, <<"%u">>}], |
114 |
|
<<"dn_filter">> => {undefined, []}, |
115 |
|
<<"local_filter">> => undefined}, |
116 |
200 |
CommonLDAPSpec#section{items = maps:merge(CommonLDAPSpec#section.items, Items), |
117 |
|
defaults = maps:merge(CommonLDAPSpec#section.defaults, Defaults)}. |
118 |
|
|
119 |
|
-spec start_link(HostType :: mongooseim:host_type()) -> {ok, pid()} | {error, any()}. |
120 |
|
start_link(HostType) -> |
121 |
:-( |
Proc = gen_mod:get_module_proc(HostType, ?MODULE), |
122 |
:-( |
gen_server:start_link({local, Proc}, ?MODULE, HostType, []). |
123 |
|
|
124 |
:-( |
terminate(_Reason, _State) -> ok. |
125 |
|
|
126 |
|
-spec init(HostType :: mongooseim:host_type()) -> {'ok', state()}. |
127 |
|
init(HostType) -> |
128 |
:-( |
State = parse_options(HostType), |
129 |
:-( |
{ok, State}. |
130 |
|
|
131 |
|
-spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean(). |
132 |
:-( |
supports_sasl_module(_, cyrsasl_plain) -> true; |
133 |
:-( |
supports_sasl_module(_, cyrsasl_external) -> true; |
134 |
:-( |
supports_sasl_module(_, _) -> false. |
135 |
|
|
136 |
|
-spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()} |
137 |
|
| {error, any()}. |
138 |
|
authorize(Creds) -> |
139 |
:-( |
case mongoose_credentials:get(Creds, cert_file, false) of |
140 |
:-( |
true -> verify_user_exists(Creds); |
141 |
:-( |
false -> ejabberd_auth:authorize_with_check_password(?MODULE, Creds) |
142 |
|
end. |
143 |
|
|
144 |
|
-spec check_password(HostType :: mongooseim:host_type(), |
145 |
|
LUser :: jid:luser(), |
146 |
|
LServer :: jid:lserver(), |
147 |
|
Password :: binary()) -> boolean(). |
148 |
:-( |
check_password(_HostType, _LUser, _LServer, <<"">>) -> false; |
149 |
|
check_password(HostType, LUser, LServer, Password) -> |
150 |
:-( |
case catch check_password_ldap(HostType, LUser, LServer, Password) of |
151 |
:-( |
{'EXIT', _} -> false; |
152 |
:-( |
Result -> Result |
153 |
|
end. |
154 |
|
|
155 |
|
-spec check_password(HostType :: mongooseim:host_type(), |
156 |
|
LUser :: jid:luser(), |
157 |
|
LServer :: jid:lserver(), |
158 |
|
Password :: binary(), |
159 |
|
Digest :: binary(), |
160 |
|
DigestGen :: fun()) -> boolean(). |
161 |
|
check_password(HostType, LUser, LServer, Password, _Digest, |
162 |
|
_DigestGen) -> |
163 |
:-( |
check_password(HostType, LUser, LServer, Password). |
164 |
|
|
165 |
|
|
166 |
|
-spec set_password(HostType :: mongooseim:host_type(), |
167 |
|
LUser :: jid:luser(), |
168 |
|
LServer :: jid:lserver(), |
169 |
|
Password :: binary()) |
170 |
|
-> ok | {error, not_allowed | invalid_jid | user_not_found}. |
171 |
|
set_password(HostType, LUser, LServer, Password) -> |
172 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
173 |
:-( |
case find_user_dn(LUser, LServer, State) of |
174 |
:-( |
false -> {error, user_not_found}; |
175 |
|
DN -> |
176 |
:-( |
eldap_pool:modify_passwd(State#state.eldap_id, DN, Password) |
177 |
|
end. |
178 |
|
|
179 |
|
%% TODO Support multiple domains |
180 |
|
-spec try_register(HostType :: mongooseim:host_type(), LUser :: jid:luser(), |
181 |
|
LServer :: jid:lserver(), Password :: binary()) -> |
182 |
|
ok | {error, exists}. |
183 |
|
try_register(HostType, LUser, _LServer, Password) -> |
184 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
185 |
:-( |
UserStr = binary_to_list(LUser), |
186 |
:-( |
DN = "cn=" ++ UserStr ++ ", " ++ binary_to_list(State#state.base), |
187 |
:-( |
Attrs = [{"objectclass", ["inetOrgPerson"]}, |
188 |
|
{"cn", [UserStr]}, |
189 |
|
{"sn", [UserStr]}, |
190 |
|
{"userPassword", [binary_to_list(Password)]}, |
191 |
|
{"uid", [UserStr]}], |
192 |
:-( |
case eldap_pool:add(State#state.eldap_id, DN, Attrs) of |
193 |
:-( |
ok -> ok; |
194 |
:-( |
_ -> {error, exists} |
195 |
|
end. |
196 |
|
|
197 |
|
-spec get_registered_users(HostType :: mongooseim:host_type(), |
198 |
|
LServer :: jid:lserver(), |
199 |
|
Opts :: list()) -> [jid:simple_bare_jid()]. |
200 |
|
get_registered_users(HostType, LServer, _) -> |
201 |
:-( |
case catch get_registered_users_ldap(HostType, LServer) of |
202 |
:-( |
{'EXIT', _} -> []; |
203 |
:-( |
Result -> Result |
204 |
|
end. |
205 |
|
|
206 |
|
|
207 |
|
-spec get_registered_users_number(HostType :: mongooseim:host_type(), |
208 |
|
LServer :: jid:lserver(), |
209 |
|
Opts :: list()) -> non_neg_integer(). |
210 |
|
get_registered_users_number(HostType, LServer, Opts) -> |
211 |
:-( |
length(get_registered_users(HostType, LServer, Opts)). |
212 |
|
|
213 |
|
|
214 |
|
-spec does_user_exist(HostType :: mongooseim:host_type(), |
215 |
|
LUser :: jid:luser(), |
216 |
|
LServer :: jid:lserver()) -> boolean() | {error, atom()}. |
217 |
|
does_user_exist(HostType, LUser, LServer) -> |
218 |
:-( |
case catch does_user_exist_in_ldap(HostType, LUser, LServer) of |
219 |
:-( |
{'EXIT', Error} -> {error, Error}; |
220 |
:-( |
Result -> Result |
221 |
|
end. |
222 |
|
|
223 |
|
|
224 |
|
-spec remove_user(HostType :: mongooseim:host_type(), |
225 |
|
LUser :: jid:luser(), |
226 |
|
LServer :: jid:lserver()) -> ok | {error, not_allowed}. |
227 |
|
remove_user(HostType, LUser, LServer) -> |
228 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
229 |
:-( |
case find_user_dn(LUser, LServer, State) of |
230 |
:-( |
false -> {error, not_allowed}; |
231 |
:-( |
DN -> eldap_pool:delete(State#state.eldap_id, DN) |
232 |
|
end. |
233 |
|
|
234 |
|
%% Multiple domains are not supported for in-band registration |
235 |
|
-spec supported_features() -> [atom()]. |
236 |
:-( |
supported_features() -> [dynamic_domains]. |
237 |
|
|
238 |
|
%%%---------------------------------------------------------------------- |
239 |
|
%%% Internal functions |
240 |
|
%%%---------------------------------------------------------------------- |
241 |
|
|
242 |
|
-spec verify_user_exists(mongoose_credentials:t()) -> |
243 |
|
{ok, mongoose_credentials:t()} | {error, not_authorized}. |
244 |
|
verify_user_exists(Creds) -> |
245 |
:-( |
User = mongoose_credentials:get(Creds, username), |
246 |
:-( |
case jid:nodeprep(User) of |
247 |
|
error -> |
248 |
:-( |
error({nodeprep_error, User}); |
249 |
|
LUser -> |
250 |
:-( |
LServer = mongoose_credentials:lserver(Creds), |
251 |
:-( |
HostType = mongoose_credentials:host_type(Creds), |
252 |
:-( |
case does_user_exist(HostType, LUser, LServer) of |
253 |
:-( |
true -> {ok, mongoose_credentials:extend(Creds, [{auth_module, ?MODULE}])}; |
254 |
:-( |
false -> {error, not_authorized} |
255 |
|
end |
256 |
|
end. |
257 |
|
|
258 |
|
-spec check_password_ldap(HostType :: mongooseim:host_type(), |
259 |
|
LUser :: jid:luser(), |
260 |
|
LServer :: jid:lserver(), |
261 |
|
Password :: binary()) -> boolean(). |
262 |
|
check_password_ldap(HostType, LUser, LServer, Password) -> |
263 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
264 |
:-( |
case find_user_dn(LUser, LServer, State) of |
265 |
:-( |
false -> false; |
266 |
|
DN -> |
267 |
:-( |
case eldap_pool:bind(State#state.bind_eldap_id, DN, Password) of |
268 |
:-( |
ok -> true; |
269 |
:-( |
_ -> false |
270 |
|
end |
271 |
|
end. |
272 |
|
|
273 |
|
|
274 |
|
-spec get_registered_users_ldap(mongooseim:host_type(), jid:lserver()) -> [jid:simple_bare_jid()]. |
275 |
|
get_registered_users_ldap(HostType, LServer) -> |
276 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
277 |
:-( |
UIDs = State#state.uids, |
278 |
:-( |
EldapID = State#state.eldap_id, |
279 |
:-( |
ResAttrs = result_attrs(State), |
280 |
:-( |
case eldap_filter:parse(State#state.sfilter) of |
281 |
|
{ok, EldapFilter} -> |
282 |
:-( |
case eldap_pool:search(EldapID, |
283 |
|
[{base, State#state.base}, |
284 |
|
{filter, EldapFilter}, |
285 |
|
{timeout, ?LDAP_SEARCH_TIMEOUT}, |
286 |
|
{deref, State#state.deref}, |
287 |
|
{attributes, ResAttrs}]) of |
288 |
|
#eldap_search_result{entries = Entries} -> |
289 |
:-( |
get_users_from_ldap_entries(Entries, UIDs, LServer, State); |
290 |
:-( |
_ -> [] |
291 |
|
end; |
292 |
:-( |
_ -> [] |
293 |
|
end. |
294 |
|
|
295 |
|
-spec get_users_from_ldap_entries(list(), [{binary()} | {binary(), binary()}], |
296 |
|
jid:lserver(), state()) -> list(). |
297 |
|
get_users_from_ldap_entries(LDAPEntries, UIDs, LServer, State) -> |
298 |
:-( |
lists:flatmap( |
299 |
|
fun(#eldap_entry{attributes = Attrs, |
300 |
|
object_name = DN}) -> |
301 |
:-( |
case is_valid_dn(DN, LServer, Attrs, State) of |
302 |
:-( |
false -> []; |
303 |
|
true -> |
304 |
:-( |
get_user_from_ldap_attributes(UIDs, Attrs, LServer) |
305 |
|
end |
306 |
|
end, |
307 |
|
LDAPEntries). |
308 |
|
|
309 |
|
-spec get_user_from_ldap_attributes([{binary()} | {binary(), binary()}], |
310 |
|
[{binary(), [binary()]}], jid:lserver()) |
311 |
|
-> list(). |
312 |
|
get_user_from_ldap_attributes(UIDs, Attributes, LServer) -> |
313 |
:-( |
case eldap_utils:find_ldap_attrs(UIDs, Attributes) of |
314 |
:-( |
<<"">> -> []; |
315 |
|
{User, UIDFormat} -> |
316 |
:-( |
case eldap_utils:get_user_part(User, UIDFormat) of |
317 |
|
{ok, U} -> |
318 |
:-( |
[{U, LServer}]; |
319 |
:-( |
_ -> [] |
320 |
|
end |
321 |
|
end. |
322 |
|
|
323 |
|
-spec does_user_exist_in_ldap(HostType :: mongooseim:host_type(), |
324 |
|
LUser :: jid:luser(), |
325 |
|
LServer :: jid:lserver()) -> boolean(). |
326 |
|
does_user_exist_in_ldap(HostType, LUser, LServer) -> |
327 |
:-( |
{ok, State} = eldap_utils:get_state(HostType, ?MODULE), |
328 |
:-( |
case find_user_dn(LUser, LServer, State) of |
329 |
:-( |
false -> false; |
330 |
:-( |
_DN -> true |
331 |
|
end. |
332 |
|
|
333 |
|
handle_call(get_state, _From, State) -> |
334 |
:-( |
{reply, {ok, State}, State}; |
335 |
|
handle_call(stop, _From, State) -> |
336 |
:-( |
{stop, normal, ok, State}; |
337 |
|
handle_call(_Request, _From, State) -> |
338 |
:-( |
{reply, bad_request, State}. |
339 |
|
|
340 |
|
-spec find_user_dn(LUser :: jid:luser(), |
341 |
|
LServer :: jid:lserver(), |
342 |
|
State :: state()) -> false | eldap_utils:dn(). |
343 |
|
find_user_dn(LUser, LServer, State) -> |
344 |
:-( |
ResAttrs = result_attrs(State), |
345 |
:-( |
case eldap_filter:parse(State#state.ufilter, [{<<"%u">>, LUser}]) of |
346 |
|
{ok, Filter} -> |
347 |
:-( |
SearchOpts = find_user_opts(Filter, ResAttrs, State), |
348 |
:-( |
case eldap_pool:search(State#state.eldap_id, SearchOpts) of |
349 |
|
#eldap_search_result{entries = |
350 |
|
[#eldap_entry{attributes = Attrs, |
351 |
|
object_name = DN} |
352 |
|
| _]} -> |
353 |
:-( |
dn_filter(DN, LServer, Attrs, State); |
354 |
:-( |
_ -> false |
355 |
|
end; |
356 |
:-( |
_ -> false |
357 |
|
end. |
358 |
|
|
359 |
|
find_user_opts(Filter, ResAttrs, State) -> |
360 |
:-( |
[{base, State#state.base}, {filter, Filter}, |
361 |
|
{deref, State#state.deref}, {attributes, ResAttrs}]. |
362 |
|
|
363 |
|
|
364 |
|
%% @doc apply the dn filter and the local filter: |
365 |
|
-spec dn_filter(DN :: eldap_utils:dn(), |
366 |
|
LServer :: jid:lserver(), |
367 |
|
Attrs :: [{binary(), [any()]}], |
368 |
|
State :: state()) -> false | eldap_utils:dn(). |
369 |
|
dn_filter(DN, LServer, Attrs, State) -> |
370 |
:-( |
case check_local_filter(Attrs, State) of |
371 |
:-( |
false -> false; |
372 |
|
true -> |
373 |
:-( |
case is_valid_dn(DN, LServer, Attrs, State) of |
374 |
:-( |
true -> DN; |
375 |
:-( |
false -> false |
376 |
|
end |
377 |
|
end. |
378 |
|
|
379 |
|
%% @doc Check that the DN is valid, based on the dn filter |
380 |
|
-spec is_valid_dn(DN :: eldap_utils:dn(), |
381 |
|
LServer :: jid:lserver(), |
382 |
|
Attrs :: [{binary(), [any()]}], |
383 |
|
State :: state()) -> boolean(). |
384 |
:-( |
is_valid_dn(_DN, _LServer, _, #state{dn_filter = undefined}) -> true; |
385 |
|
is_valid_dn(DN, LServer, Attrs, State) -> |
386 |
:-( |
DNAttrs = State#state.dn_filter_attrs, |
387 |
:-( |
UIDs = State#state.uids, |
388 |
:-( |
Values = [{<<"%s">>, eldap_utils:get_ldap_attr(Attr, Attrs), 1} |
389 |
:-( |
|| Attr <- DNAttrs], |
390 |
:-( |
SubstValues = case eldap_utils:find_ldap_attrs(UIDs, Attrs) of |
391 |
:-( |
<<>> -> Values; |
392 |
|
{S, UAF} -> |
393 |
:-( |
case eldap_utils:get_user_part(S, UAF) of |
394 |
:-( |
{ok, U} -> [{<<"%u">>, U} | Values]; |
395 |
:-( |
_ -> Values |
396 |
|
end |
397 |
|
end ++ [{<<"%d">>, LServer}, {<<"%D">>, DN}], |
398 |
:-( |
case eldap_filter:parse(State#state.dn_filter, SubstValues) of |
399 |
|
{ok, EldapFilter} -> |
400 |
:-( |
case eldap_pool:search(State#state.eldap_id, |
401 |
|
[{base, State#state.base}, |
402 |
|
{filter, EldapFilter}, |
403 |
|
{deref, State#state.deref}, |
404 |
|
{attributes, [<<"dn">>]}]) |
405 |
|
of |
406 |
:-( |
#eldap_search_result{entries = [_ | _]} -> true; |
407 |
:-( |
_ -> false |
408 |
|
end; |
409 |
:-( |
_ -> false |
410 |
|
end. |
411 |
|
|
412 |
|
|
413 |
|
%% @doc The local filter is used to check an attribute in ejabberd |
414 |
|
%% and not in LDAP to limit the load on the LDAP directory. |
415 |
|
%% A local rule can be either: |
416 |
|
%% {equal, {"accountStatus", ["active"]}} |
417 |
|
%% {notequal, {"accountStatus", ["disabled"]}} |
418 |
|
%% {ldap_local_filter, {notequal, {"accountStatus", ["disabled"]}}} |
419 |
|
-spec check_local_filter(Attrs :: [{binary(), [any()]}], |
420 |
|
State :: state()) -> boolean(). |
421 |
|
check_local_filter(_Attrs, |
422 |
|
#state{lfilter = undefined}) -> |
423 |
:-( |
true; |
424 |
|
check_local_filter(Attrs, |
425 |
|
#state{lfilter = LocalFilter}) -> |
426 |
:-( |
{Operation, FilterMatch} = LocalFilter, |
427 |
:-( |
local_filter(Operation, Attrs, FilterMatch). |
428 |
|
|
429 |
|
|
430 |
|
-spec local_filter('equal' | 'notequal', |
431 |
|
Attrs :: [{binary(), [any()]}], |
432 |
|
FilterMatch :: {_, _}) -> boolean(). |
433 |
|
local_filter(equal, Attrs, FilterMatch) -> |
434 |
:-( |
{Attr, Value} = FilterMatch, |
435 |
:-( |
case lists:keysearch(Attr, 1, Attrs) of |
436 |
:-( |
false -> false; |
437 |
:-( |
{value, {Attr, Value}} -> true; |
438 |
:-( |
_ -> false |
439 |
|
end; |
440 |
|
local_filter(notequal, Attrs, FilterMatch) -> |
441 |
:-( |
not local_filter(equal, Attrs, FilterMatch). |
442 |
|
|
443 |
|
|
444 |
|
-spec result_attrs(state()) -> maybe_improper_list(). |
445 |
|
result_attrs(#state{uids = UIDs, |
446 |
|
dn_filter_attrs = DNFilterAttrs}) -> |
447 |
:-( |
lists:foldl(fun ({UID}, Acc) -> [UID | Acc]; |
448 |
:-( |
({UID, _}, Acc) -> [UID | Acc] |
449 |
|
end, |
450 |
|
DNFilterAttrs, UIDs). |
451 |
|
|
452 |
|
%%%---------------------------------------------------------------------- |
453 |
|
%%% Auxiliary functions |
454 |
|
%%%---------------------------------------------------------------------- |
455 |
|
|
456 |
|
-spec parse_options(HostType :: mongooseim:host_type()) -> state(). |
457 |
|
parse_options(HostType) -> |
458 |
:-( |
Opts = mongoose_config:get_opt([{auth, HostType}, ldap]), |
459 |
:-( |
#{pool_tag := PoolTag, |
460 |
|
bind_pool_tag := BindPoolTag, |
461 |
|
base := Base, |
462 |
|
deref := Deref, |
463 |
|
uids := RawUIDs, |
464 |
|
filter := RawUserFilter, |
465 |
|
dn_filter := {DNFilter, DNFilterAttrs}, |
466 |
|
local_filter := LocalFilter} = Opts, |
467 |
:-( |
EldapID = {HostType, PoolTag}, |
468 |
:-( |
BindEldapID = {HostType, BindPoolTag}, |
469 |
:-( |
DerefAliases = eldap_utils:deref_aliases(Deref), |
470 |
:-( |
UIDs = eldap_utils:uids_domain_subst(HostType, RawUIDs), |
471 |
:-( |
UserFilter = eldap_utils:process_user_filter(UIDs, RawUserFilter), |
472 |
:-( |
SearchFilter = eldap_utils:get_search_filter(UserFilter), |
473 |
:-( |
#state{host_type = HostType, |
474 |
|
eldap_id = EldapID, |
475 |
|
bind_eldap_id = BindEldapID, |
476 |
|
base = Base, |
477 |
|
deref = DerefAliases, |
478 |
|
uids = UIDs, |
479 |
|
ufilter = UserFilter, |
480 |
|
sfilter = SearchFilter, |
481 |
|
lfilter = LocalFilter, |
482 |
|
dn_filter = DNFilter, |
483 |
|
dn_filter_attrs = DNFilterAttrs}. |