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 |
72 |
lists:foreach(fun start/1, ?ALL_HOST_TYPES). |
99 |
|
|
100 |
|
-spec start(HostType :: mongooseim:host_type()) -> 'ok'. |
101 |
|
start(HostType) -> |
102 |
384 |
ensure_metrics(HostType), |
103 |
384 |
F = fun(Mod) -> mongoose_gen_auth:start(Mod, HostType) end, |
104 |
384 |
call_auth_modules_for_host_type(HostType, F, #{op => map}), |
105 |
384 |
gen_hook:add_handlers(hooks(HostType)), |
106 |
384 |
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 |
385 |
[ |
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 |
142938 |
F = fun(Mod) -> |
127 |
143004 |
case mongoose_gen_auth:supports_sasl_module(Mod, HostType, SASLModule) of |
128 |
41317 |
true -> {stop, true}; |
129 |
101687 |
false -> continue |
130 |
|
end |
131 |
|
end, |
132 |
142938 |
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 |
6040 |
F = fun(Mod, {_CurResult, CurCreds}) -> |
138 |
6040 |
case mongoose_gen_auth:authorize(Mod, CurCreds) of |
139 |
|
{ok, NewCreds} -> |
140 |
6028 |
{stop, {ok, mongoose_credentials:register(NewCreds, Mod, success)}}; |
141 |
|
Error -> |
142 |
12 |
NewCreds = mongoose_credentials:register(CurCreds, Mod, {failure, Error}), |
143 |
12 |
{continue, {not_authorized, NewCreds}} |
144 |
|
end |
145 |
|
end, |
146 |
6040 |
Opts = #{default => {not_authorized, Creds}, metric => authorize}, |
147 |
6040 |
case call_auth_modules_with_creds(Creds, F, Opts) of |
148 |
6028 |
Res = {ok, _Creds} -> Res; |
149 |
12 |
{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 |
5317 |
Exists = does_user_exist(JID), |
221 |
5317 |
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 |
13 |
{error, exists}; |
227 |
|
do_try_register_if_does_not_exist(_, JID, Password) -> |
228 |
5304 |
{LUser, LServer} = jid:to_lus(JID), |
229 |
5304 |
F = fun(HostType, Mod) -> |
230 |
5299 |
case mongoose_gen_auth:try_register(Mod, HostType, LUser, LServer, Password) of |
231 |
|
ok -> |
232 |
5298 |
mongoose_hooks:register_user(HostType, LServer, LUser), |
233 |
5298 |
{stop, ok}; |
234 |
|
{error, _Error} -> |
235 |
1 |
continue |
236 |
|
end |
237 |
|
end, |
238 |
5304 |
Opts = #{default => {error, not_allowed}, metric => try_register}, |
239 |
5304 |
case is_user_number_below_limit(LServer) of |
240 |
|
true -> |
241 |
5301 |
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 |
236 |
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 |
241 |
LServer = jid:nameprep(Server), |
255 |
241 |
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 |
241 |
F = fun(HostType, Mod) -> |
261 |
241 |
mongoose_gen_auth:get_registered_users(Mod, HostType, LServer, Opts) |
262 |
|
end, |
263 |
241 |
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 |
5495 |
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 |
5496 |
LServer = jid:nameprep(Server), |
272 |
5496 |
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 |
5496 |
F = fun(HostType, Mod) -> |
278 |
5496 |
mongoose_gen_auth:get_registered_users_number(Mod, HostType, LServer, Opts) |
279 |
|
end, |
280 |
5496 |
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 |
12048 |
F = fun(HostType, Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) end, |
310 |
12048 |
case call_auth_modules_for_domain(LServer, F, #{default => false, metric => does_user_exist}) of |
311 |
:-( |
{error, _Error} -> false; |
312 |
12048 |
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 |
14086 |
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 |
2523 |
{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 |
2525 |
F = fun(ejabberd_auth_anonymous) -> continue; |
344 |
2523 |
(Mod) -> does_user_exist_in_module(HostType, LUser, LServer, Mod) |
345 |
|
end, |
346 |
2525 |
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 |
14526 |
case mongoose_gen_auth:does_user_exist(Mod, HostType, LUser, LServer) of |
352 |
8545 |
true -> {stop, true}; |
353 |
5981 |
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 |
167 |
Module = auth_method_to_module(AuthMethod), |
365 |
167 |
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 |
5345 |
JID = jid:make_bare(LUser, LServer), |
374 |
5345 |
F = fun(HostType, Mod) -> |
375 |
5298 |
case mongoose_gen_auth:remove_user(Mod, HostType, LUser, LServer) of |
376 |
5298 |
ok -> {continue, {ok, HostType}}; |
377 |
:-( |
{error, _Error} -> continue |
378 |
|
end |
379 |
|
end, |
380 |
5345 |
case ejabberd_auth:does_user_exist(JID) of |
381 |
|
true -> |
382 |
5298 |
case call_auth_modules_for_domain(LServer, F, #{default => {error, not_allowed}}) of |
383 |
|
{ok, HostType} -> |
384 |
5298 |
Acc = mongoose_acc:new(#{location => ?LOCATION, |
385 |
|
host_type => HostType, |
386 |
|
lserver => LServer, |
387 |
|
element => undefined}), |
388 |
5298 |
mongoose_hooks:remove_user(Acc, LServer, LUser), |
389 |
5298 |
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 |
47 |
{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 |
1296 |
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 |
180577 |
Methods = auth_methods(HostType), |
437 |
180577 |
methods_to_modules(Methods). |
438 |
|
|
439 |
|
-spec methods_to_modules([atom()]) -> [authmodule()]. |
440 |
|
methods_to_modules(Methods) -> |
441 |
180579 |
[auth_method_to_module(M) || M <- Methods]. |
442 |
|
|
443 |
|
-spec auth_methods(mongooseim:host_type()) -> [atom()]. |
444 |
|
auth_methods(HostType) -> |
445 |
180577 |
mongoose_config:get_opt([{auth, HostType}, methods]). |
446 |
|
|
447 |
|
-spec auth_method_to_module(atom()) -> authmodule(). |
448 |
|
auth_method_to_module(Method) -> |
449 |
182123 |
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 |
6 |
F = fun() -> |
455 |
6 |
FAuth = fun(Mod) -> mongoose_gen_auth:remove_domain(Mod, HostType, Domain) end, |
456 |
6 |
call_auth_modules_for_host_type(HostType, FAuth, #{op => map}), |
457 |
6 |
Acc |
458 |
|
end, |
459 |
6 |
mongoose_domain_api:remove_domain_wrapper(Acc, F, ?MODULE). |
460 |
|
|
461 |
|
ensure_metrics(Host) -> |
462 |
384 |
Metrics = [authorize, check_password, try_register, does_user_exist], |
463 |
384 |
[mongoose_metrics:ensure_metric(Host, ?METRIC(Metric), histogram) |
464 |
384 |
|| 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 |
6028 |
User = mongoose_credentials:get(Creds, username), |
473 |
6028 |
LUser = jid:nodeprep(User), |
474 |
6028 |
LUser == error andalso error({nodeprep_error, User}), |
475 |
6028 |
LServer = mongoose_credentials:lserver(Creds), |
476 |
6028 |
HostType = mongoose_credentials:host_type(Creds), |
477 |
6028 |
Password = mongoose_credentials:get(Creds, password), |
478 |
6028 |
Digest = mongoose_credentials:get(Creds, digest, undefined), |
479 |
6028 |
DigestGen = mongoose_credentials:get(Creds, digest_gen, undefined), |
480 |
6028 |
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 |
6027 |
mongoose_gen_auth:check_password(Module, HostType, LUser, LServer, Password) |
486 |
|
end, |
487 |
6028 |
case Result of |
488 |
6016 |
true -> {ok, mongoose_credentials:set(Creds, auth_module, Module)}; |
489 |
12 |
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 |
28420 |
case mongoose_domain_api:get_domain_host_type(Domain) of |
513 |
|
{ok, HostType} -> |
514 |
28371 |
StepF = bind_host_type(HostType, F), |
515 |
28371 |
case maps:take(metric, Opts) of |
516 |
|
{Metric, NewOpts} -> |
517 |
17308 |
{Time, Result} = timer:tc(fun call_auth_modules_for_host_type/3, |
518 |
|
[HostType, StepF, NewOpts]), |
519 |
17308 |
mongoose_metrics:update(HostType, ?METRIC(Metric), Time), |
520 |
17308 |
Result; |
521 |
|
error -> |
522 |
11063 |
call_auth_modules_for_host_type(HostType, StepF, Opts) |
523 |
|
end; |
524 |
|
{error, not_found} -> |
525 |
49 |
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 |
28371 |
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 |
174350 |
Modules = auth_modules_for_host_type(HostType), |
537 |
174350 |
case maps:take(metric, Opts) of |
538 |
|
{Metric, NewOpts} -> |
539 |
2525 |
{Time, Result} = timer:tc(fun call_auth_modules/3, [Modules, F, NewOpts]), |
540 |
2525 |
mongoose_metrics:update(HostType, ?METRIC(Metric), Time), |
541 |
2525 |
Result; |
542 |
|
error -> |
543 |
171825 |
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 |
6040 |
Modules = mongoose_credentials:auth_modules(Creds), |
551 |
6040 |
case maps:take(metric, Opts) of |
552 |
|
{Metric, NewOpts} -> |
553 |
6040 |
HostType = mongoose_credentials:host_type(Creds), |
554 |
6040 |
{Time, Result} = timer:tc(fun call_auth_modules/3, |
555 |
|
[Modules, F, NewOpts]), |
556 |
6040 |
mongoose_metrics:update(HostType, ?METRIC(Metric), Time), |
557 |
6040 |
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 |
6128 |
lists:map(F, Modules); |
568 |
|
call_auth_modules(Modules, F, Opts) when is_function(F, 1) -> |
569 |
168222 |
call_auth_modules(Modules, fun(Mod, _) -> F(Mod) end, Opts); |
570 |
|
call_auth_modules(Modules, F, #{default := Default}) when is_function(F, 2) -> |
571 |
174262 |
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 |
112925 |
FinalAcc; |
577 |
|
fold_auth_modules([AuthModule | AuthModules], F, CurAcc) -> |
578 |
174328 |
case F(AuthModule, CurAcc) of |
579 |
|
continue -> |
580 |
107681 |
fold_auth_modules(AuthModules, F, CurAcc); |
581 |
|
{continue, NewAcc} -> |
582 |
5310 |
fold_auth_modules(AuthModules, F, NewAcc); |
583 |
|
{stop, Value} -> |
584 |
61337 |
Value |
585 |
|
end. |
586 |
|
|
587 |
|
is_user_number_below_limit(Domain) -> |
588 |
5304 |
case mongoose_domain_api:get_domain_host_type(Domain) of |
589 |
|
{ok, HostType} -> |
590 |
5302 |
Limit = mongoose_config:get_opt([{auth, HostType}, max_users_per_domain]), |
591 |
5302 |
Current = get_vh_registered_users_number(Domain), |
592 |
5302 |
Current < Limit; |
593 |
|
{error, not_found} -> |
594 |
2 |
true |
595 |
|
end. |