./ct_report/coverage/ejabberd_auth_rdbms.COVER.html

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 235 prepare_queries(HostType),
81 235 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 186 #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 17502 supports_sasl_module(_HostType, cyrsasl_plain) -> true;
96 7 supports_sasl_module(HostType, cyrsasl_digest) -> not mongoose_scram:enabled(HostType);
97 129295 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 6188 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 6193 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 6174 case mongoose_scram:deserialize(PassDetails) of
116 {ok, Scram} ->
117 6174 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 10 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 5377 PreparedPass = prepare_password(HostType, Password),
188 5377 try execute_add_user(HostType, LServer, LUser, PreparedPass) of
189 {updated, 1} ->
190 5377 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 253 try
205 253 {selected, Res} = execute_list_users(HostType, LServer, maps:from_list(Opts)),
206 253 [{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 5596 try
217 5596 Selected = execute_count_users(HostType, LServer, maps:from_list(Opts)),
218 5596 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 14653 try execute_get_password(HostType, LServer, LUser) of
271 {selected, [{_Password, _}]} ->
272 8530 true; %% Account exists
273 {selected, []} ->
274 6123 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 5350 try
290 5350 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 5350 ok.
298
299 -spec remove_domain(mongooseim:host_type(), jid:lserver()) -> ok.
300 remove_domain(HostType, Domain) ->
301 21 execute_successfully(HostType, auth_remove_domain, [Domain]),
302 21 ok.
303
304 -spec supported_features() -> [atom()].
305 78 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 5356 Scram = mongoose_scram:password_to_scram(HostType, Password, Iterations),
317 5356 #{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 5388 case mongoose_scram:enabled(HostType) of
324 true ->
325 5356 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 235 prepare(auth_get_password, users,
394 [server, username],
395 <<"SELECT password, pass_details FROM users "
396 "WHERE server = ? AND username = ?">>),
397 235 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 235 prepare(auth_set_password, users,
402 [password, server, username],
403 <<"UPDATE users SET password = ? WHERE server = ? AND username = ?">>),
404 235 prepare(auth_add_user_scram, users,
405 [server, username, password, pass_details],
406 <<"INSERT INTO users(server, username, password, pass_details) VALUES (?, ?, ?, ?)">>),
407 235 prepare(auth_add_user, users,
408 [server, username, password],
409 <<"INSERT INTO users(server, username, password) VALUES (?, ?, ?)">>),
410 235 prepare(auth_delete_user, users,
411 [server, username],
412 <<"DELETE FROM users WHERE server = ? AND username = ?">>),
413 235 prepare(auth_list_users, users, [server],
414 <<"SELECT username FROM users WHERE server = ?">>),
415 235 LimitOffset = rdbms_queries:limit_offset_sql(),
416 235 prepare(auth_list_users_range, users,
417 [server, limit, offset],
418 <<"SELECT username FROM users WHERE server = ? ORDER BY username ",
419 LimitOffset/binary>>),
420 235 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 235 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 235 {LimitSQL, LimitMSSQL} = rdbms_queries:get_db_specific_limits_binaries(),
430 235 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 235 prepare(auth_count_users_prefix, users,
435 [server, username],
436 <<"SELECT COUNT(*) FROM users WHERE server = ? AND username LIKE ? ESCAPE '$'">>),
437 235 prepare_count_users(HostType),
438 235 prepare(auth_count_users_without_scram, users, [server],
439 <<"SELECT COUNT(*) FROM users WHERE server = ? AND pass_details is NULL">>),
440 235 prepare(auth_remove_domain, users, [server],
441 <<"DELETE FROM users WHERE server = ?">>).
442
443 prepare_count_users(HostType) ->
444 235 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 234 prepare_count_users()
457 end.
458
459 prepare_count_users() ->
460 235 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 20993 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 5345 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 5350 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 248 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 5595 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 5594 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, $%>>.
Line Hits Source