./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
58 %% Library functions for reuse in ejabberd_auth_* modules
59 -export([authorize_with_check_password/2]).
60
61 %% Hook handlers
62 -export([remove_domain/3]).
63 -export([does_user_exist/4]).
64
65 -ignore_xref([
66 auth_methods/1, auth_modules/1, check_password/4, does_user_exist/4,
67 get_vh_registered_users/2, get_vh_registered_users_number/2,
68 remove_domain/3, start/1, stop/1]).
69
70 -include("mongoose.hrl").
71 -include("jlib.hrl").
72
73 -export_type([authmodule/0,
74 passterm/0,
75 exist_type/0]).
76
77 -type authmodule() :: module().
78 -type passterm() :: binary() | mongoose_scram:scram_tuple() | mongoose_scram:scram_map().
79 -type exist_type() :: stored | with_anonymous.
80
81 %% Types defined below are used in call_auth_modules_*
82 -type mod_res() :: any().
83 -type host_type_mod_fun() :: fun((mongooseim:host_type(), authmodule()) -> mod_res()).
84 -type mod_fun() :: fun((authmodule()) -> mod_res()).
85 -type mod_fold_fun() :: fun((authmodule(), mod_res()) -> continue |
86 {continue, mod_res()} |
87 {stop, mod_res()}).
88 -type call_opts() :: #{default => mod_res(), op => map, metric => atom()}.
89
90 -define(METRIC(Name), [backends, auth, Name]).
91
92 %%%----------------------------------------------------------------------
93 %%% API
94 %%%----------------------------------------------------------------------
95 -spec start() -> 'ok'.
96 start() ->
97 61 lists:foreach(fun start/1, ?ALL_HOST_TYPES).
98
99 -spec start(HostType :: mongooseim:host_type()) -> 'ok'.
100 start(HostType) ->
101 296 ensure_metrics(HostType),
102 296 F = fun(Mod) -> mongoose_gen_auth:start(Mod, HostType) end,
103 296 call_auth_modules_for_host_type(HostType, F, #{op => map}),
104 296 ejabberd_hooks:add(hooks(HostType)),
105 296 ok.
106
107 -spec stop(HostType :: mongooseim:host_type()) -> 'ok'.
108 stop(HostType) ->
109 1 ejabberd_hooks:delete(hooks(HostType)),
110 1 F = fun(Mod) -> mongoose_gen_auth:stop(Mod, HostType) end,
111 1 call_auth_modules_for_host_type(HostType, F, #{op => map}),
112 1 ok.
113
114 hooks(HostType) ->
115 297 [
116 %% These hooks must run in between those of mod_cache_users
117 {does_user_exist, HostType, ?MODULE, does_user_exist, 50},
118 %% It is important that this handler happens _before_ all other modules
119 {remove_domain, HostType, ?MODULE, remove_domain, 10}
120 ].
121
122 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
123 supports_sasl_module(HostType, SASLModule) ->
124 65434 F = fun(Mod) ->
125 65434 case mongoose_gen_auth:supports_sasl_module(Mod, HostType, SASLModule) of
126 9050 true -> {stop, true};
127 56384 false -> continue
128 end
129 end,
130 65434 call_auth_modules_for_host_type(HostType, F, #{default => false}).
131
132 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
133 | {error, not_authorized}.
134 authorize(Creds) ->
135 2615 HostType = mongoose_credentials:host_type(Creds),
136 2615 F = fun(Mod, {_CurResult, CurCreds}) ->
137 2615 case mongoose_gen_auth:authorize(Mod, CurCreds) of
138 {ok, NewCreds} ->
139 2601 {stop, {ok, mongoose_credentials:register(NewCreds, Mod, success)}};
140 Error ->
141 14 NewCreds = mongoose_credentials:register(CurCreds, Mod, {failure, Error}),
142 14 {continue, {not_authorized, NewCreds}}
143 end
144 end,
145 2615 Opts = #{default => {not_authorized, Creds}, metric => authorize},
146 2615 case call_auth_modules_for_host_type(HostType, F, Opts) of
147 2601 Res = {ok, _Creds} -> Res;
148 14 {not_authorized, _Creds} -> {error, not_authorized}
149 end.
150
151 %% @doc Check if at least one authentication method accepts the user and the password.
152 -spec check_password(JID :: jid:jid() | error, Password :: binary()) -> boolean().
153 check_password(error, _Password) ->
154
:-(
false;
155 check_password(#jid{luser = LUser, lserver = LServer}, Password) ->
156 4 F = fun(HostType, Mod) ->
157 4 case mongoose_gen_auth:check_password(Mod, HostType, LUser, LServer, Password) of
158 2 true -> {stop, true};
159 2 false -> continue
160 end
161 end,
162 4 Opts = #{default => false, metric => check_password},
163 4 call_auth_modules_for_domain(LServer, F, Opts).
164
165 %% @doc Check if at least one authentication method accepts the user and the password.
166 -spec check_password(JID :: jid:jid() | error,
167 Password :: binary(),
168 Digest :: binary(),
169 DigestGen :: fun((binary()) -> binary())) -> boolean().
170 check_password(error, _, _, _) ->
171
:-(
false;
172 check_password(#jid{luser = LUser, lserver = LServer}, Password, Digest, DigestGen) ->
173
:-(
F = fun(HostType, Mod) ->
174
:-(
case mongoose_gen_auth:check_password(Mod, HostType, LUser, LServer,
175 Password, Digest, DigestGen) of
176
:-(
true -> {stop, true};
177
:-(
false -> continue
178 end
179 end,
180
:-(
Opts = #{default => false, metric => check_password},
181
:-(
call_auth_modules_for_domain(LServer, F, Opts).
182
183 -spec check_digest(binary(), fun((binary()) -> binary()), binary(), binary()) -> boolean().
184 check_digest(<<>>, _, <<>>, _) ->
185
:-(
false; %% empty digest and password
186 check_digest(Digest, DigestGen, _Password, Passwd) ->
187
:-(
Digest == DigestGen(Passwd).
188
189 -spec set_password(jid:jid() | error, binary()) ->
190 ok | {error, empty_password | not_allowed | invalid_jid}.
191 set_password(_, <<"">>) ->
192 5 {error, empty_password};
193 set_password(error, _) ->
194 1 {error, invalid_jid};
195 set_password(#jid{luser = LUser, lserver = LServer}, Password) ->
196 13 F = fun(HostType, Mod) ->
197 11 case mongoose_gen_auth:set_password(Mod, HostType, LUser, LServer, Password) of
198 11 ok -> {stop, ok};
199
:-(
{error, Error} -> {continue, {error, Error}}
200 end
201 end,
202 13 Opts = #{default => {error, not_allowed}},
203 13 call_auth_modules_for_domain(LServer, F, Opts).
204
205 -spec try_register(jid:jid() | error, binary()) ->
206 ok | {error, exists | not_allowed | invalid_jid | null_password}.
207 try_register(_, <<"">>) ->
208 1 {error, null_password};
209 try_register(error, _) ->
210 1 {error, invalid_jid};
211 try_register(JID, Password) ->
212 2123 Exists = does_user_exist(JID),
213 2123 do_try_register_if_does_not_exist(Exists, JID, Password).
214
215 -spec do_try_register_if_does_not_exist(boolean(), jid:jid(), binary()) ->
216 ok | {error, exists | not_allowed | invalid_jid | null_password}.
217 do_try_register_if_does_not_exist(true, _, _) ->
218 18 {error, exists};
219 do_try_register_if_does_not_exist(_, JID, Password) ->
220 2105 {LUser, LServer} = jid:to_lus(JID),
221 2105 F = fun(HostType, Mod) ->
222 2105 case mongoose_gen_auth:try_register(Mod, HostType, LUser, LServer, Password) of
223 ok ->
224 2105 mongoose_hooks:register_user(HostType, LServer, LUser),
225 2105 {stop, ok};
226 {error, _Error} ->
227
:-(
continue
228 end
229 end,
230 2105 Opts = #{default => {error, not_allowed}, metric => try_register},
231 2105 call_auth_modules_for_domain(LServer, F, Opts).
232
233 %% @doc Registered users list do not include anonymous users logged
234 -spec get_vh_registered_users(Server :: jid:server()) -> [jid:simple_bare_jid()].
235 get_vh_registered_users(Server) ->
236 130 get_vh_registered_users(Server, []).
237
238 -spec get_vh_registered_users(Server :: jid:server(), Opts :: [any()]) ->
239 [jid:simple_bare_jid()].
240 get_vh_registered_users(Server, Opts) ->
241 130 LServer = jid:nameprep(Server),
242 130 do_get_vh_registered_users(LServer, Opts).
243
244 do_get_vh_registered_users(error, _) ->
245
:-(
[];
246 do_get_vh_registered_users(LServer, Opts) ->
247 130 F = fun(HostType, Mod) ->
248 129 mongoose_gen_auth:get_registered_users(Mod, HostType, LServer, Opts)
249 end,
250 130 lists:append(call_auth_modules_for_domain(LServer, F, #{default => [], op => map})).
251
252 -spec get_vh_registered_users_number(Server :: jid:server()) -> integer().
253 get_vh_registered_users_number(Server) ->
254 122 get_vh_registered_users_number(Server, []).
255
256 -spec get_vh_registered_users_number(Server :: jid:server(), Opts :: list()) -> integer().
257 get_vh_registered_users_number(Server, Opts) ->
258 122 LServer = jid:nameprep(Server),
259 122 do_get_vh_registered_users_number(LServer, Opts).
260
261 do_get_vh_registered_users_number(error, _) ->
262
:-(
0;
263 do_get_vh_registered_users_number(LServer, Opts) ->
264 122 F = fun(HostType, Mod) ->
265 121 mongoose_gen_auth:get_registered_users_number(Mod, HostType, LServer, Opts)
266 end,
267 122 lists:sum(call_auth_modules_for_domain(LServer, F, #{default => [], op => map})).
268
269 -spec get_password_s(JID :: jid:jid() | error) -> binary().
270 get_password_s(#jid{luser = LUser, lserver = LServer}) ->
271 2 F = fun(HostType, Mod) ->
272 1 case mongoose_gen_auth:get_password_s(Mod, HostType, LUser, LServer) of
273 1 <<>> -> continue;
274
:-(
Password when is_binary(Password) -> {stop, Password}
275 end
276 end,
277 2 call_auth_modules_for_domain(LServer, F, #{default => <<>>}).
278
279 %% @doc Get the password(like thing) of the user and the auth module.
280 -spec get_passterm_with_authmodule(mongooseim:host_type(), error | jid:jid()) ->
281 {passterm(), authmodule()} | false.
282 get_passterm_with_authmodule(HostType, #jid{luser = LUser, lserver = LServer}) ->
283
:-(
F = fun(Mod) ->
284
:-(
case mongoose_gen_auth:get_password(Mod, HostType, LUser, LServer) of
285
:-(
false -> continue;
286
:-(
PassTerm -> {stop, {PassTerm, Mod}}
287 end
288 end,
289
:-(
call_auth_modules_for_host_type(HostType, F, #{default => false}).
290
291 %% @doc Returns true if the user exists in the DB
292 %% or if an anonymous user is logged under the given name
293 %% Returns 'false' in case of an error
294 -spec does_user_exist(JID :: jid:jid() | error) -> boolean().
295 does_user_exist(#jid{luser = LUser, lserver = LServer}) ->
296 2508 F = fun(HostType, Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) end,
297 2508 case call_auth_modules_for_domain(LServer, F,
298 #{default => false, metric => does_user_exist}) of
299
:-(
{error, _Error} -> false;
300 2508 Result -> Result
301 end;
302 does_user_exist(error) ->
303
:-(
false.
304
305 %% Hook interface
306 -spec does_user_exist(mongooseim:host_type(), jid:jid(), exist_type()) -> boolean().
307 does_user_exist(HostType, Jid, RequestType) ->
308 344 mongoose_hooks:does_user_exist(HostType, Jid, RequestType).
309
310 %% @doc does_user_exist hook handler
311 %% Returns 'false' in case of an error
312 -spec does_user_exist(boolean(), mongooseim:host_type(), jid:jid(), exist_type()) -> boolean().
313 does_user_exist(false, HostType, Jid, stored) ->
314 215 true =:= does_stored_user_exist(HostType, Jid);
315 does_user_exist(false, HostType, #jid{luser = LUser, lserver = LServer}, with_anonymous) ->
316
:-(
F = fun(Mod) ->
317
:-(
does_user_exist_in_module(HostType, LUser, LServer, Mod)
318 end,
319
:-(
call_auth_modules_for_host_type(HostType, F, #{default => false});
320 does_user_exist(Status, _, _, _) ->
321
:-(
Status.
322
323 %% @doc Returns true if the user exists in the DB
324 %% In case of a backend error, it is propagated to the caller
325 -spec does_stored_user_exist(mongooseim:host_type(), jid:jid() | error) ->
326 boolean() | {error, any()}.
327 does_stored_user_exist(HostType, #jid{luser = LUser, lserver = LServer}) ->
328 215 F = fun(Mod) when Mod =/= ejabberd_auth_anonymous ->
329 215 does_user_exist_in_module(HostType, LUser, LServer, Mod)
330 end,
331 215 call_auth_modules_for_host_type(HostType, F, #{default => false});
332 does_stored_user_exist(_HostType, error) ->
333
:-(
false.
334
335 does_user_exist_in_module(HostType, LUser, LServer, Mod) ->
336 2712 case mongoose_gen_auth:does_user_exist(Mod, HostType, LUser, LServer) of
337 510 true -> {stop, true};
338 2202 false -> continue;
339 {error, Reason} = Error ->
340
:-(
?LOG_ERROR(#{what => does_user_exist_failed,
341 text => <<"The authentication module returned an error">>,
342 auth_module => Mod, reason => Reason,
343
:-(
user => LUser, server => LServer}),
344
:-(
{continue, Error}
345 end.
346
347 -spec does_method_support(AuthMethod :: atom(), Feature :: atom()) -> boolean().
348 does_method_support(AuthMethod, Feature) ->
349 115 Module = auth_method_to_module(AuthMethod),
350 115 lists:member(Feature, mongoose_gen_auth:supported_features(Module)).
351
352 %% @doc Remove user.
353 %% Note: it may return ok even if there was some problem removing the user.
354 -spec remove_user(JID :: jid:jid()) -> ok | {error, not_allowed};
355 (error) -> error.
356 1 remove_user(error) -> error;
357 remove_user(#jid{luser = LUser, lserver = LServer}) ->
358 2158 F = fun(HostType, Mod) ->
359 2152 case mongoose_gen_auth:remove_user(Mod, HostType, LUser, LServer) of
360 2100 ok -> {continue, {ok, HostType}};
361 52 {error, _Error} -> continue
362 end
363 end,
364 2158 case call_auth_modules_for_domain(LServer, F, #{default => {error, not_allowed}}) of
365 {ok, HostType} ->
366 2100 Acc = mongoose_acc:new(#{ location => ?LOCATION,
367 host_type => HostType,
368 lserver => LServer,
369 element => undefined }),
370 2100 mongoose_hooks:remove_user(Acc, LServer, LUser),
371 2100 ok;
372 Error ->
373 58 ?LOG_ERROR(#{what => backend_disallows_user_removal,
374 user => LUser, server => LServer,
375
:-(
reason => Error}),
376 58 Error
377 end.
378
379 %% @doc Calculate informational entropy.
380 -spec entropy(iolist()) -> float().
381 entropy(IOList) ->
382
:-(
case binary_to_list(iolist_to_binary(IOList)) of
383 "" ->
384
:-(
0.0;
385 InputList ->
386
:-(
Set = lists:foldl(
387 fun(IOContent, Acc) ->
388
:-(
get_type_information(IOContent, Acc)
389 end, [0, 0, 0, 0, 0], InputList),
390
:-(
length(InputList) * math:log(lists:sum(Set))/math:log(2)
391 end.
392
393 -spec config_spec(atom()) -> mongoose_config_spec:config_section().
394 config_spec(Method) ->
395 1220 mongoose_gen_auth:config_spec(auth_method_to_module(Method)).
396
397 %%%----------------------------------------------------------------------
398 %%% Internal functions
399 %%%----------------------------------------------------------------------
400 %% Return the list of authenticated modules for a given domain
401 %% TODO: rework is_anonymous_user/1 at mongoose_users module,
402 %% so there is no need for exporting auth_modules/1 function.
403 %% after that completely get rid of this interface, we should
404 %% use auth_modules_for_host_type/1 function instead.
405 -spec auth_modules(Server :: jid:lserver()) -> [authmodule()].
406 auth_modules(LServer) ->
407 12 case mongoose_domain_api:get_domain_host_type(LServer) of
408 12 {ok, HostType} -> auth_modules_for_host_type(HostType);
409
:-(
{error, not_found} -> []
410 end.
411
412 %% Return the list of authenticated modules for a given host type
413 -spec auth_modules_for_host_type(HostType :: mongooseim:host_type()) -> [authmodule()].
414 auth_modules_for_host_type(HostType) ->
415 75593 Methods = auth_methods(HostType),
416 75593 [auth_method_to_module(M) || M <- Methods].
417
418 -spec auth_methods(mongooseim:host_type()) -> [atom()].
419 auth_methods(HostType) ->
420 75593 mongoose_config:get_opt([{auth, HostType}, methods]).
421
422 -spec auth_method_to_module(atom()) -> authmodule().
423 auth_method_to_module(Method) ->
424 76928 list_to_atom("ejabberd_auth_" ++ atom_to_list(Method)).
425
426 -spec remove_domain(mongoose_hooks:simple_acc(), mongooseim:host_type(), jid:lserver()) ->
427 mongoose_hooks:simple_acc().
428 remove_domain(Acc, HostType, Domain) ->
429
:-(
F = fun(Mod) -> mongoose_gen_auth:remove_domain(Mod, HostType, Domain) end,
430
:-(
call_auth_modules_for_host_type(HostType, F, #{op => map}),
431
:-(
Acc.
432
433 ensure_metrics(Host) ->
434 296 Metrics = [authorize, check_password, try_register, does_user_exist],
435 296 [mongoose_metrics:ensure_metric(Host, ?METRIC(Metric), histogram)
436 296 || Metric <- Metrics].
437
438 %% Library functions for reuse in ejabberd_auth_* modules
439 -spec authorize_with_check_password(Module, Creds) -> {ok, Creds}
440 | {error, any()} when
441 Module :: authmodule(),
442 Creds :: mongoose_credentials:t().
443 authorize_with_check_password(Module, Creds) ->
444 2612 User = mongoose_credentials:get(Creds, username),
445 2612 LUser = jid:nodeprep(User),
446 2612 LUser == error andalso error({nodeprep_error, User}),
447 2612 LServer = mongoose_credentials:lserver(Creds),
448 2612 HostType = mongoose_credentials:host_type(Creds),
449 2612 Password = mongoose_credentials:get(Creds, password),
450 2612 Digest = mongoose_credentials:get(Creds, digest, undefined),
451 2612 DigestGen = mongoose_credentials:get(Creds, digest_gen, undefined),
452 2612 Result = case {Digest, DigestGen} of
453 _ when Digest /= undefined andalso DigestGen /= undefined ->
454
:-(
mongoose_gen_auth:check_password(Module, HostType, LUser, LServer,
455 Password, Digest, DigestGen);
456 _ ->
457 2612 mongoose_gen_auth:check_password(Module, HostType, LUser, LServer, Password)
458 end,
459 2612 case Result of
460 2598 true -> {ok, mongoose_credentials:set(Creds, auth_module, Module)};
461 14 false -> {error, not_authorized}
462 end.
463
464 -spec get_type_information(integer(), list()) -> list().
465 get_type_information(IOContent, [Digit, Printable, _, HiLetter, Other])
466 when IOContent >= $a andalso IOContent =< $z ->
467
:-(
[Digit, Printable, 26, HiLetter, Other];
468 get_type_information(IOContent, [_, Printable, LowLetter, HiLetter, Other])
469 when IOContent >= $0 andalso IOContent =< $9 ->
470
:-(
[9, Printable, LowLetter, HiLetter, Other];
471 get_type_information(IOContent, [Digit, Printable, LowLetter, _, Other])
472 when IOContent >= $A andalso IOContent =< $Z ->
473
:-(
[Digit, Printable, LowLetter, 26, Other];
474 get_type_information(IOContent, [Digit, _, LowLetter, HiLetter, Other])
475 when IOContent >= 16#21 andalso IOContent =< 16#7e ->
476
:-(
[Digit, 33, LowLetter, HiLetter, Other];
477 get_type_information(_IOContent, [Digit, Printable, LowLetter, HiLetter, _Other]) ->
478
:-(
[Digit, Printable, LowLetter, HiLetter, 128].
479
480 %% @doc If the domain corresponds to a valid host type, call auth modules for that host type
481 -spec call_auth_modules_for_domain(jid:lserver(), host_type_mod_fun(), call_opts()) ->
482 mod_res() | [mod_res()].
483 call_auth_modules_for_domain(Domain, F, Opts = #{default := Default}) ->
484 7042 case mongoose_domain_api:get_domain_host_type(Domain) of
485 {ok, HostType} ->
486 7020 StepF = bind_host_type(HostType, F),
487 7020 case maps:take(metric, Opts) of
488 {Metric, NewOpts} ->
489 4606 {Time, Result} = timer:tc(fun call_auth_modules_for_host_type/3,
490 [HostType, StepF, NewOpts]),
491 4606 mongoose_metrics:update(HostType, ?METRIC(Metric), Time),
492 4606 Result;
493 error ->
494 2414 call_auth_modules_for_host_type(HostType, StepF, Opts)
495 end;
496 {error, not_found} ->
497 22 Default
498 end.
499
500 -spec bind_host_type(mongooseim:host_type(), host_type_mod_fun()) -> mod_fun().
501 bind_host_type(HostType, F) when is_function(F, 2) ->
502 7020 fun(Mod) -> F(HostType, Mod) end.
503
504 -spec call_auth_modules_for_host_type(mongooseim:host_type(),
505 mod_fun() | mod_fold_fun(), call_opts()) ->
506 mod_res() | [mod_res()].
507 call_auth_modules_for_host_type(HostType, F, Opts) ->
508 75581 Modules = auth_modules_for_host_type(HostType),
509 75581 call_auth_modules(Modules, F, Opts).
510
511 %% @doc Perform a map or a fold operation with function F over the provided Modules
512 -spec call_auth_modules([authmodule()], mod_fun() | mod_fold_fun(), call_opts()) ->
513 mod_res() | [mod_res()].
514 call_auth_modules(Modules, F, #{op := map}) when is_function(F, 1) ->
515 547 lists:map(F, Modules);
516 call_auth_modules(Modules, F, Opts) when is_function(F, 1) ->
517 72419 call_auth_modules(Modules, fun(Mod, _) -> F(Mod) end, Opts);
518 call_auth_modules(Modules, F, #{default := Default}) when is_function(F, 2) ->
519 75034 fold_auth_modules(Modules, F, Default).
520
521 %% @doc Apply function F to all consecutive auth modules with an accumulator and a stop condition
522 -spec fold_auth_modules([authmodule()], mod_fold_fun(), mod_res()) -> mod_res().
523 fold_auth_modules([], _F, FinalAcc) ->
524 60755 FinalAcc;
525 fold_auth_modules([AuthModule | AuthModules], F, CurAcc) ->
526 75034 case F(AuthModule, CurAcc) of
527 continue ->
528 58641 fold_auth_modules(AuthModules, F, CurAcc);
529 {continue, NewAcc} ->
530 2114 fold_auth_modules(AuthModules, F, NewAcc);
531 {stop, Value} ->
532 14279 Value
533 end.
Line Hits Source