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