1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : ejabberd_auth_rdbms.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Authentification via RDBMS |
5 |
|
%%% Created : 12 Dec 2004 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(ejabberd_auth_rdbms). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
%% External exports |
30 |
|
-behaviour(mongoose_gen_auth). |
31 |
|
|
32 |
|
-export([start/1, |
33 |
|
stop/1, |
34 |
|
config_spec/0, |
35 |
|
authorize/1, |
36 |
|
set_password/4, |
37 |
|
try_register/4, |
38 |
|
get_registered_users/3, |
39 |
|
get_registered_users_number/3, |
40 |
|
get_password/3, |
41 |
|
get_password_s/3, |
42 |
|
does_user_exist/3, |
43 |
|
remove_user/3, |
44 |
|
remove_domain/2, |
45 |
|
supports_sasl_module/2, |
46 |
|
supported_features/0 |
47 |
|
]). |
48 |
|
|
49 |
|
%% Internal |
50 |
|
-export([check_password/4, |
51 |
|
check_password/6]). |
52 |
|
|
53 |
|
-export([scram_passwords/2, scram_passwords/4]). |
54 |
|
|
55 |
|
-ignore_xref([scram_passwords/2, scram_passwords/4]). |
56 |
|
|
57 |
|
-import(mongoose_rdbms, [prepare/4, execute_successfully/3]). |
58 |
|
|
59 |
|
-include("mongoose.hrl"). |
60 |
|
-include("mongoose_config_spec.hrl"). |
61 |
|
|
62 |
|
-define(DEFAULT_SCRAMMIFY_COUNT, 10000). |
63 |
|
-define(DEFAULT_SCRAMMIFY_INTERVAL, 1000). |
64 |
|
|
65 |
|
|
66 |
|
%%%---------------------------------------------------------------------- |
67 |
|
%%% Types |
68 |
|
%%%---------------------------------------------------------------------- |
69 |
|
|
70 |
|
-type prepared_password() :: |
71 |
|
#{password := binary(), |
72 |
|
details => binary()}. |
73 |
|
|
74 |
|
%%%---------------------------------------------------------------------- |
75 |
|
%%% API |
76 |
|
%%%---------------------------------------------------------------------- |
77 |
|
|
78 |
|
-spec start(mongooseim:host_type()) -> ok. |
79 |
|
start(HostType) -> |
80 |
266 |
prepare_queries(HostType), |
81 |
266 |
ok. |
82 |
|
|
83 |
|
-spec stop(mongooseim:host_type()) -> ok. |
84 |
|
stop(_HostType) -> |
85 |
1 |
ok. |
86 |
|
|
87 |
|
-spec config_spec() -> mongoose_config_spec:config_section(). |
88 |
|
config_spec() -> |
89 |
208 |
#section{ |
90 |
|
items = #{<<"users_number_estimate">> => #option{type = boolean}}, |
91 |
|
defaults = #{<<"users_number_estimate">> => false} |
92 |
|
}. |
93 |
|
|
94 |
|
-spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean(). |
95 |
20057 |
supports_sasl_module(_HostType, cyrsasl_plain) -> true; |
96 |
7 |
supports_sasl_module(HostType, cyrsasl_digest) -> not mongoose_scram:enabled(HostType); |
97 |
148754 |
supports_sasl_module(HostType, Mechanism) -> mongoose_scram:enabled(HostType, Mechanism). |
98 |
|
|
99 |
|
-spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()} |
100 |
|
| {error, any()}. |
101 |
|
authorize(Creds) -> |
102 |
6974 |
ejabberd_auth:authorize_with_check_password(?MODULE, Creds). |
103 |
|
|
104 |
|
-spec check_password(HostType :: mongooseim:host_type(), |
105 |
|
LUser :: jid:luser(), |
106 |
|
LServer :: jid:lserver(), |
107 |
|
Password :: binary()) -> boolean(). |
108 |
|
check_password(HostType, LUser, LServer, Password) -> |
109 |
6979 |
try execute_get_password(HostType, LServer, LUser) of |
110 |
|
{selected, [{Password, null}]} -> |
111 |
9 |
Password /= <<"">>; %% Password is correct, and not empty |
112 |
|
{selected, [{_Password2, null}]} -> |
113 |
:-( |
false; |
114 |
|
{selected, [{_Password2, PassDetails}]} -> |
115 |
6956 |
case mongoose_scram:deserialize(PassDetails) of |
116 |
|
{ok, Scram} -> |
117 |
6956 |
mongoose_scram:check_password(Password, Scram); |
118 |
|
{error, Reason} -> |
119 |
:-( |
?LOG_WARNING(#{what => scram_serialisation_incorrect, reason => Reason, |
120 |
:-( |
user => LUser, server => LServer}), |
121 |
:-( |
false %% Password is not correct |
122 |
|
end; |
123 |
|
{selected, []} -> |
124 |
14 |
false %% Account does not exist |
125 |
|
catch |
126 |
|
error:Reason:StackTrace -> |
127 |
:-( |
?LOG_ERROR(#{what => check_password_failed, |
128 |
|
user => LUser, server => LServer, |
129 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
130 |
:-( |
false %% Database error |
131 |
|
end. |
132 |
|
|
133 |
|
-spec check_password(HostType :: mongooseim:host_type(), |
134 |
|
LUser :: jid:luser(), |
135 |
|
LServer :: jid:lserver(), |
136 |
|
Password :: binary(), |
137 |
|
Digest :: binary(), |
138 |
|
DigestGen :: fun()) -> boolean(). |
139 |
|
|
140 |
|
check_password(HostType, LUser, LServer, Password, Digest, DigestGen) -> |
141 |
1 |
try execute_get_password(HostType, LServer, LUser) of |
142 |
|
{selected, [{Passwd, null}]} -> |
143 |
1 |
ejabberd_auth:check_digest(Digest, DigestGen, Password, Passwd); |
144 |
|
{selected, [{_Passwd, PassDetails}]} -> |
145 |
:-( |
case mongoose_scram:deserialize(PassDetails) of |
146 |
|
{ok, Scram} -> |
147 |
:-( |
mongoose_scram:check_digest(Scram, Digest, DigestGen, Password); |
148 |
|
{error, Reason} -> |
149 |
:-( |
?LOG_WARNING(#{what => scram_serialisation_incorrect, reason => Reason, |
150 |
:-( |
user => LUser, server => LServer}), |
151 |
:-( |
false |
152 |
|
end; |
153 |
|
{selected, []} -> |
154 |
:-( |
false %% Account does not exist |
155 |
|
catch |
156 |
|
error:Reason:StackTrace -> |
157 |
:-( |
?LOG_ERROR(#{what => check_password_failed, |
158 |
|
user => LUser, server => LServer, |
159 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
160 |
:-( |
false %% Database error |
161 |
|
end. |
162 |
|
|
163 |
|
-spec set_password(HostType :: mongooseim:host_type(), |
164 |
|
LUser :: jid:luser(), |
165 |
|
LServer :: jid:lserver(), |
166 |
|
Password :: binary() |
167 |
|
) -> ok | {error, not_allowed}. |
168 |
|
set_password(HostType, LUser, LServer, Password) -> |
169 |
11 |
PreparedPass = prepare_password(HostType, Password), |
170 |
11 |
try |
171 |
11 |
execute_set_password(HostType, LServer, LUser, PreparedPass), |
172 |
11 |
ok |
173 |
|
catch |
174 |
|
error:Reason:StackTrace -> |
175 |
:-( |
?LOG_ERROR(#{what => set_password_failed, |
176 |
|
user => LUser, server => LServer, |
177 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
178 |
:-( |
{error, not_allowed} |
179 |
|
end. |
180 |
|
|
181 |
|
-spec try_register(HostType :: mongooseim:host_type(), |
182 |
|
LUser :: jid:luser(), |
183 |
|
LServer :: jid:lserver(), |
184 |
|
Password :: binary() |
185 |
|
) -> ok | {error, exists}. |
186 |
|
try_register(HostType, LUser, LServer, Password) -> |
187 |
6130 |
PreparedPass = prepare_password(HostType, Password), |
188 |
6130 |
try execute_add_user(HostType, LServer, LUser, PreparedPass) of |
189 |
|
{updated, 1} -> |
190 |
6130 |
ok; |
191 |
|
{updated, 0} -> |
192 |
:-( |
{error, exists} |
193 |
|
catch |
194 |
|
error:Reason:StackTrace -> |
195 |
:-( |
?LOG_ERROR(#{what => registration_failed, |
196 |
|
user => LUser, server => LServer, |
197 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
198 |
:-( |
{error, exists} %% XXX wrong error type - fix type in a separate PR |
199 |
|
end. |
200 |
|
|
201 |
|
-spec get_registered_users(mongooseim:host_type(), jid:lserver(), Opts :: list()) -> |
202 |
|
[jid:simple_bare_jid()]. |
203 |
|
get_registered_users(HostType, LServer, Opts) -> |
204 |
259 |
try |
205 |
259 |
{selected, Res} = execute_list_users(HostType, LServer, maps:from_list(Opts)), |
206 |
259 |
[{U, LServer} || {U} <- Res] |
207 |
|
catch error:Reason:StackTrace -> |
208 |
:-( |
?LOG_ERROR(#{what => get_vh_registered_users_failed, server => LServer, |
209 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
210 |
:-( |
[] |
211 |
|
end. |
212 |
|
|
213 |
|
-spec get_registered_users_number(mongooseim:host_type(), jid:lserver(), Opts :: list()) -> |
214 |
|
non_neg_integer(). |
215 |
|
get_registered_users_number(HostType, LServer, Opts) -> |
216 |
6371 |
try |
217 |
6371 |
Selected = execute_count_users(HostType, LServer, maps:from_list(Opts)), |
218 |
6371 |
mongoose_rdbms:selected_to_integer(Selected) |
219 |
|
catch error:Reason:StackTrace -> |
220 |
:-( |
?LOG_ERROR(#{what => get_vh_registered_users_numbers_failed, server => LServer, |
221 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
222 |
:-( |
0 |
223 |
|
end. |
224 |
|
|
225 |
|
-spec get_password(mongooseim:host_type(), jid:luser(), jid:lserver()) -> |
226 |
|
ejabberd_auth:passterm() | false. |
227 |
|
get_password(HostType, LUser, LServer) -> |
228 |
129 |
try execute_get_password(HostType, LServer, LUser) of |
229 |
|
{selected, [{Password, null}]} -> |
230 |
45 |
Password; %% Plain password |
231 |
|
{selected, [{_Password, PassDetails}]} -> |
232 |
82 |
case mongoose_scram:deserialize(PassDetails) of |
233 |
|
{ok, Scram} -> |
234 |
82 |
Scram; |
235 |
|
{error, Reason} -> |
236 |
:-( |
?LOG_WARNING(#{what => scram_serialisation_incorrect, reason => Reason, |
237 |
:-( |
user => LUser, server => LServer}), |
238 |
:-( |
false |
239 |
|
end; |
240 |
|
{selected, []} -> |
241 |
2 |
false |
242 |
|
catch |
243 |
|
error:Reason:StackTrace -> |
244 |
:-( |
?LOG_ERROR(#{what => get_password_failed, |
245 |
|
user => LUser, server => LServer, |
246 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
247 |
:-( |
false |
248 |
|
end. |
249 |
|
|
250 |
|
|
251 |
|
-spec get_password_s(mongooseim:host_type(), jid:luser(), jid:lserver()) -> binary(). |
252 |
|
get_password_s(HostType, LUser, LServer) -> |
253 |
17 |
try execute_get_password(HostType, LServer, LUser) of |
254 |
|
{selected, [{Password, _}]} -> |
255 |
15 |
Password; |
256 |
|
{selected, []} -> |
257 |
2 |
<<>> |
258 |
|
catch |
259 |
|
error:Reason:StackTrace -> |
260 |
:-( |
?LOG_ERROR(#{what => get_password_s_failed, |
261 |
|
user => LUser, server => LServer, |
262 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
263 |
:-( |
<<>> |
264 |
|
end. |
265 |
|
|
266 |
|
|
267 |
|
-spec does_user_exist(mongooseim:host_type(), jid:luser(), jid:lserver()) -> |
268 |
|
boolean() | {error, atom()}. |
269 |
|
does_user_exist(HostType, LUser, LServer) -> |
270 |
16613 |
try execute_get_password(HostType, LServer, LUser) of |
271 |
|
{selected, [{_Password, _}]} -> |
272 |
9600 |
true; %% Account exists |
273 |
|
{selected, []} -> |
274 |
7013 |
false %% Account does not exist |
275 |
|
catch |
276 |
|
error:Reason:StackTrace -> |
277 |
:-( |
?LOG_ERROR(#{what => does_user_exist_failed, |
278 |
|
user => LUser, server => LServer, |
279 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}), |
280 |
:-( |
{error, Reason} %% Database error |
281 |
|
|
282 |
|
end. |
283 |
|
|
284 |
|
|
285 |
|
%% @doc Remove user. |
286 |
|
%% Note: it may return ok even if there was some problem removing the user. |
287 |
|
-spec remove_user(mongooseim:host_type(), jid:luser(), jid:lserver()) -> ok. |
288 |
|
remove_user(HostType, LUser, LServer) -> |
289 |
6103 |
try |
290 |
6103 |
execute_delete_user(HostType, LServer, LUser) |
291 |
|
catch |
292 |
|
error:Reason:StackTrace -> |
293 |
:-( |
?LOG_ERROR(#{what => remove_user_failed, |
294 |
|
user => LUser, server => LServer, |
295 |
:-( |
class => error, reason => Reason, stacktrace => StackTrace}) |
296 |
|
end, |
297 |
6103 |
ok. |
298 |
|
|
299 |
|
-spec remove_domain(mongooseim:host_type(), jid:lserver()) -> ok. |
300 |
|
remove_domain(HostType, Domain) -> |
301 |
19 |
execute_successfully(HostType, auth_remove_domain, [Domain]), |
302 |
19 |
ok. |
303 |
|
|
304 |
|
-spec supported_features() -> [atom()]. |
305 |
87 |
supported_features() -> [dynamic_domains]. |
306 |
|
|
307 |
|
%%%------------------------------------------------------------------ |
308 |
|
%%% Scram |
309 |
|
%%%------------------------------------------------------------------ |
310 |
|
|
311 |
|
-spec prepare_scrammed_password(HostType, Iterations, Password) -> prepared_password() when |
312 |
|
HostType :: mongooseim:host_type(), |
313 |
|
Iterations :: pos_integer(), |
314 |
|
Password :: binary(). |
315 |
|
prepare_scrammed_password(HostType, Iterations, Password) when is_integer(Iterations) -> |
316 |
6109 |
Scram = mongoose_scram:password_to_scram(HostType, Password, Iterations), |
317 |
6109 |
#{password => <<>>, |
318 |
|
details => mongoose_scram:serialize(Scram)}. |
319 |
|
|
320 |
|
-spec prepare_password(HostType :: mongooseim:host_type(), Password :: binary()) -> |
321 |
|
prepared_password(). |
322 |
|
prepare_password(HostType, Password) -> |
323 |
6141 |
case mongoose_scram:enabled(HostType) of |
324 |
|
true -> |
325 |
6109 |
prepare_scrammed_password(HostType, mongoose_scram:iterations(HostType), Password); |
326 |
|
false -> |
327 |
32 |
#{password => Password} |
328 |
|
end. |
329 |
|
|
330 |
|
-spec scram_passwords(Server, ScramIterationCount) -> ok | {error, atom()} when |
331 |
|
Server :: jid:lserver(), |
332 |
|
ScramIterationCount :: pos_integer(). |
333 |
|
scram_passwords(Server, ScramIterationCount) -> |
334 |
:-( |
scram_passwords(Server, ?DEFAULT_SCRAMMIFY_COUNT, |
335 |
|
?DEFAULT_SCRAMMIFY_INTERVAL, ScramIterationCount). |
336 |
|
|
337 |
|
-spec scram_passwords(Server, Count, Interval, ScramIterationCount) -> |
338 |
|
ok | {error, atom()} when |
339 |
|
Server :: jid:lserver(), |
340 |
|
Count :: pos_integer(), |
341 |
|
Interval :: pos_integer(), |
342 |
|
ScramIterationCount :: pos_integer(). |
343 |
|
scram_passwords(Server, Count, Interval, ScramIterationCount) -> |
344 |
:-( |
LServer = jid:nameprep(Server), |
345 |
:-( |
{ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer), |
346 |
:-( |
?LOG_INFO(#{what => scram_passwords, server => Server, |
347 |
:-( |
text => <<"Converting the stored passwords into SCRAM bits">>}), |
348 |
:-( |
Selected = execute_count_users_without_scram(HostType, LServer), |
349 |
:-( |
ToConvertCount = mongoose_rdbms:selected_to_integer(Selected), |
350 |
|
|
351 |
:-( |
?LOG_INFO(#{what => scram_passwords, server => Server, |
352 |
|
convert_count => ToConvertCount, |
353 |
:-( |
text => <<"Users to scrammify">>}), |
354 |
:-( |
scram_passwords1(HostType, LServer, Count, Interval, ScramIterationCount). |
355 |
|
|
356 |
|
-spec scram_passwords1(HostType, LServer, Count, Interval, ScramIterationCount) -> |
357 |
|
ok | {error, interrupted} when |
358 |
|
HostType :: mongooseim:host_type(), |
359 |
|
LServer :: jid:lserver(), |
360 |
|
Count :: pos_integer(), |
361 |
|
Interval :: pos_integer(), |
362 |
|
ScramIterationCount :: pos_integer(). |
363 |
|
scram_passwords1(HostType, LServer, Count, Interval, ScramIterationCount) -> |
364 |
:-( |
case execute_list_users_without_scram(HostType, LServer, Count) of |
365 |
|
{selected, []} -> |
366 |
:-( |
?LOG_INFO(#{what => scram_passwords_completed, |
367 |
:-( |
text => <<"All users scrammed">>}), |
368 |
:-( |
ok; |
369 |
|
{selected, Results} -> |
370 |
:-( |
?LOG_INFO(#{what => scram_passwords_progress, |
371 |
|
user_count => length(Results), |
372 |
:-( |
text => <<"Scramming users in progress...">>}), |
373 |
:-( |
lists:foreach( |
374 |
|
fun({Username, Password}) -> |
375 |
:-( |
ScrammedPassword = prepare_scrammed_password(HostType, |
376 |
|
ScramIterationCount, |
377 |
|
Password), |
378 |
:-( |
execute_set_password(HostType, LServer, Username, ScrammedPassword) |
379 |
|
end, Results), |
380 |
:-( |
?LOG_INFO(#{what => scram_passwords_progress, |
381 |
|
user_count => length(Results), interval => Interval, |
382 |
:-( |
text => io_lib:format("Scrammed. Waiting for ~pms", [Interval])}), |
383 |
:-( |
timer:sleep(Interval), |
384 |
:-( |
scram_passwords1(HostType, LServer, Count, Interval, ScramIterationCount) |
385 |
|
end. |
386 |
|
|
387 |
|
%%%------------------------------------------------------------------ |
388 |
|
%%% DB Queries |
389 |
|
%%%------------------------------------------------------------------ |
390 |
|
|
391 |
|
-spec prepare_queries(mongooseim:host_type()) -> any(). |
392 |
|
prepare_queries(HostType) -> |
393 |
266 |
prepare(auth_get_password, users, |
394 |
|
[server, username], |
395 |
|
<<"SELECT password, pass_details FROM users " |
396 |
|
"WHERE server = ? AND username = ?">>), |
397 |
266 |
prepare(auth_set_password_scram, users, |
398 |
|
[password, pass_details, server, username], |
399 |
|
<<"UPDATE users SET password = ?, pass_details = ? " |
400 |
|
"WHERE server = ? AND username = ?">>), |
401 |
266 |
prepare(auth_set_password, users, |
402 |
|
[password, server, username], |
403 |
|
<<"UPDATE users SET password = ? WHERE server = ? AND username = ?">>), |
404 |
266 |
prepare(auth_add_user_scram, users, |
405 |
|
[server, username, password, pass_details], |
406 |
|
<<"INSERT INTO users(server, username, password, pass_details) VALUES (?, ?, ?, ?)">>), |
407 |
266 |
prepare(auth_add_user, users, |
408 |
|
[server, username, password], |
409 |
|
<<"INSERT INTO users(server, username, password) VALUES (?, ?, ?)">>), |
410 |
266 |
prepare(auth_delete_user, users, |
411 |
|
[server, username], |
412 |
|
<<"DELETE FROM users WHERE server = ? AND username = ?">>), |
413 |
266 |
prepare(auth_list_users, users, [server], |
414 |
|
<<"SELECT username FROM users WHERE server = ?">>), |
415 |
266 |
LimitOffset = rdbms_queries:limit_offset_sql(), |
416 |
266 |
prepare(auth_list_users_range, users, |
417 |
|
[server, limit, offset], |
418 |
|
<<"SELECT username FROM users WHERE server = ? ORDER BY username ", |
419 |
|
LimitOffset/binary>>), |
420 |
266 |
prepare(auth_list_users_prefix, users, |
421 |
|
[server, username], |
422 |
|
<<"SELECT username FROM users " |
423 |
|
"WHERE server = ? AND username LIKE ? ESCAPE '$' ORDER BY username">>), |
424 |
266 |
prepare(auth_list_users_prefix_range, users, |
425 |
|
[server, username, limit, offset], |
426 |
|
<<"SELECT username FROM users " |
427 |
|
"WHERE server = ? AND username LIKE ? ESCAPE '$' ORDER BY username ", |
428 |
|
LimitOffset/binary>>), |
429 |
266 |
{LimitSQL, LimitMSSQL} = rdbms_queries:get_db_specific_limits_binaries(), |
430 |
266 |
prepare(auth_list_users_without_scram, users, |
431 |
|
[server, limit], |
432 |
|
<<"SELECT ", LimitMSSQL/binary, " username, password FROM users " |
433 |
|
"WHERE server = ? AND pass_details is NULL ", LimitSQL/binary>>), |
434 |
266 |
prepare(auth_count_users_prefix, users, |
435 |
|
[server, username], |
436 |
|
<<"SELECT COUNT(*) FROM users WHERE server = ? AND username LIKE ? ESCAPE '$'">>), |
437 |
266 |
prepare_count_users(HostType), |
438 |
266 |
prepare(auth_count_users_without_scram, users, [server], |
439 |
|
<<"SELECT COUNT(*) FROM users WHERE server = ? AND pass_details is NULL">>), |
440 |
266 |
prepare(auth_remove_domain, users, [server], |
441 |
|
<<"DELETE FROM users WHERE server = ?">>). |
442 |
|
|
443 |
|
prepare_count_users(HostType) -> |
444 |
266 |
case {mongoose_config:get_opt([{auth, HostType}, rdbms, users_number_estimate]), |
445 |
|
mongoose_rdbms:db_engine(HostType)} of |
446 |
|
{true, mysql} -> |
447 |
:-( |
prepare(auth_count_users_estimate, 'information_schema.tables', [], |
448 |
|
<<"SELECT table_rows FROM information_schema.tables " |
449 |
|
"WHERE table_name = 'users'">>); |
450 |
|
{true, pgsql} -> |
451 |
1 |
prepare_count_users(), |
452 |
1 |
prepare(auth_count_users_estimate, pg_class, [], |
453 |
|
<<"SELECT reltuples::numeric FROM pg_class " |
454 |
|
"WHERE oid = 'users'::regclass::oid">>); |
455 |
|
_ -> |
456 |
265 |
prepare_count_users() |
457 |
|
end. |
458 |
|
|
459 |
|
prepare_count_users() -> |
460 |
266 |
prepare(auth_count_users, users, [server], <<"SELECT COUNT(*) FROM users WHERE server = ?">>). |
461 |
|
|
462 |
|
-spec execute_get_password(mongooseim:host_type(), jid:lserver(), jid:luser()) -> |
463 |
|
mongoose_rdbms:query_result(). |
464 |
|
execute_get_password(HostType, LServer, LUser) -> |
465 |
23739 |
execute_successfully(HostType, auth_get_password, [LServer, LUser]). |
466 |
|
|
467 |
|
-spec execute_set_password(mongooseim:host_type(), jid:lserver(), jid:luser(), |
468 |
|
prepared_password()) -> |
469 |
|
mongoose_rdbms:query_result(). |
470 |
|
execute_set_password(HostType, LServer, LUser, #{password := Pass, details := PassDetails}) -> |
471 |
11 |
execute_successfully(HostType, auth_set_password_scram, [Pass, PassDetails, LServer, LUser]); |
472 |
|
execute_set_password(HostType, LServer, LUser, #{password := Pass}) -> |
473 |
:-( |
execute_successfully(HostType, auth_set_password, [Pass, LServer, LUser]). |
474 |
|
|
475 |
|
-spec execute_add_user(mongooseim:host_type(), jid:lserver(), jid:luser(), prepared_password()) -> |
476 |
|
mongoose_rdbms:query_result(). |
477 |
|
execute_add_user(HostType, LServer, LUser, #{password := Pass, details := PassDetails}) -> |
478 |
6098 |
execute_successfully(HostType, auth_add_user_scram, [LServer, LUser, Pass, PassDetails]); |
479 |
|
execute_add_user(HostType, LServer, LUser, #{password := Pass}) -> |
480 |
32 |
execute_successfully(HostType, auth_add_user, [LServer, LUser, Pass]). |
481 |
|
|
482 |
|
-spec execute_delete_user(mongooseim:host_type(), jid:lserver(), jid:luser()) -> |
483 |
|
mongoose_rdbms:query_result(). |
484 |
|
execute_delete_user(HostType, LServer, LUser) -> |
485 |
6103 |
execute_successfully(HostType, auth_delete_user, [LServer, LUser]). |
486 |
|
|
487 |
|
-spec execute_list_users(mongooseim:host_type(), jid:lserver(), map()) -> |
488 |
|
mongoose_rdbms:query_result(). |
489 |
|
execute_list_users(HostType, LServer, #{from := Start, to := End} = OptMap) -> |
490 |
4 |
Map = maps:without([from, to], OptMap), |
491 |
4 |
execute_list_users(HostType, LServer, Map#{limit => End - Start + 1, offset => Start - 1}); |
492 |
|
execute_list_users(HostType, LServer, #{prefix := Prefix, limit := Limit, offset := Offset}) -> |
493 |
2 |
Args = [LServer, prefix_to_like(Prefix)] ++ rdbms_queries:limit_offset_args(Limit, Offset), |
494 |
2 |
execute_successfully(HostType, auth_list_users_prefix_range, Args); |
495 |
|
execute_list_users(HostType, LServer, #{prefix := Prefix}) -> |
496 |
1 |
Args = [LServer, prefix_to_like(Prefix)], |
497 |
1 |
execute_successfully(HostType, auth_list_users_prefix, Args); |
498 |
|
execute_list_users(HostType, LServer, #{limit := Limit, offset := Offset}) -> |
499 |
2 |
Args = [LServer] ++ rdbms_queries:limit_offset_args(Limit, Offset), |
500 |
2 |
execute_successfully(HostType, auth_list_users_range, Args); |
501 |
|
execute_list_users(HostType, LServer, #{}) -> |
502 |
254 |
execute_successfully(HostType, auth_list_users, [LServer]). |
503 |
|
|
504 |
|
-spec execute_list_users_without_scram(mongooseim:host_type(), jid:lserver(), non_neg_integer()) -> |
505 |
|
mongoose_rdbms:query_result(). |
506 |
|
execute_list_users_without_scram(HostType, LServer, Limit) -> |
507 |
:-( |
execute_successfully(HostType, auth_list_users_without_scram, [LServer, Limit]). |
508 |
|
|
509 |
|
-spec execute_count_users(mongooseim:host_type(), jid:lserver(), map()) -> |
510 |
|
mongoose_rdbms:query_result(). |
511 |
|
execute_count_users(HostType, LServer, #{prefix := Prefix}) -> |
512 |
1 |
Args = [LServer, prefix_to_like(Prefix)], |
513 |
1 |
execute_successfully(HostType, auth_count_users_prefix, Args); |
514 |
|
execute_count_users(HostType, LServer, #{}) -> |
515 |
6370 |
case {mongoose_config:get_opt([{auth, HostType}, rdbms, users_number_estimate]), |
516 |
|
mongoose_rdbms:db_engine(LServer)} of |
517 |
|
{true, mysql} -> |
518 |
:-( |
execute_successfully(HostType, auth_count_users_estimate, []); |
519 |
|
{true, pgsql} -> |
520 |
1 |
case execute_successfully(HostType, auth_count_users_estimate, []) of |
521 |
|
{selected,[{<<"-1">>}]} -> |
522 |
1 |
execute_successfully(HostType, auth_count_users, [LServer]); |
523 |
|
Otherwise -> |
524 |
:-( |
Otherwise |
525 |
|
end; |
526 |
|
_ -> |
527 |
6369 |
execute_successfully(HostType, auth_count_users, [LServer]) |
528 |
|
end. |
529 |
|
|
530 |
|
-spec execute_count_users_without_scram(mongooseim:host_type(), jid:lserver()) -> |
531 |
|
mongoose_rdbms:query_result(). |
532 |
|
execute_count_users_without_scram(HostType, LServer) -> |
533 |
:-( |
execute_successfully(HostType, auth_count_users_without_scram, [LServer]). |
534 |
|
|
535 |
|
-spec prefix_to_like(binary()) -> binary(). |
536 |
|
prefix_to_like(Prefix) -> |
537 |
4 |
EscapedPrefix = mongoose_rdbms:escape_prepared_like(Prefix), |
538 |
4 |
<<EscapedPrefix/binary, $%>>. |