./ct_report/coverage/ejabberd_auth.COVER.html

1 %%% File : ejabberd_auth.erl
2 %%% Author : Alexey Shchepin <alexey@process-one.net>
3 %%% Purpose : Authentification
4 %%% Created : 23 Nov 2002 by Alexey Shchepin <alexey@process-one.net>
5 %%%
6 %%%
7 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
8 %%%
9 %%% This program is free software; you can redistribute it and/or
10 %%% modify it under the terms of the GNU General Public License as
11 %%% published by the Free Software Foundation; either version 2 of the
12 %%% License, or (at your option) any later version.
13 %%%
14 %%% This program is distributed in the hope that it will be useful,
15 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
16 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 %%% General Public License for more details.
18 %%%
19 %%% You should have received a copy of the GNU General Public License
20 %%% along with this program; if not, write to the Free Software
21 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 %%%
23 %%%----------------------------------------------------------------------
24
25 -module(ejabberd_auth).
26 -author('alexey@process-one.net').
27
28 %% External exports
29 -export([start/0,
30 start/1,
31 stop/1,
32 authorize/1,
33 set_password/2,
34 check_password/2,
35 check_password/4,
36 try_register/2,
37 get_vh_registered_users/1,
38 get_vh_registered_users/2,
39 get_vh_registered_users_number/1,
40 get_vh_registered_users_number/2,
41 get_password_s/1,
42 get_passterm_with_authmodule/2,
43 does_user_exist/1,
44 does_user_exist/3,
45 does_stored_user_exist/2,
46 does_method_support/2,
47 remove_user/1,
48 supports_sasl_module/2,
49 entropy/1,
50 config_spec/1
51 ]).
52
53 -export([check_digest/4]).
54
55 -export([auth_modules/1,
56 auth_methods/1,
57 auth_modules_for_host_type/1,
58 methods_to_modules/1]).
59
60 %% Library functions for reuse in ejabberd_auth_* modules
61 -export([authorize_with_check_password/2]).
62
63 %% Hook handlers
64 -export([remove_domain/3]).
65 -export([on_does_user_exist/3]).
66
67 -ignore_xref([
68 auth_methods/1, auth_modules/1, check_password/4, get_vh_registered_users/2,
69 get_vh_registered_users_number/2, start/1, stop/1]).
70
71 -include("mongoose.hrl").
72 -include("jlib.hrl").
73
74 -export_type([authmodule/0,
75 passterm/0,
76 exist_type/0]).
77
78 -type authmodule() :: module().
79 -type passterm() :: binary() | mongoose_scram:scram_tuple() | mongoose_scram:scram_map().
80 -type exist_type() :: stored | with_anonymous.
81
82 %% Types defined below are used in call_auth_modules_*
83 -type mod_res() :: any().
84 -type host_type_mod_fun() :: fun((mongooseim:host_type(), authmodule()) -> mod_res()).
85 -type mod_fun() :: fun((authmodule()) -> mod_res()).
86 -type mod_fold_fun() :: fun((authmodule(), mod_res()) -> continue |
87 {continue, mod_res()} |
88 {stop, mod_res()}).
89 -type call_opts() :: #{default => mod_res(), op => map, metric => atom()}.
90
91 -define(METRIC(Name), [backends, auth, Name]).
92
93 %%%----------------------------------------------------------------------
94 %%% API
95 %%%----------------------------------------------------------------------
96 -spec start() -> 'ok'.
97 start() ->
98 12 lists:foreach(fun start/1, ?ALL_HOST_TYPES).
99
100 -spec start(HostType :: mongooseim:host_type()) -> 'ok'.
101 start(HostType) ->
102 59 ensure_metrics(HostType),
103 59 F = fun(Mod) -> mongoose_gen_auth:start(Mod, HostType) end,
104 59 call_auth_modules_for_host_type(HostType, F, #{op => map}),
105 59 gen_hook:add_handlers(hooks(HostType)),
106 59 ok.
107
108 -spec stop(HostType :: mongooseim:host_type()) -> 'ok'.
109 stop(HostType) ->
110 1 gen_hook:delete_handlers(hooks(HostType)),
111 1 F = fun(Mod) -> mongoose_gen_auth:stop(Mod, HostType) end,
112 1 call_auth_modules_for_host_type(HostType, F, #{op => map}),
113 1 ok.
114
115 -spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
116 hooks(HostType) ->
117 60 [
118 %% These hooks must run in between those of mod_cache_users
119 {does_user_exist, HostType, fun ?MODULE:on_does_user_exist/3, #{}, 50},
120 %% It is important that this handler happens _before_ all other modules
121 {remove_domain, HostType, fun ?MODULE:remove_domain/3, #{}, 10}
122 ].
123
124 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
125 supports_sasl_module(HostType, SASLModule) ->
126 147112 F = fun(Mod) ->
127 147178 case mongoose_gen_auth:supports_sasl_module(Mod, HostType, SASLModule) of
128 44404 true -> {stop, true};
129 102774 false -> continue
130 end
131 end,
132 147112 call_auth_modules_for_host_type(HostType, F, #{default => false}).
133
134 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
135 | {error, not_authorized}.
136 authorize(Creds) ->
137 6098 F = fun(Mod, {_CurResult, CurCreds}) ->
138 6098 case mongoose_gen_auth:authorize(Mod, CurCreds) of
139 {ok, NewCreds} ->
140 6082 {stop, {ok, mongoose_credentials:register(NewCreds, Mod, success)}};
141 Error ->
142 16 NewCreds = mongoose_credentials:register(CurCreds, Mod, {failure, Error}),
143 16 {continue, {not_authorized, NewCreds}}
144 end
145 end,
146 6098 Opts = #{default => {not_authorized, Creds}, metric => authorize},
147 6098 case call_auth_modules_with_creds(Creds, F, Opts) of
148 6082 Res = {ok, _Creds} -> Res;
149 16 {not_authorized, _Creds} -> {error, not_authorized}
150 end.
151
152 %% @doc Check if at least one authentication method accepts the user and the password.
153 -spec check_password(JID :: jid:jid() | error, Password :: binary()) -> boolean().
154 check_password(error, _Password) ->
155
:-(
false;
156 check_password(#jid{luser = LUser, lserver = LServer}, Password) ->
157 6 F = fun(HostType, Mod) ->
158 6 case mongoose_gen_auth:check_password(Mod, HostType, LUser, LServer, Password) of
159 3 true -> {stop, true};
160 3 false -> continue
161 end
162 end,
163 6 Opts = #{default => false, metric => check_password},
164 6 call_auth_modules_for_domain(LServer, F, Opts).
165
166 %% @doc Check if at least one authentication method accepts the user and the password.
167 -spec check_password(JID :: jid:jid() | error,
168 Password :: binary(),
169 Digest :: binary(),
170 DigestGen :: fun((binary()) -> binary())) -> boolean().
171 check_password(error, _, _, _) ->
172
:-(
false;
173 check_password(#jid{luser = LUser, lserver = LServer}, Password, Digest, DigestGen) ->
174
:-(
F = fun(HostType, Mod) ->
175
:-(
case mongoose_gen_auth:check_password(Mod, HostType, LUser, LServer,
176 Password, Digest, DigestGen) of
177
:-(
true -> {stop, true};
178
:-(
false -> continue
179 end
180 end,
181
:-(
Opts = #{default => false, metric => check_password},
182
:-(
call_auth_modules_for_domain(LServer, F, Opts).
183
184 -spec check_digest(binary(), fun((binary()) -> binary()), binary(), binary()) -> boolean().
185 check_digest(<<>>, _, <<>>, _) ->
186
:-(
false; %% empty digest and password
187 check_digest(Digest, DigestGen, _Password, Passwd) ->
188 1 Digest == DigestGen(Passwd).
189
190 -spec set_password(jid:jid() | error, binary()) ->
191 ok | {error, empty_password | not_allowed | invalid_jid}.
192 set_password(_, <<"">>) ->
193 7 {error, empty_password};
194 set_password(error, _) ->
195 1 {error, invalid_jid};
196 set_password(#jid{luser = LUser, lserver = LServer}, Password) ->
197 16 F = fun(HostType, Mod) ->
198 11 case mongoose_gen_auth:set_password(Mod, HostType, LUser, LServer, Password) of
199 11 ok -> {stop, ok};
200
:-(
{error, Error} -> {continue, {error, Error}}
201 end
202 end,
203 16 Opts = #{default => {error, not_allowed}},
204 16 case ejabberd_auth:does_user_exist(jid:make_bare(LUser, LServer)) of
205 true ->
206 11 call_auth_modules_for_domain(LServer, F, Opts);
207 false ->
208 5 {error, not_allowed}
209 end.
210
211 -spec try_register(jid:jid() | error, binary()) ->
212 ok | {error, exists | not_allowed | invalid_jid | null_password | limit_per_domain_exceeded}.
213 try_register(_, <<>>) ->
214 4 {error, null_password};
215 try_register(#jid{luser = <<>>}, _) ->
216 2 {error, invalid_jid};
217 try_register(error, _) ->
218 1 {error, invalid_jid};
219 try_register(JID, Password) ->
220 5374 Exists = does_user_exist(JID),
221 5374 do_try_register_if_does_not_exist(Exists, JID, Password).
222
223 -spec do_try_register_if_does_not_exist(boolean(), jid:jid(), binary()) ->
224 ok | {error, exists | not_allowed | invalid_jid | null_password | limit_per_domain_exceeded}.
225 do_try_register_if_does_not_exist(true, _, _) ->
226 17 {error, exists};
227 do_try_register_if_does_not_exist(_, JID, Password) ->
228 5357 {LUser, LServer} = jid:to_lus(JID),
229 5357 F = fun(HostType, Mod) ->
230 5352 case mongoose_gen_auth:try_register(Mod, HostType, LUser, LServer, Password) of
231 ok ->
232 5351 mongoose_hooks:register_user(HostType, LServer, LUser),
233 5351 {stop, ok};
234 {error, _Error} ->
235 1 continue
236 end
237 end,
238 5357 Opts = #{default => {error, not_allowed}, metric => try_register},
239 5357 case is_user_number_below_limit(LServer) of
240 true ->
241 5354 call_auth_modules_for_domain(LServer, F, Opts);
242 false ->
243 3 {error, limit_per_domain_exceeded}
244 end.
245
246 %% @doc Registered users list do not include anonymous users logged
247 -spec get_vh_registered_users(Server :: jid:server()) -> [jid:simple_bare_jid()].
248 get_vh_registered_users(Server) ->
249 224 get_vh_registered_users(Server, []).
250
251 -spec get_vh_registered_users(Server :: jid:server(), Opts :: [any()]) ->
252 [jid:simple_bare_jid()].
253 get_vh_registered_users(Server, Opts) ->
254 229 LServer = jid:nameprep(Server),
255 229 do_get_vh_registered_users(LServer, Opts).
256
257 do_get_vh_registered_users(error, _) ->
258
:-(
[];
259 do_get_vh_registered_users(LServer, Opts) ->
260 229 F = fun(HostType, Mod) ->
261 229 mongoose_gen_auth:get_registered_users(Mod, HostType, LServer, Opts)
262 end,
263 229 lists:append(call_auth_modules_for_domain(LServer, F, #{default => [], op => map})).
264
265 -spec get_vh_registered_users_number(Server :: jid:server()) -> integer().
266 get_vh_registered_users_number(Server) ->
267 5560 get_vh_registered_users_number(Server, []).
268
269 -spec get_vh_registered_users_number(Server :: jid:server(), Opts :: list()) -> integer().
270 get_vh_registered_users_number(Server, Opts) ->
271 5561 LServer = jid:nameprep(Server),
272 5561 do_get_vh_registered_users_number(LServer, Opts).
273
274 do_get_vh_registered_users_number(error, _) ->
275
:-(
0;
276 do_get_vh_registered_users_number(LServer, Opts) ->
277 5561 F = fun(HostType, Mod) ->
278 5561 mongoose_gen_auth:get_registered_users_number(Mod, HostType, LServer, Opts)
279 end,
280 5561 lists:sum(call_auth_modules_for_domain(LServer, F, #{default => [], op => map})).
281
282 -spec get_password_s(JID :: jid:jid() | error) -> binary().
283 get_password_s(#jid{luser = LUser, lserver = LServer}) ->
284 19 F = fun(HostType, Mod) ->
285 17 case mongoose_gen_auth:get_password_s(Mod, HostType, LUser, LServer) of
286 5 <<>> -> continue;
287 12 Password when is_binary(Password) -> {stop, Password}
288 end
289 end,
290 19 call_auth_modules_for_domain(LServer, F, #{default => <<>>}).
291
292 %% @doc Get the password(like thing) of the user and the auth module.
293 -spec get_passterm_with_authmodule(mongooseim:host_type(), error | jid:jid()) ->
294 {passterm(), authmodule()} | false.
295 get_passterm_with_authmodule(HostType, #jid{luser = LUser, lserver = LServer}) ->
296 125 F = fun(Mod) ->
297 125 case mongoose_gen_auth:get_password(Mod, HostType, LUser, LServer) of
298 2 false -> continue;
299 123 PassTerm -> {stop, {PassTerm, Mod}}
300 end
301 end,
302 125 call_auth_modules_for_host_type(HostType, F, #{default => false}).
303
304 %% @doc Returns true if the user exists in the DB
305 %% or if an anonymous user is logged under the given name
306 %% Returns 'false' in case of an error
307 -spec does_user_exist(JID :: jid:jid() | error) -> boolean().
308 does_user_exist(#jid{luser = LUser, lserver = LServer}) ->
309 12237 F = fun(HostType, Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) end,
310 12237 case call_auth_modules_for_domain(LServer, F, #{default => false, metric => does_user_exist}) of
311
:-(
{error, _Error} -> false;
312 12237 Result -> Result
313 end;
314 does_user_exist(error) ->
315
:-(
false.
316
317 %% Hook interface
318 -spec does_user_exist(mongooseim:host_type(), jid:jid(), exist_type()) -> boolean().
319 does_user_exist(HostType, Jid, RequestType) ->
320 11673 mongoose_hooks:does_user_exist(HostType, Jid, RequestType).
321
322 %% @doc does_user_exist hook handler
323 %% Returns 'false' in case of an error
324 -spec on_does_user_exist(Acc, Params, Extra) -> {ok, Acc} when
325 Acc :: boolean(),
326 Params :: map(),
327 Extra :: map().
328 on_does_user_exist(false, #{jid := Jid, request_type := stored}, #{host_type := HostType}) ->
329 2431 {ok, true =:= does_stored_user_exist(HostType, Jid)};
330 on_does_user_exist(false,
331 #{jid := #jid{luser = LUser, lserver = LServer}, request_type := with_anonymous},
332 #{host_type := HostType}) ->
333
:-(
F = fun(Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) end,
334
:-(
{ok, call_auth_modules_for_host_type(HostType, F, #{default => false, metric => does_user_exist})};
335 on_does_user_exist(Status, _, _) ->
336
:-(
{ok, Status}.
337
338 %% @doc Returns true if the user exists in the DB
339 %% In case of a backend error, it is propagated to the caller
340 -spec does_stored_user_exist(mongooseim:host_type(), jid:jid() | error) ->
341 boolean() | {error, any()}.
342 does_stored_user_exist(HostType, #jid{luser = LUser, lserver = LServer}) ->
343 2433 F = fun(ejabberd_auth_anonymous) -> continue;
344 2431 (Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod)
345 end,
346 2433 call_auth_modules_for_host_type(HostType, F, #{default => false, metric => does_user_exist});
347 does_stored_user_exist(_HostType, error) ->
348
:-(
false.
349
350 does_user_exist_in_module(HostType, LUser, LServer, Mod) ->
351 14613 case mongoose_gen_auth:does_user_exist(Mod, HostType, LUser, LServer) of
352 8488 true -> {stop, true};
353 6125 false -> continue;
354 {error, Reason} = Error ->
355
:-(
?LOG_ERROR(#{what => does_user_exist_failed,
356 text => <<"The authentication module returned an error">>,
357 auth_module => Mod, reason => Reason,
358
:-(
user => LUser, server => LServer}),
359
:-(
{continue, Error}
360 end.
361
362 -spec does_method_support(AuthMethod :: atom(), Feature :: atom()) -> boolean().
363 does_method_support(AuthMethod, Feature) ->
364 25 Module = auth_method_to_module(AuthMethod),
365 25 lists:member(Feature, mongoose_gen_auth:supported_features(Module)).
366
367 %% @doc Remove user.
368 %% Note: it may return ok even if there was some problem removing the user.
369 -spec remove_user(JID :: jid:jid()) -> ok | {error, not_allowed | user_does_not_exist};
370 (error) -> error.
371 1 remove_user(error) -> error;
372 remove_user(#jid{luser = LUser, lserver = LServer}) ->
373 5450 JID = jid:make_bare(LUser, LServer),
374 5450 F = fun(HostType, Mod) ->
375 5351 case mongoose_gen_auth:remove_user(Mod, HostType, LUser, LServer) of
376 5351 ok -> {continue, {ok, HostType}};
377
:-(
{error, _Error} -> continue
378 end
379 end,
380 5450 case ejabberd_auth:does_user_exist(JID) of
381 true ->
382 5351 case call_auth_modules_for_domain(LServer, F, #{default => {error, not_allowed}}) of
383 {ok, HostType} ->
384 5351 Acc = mongoose_acc:new(#{location => ?LOCATION,
385 host_type => HostType,
386 lserver => LServer,
387 element => undefined}),
388 5351 mongoose_hooks:remove_user(Acc, LServer, LUser),
389 5351 ok;
390 Error ->
391
:-(
?LOG_ERROR(#{what => backend_disallows_user_removal,
392 user => LUser, server => LServer,
393
:-(
reason => Error}),
394
:-(
Error
395 end;
396 false ->
397 99 {error, user_does_not_exist}
398 end.
399
400 %% @doc Calculate informational entropy.
401 -spec entropy(iolist()) -> float().
402 entropy(IOList) ->
403
:-(
case binary_to_list(iolist_to_binary(IOList)) of
404 "" ->
405
:-(
0.0;
406 InputList ->
407
:-(
Set = lists:foldl(
408 fun(IOContent, Acc) ->
409
:-(
get_type_information(IOContent, Acc)
410 end, [0, 0, 0, 0, 0], InputList),
411
:-(
length(InputList) * math:log(lists:sum(Set))/math:log(2)
412 end.
413
414 -spec config_spec(atom()) -> mongoose_config_spec:config_section().
415 config_spec(Method) ->
416 216 mongoose_gen_auth:config_spec(auth_method_to_module(Method)).
417
418 %%%----------------------------------------------------------------------
419 %%% Internal functions
420 %%%----------------------------------------------------------------------
421 %% Return the list of authenticated modules for a given domain
422 %% TODO: rework is_anonymous_user/1 at mongoose_users module,
423 %% so there is no need for exporting auth_modules/1 function.
424 %% after that completely get rid of this interface, we should
425 %% use auth_modules_for_host_type/1 function instead.
426 -spec auth_modules(Server :: jid:lserver()) -> [authmodule()].
427 auth_modules(LServer) ->
428 27 case mongoose_domain_api:get_domain_host_type(LServer) of
429 27 {ok, HostType} -> auth_modules_for_host_type(HostType);
430
:-(
{error, not_found} -> []
431 end.
432
433 %% Return the list of authenticated modules for a given host type
434 -spec auth_modules_for_host_type(HostType :: mongooseim:host_type()) -> [authmodule()].
435 auth_modules_for_host_type(HostType) ->
436 184956 Methods = auth_methods(HostType),
437 184956 methods_to_modules(Methods).
438
439 -spec methods_to_modules([atom()]) -> [authmodule()].
440 methods_to_modules(Methods) ->
441 184958 [auth_method_to_module(M) || M <- Methods].
442
443 -spec auth_methods(mongooseim:host_type()) -> [atom()].
444 auth_methods(HostType) ->
445 184956 mongoose_config:get_opt([{auth, HostType}, methods]).
446
447 -spec auth_method_to_module(atom()) -> authmodule().
448 auth_method_to_module(Method) ->
449 185280 list_to_atom("ejabberd_auth_" ++ atom_to_list(Method)).
450
451 -spec remove_domain(mongoose_domain_api:remove_domain_acc(), map(), map()) ->
452 {ok | stop, mongoose_domain_api:remove_domain_acc()}.
453 remove_domain(Acc, #{domain := Domain}, #{host_type := HostType}) ->
454 5 F = fun() ->
455 5 FAuth = fun(Mod) -> mongoose_gen_auth:remove_domain(Mod, HostType, Domain) end,
456 5 call_auth_modules_for_host_type(HostType, FAuth, #{op => map}),
457 5 Acc
458 end,
459 5 mongoose_domain_api:remove_domain_wrapper(Acc, F, ?MODULE).
460
461 ensure_metrics(Host) ->
462 59 Metrics = [authorize, check_password, try_register, does_user_exist],
463 59 [mongoose_metrics:ensure_metric(Host, ?METRIC(Metric), histogram)
464 59 || Metric <- Metrics].
465
466 %% Library functions for reuse in ejabberd_auth_* modules
467 -spec authorize_with_check_password(Module, Creds) -> {ok, Creds}
468 | {error, any()} when
469 Module :: authmodule(),
470 Creds :: mongoose_credentials:t().
471 authorize_with_check_password(Module, Creds) ->
472 6092 User = mongoose_credentials:get(Creds, username),
473 6092 LUser = jid:nodeprep(User),
474 6092 LUser == error andalso error({nodeprep_error, User}),
475 6092 LServer = mongoose_credentials:lserver(Creds),
476 6092 HostType = mongoose_credentials:host_type(Creds),
477 6092 Password = mongoose_credentials:get(Creds, password),
478 6092 Digest = mongoose_credentials:get(Creds, digest, undefined),
479 6092 DigestGen = mongoose_credentials:get(Creds, digest_gen, undefined),
480 6092 Result = case {Digest, DigestGen} of
481 _ when Digest /= undefined andalso DigestGen /= undefined ->
482 1 mongoose_gen_auth:check_password(Module, HostType, LUser, LServer,
483 Password, Digest, DigestGen);
484 _ ->
485 6091 mongoose_gen_auth:check_password(Module, HostType, LUser, LServer, Password)
486 end,
487 6092 case Result of
488 6076 true -> {ok, mongoose_credentials:set(Creds, auth_module, Module)};
489 16 false -> {error, not_authorized}
490 end.
491
492 -spec get_type_information(integer(), list()) -> list().
493 get_type_information(IOContent, [Digit, Printable, _, HiLetter, Other])
494 when IOContent >= $a andalso IOContent =< $z ->
495
:-(
[Digit, Printable, 26, HiLetter, Other];
496 get_type_information(IOContent, [_, Printable, LowLetter, HiLetter, Other])
497 when IOContent >= $0 andalso IOContent =< $9 ->
498
:-(
[9, Printable, LowLetter, HiLetter, Other];
499 get_type_information(IOContent, [Digit, Printable, LowLetter, _, Other])
500 when IOContent >= $A andalso IOContent =< $Z ->
501
:-(
[Digit, Printable, LowLetter, 26, Other];
502 get_type_information(IOContent, [Digit, _, LowLetter, HiLetter, Other])
503 when IOContent >= 16#21 andalso IOContent =< 16#7e ->
504
:-(
[Digit, 33, LowLetter, HiLetter, Other];
505 get_type_information(_IOContent, [Digit, Printable, LowLetter, HiLetter, _Other]) ->
506
:-(
[Digit, Printable, LowLetter, HiLetter, 128].
507
508 %% @doc If the domain corresponds to a valid host type, call auth modules for that host type
509 -spec call_auth_modules_for_domain(jid:lserver(), host_type_mod_fun(), call_opts()) ->
510 mod_res() | [mod_res()].
511 call_auth_modules_for_domain(Domain, F, Opts = #{default := Default}) ->
512 28768 case mongoose_domain_api:get_domain_host_type(Domain) of
513 {ok, HostType} ->
514 28709 StepF = bind_host_type(HostType, F),
515 28709 case maps:take(metric, Opts) of
516 {Metric, NewOpts} ->
517 17540 {Time, Result} = timer:tc(fun call_auth_modules_for_host_type/3,
518 [HostType, StepF, NewOpts]),
519 17540 mongoose_metrics:update(HostType, ?METRIC(Metric), Time),
520 17540 Result;
521 error ->
522 11169 call_auth_modules_for_host_type(HostType, StepF, Opts)
523 end;
524 {error, not_found} ->
525 59 Default
526 end.
527
528 -spec bind_host_type(mongooseim:host_type(), host_type_mod_fun()) -> mod_fun().
529 bind_host_type(HostType, F) when is_function(F, 2) ->
530 28709 fun(Mod) -> F(HostType, Mod) end.
531
532 -spec call_auth_modules_for_host_type(mongooseim:host_type(),
533 mod_fun() | mod_fold_fun(), call_opts()) ->
534 mod_res() | [mod_res()].
535 call_auth_modules_for_host_type(HostType, F, Opts) ->
536 178444 Modules = auth_modules_for_host_type(HostType),
537 178444 case maps:take(metric, Opts) of
538 {Metric, NewOpts} ->
539 2433 {Time, Result} = timer:tc(fun call_auth_modules/3, [Modules, F, NewOpts]),
540 2433 mongoose_metrics:update(HostType, ?METRIC(Metric), Time),
541 2433 Result;
542 error ->
543 176011 call_auth_modules(Modules, F, Opts)
544 end.
545
546 -spec call_auth_modules_with_creds(mongoose_credentials:t(),
547 mod_fun() | mod_fold_fun(), call_opts()) ->
548 mod_res() | [mod_res()].
549 call_auth_modules_with_creds(Creds, F, Opts) ->
550 6098 Modules = mongoose_credentials:auth_modules(Creds),
551 6098 case maps:take(metric, Opts) of
552 {Metric, NewOpts} ->
553 6098 HostType = mongoose_credentials:host_type(Creds),
554 6098 {Time, Result} = timer:tc(fun call_auth_modules/3,
555 [Modules, F, NewOpts]),
556 6098 mongoose_metrics:update(HostType, ?METRIC(Metric), Time),
557 6098 Result;
558 error ->
559
:-(
call_auth_modules(Modules, F, Opts)
560 end.
561
562
563 %% @doc Perform a map or a fold operation with function F over the provided Modules
564 -spec call_auth_modules([authmodule()], mod_fun() | mod_fold_fun(), call_opts()) ->
565 mod_res() | [mod_res()].
566 call_auth_modules(Modules, F, #{op := map}) when is_function(F, 1) ->
567 5855 lists:map(F, Modules);
568 call_auth_modules(Modules, F, Opts) when is_function(F, 1) ->
569 172589 call_auth_modules(Modules, fun(Mod, _) -> F(Mod) end, Opts);
570 call_auth_modules(Modules, F, #{default := Default}) when is_function(F, 2) ->
571 178687 fold_auth_modules(Modules, F, Default).
572
573 %% @doc Apply function F to all consecutive auth modules with an accumulator and a stop condition
574 -spec fold_auth_modules([authmodule()], mod_fold_fun(), mod_res()) -> mod_res().
575 fold_auth_modules([], _F, FinalAcc) ->
576 114213 FinalAcc;
577 fold_auth_modules([AuthModule | AuthModules], F, CurAcc) ->
578 178753 case F(AuthModule, CurAcc) of
579 continue ->
580 108912 fold_auth_modules(AuthModules, F, CurAcc);
581 {continue, NewAcc} ->
582 5367 fold_auth_modules(AuthModules, F, NewAcc);
583 {stop, Value} ->
584 64474 Value
585 end.
586
587 is_user_number_below_limit(Domain) ->
588 5357 case mongoose_domain_api:get_domain_host_type(Domain) of
589 {ok, HostType} ->
590 5355 Limit = mongoose_config:get_opt([{auth, HostType}, max_users_per_domain]),
591 5355 Current = get_vh_registered_users_number(Domain),
592 5355 Current < Limit;
593 {error, not_found} ->
594 2 true
595 end.
Line Hits Source