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. |