./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 53 lists:foreach(fun start/1, ?ALL_HOST_TYPES).
98
99 -spec start(HostType :: mongooseim:host_type()) -> 'ok'.
100 start(HostType) ->
101 263 ensure_metrics(HostType),
102 263 F = fun(Mod) -> mongoose_gen_auth:start(Mod, HostType) end,
103 263 call_auth_modules_for_host_type(HostType, F, #{op => map}),
104 263 ejabberd_hooks:add(hooks(HostType)),
105 263 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 264 [
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 98178 F = fun(Mod) ->
125 98178 case mongoose_gen_auth:supports_sasl_module(Mod, HostType, SASLModule) of
126 28132 true -> {stop, true};
127 70046 false -> continue
128 end
129 end,
130 98178 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 4066 HostType = mongoose_credentials:host_type(Creds),
136 4066 F = fun(Mod, {_CurResult, CurCreds}) ->
137 4066 case mongoose_gen_auth:authorize(Mod, CurCreds) of
138 {ok, NewCreds} ->
139 4056 {stop, {ok, mongoose_credentials:register(NewCreds, Mod, success)}};
140 Error ->
141 10 NewCreds = mongoose_credentials:register(CurCreds, Mod, {failure, Error}),
142 10 {continue, {not_authorized, NewCreds}}
143 end
144 end,
145 4066 Opts = #{default => {not_authorized, Creds}, metric => authorize},
146 4066 case call_auth_modules_for_host_type(HostType, F, Opts) of
147 4056 Res = {ok, _Creds} -> Res;
148 10 {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 1 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 3388 Exists = does_user_exist(JID),
213 3388 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 13 {error, exists};
219 do_try_register_if_does_not_exist(_, JID, Password) ->
220 3375 {LUser, LServer} = jid:to_lus(JID),
221 3375 F = fun(HostType, Mod) ->
222 3375 case mongoose_gen_auth:try_register(Mod, HostType, LUser, LServer, Password) of
223 ok ->
224 3375 mongoose_hooks:register_user(HostType, LServer, LUser),
225 3375 {stop, ok};
226 {error, _Error} ->
227
:-(
continue
228 end
229 end,
230 3375 Opts = #{default => {error, not_allowed}, metric => try_register},
231 3375 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 139 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 144 LServer = jid:nameprep(Server),
242 144 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 144 F = fun(HostType, Mod) ->
248 143 mongoose_gen_auth:get_registered_users(Mod, HostType, LServer, Opts)
249 end,
250 144 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 118 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 119 LServer = jid:nameprep(Server),
259 119 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 119 F = fun(HostType, Mod) ->
265 118 mongoose_gen_auth:get_registered_users_number(Mod, HostType, LServer, Opts)
266 end,
267 119 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 8 F = fun(HostType, Mod) ->
272 7 case mongoose_gen_auth:get_password_s(Mod, HostType, LUser, LServer) of
273 1 <<>> -> continue;
274 6 Password when is_binary(Password) -> {stop, Password}
275 end
276 end,
277 8 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 27 F = fun(Mod) ->
284 27 case mongoose_gen_auth:get_password(Mod, HostType, LUser, LServer) of
285 1 false -> continue;
286 26 PassTerm -> {stop, {PassTerm, Mod}}
287 end
288 end,
289 27 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 3958 F = fun(HostType, Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) end,
297 3958 case call_auth_modules_for_domain(LServer, F,
298 #{default => false, metric => does_user_exist}) of
299
:-(
{error, _Error} -> false;
300 3958 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 9290 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 1640 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 1640 F = fun(Mod) when Mod =/= ejabberd_auth_anonymous ->
329 1640 does_user_exist_in_module(HostType, LUser, LServer, Mod)
330 end,
331 1640 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 5587 case mongoose_gen_auth:does_user_exist(Mod, HostType, LUser, LServer) of
337 1693 true -> {stop, true};
338 3894 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 103 Module = auth_method_to_module(AuthMethod),
350 103 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 3405 F = fun(HostType, Mod) ->
359 3404 case mongoose_gen_auth:remove_user(Mod, HostType, LUser, LServer) of
360 3404 ok -> {continue, {ok, HostType}};
361
:-(
{error, _Error} -> continue
362 end
363 end,
364 3405 case call_auth_modules_for_domain(LServer, F, #{default => {error, not_allowed}}) of
365 {ok, HostType} ->
366 3404 Acc = mongoose_acc:new(#{ location => ?LOCATION,
367 host_type => HostType,
368 lserver => LServer,
369 element => undefined }),
370 3404 mongoose_hooks:remove_user(Acc, LServer, LUser),
371 3404 ok;
372 Error ->
373 1 ?LOG_ERROR(#{what => backend_disallows_user_removal,
374 user => LUser, server => LServer,
375
:-(
reason => Error}),
376 1 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 1060 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 115200 Methods = auth_methods(HostType),
416 115200 [auth_method_to_module(M) || M <- Methods].
417
418 -spec auth_methods(mongooseim:host_type()) -> [atom()].
419 auth_methods(HostType) ->
420 115200 mongoose_config:get_opt([{auth, HostType}, methods]).
421
422 -spec auth_method_to_module(atom()) -> authmodule().
423 auth_method_to_module(Method) ->
424 116363 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 4 F = fun(Mod) -> mongoose_gen_auth:remove_domain(Mod, HostType, Domain) end,
430 4 call_auth_modules_for_host_type(HostType, F, #{op => map}),
431 4 Acc.
432
433 ensure_metrics(Host) ->
434 263 Metrics = [authorize, check_password, try_register, does_user_exist],
435 263 [mongoose_metrics:ensure_metric(Host, ?METRIC(Metric), histogram)
436 263 || 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 4055 User = mongoose_credentials:get(Creds, username),
445 4055 LUser = jid:nodeprep(User),
446 4055 LUser == error andalso error({nodeprep_error, User}),
447 4055 LServer = mongoose_credentials:lserver(Creds),
448 4055 HostType = mongoose_credentials:host_type(Creds),
449 4055 Password = mongoose_credentials:get(Creds, password),
450 4055 Digest = mongoose_credentials:get(Creds, digest, undefined),
451 4055 DigestGen = mongoose_credentials:get(Creds, digest_gen, undefined),
452 4055 Result = case {Digest, DigestGen} of
453 _ when Digest /= undefined andalso DigestGen /= undefined ->
454 1 mongoose_gen_auth:check_password(Module, HostType, LUser, LServer,
455 Password, Digest, DigestGen);
456 _ ->
457 4054 mongoose_gen_auth:check_password(Module, HostType, LUser, LServer, Password)
458 end,
459 4055 case Result of
460 4045 true -> {ok, mongoose_credentials:set(Creds, auth_module, Module)};
461 10 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 11026 case mongoose_domain_api:get_domain_host_type(Domain) of
485 {ok, HostType} ->
486 11009 StepF = bind_host_type(HostType, F),
487 11009 case maps:take(metric, Opts) of
488 {Metric, NewOpts} ->
489 7326 {Time, Result} = timer:tc(fun call_auth_modules_for_host_type/3,
490 [HostType, StepF, NewOpts]),
491 7326 mongoose_metrics:update(HostType, ?METRIC(Metric), Time),
492 7326 Result;
493 error ->
494 3683 call_auth_modules_for_host_type(HostType, StepF, Opts)
495 end;
496 {error, not_found} ->
497 17 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 11009 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 115188 Modules = auth_modules_for_host_type(HostType),
509 115188 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 529 lists:map(F, Modules);
516 call_auth_modules(Modules, F, Opts) when is_function(F, 1) ->
517 110593 call_auth_modules(Modules, fun(Mod, _) -> F(Mod) end, Opts);
518 call_auth_modules(Modules, F, #{default := Default}) when is_function(F, 2) ->
519 114659 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 77358 FinalAcc;
525 fold_auth_modules([AuthModule | AuthModules], F, CurAcc) ->
526 114659 case F(AuthModule, CurAcc) of
527 continue ->
528 73944 fold_auth_modules(AuthModules, F, CurAcc);
529 {continue, NewAcc} ->
530 3414 fold_auth_modules(AuthModules, F, NewAcc);
531 {stop, Value} ->
532 37301 Value
533 end.
Line Hits Source