mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Use cache for authentication backends
The commit introduces the following API incompatibilities: In ejabberd_auth.erl: * dirty_get_registered_users/0 is renamed to get_users/0 * get_vh_registered_users/1 is renamed to get_users/1 * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is renamed to count_users/1 * get_vh_registered_users_number/2 is renamed to count_users/2 In ejabberd_auth callbacks * plain_password_required/0 is replaced by plain_password_required/1 where the argument is a virtual host * store_type/0 is replaced by store_type/1 where the argument is a virtual host * set_password/3 is now an optional callback * remove_user/3 callback is no longer needed * remove_user/2 now should return `ok | {error, atom()}` * is_user_exists/2 now must only be implemented for backends with `external` store type * check_password/6 is no longer needed * check_password/4 now must only be implemented for backends with `external` store type * try_register/3 is now an optional callback and should return `ok | {error, atom()}` * dirty_get_registered_users/0 is no longer needed * get_vh_registered_users/1 is no longer needed * get_vh_registered_users/2 is renamed to get_users/2 * get_vh_registered_users_number/1 is no longer needed * get_vh_registered_users_number/2 is renamed to count_users/2 * get_password_s/2 is no longer needed * get_password/2 now must only be implemented for backends with `plain` or `scram` store type Additionally, the commit introduces two new callbacks: * use_cache/1 where the argument is a virtual host * cache_nodes/1 where the argument is a virtual host New options are also introduced: `auth_use_cache`, `auth_cache_missed`, `auth_cache_life_time` and `auth_cache_size`.
This commit is contained in:
parent
e890525788
commit
633b68db11
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}},
|
{deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}},
|
||||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", "470539a"}},
|
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", "470539a"}},
|
||||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", "f31d039"}},
|
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", "51eee22"}},
|
||||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.11"}}},
|
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.11"}}},
|
||||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.8"}}},
|
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.8"}}},
|
||||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.21"}}},
|
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.21"}}},
|
||||||
|
@ -111,7 +111,11 @@ mech_step(#state{step = 2} = State, ClientIn) ->
|
|||||||
{error, saslprep_failed, UserName};
|
{error, saslprep_failed, UserName};
|
||||||
true ->
|
true ->
|
||||||
{StoredKey, ServerKey, Salt, IterationCount} =
|
{StoredKey, ServerKey, Salt, IterationCount} =
|
||||||
if is_tuple(Pass) -> Pass;
|
if is_record(Pass, scram) ->
|
||||||
|
{misc:decode_base64(Pass#scram.storedkey),
|
||||||
|
misc:decode_base64(Pass#scram.serverkey),
|
||||||
|
misc:decode_base64(Pass#scram.salt),
|
||||||
|
Pass#scram.iterationcount};
|
||||||
true ->
|
true ->
|
||||||
TempSalt =
|
TempSalt =
|
||||||
randoms:bytes(?SALT_LENGTH),
|
randoms:bytes(?SALT_LENGTH),
|
||||||
|
@ -478,9 +478,9 @@ update_module(ModuleNameString) ->
|
|||||||
|
|
||||||
register(User, Host, Password) ->
|
register(User, Host, Password) ->
|
||||||
case ejabberd_auth:try_register(User, Host, Password) of
|
case ejabberd_auth:try_register(User, Host, Password) of
|
||||||
{atomic, ok} ->
|
ok ->
|
||||||
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
{ok, io_lib:format("User ~s@~s successfully registered", [User, Host])};
|
||||||
{atomic, exists} ->
|
{error, exists} ->
|
||||||
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
|
Msg = io_lib:format("User ~s@~s already registered", [User, Host]),
|
||||||
{error, conflict, 10090, Msg};
|
{error, conflict, 10090, Msg};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
@ -494,7 +494,7 @@ unregister(User, Host) ->
|
|||||||
{ok, ""}.
|
{ok, ""}.
|
||||||
|
|
||||||
registered_users(Host) ->
|
registered_users(Host) ->
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
SUsers = lists:sort(Users),
|
SUsers = lists:sort(Users),
|
||||||
lists:map(fun({U, _S}) -> U end, SUsers).
|
lists:map(fun({U, _S}) -> U end, SUsers).
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@
|
|||||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
%%%
|
%%%
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
%% TODO: Use the functions in ejabberd auth to add and remove users.
|
|
||||||
|
|
||||||
-module(ejabberd_auth).
|
-module(ejabberd_auth).
|
||||||
|
|
||||||
-behaviour(gen_server).
|
-behaviour(gen_server).
|
||||||
@ -37,10 +34,10 @@
|
|||||||
set_password/3, check_password/4,
|
set_password/3, check_password/4,
|
||||||
check_password/6, check_password_with_authmodule/4,
|
check_password/6, check_password_with_authmodule/4,
|
||||||
check_password_with_authmodule/6, try_register/3,
|
check_password_with_authmodule/6, try_register/3,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
get_users/0, get_users/1, password_to_scram/1,
|
||||||
get_vh_registered_users/2, export/1, import_info/0,
|
get_users/2, export/1, import_info/0,
|
||||||
get_vh_registered_users_number/1, import/5, import_start/2,
|
count_users/1, import/5, import_start/2,
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
count_users/2, get_password/2,
|
||||||
get_password_s/2, get_password_with_authmodule/2,
|
get_password_s/2, get_password_with_authmodule/2,
|
||||||
is_user_exists/2, is_user_exists_in_other_modules/3,
|
is_user_exists/2, is_user_exists_in_other_modules/3,
|
||||||
remove_user/2, remove_user/3, plain_password_required/1,
|
remove_user/2, remove_user/3, plain_password_required/1,
|
||||||
@ -54,10 +51,13 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
-define(AUTH_CACHE, auth_cache).
|
||||||
|
-define(SALT_LENGTH, 16).
|
||||||
|
|
||||||
-record(state, {host_modules = #{} :: map()}).
|
-record(state, {host_modules = #{} :: map()}).
|
||||||
|
|
||||||
-type scrammed_password() :: {binary(), binary(), binary(), non_neg_integer()}.
|
-type password() :: binary() | #scram{}.
|
||||||
-type password() :: binary() | scrammed_password().
|
-type digest_fun() :: fun((binary()) -> binary()).
|
||||||
-export_type([password/0]).
|
-export_type([password/0]).
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -69,24 +69,29 @@
|
|||||||
|
|
||||||
-callback start(binary()) -> any().
|
-callback start(binary()) -> any().
|
||||||
-callback stop(binary()) -> any().
|
-callback stop(binary()) -> any().
|
||||||
-callback plain_password_required() -> boolean().
|
-callback plain_password_required(binary()) -> boolean().
|
||||||
-callback store_type() -> plain | external | scram.
|
-callback store_type(binary()) -> plain | external | scram.
|
||||||
-callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}.
|
-callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}.
|
||||||
-callback remove_user(binary(), binary()) -> any().
|
-callback remove_user(binary(), binary()) -> ok | {error, any()}.
|
||||||
-callback remove_user(binary(), binary(), binary()) -> any().
|
|
||||||
-callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}.
|
-callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}.
|
||||||
-callback check_password(binary(), binary(), binary(), binary()) -> boolean().
|
-callback check_password(binary(), binary(), binary(), binary()) -> boolean().
|
||||||
-callback check_password(binary(), binary(), binary(), binary(), binary(),
|
-callback try_register(binary(), binary(), password()) -> ok | {error, atom()}.
|
||||||
fun((binary()) -> binary())) -> boolean().
|
-callback get_users(binary(), opts()) -> [{binary(), binary()}].
|
||||||
-callback try_register(binary(), binary(), binary()) -> {atomic, atom()} |
|
-callback count_users(binary(), opts()) -> number().
|
||||||
{error, atom()}.
|
-callback get_password(binary(), binary()) -> {ok, password()} | error.
|
||||||
-callback dirty_get_registered_users() -> [{binary(), binary()}].
|
-callback use_cache(binary()) -> boolean().
|
||||||
-callback get_vh_registered_users(binary()) -> [{binary(), binary()}].
|
-callback cache_nodes(binary()) -> boolean().
|
||||||
-callback get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}].
|
|
||||||
-callback get_vh_registered_users_number(binary()) -> number().
|
-optional_callbacks([set_password/3,
|
||||||
-callback get_vh_registered_users_number(binary(), opts()) -> number().
|
remove_user/2,
|
||||||
-callback get_password(binary(), binary()) -> false | password().
|
is_user_exists/2,
|
||||||
-callback get_password_s(binary(), binary()) -> password().
|
check_password/4,
|
||||||
|
try_register/3,
|
||||||
|
get_users/2,
|
||||||
|
count_users/2,
|
||||||
|
get_password/2,
|
||||||
|
use_cache/1,
|
||||||
|
cache_nodes/1]).
|
||||||
|
|
||||||
-spec start_link() -> {ok, pid()} | {error, any()}.
|
-spec start_link() -> {ok, pid()} | {error, any()}.
|
||||||
start_link() ->
|
start_link() ->
|
||||||
@ -99,9 +104,13 @@ init([]) ->
|
|||||||
HostModules = lists:foldl(
|
HostModules = lists:foldl(
|
||||||
fun(Host, Acc) ->
|
fun(Host, Acc) ->
|
||||||
Modules = auth_modules(Host),
|
Modules = auth_modules(Host),
|
||||||
start(Host, Modules),
|
|
||||||
maps:put(Host, Modules, Acc)
|
maps:put(Host, Modules, Acc)
|
||||||
end, #{}, ?MYHOSTS),
|
end, #{}, ?MYHOSTS),
|
||||||
|
lists:foreach(
|
||||||
|
fun({Host, Modules}) ->
|
||||||
|
start(Host, Modules)
|
||||||
|
end, maps:to_list(HostModules)),
|
||||||
|
init_cache(HostModules),
|
||||||
{ok, #state{host_modules = HostModules}}.
|
{ok, #state{host_modules = HostModules}}.
|
||||||
|
|
||||||
handle_call(_Request, _From, State) ->
|
handle_call(_Request, _From, State) ->
|
||||||
@ -112,11 +121,13 @@ handle_cast({host_up, Host}, #state{host_modules = HostModules} = State) ->
|
|||||||
Modules = auth_modules(Host),
|
Modules = auth_modules(Host),
|
||||||
start(Host, Modules),
|
start(Host, Modules),
|
||||||
NewHostModules = maps:put(Host, Modules, HostModules),
|
NewHostModules = maps:put(Host, Modules, HostModules),
|
||||||
|
init_cache(NewHostModules),
|
||||||
{noreply, State#state{host_modules = NewHostModules}};
|
{noreply, State#state{host_modules = NewHostModules}};
|
||||||
handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) ->
|
handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) ->
|
||||||
Modules = maps:get(Host, HostModules, []),
|
Modules = maps:get(Host, HostModules, []),
|
||||||
stop(Host, Modules),
|
stop(Host, Modules),
|
||||||
NewHostModules = maps:remove(Host, HostModules),
|
NewHostModules = maps:remove(Host, HostModules),
|
||||||
|
init_cache(NewHostModules),
|
||||||
{noreply, State#state{host_modules = NewHostModules}};
|
{noreply, State#state{host_modules = NewHostModules}};
|
||||||
handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
||||||
NewHostModules = lists:foldl(
|
NewHostModules = lists:foldl(
|
||||||
@ -127,6 +138,7 @@ handle_cast(config_reloaded, #state{host_modules = HostModules} = State) ->
|
|||||||
stop(Host, OldModules -- NewModules),
|
stop(Host, OldModules -- NewModules),
|
||||||
maps:put(Host, NewModules, Acc)
|
maps:put(Host, NewModules, Acc)
|
||||||
end, HostModules, ?MYHOSTS),
|
end, HostModules, ?MYHOSTS),
|
||||||
|
init_cache(NewHostModules),
|
||||||
{noreply, State#state{host_modules = NewHostModules}};
|
{noreply, State#state{host_modules = NewHostModules}};
|
||||||
handle_cast(Msg, State) ->
|
handle_cast(Msg, State) ->
|
||||||
?WARNING_MSG("unexpected cast: ~p", [Msg]),
|
?WARNING_MSG("unexpected cast: ~p", [Msg]),
|
||||||
@ -162,306 +174,266 @@ host_down(Host) ->
|
|||||||
config_reloaded() ->
|
config_reloaded() ->
|
||||||
gen_server:cast(?MODULE, config_reloaded).
|
gen_server:cast(?MODULE, config_reloaded).
|
||||||
|
|
||||||
|
-spec plain_password_required(binary()) -> boolean().
|
||||||
plain_password_required(Server) ->
|
plain_password_required(Server) ->
|
||||||
lists:any(fun (M) -> M:plain_password_required() end,
|
lists:any(fun (M) -> M:plain_password_required(Server) end,
|
||||||
auth_modules(Server)).
|
auth_modules(Server)).
|
||||||
|
|
||||||
|
-spec store_type(binary()) -> plain | scram | external.
|
||||||
store_type(Server) ->
|
store_type(Server) ->
|
||||||
%% @doc Check if the user and password can login in server.
|
lists:foldl(
|
||||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
fun(_, external) -> external;
|
||||||
%% true | false
|
(M, scram) ->
|
||||||
lists:foldl(fun (_, external) -> external;
|
case M:store_type(Server) of
|
||||||
(M, scram) ->
|
external -> external;
|
||||||
case M:store_type() of
|
_ -> scram
|
||||||
external -> external;
|
end;
|
||||||
_Else -> scram
|
(M, plain) ->
|
||||||
end;
|
M:store_type(Server)
|
||||||
(M, plain) -> M:store_type()
|
end, plain, auth_modules(Server)).
|
||||||
end,
|
|
||||||
plain, auth_modules(Server)).
|
|
||||||
|
|
||||||
-spec check_password(binary(), binary(), binary(), binary()) -> boolean().
|
-spec check_password(binary(), binary(), binary(), binary()) -> boolean().
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
check_password(User, AuthzId, Server, Password) ->
|
||||||
case check_password_with_authmodule(User, AuthzId, Server,
|
check_password(User, AuthzId, Server, Password, <<"">>, undefined).
|
||||||
Password)
|
|
||||||
of
|
|
||||||
{true, _AuthModule} -> true;
|
|
||||||
false -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @doc Check if the user and password can login in server.
|
|
||||||
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string(),
|
|
||||||
%% Digest::string(), DigestGen::function()) ->
|
|
||||||
%% true | false
|
|
||||||
-spec check_password(binary(), binary(), binary(), binary(), binary(),
|
-spec check_password(binary(), binary(), binary(), binary(), binary(),
|
||||||
fun((binary()) -> binary())) -> boolean().
|
digest_fun() | undefined) -> boolean().
|
||||||
|
check_password(User, AuthzId, Server, Password, Digest, DigestGen) ->
|
||||||
check_password(User, AuthzId, Server, Password, Digest,
|
case check_password_with_authmodule(
|
||||||
DigestGen) ->
|
User, AuthzId, Server, Password, Digest, DigestGen) of
|
||||||
case check_password_with_authmodule(User, AuthzId, Server,
|
{true, _AuthModule} -> true;
|
||||||
Password, Digest, DigestGen)
|
false -> false
|
||||||
of
|
|
||||||
{true, _AuthModule} -> true;
|
|
||||||
false -> false
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Check if the user and password can login in server.
|
-spec check_password_with_authmodule(binary(), binary(),
|
||||||
%% The user can login if at least an authentication method accepts the user
|
binary(), binary()) -> false | {true, atom()}.
|
||||||
%% and the password.
|
check_password_with_authmodule(User, AuthzId, Server, Password) ->
|
||||||
%% The first authentication method that accepts the credentials is returned.
|
check_password_with_authmodule(
|
||||||
%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string()) ->
|
User, AuthzId, Server, Password, <<"">>, undefined).
|
||||||
%% {true, AuthModule} | false
|
|
||||||
%% where
|
|
||||||
%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external
|
|
||||||
%% | ejabberd_auth_mnesia | ejabberd_auth_ldap
|
|
||||||
%% | ejabberd_auth_sql | ejabberd_auth_pam | ejabberd_auth_riak
|
|
||||||
-spec check_password_with_authmodule(binary(), binary(), binary(), binary()) -> false |
|
|
||||||
{true, atom()}.
|
|
||||||
|
|
||||||
check_password_with_authmodule(User, AuthzId, Server,
|
-spec check_password_with_authmodule(
|
||||||
Password) ->
|
binary(), binary(), binary(), binary(), binary(),
|
||||||
check_password_loop(auth_modules(Server),
|
digest_fun() | undefined) -> false | {true, atom()}.
|
||||||
[User, AuthzId, Server, Password]).
|
check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) ->
|
||||||
|
case validate_credentials(User, Server) of
|
||||||
-spec check_password_with_authmodule(binary(), binary(), binary(), binary(), binary(),
|
{ok, LUser, LServer} ->
|
||||||
fun((binary()) -> binary())) -> false |
|
lists:foldl(
|
||||||
{true, atom()}.
|
fun(Mod, false) ->
|
||||||
|
case db_check_password(
|
||||||
check_password_with_authmodule(User, AuthzId, Server, Password,
|
LUser, AuthzId, LServer, Password,
|
||||||
Digest, DigestGen) ->
|
Digest, DigestGen, Mod) of
|
||||||
check_password_loop(auth_modules(Server),
|
true -> {true, Mod};
|
||||||
[User, AuthzId, Server, Password, Digest, DigestGen]).
|
false -> false
|
||||||
|
end;
|
||||||
check_password_loop([], _Args) -> false;
|
(_, Acc) ->
|
||||||
check_password_loop([AuthModule | AuthModules], Args) ->
|
Acc
|
||||||
case apply(AuthModule, check_password, Args) of
|
end, false, auth_modules(LServer));
|
||||||
true -> {true, AuthModule};
|
_ ->
|
||||||
false -> check_password_loop(AuthModules, Args)
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec set_password(binary(), binary(), binary()) -> ok |
|
-spec set_password(binary(), binary(), password()) -> ok | {error, atom()}.
|
||||||
{error, atom()}.
|
|
||||||
|
|
||||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
|
||||||
%% ok | {error, ErrorType}
|
|
||||||
%% where ErrorType = empty_password | not_allowed | invalid_jid
|
|
||||||
set_password(_User, _Server, <<"">>) ->
|
|
||||||
{error, empty_password};
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed}
|
case validate_credentials(User, Server, Password) of
|
||||||
lists:foldl(fun (M, {error, _}) ->
|
{ok, LUser, LServer} ->
|
||||||
M:set_password(User, Server, Password);
|
lists:foldl(
|
||||||
(_M, Res) -> Res
|
fun(M, {error, _}) ->
|
||||||
end,
|
db_set_password(LUser, LServer, Password, M);
|
||||||
{error, not_allowed}, auth_modules(Server)).
|
(_, ok) ->
|
||||||
|
ok
|
||||||
-spec try_register(binary(), binary(), binary()) -> {atomic, atom()} |
|
end, {error, not_allowed}, auth_modules(LServer));
|
||||||
{error, atom()}.
|
Err ->
|
||||||
|
Err
|
||||||
try_register(_User, _Server, <<"">>) ->
|
|
||||||
{error, not_allowed};
|
|
||||||
try_register(User, Server, Password) ->
|
|
||||||
case is_user_exists(User, Server) of
|
|
||||||
true -> {atomic, exists};
|
|
||||||
false ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_router:is_my_host(LServer) of
|
|
||||||
true ->
|
|
||||||
Res = lists:foldl(fun (_M, {atomic, ok} = Res) -> Res;
|
|
||||||
(M, _) ->
|
|
||||||
M:try_register(User, Server, Password)
|
|
||||||
end,
|
|
||||||
{error, not_allowed}, auth_modules(Server)),
|
|
||||||
case Res of
|
|
||||||
{atomic, ok} ->
|
|
||||||
ejabberd_hooks:run(register_user, Server,
|
|
||||||
[User, Server]),
|
|
||||||
{atomic, ok};
|
|
||||||
_ -> Res
|
|
||||||
end;
|
|
||||||
false -> {error, not_allowed}
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% Registered users list do not include anonymous users logged
|
-spec try_register(binary(), binary(), password()) -> ok | {error, atom()}.
|
||||||
-spec dirty_get_registered_users() -> [{binary(), binary()}].
|
try_register(User, Server, Password) ->
|
||||||
|
case validate_credentials(User, Server, Password) of
|
||||||
|
{ok, LUser, LServer} ->
|
||||||
|
case is_user_exists(LUser, LServer) of
|
||||||
|
true ->
|
||||||
|
{error, exists};
|
||||||
|
false ->
|
||||||
|
case ejabberd_router:is_my_host(LServer) of
|
||||||
|
true ->
|
||||||
|
case lists:foldl(
|
||||||
|
fun(_, ok) ->
|
||||||
|
ok;
|
||||||
|
(Mod, _) ->
|
||||||
|
db_try_register(
|
||||||
|
User, Server, Password, Mod)
|
||||||
|
end, {error, not_allowed},
|
||||||
|
auth_modules(LServer)) of
|
||||||
|
ok ->
|
||||||
|
ejabberd_hooks:run(
|
||||||
|
register_user, Server, [User, Server]);
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, not_allowed}
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
-spec get_users() -> [{binary(), binary()}].
|
||||||
lists:flatmap(fun (M) -> M:dirty_get_registered_users()
|
get_users() ->
|
||||||
end,
|
lists:flatmap(
|
||||||
auth_modules()).
|
fun({Host, Mod}) ->
|
||||||
|
db_get_users(Host, [], Mod)
|
||||||
|
end, auth_modules()).
|
||||||
|
|
||||||
-spec get_vh_registered_users(binary()) -> [{binary(), binary()}].
|
-spec get_users(binary()) -> [{binary(), binary()}].
|
||||||
|
get_users(Server) ->
|
||||||
|
get_users(Server, []).
|
||||||
|
|
||||||
%% Registered users list do not include anonymous users logged
|
-spec get_users(binary(), opts()) -> [{binary(), binary()}].
|
||||||
get_vh_registered_users(Server) ->
|
get_users(Server, Opts) ->
|
||||||
lists:flatmap(fun (M) ->
|
case jid:nameprep(Server) of
|
||||||
M:get_vh_registered_users(Server)
|
error -> [];
|
||||||
end,
|
LServer ->
|
||||||
auth_modules(Server)).
|
lists:flatmap(
|
||||||
|
fun(M) -> db_get_users(LServer, Opts, M) end,
|
||||||
|
auth_modules(LServer))
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}].
|
-spec count_users(binary()) -> non_neg_integer().
|
||||||
|
count_users(Server) ->
|
||||||
|
count_users(Server, []).
|
||||||
|
|
||||||
get_vh_registered_users(Server, Opts) ->
|
-spec count_users(binary(), opts()) -> non_neg_integer().
|
||||||
lists:flatmap(fun (M) ->
|
count_users(Server, Opts) ->
|
||||||
case erlang:function_exported(M,
|
case jid:nameprep(Server) of
|
||||||
get_vh_registered_users,
|
error -> 0;
|
||||||
2)
|
LServer ->
|
||||||
of
|
lists:sum(
|
||||||
true -> M:get_vh_registered_users(Server, Opts);
|
lists:map(
|
||||||
false -> M:get_vh_registered_users(Server)
|
fun(M) -> db_count_users(LServer, Opts, M) end,
|
||||||
end
|
auth_modules(LServer)))
|
||||||
end,
|
end.
|
||||||
auth_modules(Server)).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
lists:sum(lists:map(fun (M) ->
|
|
||||||
case erlang:function_exported(M,
|
|
||||||
get_vh_registered_users_number,
|
|
||||||
1)
|
|
||||||
of
|
|
||||||
true ->
|
|
||||||
M:get_vh_registered_users_number(Server);
|
|
||||||
false ->
|
|
||||||
length(M:get_vh_registered_users(Server))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
auth_modules(Server))).
|
|
||||||
|
|
||||||
-spec get_vh_registered_users_number(binary(), opts()) -> number().
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, Opts) ->
|
|
||||||
%% @doc Get the password of the user.
|
|
||||||
%% @spec (User::string(), Server::string()) -> Password::string()
|
|
||||||
lists:sum(lists:map(fun (M) ->
|
|
||||||
case erlang:function_exported(M,
|
|
||||||
get_vh_registered_users_number,
|
|
||||||
2)
|
|
||||||
of
|
|
||||||
true ->
|
|
||||||
M:get_vh_registered_users_number(Server,
|
|
||||||
Opts);
|
|
||||||
false ->
|
|
||||||
length(M:get_vh_registered_users(Server))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
auth_modules(Server))).
|
|
||||||
|
|
||||||
-spec get_password(binary(), binary()) -> false | password().
|
-spec get_password(binary(), binary()) -> false | password().
|
||||||
|
|
||||||
get_password(User, Server) ->
|
get_password(User, Server) ->
|
||||||
lists:foldl(fun (M, false) ->
|
case validate_credentials(User, Server) of
|
||||||
M:get_password(User, Server);
|
{ok, LUser, LServer} ->
|
||||||
(_M, Password) -> Password
|
case lists:foldl(
|
||||||
end,
|
fun(M, error) -> db_get_password(LUser, LServer, M);
|
||||||
false, auth_modules(Server)).
|
(_M, Acc) -> Acc
|
||||||
|
end, error, auth_modules(LServer)) of
|
||||||
|
{ok, Password} ->
|
||||||
|
Password;
|
||||||
|
error ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
-spec get_password_s(binary(), binary()) -> password().
|
-spec get_password_s(binary(), binary()) -> password().
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
get_password_s(User, Server) ->
|
||||||
case get_password(User, Server) of
|
case get_password(User, Server) of
|
||||||
false -> <<"">>;
|
false -> <<"">>;
|
||||||
Password -> Password
|
Password -> Password
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Get the password of the user and the auth module.
|
|
||||||
%% @spec (User::string(), Server::string()) ->
|
|
||||||
%% {Password::string(), AuthModule::atom()} | {false, none}
|
|
||||||
-spec get_password_with_authmodule(binary(), binary()) -> {false | password(), module()}.
|
-spec get_password_with_authmodule(binary(), binary()) -> {false | password(), module()}.
|
||||||
|
|
||||||
get_password_with_authmodule(User, Server) ->
|
get_password_with_authmodule(User, Server) ->
|
||||||
%% Returns true if the user exists in the DB or if an anonymous user is logged
|
case validate_credentials(User, Server) of
|
||||||
%% under the given name
|
{ok, LUser, LServer} ->
|
||||||
lists:foldl(fun (M, {false, _}) ->
|
case lists:foldl(
|
||||||
{M:get_password(User, Server), M};
|
fun(M, {error, _}) ->
|
||||||
(_M, {Password, AuthModule}) -> {Password, AuthModule}
|
{db_get_password(LUser, LServer, M), M};
|
||||||
end,
|
(_M, Acc) ->
|
||||||
{false, none}, auth_modules(Server)).
|
Acc
|
||||||
|
end, {error, undefined}, auth_modules(LServer)) of
|
||||||
|
{{ok, Password}, Module} ->
|
||||||
|
{Password, Module};
|
||||||
|
{error, Module} ->
|
||||||
|
{false, Module}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{false, undefined}
|
||||||
|
end.
|
||||||
|
|
||||||
-spec is_user_exists(binary(), binary()) -> boolean().
|
-spec is_user_exists(binary(), binary()) -> boolean().
|
||||||
|
|
||||||
is_user_exists(_User, <<"">>) ->
|
is_user_exists(_User, <<"">>) ->
|
||||||
false;
|
false;
|
||||||
|
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
%% Check if the user exists in all authentications module except the module
|
case validate_credentials(User, Server) of
|
||||||
%% passed as parameter
|
{ok, LUser, LServer} ->
|
||||||
%% @spec (Module::atom(), User, Server) -> true | false | maybe
|
lists:any(
|
||||||
lists:any(fun (M) ->
|
fun(M) ->
|
||||||
case M:is_user_exists(User, Server) of
|
case db_is_user_exists(LUser, LServer, M) of
|
||||||
{error, Error} ->
|
{error, _} ->
|
||||||
?ERROR_MSG("The authentication module ~p returned "
|
false;
|
||||||
"an error~nwhen checking user ~p in server "
|
Else ->
|
||||||
"~p~nError message: ~p",
|
Else
|
||||||
[M, User, Server, Error]),
|
|
||||||
false;
|
|
||||||
Else -> Else
|
|
||||||
end
|
end
|
||||||
end,
|
end, auth_modules(LServer));
|
||||||
auth_modules(Server)).
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
-spec is_user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe.
|
-spec is_user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe.
|
||||||
|
|
||||||
is_user_exists_in_other_modules(Module, User, Server) ->
|
is_user_exists_in_other_modules(Module, User, Server) ->
|
||||||
is_user_exists_in_other_modules_loop(auth_modules(Server)
|
is_user_exists_in_other_modules_loop(
|
||||||
-- [Module],
|
auth_modules(Server) -- [Module], User, Server).
|
||||||
User, Server).
|
|
||||||
|
|
||||||
is_user_exists_in_other_modules_loop([], _User,
|
is_user_exists_in_other_modules_loop([], _User, _Server) ->
|
||||||
_Server) ->
|
|
||||||
false;
|
false;
|
||||||
is_user_exists_in_other_modules_loop([AuthModule
|
is_user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) ->
|
||||||
| AuthModules],
|
case db_is_user_exists(User, Server, AuthModule) of
|
||||||
User, Server) ->
|
true ->
|
||||||
case AuthModule:is_user_exists(User, Server) of
|
true;
|
||||||
true -> true;
|
false ->
|
||||||
false ->
|
is_user_exists_in_other_modules_loop(AuthModules, User, Server);
|
||||||
is_user_exists_in_other_modules_loop(AuthModules, User,
|
{error, _} ->
|
||||||
Server);
|
maybe
|
||||||
{error, Error} ->
|
|
||||||
?DEBUG("The authentication module ~p returned "
|
|
||||||
"an error~nwhen checking user ~p in server "
|
|
||||||
"~p~nError message: ~p",
|
|
||||||
[AuthModule, User, Server, Error]),
|
|
||||||
maybe
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec remove_user(binary(), binary()) -> ok.
|
-spec remove_user(binary(), binary()) -> ok.
|
||||||
|
|
||||||
%% @spec (User, Server) -> ok
|
|
||||||
%% @doc Remove user.
|
|
||||||
%% Note: it may return ok even if there was some problem removing the user.
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
lists:foreach(fun (M) -> M:remove_user(User, Server)
|
case validate_credentials(User, Server) of
|
||||||
end,
|
{ok, LUser, LServer} ->
|
||||||
auth_modules(Server)),
|
lists:foreach(
|
||||||
ejabberd_hooks:run(remove_user, jid:nameprep(Server),
|
fun(Mod) -> db_remove_user(LUser, LServer, Mod) end,
|
||||||
[User, Server]),
|
auth_modules(LServer)),
|
||||||
ok.
|
ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]);
|
||||||
|
_Err ->
|
||||||
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error
|
ok
|
||||||
%% @doc Try to remove user if the provided password is correct.
|
end.
|
||||||
%% The removal is attempted in each auth method provided:
|
|
||||||
%% when one returns 'ok' the loop stops;
|
|
||||||
%% if no method returns 'ok' then it returns the error message indicated by the last method attempted.
|
|
||||||
-spec remove_user(binary(), binary(), binary()) -> any().
|
|
||||||
|
|
||||||
|
-spec remove_user(binary(), binary(), password()) -> ok | {error, atom()}.
|
||||||
remove_user(User, Server, Password) ->
|
remove_user(User, Server, Password) ->
|
||||||
R = lists:foldl(fun (_M, ok = Res) -> Res;
|
case validate_credentials(User, Server, Password) of
|
||||||
(M, _) -> M:remove_user(User, Server, Password)
|
{ok, LUser, LServer} ->
|
||||||
end,
|
case lists:foldl(
|
||||||
error, auth_modules(Server)),
|
fun (_, ok) ->
|
||||||
case R of
|
ok;
|
||||||
ok ->
|
(Mod, _) ->
|
||||||
ejabberd_hooks:run(remove_user, jid:nameprep(Server),
|
case db_check_password(
|
||||||
[User, Server]);
|
LUser, <<"">>, LServer, Password,
|
||||||
_ -> none
|
<<"">>, undefined, Mod) of
|
||||||
end,
|
true ->
|
||||||
R.
|
db_remove_user(LUser, LServer, Mod);
|
||||||
|
false ->
|
||||||
|
{error, not_allowed}
|
||||||
|
end
|
||||||
|
end, {error, not_allowed}, auth_modules(Server)) of
|
||||||
|
ok ->
|
||||||
|
ejabberd_hooks:run(
|
||||||
|
remove_user, LServer, [LUser, LServer]);
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
%% @spec (IOList) -> non_negative_float()
|
|
||||||
%% @doc Calculate informational entropy.
|
%% @doc Calculate informational entropy.
|
||||||
|
-spec entropy(iodata()) -> float().
|
||||||
entropy(B) ->
|
entropy(B) ->
|
||||||
case binary_to_list(B) of
|
case binary_to_list(B) of
|
||||||
"" -> 0.0;
|
"" -> 0.0;
|
||||||
@ -497,15 +469,266 @@ backend_type(Mod) ->
|
|||||||
_ -> Mod
|
_ -> Mod
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
-spec password_format(binary() | global) -> plain | scram.
|
||||||
password_format(LServer) ->
|
password_format(LServer) ->
|
||||||
ejabberd_config:get_option({auth_password_format, LServer}, plain).
|
ejabberd_config:get_option({auth_password_format, LServer}, plain).
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Backend calls
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
db_try_register(User, Server, Password, Mod) ->
|
||||||
|
case erlang:function_exported(Mod, try_register, 3) of
|
||||||
|
true ->
|
||||||
|
Password1 = case Mod:store_type(Server) of
|
||||||
|
scram -> password_to_scram(Password);
|
||||||
|
_ -> Password
|
||||||
|
end,
|
||||||
|
case use_cache(Mod, Server) of
|
||||||
|
true ->
|
||||||
|
case ets_cache:update(
|
||||||
|
?AUTH_CACHE, {User, Server}, {ok, Password},
|
||||||
|
fun() -> Mod:try_register(User, Server, Password1) end,
|
||||||
|
cache_nodes(Mod, Server)) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, _} = Err -> Err
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
Mod:try_register(User, Server, Password1)
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, not_allowed}
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_set_password(User, Server, Password, Mod) ->
|
||||||
|
case erlang:function_exported(Mod, set_password, 3) of
|
||||||
|
true ->
|
||||||
|
Password1 = case Mod:store_type(Server) of
|
||||||
|
scram -> password_to_scram(Password);
|
||||||
|
_ -> Password
|
||||||
|
end,
|
||||||
|
case use_cache(Mod, Server) of
|
||||||
|
true ->
|
||||||
|
case ets_cache:update(
|
||||||
|
?AUTH_CACHE, {User, Server}, {ok, Password},
|
||||||
|
fun() -> Mod:set_password(User, Server, Password1) end,
|
||||||
|
cache_nodes(Mod, Server)) of
|
||||||
|
{ok, _} -> ok;
|
||||||
|
{error, _} = Err -> Err
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
Mod:set_password(User, Server, Password1)
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, not_allowed}
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_get_password(User, Server, Mod) ->
|
||||||
|
UseCache = use_cache(Mod, Server),
|
||||||
|
case erlang:function_exported(Mod, get_password, 2) of
|
||||||
|
false when UseCache ->
|
||||||
|
ets_cache:lookup(?AUTH_CACHE, {User, Server});
|
||||||
|
false ->
|
||||||
|
error;
|
||||||
|
true when UseCache ->
|
||||||
|
ets_cache:lookup(
|
||||||
|
?AUTH_CACHE, {User, Server},
|
||||||
|
fun() -> Mod:get_password(User, Server) end);
|
||||||
|
true ->
|
||||||
|
Mod:get_password(User, Server)
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_is_user_exists(User, Server, Mod) ->
|
||||||
|
case db_get_password(User, Server, Mod) of
|
||||||
|
{ok, _} ->
|
||||||
|
true;
|
||||||
|
error ->
|
||||||
|
case Mod:store_type(Server) of
|
||||||
|
external ->
|
||||||
|
Mod:is_user_exists(User, Server);
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_check_password(User, AuthzId, Server, ProvidedPassword,
|
||||||
|
Digest, DigestFun, Mod) ->
|
||||||
|
case db_get_password(User, Server, Mod) of
|
||||||
|
{ok, ValidPassword} ->
|
||||||
|
match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun);
|
||||||
|
error ->
|
||||||
|
case {Mod:store_type(Server), use_cache(Mod, Server)} of
|
||||||
|
{external, true} ->
|
||||||
|
case ets_cache:update(
|
||||||
|
?AUTH_CACHE, {User, Server}, {ok, ProvidedPassword},
|
||||||
|
fun() ->
|
||||||
|
case Mod:check_password(
|
||||||
|
User, AuthzId, Server, ProvidedPassword) of
|
||||||
|
true ->
|
||||||
|
{ok, ProvidedPassword};
|
||||||
|
false ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end, cache_nodes(Mod, Server)) of
|
||||||
|
{ok, _} ->
|
||||||
|
true;
|
||||||
|
error ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
{external, false} ->
|
||||||
|
Mod:check_password(User, AuthzId, Server, ProvidedPassword);
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_remove_user(User, Server, Mod) ->
|
||||||
|
case erlang:function_exported(Mod, remove_user, 2) of
|
||||||
|
true ->
|
||||||
|
case Mod:remove_user(User, Server) of
|
||||||
|
ok ->
|
||||||
|
case use_cache(Mod, Server) of
|
||||||
|
true ->
|
||||||
|
ets_cache:delete(?AUTH_CACHE, {User, Server},
|
||||||
|
cache_nodes(Mod, Server));
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end;
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
{error, not_allowed}
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_get_users(Server, Opts, Mod) ->
|
||||||
|
case erlang:function_exported(Mod, get_users, 2) of
|
||||||
|
true ->
|
||||||
|
Mod:get_users(Server, Opts);
|
||||||
|
false ->
|
||||||
|
case use_cache(Mod, Server) of
|
||||||
|
true ->
|
||||||
|
ets_cache:fold(
|
||||||
|
fun({User, S}, {ok, _}, Users) when S == Server ->
|
||||||
|
[{User, Server}|Users];
|
||||||
|
(_, _, Users) ->
|
||||||
|
Users
|
||||||
|
end, [], ?AUTH_CACHE);
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
db_count_users(Server, Opts, Mod) ->
|
||||||
|
case erlang:function_exported(Mod, count_users, 2) of
|
||||||
|
true ->
|
||||||
|
Mod:count_users(Server, Opts);
|
||||||
|
false ->
|
||||||
|
case use_cache(Mod, Server) of
|
||||||
|
true ->
|
||||||
|
ets_cache:fold(
|
||||||
|
fun({_, S}, {ok, _}, Num) when S == Server ->
|
||||||
|
Num + 1;
|
||||||
|
(_, _, Num) ->
|
||||||
|
Num
|
||||||
|
end, 0, ?AUTH_CACHE);
|
||||||
|
false ->
|
||||||
|
0
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% SCRAM stuff
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
is_password_scram_valid(Password, Scram) ->
|
||||||
|
case jid:resourceprep(Password) of
|
||||||
|
error ->
|
||||||
|
false;
|
||||||
|
_ ->
|
||||||
|
IterationCount = Scram#scram.iterationcount,
|
||||||
|
Salt = misc:decode_base64(Scram#scram.salt),
|
||||||
|
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
|
||||||
|
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
|
||||||
|
misc:decode_base64(Scram#scram.storedkey) == StoredKey
|
||||||
|
end.
|
||||||
|
|
||||||
|
password_to_scram(Password) ->
|
||||||
|
password_to_scram(Password, ?SCRAM_DEFAULT_ITERATION_COUNT).
|
||||||
|
|
||||||
|
password_to_scram(#scram{} = Password, _IterationCount) ->
|
||||||
|
Password;
|
||||||
|
password_to_scram(Password, IterationCount) ->
|
||||||
|
Salt = randoms:bytes(?SALT_LENGTH),
|
||||||
|
SaltedPassword = scram:salted_password(Password, Salt, IterationCount),
|
||||||
|
StoredKey = scram:stored_key(scram:client_key(SaltedPassword)),
|
||||||
|
ServerKey = scram:server_key(SaltedPassword),
|
||||||
|
#scram{storedkey = misc:encode_base64(StoredKey),
|
||||||
|
serverkey = misc:encode_base64(ServerKey),
|
||||||
|
salt = misc:encode_base64(Salt),
|
||||||
|
iterationcount = IterationCount}.
|
||||||
|
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% Cache stuff
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
-spec init_cache(map()) -> ok.
|
||||||
|
init_cache(HostModules) ->
|
||||||
|
case use_cache(HostModules) of
|
||||||
|
true ->
|
||||||
|
ets_cache:new(?AUTH_CACHE, cache_opts());
|
||||||
|
false ->
|
||||||
|
ets_cache:delete(?AUTH_CACHE)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec cache_opts() -> [proplists:property()].
|
||||||
|
cache_opts() ->
|
||||||
|
MaxSize = ejabberd_config:get_option(
|
||||||
|
auth_cache_size,
|
||||||
|
ejabberd_config:cache_size(global)),
|
||||||
|
CacheMissed = ejabberd_config:get_option(
|
||||||
|
auth_cache_missed,
|
||||||
|
ejabberd_config:cache_missed(global)),
|
||||||
|
LifeTime = case ejabberd_config:get_option(
|
||||||
|
auth_cache_life_time,
|
||||||
|
ejabberd_config:cache_life_time(global)) of
|
||||||
|
infinity -> infinity;
|
||||||
|
I -> timer:seconds(I)
|
||||||
|
end,
|
||||||
|
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||||
|
|
||||||
|
-spec use_cache(map()) -> boolean().
|
||||||
|
use_cache(HostModules) ->
|
||||||
|
lists:any(
|
||||||
|
fun({Host, Modules}) ->
|
||||||
|
lists:any(fun(Module) ->
|
||||||
|
use_cache(Module, Host)
|
||||||
|
end, Modules)
|
||||||
|
end, maps:to_list(HostModules)).
|
||||||
|
|
||||||
|
-spec use_cache(module(), binary()) -> boolean().
|
||||||
|
use_cache(Mod, LServer) ->
|
||||||
|
case erlang:function_exported(Mod, use_cache, 1) of
|
||||||
|
true -> Mod:use_cache(LServer);
|
||||||
|
false ->
|
||||||
|
ejabberd_config:get_option(
|
||||||
|
{auth_use_cache, LServer},
|
||||||
|
ejabberd_config:use_cache(LServer))
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec cache_nodes(module(), binary()) -> [node()].
|
||||||
|
cache_nodes(Mod, LServer) ->
|
||||||
|
case erlang:function_exported(Mod, cache_nodes, 1) of
|
||||||
|
true -> Mod:cache_nodes(LServer);
|
||||||
|
false -> ejabberd_cluster:get_nodes()
|
||||||
|
end.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
-spec auth_modules() -> [module()].
|
-spec auth_modules() -> [{binary(), module()}].
|
||||||
auth_modules() ->
|
auth_modules() ->
|
||||||
lists:usort(lists:flatmap(fun auth_modules/1, ?MYHOSTS)).
|
lists:flatmap(
|
||||||
|
fun(Host) ->
|
||||||
|
[{Host, Mod} || Mod <- auth_modules(Host)]
|
||||||
|
end, ?MYHOSTS).
|
||||||
|
|
||||||
-spec auth_modules(binary()) -> [module()].
|
-spec auth_modules(binary()) -> [module()].
|
||||||
auth_modules(Server) ->
|
auth_modules(Server) ->
|
||||||
@ -516,6 +739,65 @@ auth_modules(Server) ->
|
|||||||
(misc:atom_to_binary(M))/binary>>)
|
(misc:atom_to_binary(M))/binary>>)
|
||||||
|| M <- Methods].
|
|| M <- Methods].
|
||||||
|
|
||||||
|
-spec match_passwords(password(), password(),
|
||||||
|
binary(), digest_fun() | undefined) -> boolean().
|
||||||
|
match_passwords(Password, #scram{} = Scram, <<"">>, undefined) ->
|
||||||
|
is_password_scram_valid(Password, Scram);
|
||||||
|
match_passwords(Password, #scram{} = Scram, Digest, DigestFun) ->
|
||||||
|
StoredKey = misc:decode_base64(Scram#scram.storedkey),
|
||||||
|
DigRes = if Digest /= <<"">> ->
|
||||||
|
Digest == DigestFun(StoredKey);
|
||||||
|
true -> false
|
||||||
|
end,
|
||||||
|
if DigRes ->
|
||||||
|
true;
|
||||||
|
true ->
|
||||||
|
StoredKey == Password andalso Password /= <<"">>
|
||||||
|
end;
|
||||||
|
match_passwords(ProvidedPassword, ValidPassword, <<"">>, undefined) ->
|
||||||
|
ProvidedPassword == ValidPassword andalso ProvidedPassword /= <<"">>;
|
||||||
|
match_passwords(ProvidedPassword, ValidPassword, Digest, DigestFun) ->
|
||||||
|
DigRes = if Digest /= <<"">> ->
|
||||||
|
Digest == DigestFun(ValidPassword);
|
||||||
|
true -> false
|
||||||
|
end,
|
||||||
|
if DigRes ->
|
||||||
|
true;
|
||||||
|
true ->
|
||||||
|
ValidPassword == ProvidedPassword andalso ProvidedPassword /= <<"">>
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec validate_credentials(binary(), binary()) ->
|
||||||
|
{ok, binary(), binary()} | {error, invalid_jid}.
|
||||||
|
validate_credentials(User, Server) ->
|
||||||
|
validate_credentials(User, Server, #scram{}).
|
||||||
|
|
||||||
|
-spec validate_credentials(binary(), binary(), password()) ->
|
||||||
|
{ok, binary(), binary()} | {error, invalid_jid | invalid_password}.
|
||||||
|
validate_credentials(_User, _Server, <<"">>) ->
|
||||||
|
{error, invalid_password};
|
||||||
|
validate_credentials(User, Server, Password) ->
|
||||||
|
case jid:nodeprep(User) of
|
||||||
|
error ->
|
||||||
|
{error, invalid_jid};
|
||||||
|
LUser ->
|
||||||
|
case jid:nameprep(Server) of
|
||||||
|
error ->
|
||||||
|
{error, invalid_jid};
|
||||||
|
LServer ->
|
||||||
|
if is_record(Password, scram) ->
|
||||||
|
{ok, LUser, LServer};
|
||||||
|
true ->
|
||||||
|
case jid:resourceprep(Password) of
|
||||||
|
error ->
|
||||||
|
{error, invalid_password};
|
||||||
|
_ ->
|
||||||
|
{ok, LUser, LServer}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
export(Server) ->
|
export(Server) ->
|
||||||
ejabberd_auth_mnesia:export(Server).
|
ejabberd_auth_mnesia:export(Server).
|
||||||
|
|
||||||
@ -536,6 +818,10 @@ import(_LServer, {sql, _}, sql, <<"users">>, _) ->
|
|||||||
|
|
||||||
-spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]);
|
-spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]);
|
||||||
(auth_password_format) -> fun((plain | scram) -> plain | scram);
|
(auth_password_format) -> fun((plain | scram) -> plain | scram);
|
||||||
|
(auth_use_cache) -> fun((boolean()) -> boolean());
|
||||||
|
(auth_cache_missed) -> fun((boolean()) -> boolean());
|
||||||
|
(auth_cache_life_time) -> fun((timeout()) -> timeout());
|
||||||
|
(auth_cache_size) -> fun((timeout()) -> timeout());
|
||||||
(atom()) -> [atom()].
|
(atom()) -> [atom()].
|
||||||
opt_type(auth_method) ->
|
opt_type(auth_method) ->
|
||||||
fun (V) when is_list(V) ->
|
fun (V) when is_list(V) ->
|
||||||
@ -546,4 +832,20 @@ opt_type(auth_password_format) ->
|
|||||||
fun (plain) -> plain;
|
fun (plain) -> plain;
|
||||||
(scram) -> scram
|
(scram) -> scram
|
||||||
end;
|
end;
|
||||||
opt_type(_) -> [auth_method, auth_password_format].
|
opt_type(auth_use_cache) ->
|
||||||
|
fun(B) when is_boolean(B) -> B end;
|
||||||
|
opt_type(auth_cache_missed) ->
|
||||||
|
fun(B) when is_boolean(B) -> B end;
|
||||||
|
opt_type(auth_cache_life_time) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I;
|
||||||
|
(unlimited) -> infinity;
|
||||||
|
(infinity) -> infinity
|
||||||
|
end;
|
||||||
|
opt_type(auth_cache_size) ->
|
||||||
|
fun(I) when is_integer(I), I>0 -> I;
|
||||||
|
(unlimited) -> infinity;
|
||||||
|
(infinity) -> infinity
|
||||||
|
end;
|
||||||
|
opt_type(_) ->
|
||||||
|
[auth_method, auth_password_format, auth_use_cache,
|
||||||
|
auth_cache_missed, auth_cache_life_time, auth_cache_size].
|
||||||
|
@ -40,15 +40,9 @@
|
|||||||
unregister_connection/3
|
unregister_connection/3
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-export([login/2, set_password/3, check_password/4,
|
-export([login/2, check_password/4, is_user_exists/2,
|
||||||
check_password/6, try_register/3,
|
get_users/2, count_users/2, store_type/1,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
plain_password_required/1, opt_type/1]).
|
||||||
get_vh_registered_users/2,
|
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password_s/2,
|
|
||||||
get_password/2, get_password/3, is_user_exists/2,
|
|
||||||
remove_user/2, remove_user/3, store_type/0,
|
|
||||||
plain_password_required/0, opt_type/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -139,15 +133,7 @@ unregister_connection(_SID,
|
|||||||
%% ---------------------------------
|
%% ---------------------------------
|
||||||
%% Specific anonymous auth functions
|
%% Specific anonymous auth functions
|
||||||
%% ---------------------------------
|
%% ---------------------------------
|
||||||
|
check_password(User, _AuthzId, Server, _Password) ->
|
||||||
%% When anonymous login is enabled, check the password for permenant users
|
|
||||||
%% before allowing access
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
|
||||||
check_password(User, AuthzId, Server, Password, undefined,
|
|
||||||
undefined).
|
|
||||||
|
|
||||||
check_password(User, _AuthzId, Server, _Password, _Digest,
|
|
||||||
_DigestGen) ->
|
|
||||||
case
|
case
|
||||||
ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
|
ejabberd_auth:is_user_exists_in_other_modules(?MODULE,
|
||||||
User, Server)
|
User, Server)
|
||||||
@ -173,68 +159,20 @@ login(User, Server) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% When anonymous login is enabled, check that the user is permanent before
|
get_users(Server, _) ->
|
||||||
%% changing its password
|
[{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)].
|
||||||
set_password(User, Server, _Password) ->
|
|
||||||
case anonymous_user_exist(User, Server) of
|
|
||||||
true -> ok;
|
|
||||||
false -> {error, not_allowed}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% When anonymous login is enabled, check if permanent users are allowed on
|
count_users(Server, Opts) ->
|
||||||
%% the server:
|
length(get_users(Server, Opts)).
|
||||||
try_register(_User, _Server, _Password) ->
|
|
||||||
{error, not_allowed}.
|
|
||||||
|
|
||||||
dirty_get_registered_users() -> [].
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
[{U, S}
|
|
||||||
|| {U, S, _R}
|
|
||||||
<- ejabberd_sm:get_vh_session_list(Server)].
|
|
||||||
|
|
||||||
get_vh_registered_users(Server, _) ->
|
|
||||||
get_vh_registered_users(Server).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
length(get_vh_registered_users(Server)).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, _) ->
|
|
||||||
get_vh_registered_users_number(Server).
|
|
||||||
|
|
||||||
%% Return password of permanent user or false for anonymous users
|
|
||||||
get_password(User, Server) ->
|
|
||||||
get_password(User, Server, <<"">>).
|
|
||||||
|
|
||||||
get_password(User, Server, DefaultValue) ->
|
|
||||||
case anonymous_user_exist(User, Server) or
|
|
||||||
login(User, Server)
|
|
||||||
of
|
|
||||||
%% We return the default value if the user is anonymous
|
|
||||||
true -> DefaultValue;
|
|
||||||
%% We return the permanent user password otherwise
|
|
||||||
false -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
|
||||||
case get_password(User, Server) of
|
|
||||||
false ->
|
|
||||||
<<"">>;
|
|
||||||
Password ->
|
|
||||||
Password
|
|
||||||
end.
|
|
||||||
|
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
anonymous_user_exist(User, Server).
|
anonymous_user_exist(User, Server).
|
||||||
|
|
||||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
plain_password_required(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
store_type(_) ->
|
||||||
|
external.
|
||||||
plain_password_required() -> false.
|
|
||||||
|
|
||||||
store_type() ->
|
|
||||||
plain.
|
|
||||||
|
|
||||||
-spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean());
|
-spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean());
|
||||||
(anonymous_protocol) -> fun((sasl_anon | login_anon | both) ->
|
(anonymous_protocol) -> fun((sasl_anon | login_anon | both) ->
|
||||||
|
@ -32,14 +32,8 @@
|
|||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, set_password/3, check_password/4,
|
||||||
check_password/6, try_register/3,
|
try_register/3, is_user_exists/2, remove_user/2,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
store_type/1, plain_password_required/1, opt_type/1]).
|
||||||
get_vh_registered_users/2,
|
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, plain_password_required/0,
|
|
||||||
opt_type/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
@ -49,275 +43,60 @@
|
|||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
start(Host) ->
|
start(Host) ->
|
||||||
Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"),
|
Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"),
|
||||||
extauth:start(Host, Cmd),
|
extauth:start(Host, Cmd).
|
||||||
check_cache_last_options(Host),
|
|
||||||
ejabberd_auth_mnesia:start(Host).
|
|
||||||
|
|
||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
extauth:stop(Host),
|
extauth:stop(Host).
|
||||||
ejabberd_auth_mnesia:stop(Host).
|
|
||||||
|
|
||||||
check_cache_last_options(Server) ->
|
plain_password_required(_) -> true.
|
||||||
case get_cache_option(Server) of
|
|
||||||
false -> no_cache;
|
|
||||||
{true, _CacheTime} ->
|
|
||||||
case get_mod_last_configured(Server) of
|
|
||||||
no_mod_last ->
|
|
||||||
?ERROR_MSG("In host ~p extauth is used, extauth_cache "
|
|
||||||
"is enabled but mod_last is not enabled.",
|
|
||||||
[Server]),
|
|
||||||
no_cache;
|
|
||||||
_ -> cache
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
plain_password_required() -> true.
|
store_type(_) -> external.
|
||||||
|
|
||||||
store_type() -> external.
|
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
check_password(User, AuthzId, Server, Password) ->
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||||
false;
|
false;
|
||||||
true ->
|
true ->
|
||||||
case get_cache_option(Server) of
|
check_password_extauth(User, AuthzId, Server, Password)
|
||||||
false ->
|
|
||||||
check_password_extauth(User, AuthzId, Server, Password);
|
|
||||||
{true, CacheTime} ->
|
|
||||||
check_password_cache(User, AuthzId, Server, Password,
|
|
||||||
CacheTime)
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password, _Digest,
|
|
||||||
_DigestGen) ->
|
|
||||||
check_password(User, AuthzId, Server, Password).
|
|
||||||
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
case extauth:set_password(User, Server, Password) of
|
case extauth:set_password(User, Server, Password) of
|
||||||
true ->
|
true -> ok;
|
||||||
set_password_mnesia(User, Server, Password), ok;
|
_ -> {error, db_failure}
|
||||||
_ -> {error, unknown_problem}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
try_register(User, Server, Password) ->
|
try_register(User, Server, Password) ->
|
||||||
case get_cache_option(Server) of
|
extauth:try_register(User, Server, Password).
|
||||||
false -> try_register_extauth(User, Server, Password);
|
|
||||||
{true, _CacheTime} ->
|
|
||||||
try_register_external_cache(User, Server, Password)
|
|
||||||
end.
|
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
|
||||||
ejabberd_auth_mnesia:dirty_get_registered_users().
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
ejabberd_auth_mnesia:get_vh_registered_users(Server).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server, Data) ->
|
|
||||||
ejabberd_auth_mnesia:get_vh_registered_users(Server,
|
|
||||||
Data).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
ejabberd_auth_mnesia:get_vh_registered_users_number(Server).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, Data) ->
|
|
||||||
ejabberd_auth_mnesia:get_vh_registered_users_number(Server,
|
|
||||||
Data).
|
|
||||||
|
|
||||||
%% The password can only be returned if cache is enabled, cached info exists and is fresh enough.
|
|
||||||
get_password(User, Server) ->
|
|
||||||
case get_cache_option(Server) of
|
|
||||||
false -> false;
|
|
||||||
{true, CacheTime} ->
|
|
||||||
get_password_cache(User, Server, CacheTime)
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
|
||||||
case get_password(User, Server) of
|
|
||||||
false -> <<"">>;
|
|
||||||
Other -> Other
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> true | false | {error, Error}
|
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
try extauth:is_user_exists(User, Server) of
|
try extauth:is_user_exists(User, Server) of
|
||||||
Res -> Res
|
Res -> Res
|
||||||
catch
|
catch
|
||||||
_:Error -> {error, Error}
|
_:Error ->
|
||||||
|
?ERROR_MSG("external authentication program failure: ~p",
|
||||||
|
[Error]),
|
||||||
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
case extauth:remove_user(User, Server) of
|
case extauth:remove_user(User, Server) of
|
||||||
false -> false;
|
false -> {error, not_allowed};
|
||||||
true ->
|
true -> ok
|
||||||
case get_cache_option(Server) of
|
|
||||||
false -> false;
|
|
||||||
{true, _CacheTime} ->
|
|
||||||
ejabberd_auth_mnesia:remove_user(User, Server)
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(User, Server, Password) ->
|
|
||||||
case extauth:remove_user(User, Server, Password) of
|
|
||||||
false -> false;
|
|
||||||
true ->
|
|
||||||
case get_cache_option(Server) of
|
|
||||||
false -> false;
|
|
||||||
{true, _CacheTime} ->
|
|
||||||
ejabberd_auth_mnesia:remove_user(User, Server,
|
|
||||||
Password)
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%
|
|
||||||
%%% Extauth cache management
|
|
||||||
%%%
|
|
||||||
|
|
||||||
%% @spec (Host::string()) -> false | {true, CacheTime::integer()}
|
|
||||||
get_cache_option(Host) ->
|
|
||||||
case ejabberd_config:get_option({extauth_cache, Host}, false) of
|
|
||||||
false -> false;
|
|
||||||
CacheTime -> {true, CacheTime}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, AuthzId, Server, Password) -> true | false
|
|
||||||
check_password_extauth(User, _AuthzId, Server, Password) ->
|
check_password_extauth(User, _AuthzId, Server, Password) ->
|
||||||
extauth:check_password(User, Server, Password) andalso
|
extauth:check_password(User, Server, Password) andalso
|
||||||
Password /= <<"">>.
|
Password /= <<"">>.
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> true | false
|
|
||||||
try_register_extauth(User, Server, Password) ->
|
|
||||||
extauth:try_register(User, Server, Password).
|
|
||||||
|
|
||||||
check_password_cache(User, AuthzId, Server, Password, 0) ->
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password);
|
|
||||||
check_password_cache(User, AuthzId, Server, Password,
|
|
||||||
CacheTime) ->
|
|
||||||
case get_last_access(User, Server) of
|
|
||||||
online ->
|
|
||||||
check_password_mnesia(User, AuthzId, Server, Password);
|
|
||||||
never ->
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password);
|
|
||||||
mod_last_required ->
|
|
||||||
?ERROR_MSG("extauth is used, extauth_cache is enabled "
|
|
||||||
"but mod_last is not enabled in that "
|
|
||||||
"host",
|
|
||||||
[]),
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password);
|
|
||||||
TimeStamp ->
|
|
||||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
|
||||||
%% If no need to refresh, check password against Mnesia
|
|
||||||
true ->
|
|
||||||
case check_password_mnesia(User, AuthzId, Server, Password) of
|
|
||||||
%% If password valid in Mnesia, accept it
|
|
||||||
true -> true;
|
|
||||||
%% Else (password nonvalid in Mnesia), check in extauth and cache result
|
|
||||||
false ->
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password)
|
|
||||||
end;
|
|
||||||
%% Else (need to refresh), check in extauth and cache result
|
|
||||||
false ->
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password)
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_password_mnesia(User, Server) ->
|
|
||||||
ejabberd_auth_mnesia:get_password(User, Server).
|
|
||||||
|
|
||||||
-spec get_password_cache(User::binary(), Server::binary(), CacheTime::integer()) -> Password::string() | false.
|
|
||||||
get_password_cache(User, Server, CacheTime) ->
|
|
||||||
case get_last_access(User, Server) of
|
|
||||||
online -> get_password_mnesia(User, Server);
|
|
||||||
never -> false;
|
|
||||||
mod_last_required ->
|
|
||||||
?ERROR_MSG("extauth is used, extauth_cache is enabled "
|
|
||||||
"but mod_last is not enabled in that "
|
|
||||||
"host",
|
|
||||||
[]),
|
|
||||||
false;
|
|
||||||
TimeStamp ->
|
|
||||||
case is_fresh_enough(TimeStamp, CacheTime) of
|
|
||||||
true -> get_password_mnesia(User, Server);
|
|
||||||
false -> false
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Check the password using extauth; if success then cache it
|
|
||||||
check_password_external_cache(User, AuthzId, Server, Password) ->
|
|
||||||
case check_password_extauth(User, AuthzId, Server, Password) of
|
|
||||||
true ->
|
|
||||||
set_password_mnesia(User, Server, Password), true;
|
|
||||||
false -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Try to register using extauth; if success then cache it
|
|
||||||
try_register_external_cache(User, Server, Password) ->
|
|
||||||
case try_register_extauth(User, Server, Password) of
|
|
||||||
{atomic, ok} = R ->
|
|
||||||
set_password_mnesia(User, Server, Password), R;
|
|
||||||
_ -> {error, not_allowed}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, AuthzId, Server, Password) -> true | false
|
|
||||||
check_password_mnesia(User, AuthzId, Server, Password) ->
|
|
||||||
ejabberd_auth_mnesia:check_password(User, AuthzId, Server,
|
|
||||||
Password).
|
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> ok | {error, invalid_jid}
|
|
||||||
set_password_mnesia(User, Server, Password) ->
|
|
||||||
%% @spec (TimeLast, CacheTime) -> true | false
|
|
||||||
%% TimeLast = online | never | integer()
|
|
||||||
%% CacheTime = integer() | false
|
|
||||||
ejabberd_auth_mnesia:set_password(User, Server,
|
|
||||||
Password).
|
|
||||||
|
|
||||||
is_fresh_enough(TimeStampLast, CacheTime) ->
|
|
||||||
Now = p1_time_compat:system_time(seconds),
|
|
||||||
TimeStampLast + CacheTime > Now.
|
|
||||||
|
|
||||||
%% Code copied from mod_configure.erl
|
|
||||||
%% Code copied from web/ejabberd_web_admin.erl
|
|
||||||
%% TODO: Update time format to XEP-0202: Entity Time
|
|
||||||
-spec(get_last_access(User::binary(), Server::binary()) -> (online | never | mod_last_required | integer())).
|
|
||||||
get_last_access(User, Server) ->
|
|
||||||
case ejabberd_sm:get_user_resources(User, Server) of
|
|
||||||
[] ->
|
|
||||||
case get_last_info(User, Server) of
|
|
||||||
mod_last_required -> mod_last_required;
|
|
||||||
not_found -> never;
|
|
||||||
{ok, Timestamp, _Status} -> Timestamp
|
|
||||||
end;
|
|
||||||
_ -> online
|
|
||||||
end.
|
|
||||||
%% @spec (User, Server) -> {ok, Timestamp, Status} | not_found | mod_last_required
|
|
||||||
|
|
||||||
get_last_info(User, Server) ->
|
|
||||||
case get_mod_last_enabled(Server) of
|
|
||||||
mod_last -> mod_last:get_last_info(User, Server);
|
|
||||||
no_mod_last -> mod_last_required
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (Server) -> mod_last | no_mod_last
|
|
||||||
get_mod_last_enabled(Server) ->
|
|
||||||
case gen_mod:is_loaded(Server, mod_last) of
|
|
||||||
true -> mod_last;
|
|
||||||
false -> no_mod_last
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_mod_last_configured(Server) ->
|
|
||||||
case is_configured(Server, mod_last) of
|
|
||||||
true -> mod_last;
|
|
||||||
false -> no_mod_last
|
|
||||||
end.
|
|
||||||
|
|
||||||
is_configured(Host, Module) ->
|
|
||||||
Os = ejabberd_config:get_option({modules, Host}, []),
|
|
||||||
lists:keymember(Module, 1, Os).
|
|
||||||
|
|
||||||
-spec opt_type(extauth_cache) -> fun((false | non_neg_integer()) ->
|
-spec opt_type(extauth_cache) -> fun((false | non_neg_integer()) ->
|
||||||
false | non_neg_integer());
|
false | non_neg_integer());
|
||||||
(extauth_program) -> fun((binary()) -> string());
|
(extauth_program) -> fun((binary()) -> string());
|
||||||
(atom()) -> [atom()].
|
(atom()) -> [atom()].
|
||||||
opt_type(extauth_cache) ->
|
opt_type(extauth_cache) ->
|
||||||
|
?WARNING_MSG("option 'extauth_cache' is deprecated and has no effect, "
|
||||||
|
"use authentication or global cache configuration "
|
||||||
|
"options: auth_use_cache, auth_cache_life_time, "
|
||||||
|
"use_cache, cache_life_time, and so on", []),
|
||||||
fun (false) -> false;
|
fun (false) -> false;
|
||||||
(I) when is_integer(I), I >= 0 -> I
|
(I) when is_integer(I), I >= 0 -> I
|
||||||
end;
|
end;
|
||||||
|
@ -37,13 +37,9 @@
|
|||||||
handle_cast/2, terminate/2, code_change/3]).
|
handle_cast/2, terminate/2, code_change/3]).
|
||||||
|
|
||||||
-export([start/1, stop/1, start_link/1, set_password/3,
|
-export([start/1, stop/1, start_link/1, set_password/3,
|
||||||
check_password/4, check_password/6, try_register/3,
|
check_password/4, is_user_exists/2,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
get_users/2, count_users/2,
|
||||||
get_vh_registered_users/2,
|
store_type/1, plain_password_required/1,
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, plain_password_required/0,
|
|
||||||
opt_type/1]).
|
opt_type/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -112,9 +108,9 @@ init(Host) ->
|
|||||||
State#state.password, State#state.tls_options),
|
State#state.password, State#state.tls_options),
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
|
||||||
plain_password_required() -> true.
|
plain_password_required(_) -> true.
|
||||||
|
|
||||||
store_type() -> external.
|
store_type(_) -> external.
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
check_password(User, AuthzId, Server, Password) ->
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||||
@ -129,60 +125,34 @@ check_password(User, AuthzId, Server, Password) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password, _Digest,
|
|
||||||
_DigestGen) ->
|
|
||||||
check_password(User, AuthzId, Server, Password).
|
|
||||||
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||||
case find_user_dn(User, State) of
|
case find_user_dn(User, State) of
|
||||||
false -> {error, user_not_found};
|
false -> {error, notfound};
|
||||||
DN ->
|
DN ->
|
||||||
eldap_pool:modify_passwd(State#state.eldap_id, DN,
|
case eldap_pool:modify_passwd(State#state.eldap_id, DN,
|
||||||
Password)
|
Password) of
|
||||||
|
ok -> ok;
|
||||||
|
_Err -> {error, db_failure}
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> {error, not_allowed}
|
get_users(Server, []) ->
|
||||||
try_register(_User, _Server, _Password) ->
|
case catch get_users_ldap(Server) of
|
||||||
{error, not_allowed}.
|
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
|
||||||
Servers = ejabberd_config:get_vh_by_auth_method(ldap),
|
|
||||||
lists:flatmap(fun (Server) ->
|
|
||||||
get_vh_registered_users(Server)
|
|
||||||
end,
|
|
||||||
Servers).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
case catch get_vh_registered_users_ldap(Server) of
|
|
||||||
{'EXIT', _} -> [];
|
{'EXIT', _} -> [];
|
||||||
Result -> Result
|
Result -> Result
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vh_registered_users(Server, _) ->
|
count_users(Server, Opts) ->
|
||||||
get_vh_registered_users(Server).
|
length(get_users(Server, Opts)).
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
length(get_vh_registered_users(Server)).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, _) ->
|
|
||||||
get_vh_registered_users_number(Server).
|
|
||||||
|
|
||||||
get_password(_User, _Server) -> false.
|
|
||||||
|
|
||||||
get_password_s(_User, _Server) -> <<"">>.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> true | false | {error, Error}
|
%% @spec (User, Server) -> true | false | {error, Error}
|
||||||
is_user_exists(User, Server) ->
|
is_user_exists(User, Server) ->
|
||||||
case catch is_user_exists_ldap(User, Server) of
|
case catch is_user_exists_ldap(User, Server) of
|
||||||
{'EXIT', Error} -> {error, Error};
|
{'EXIT', _Error} -> {error, db_failure};
|
||||||
Result -> Result
|
Result -> Result
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
|
||||||
|
|
||||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -199,7 +169,7 @@ check_password_ldap(User, Server, Password) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vh_registered_users_ldap(Server) ->
|
get_users_ldap(Server) ->
|
||||||
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
{ok, State} = eldap_utils:get_state(Server, ?MODULE),
|
||||||
UIDs = State#state.uids,
|
UIDs = State#state.uids,
|
||||||
Eldap_ID = State#state.eldap_id,
|
Eldap_ID = State#state.eldap_id,
|
||||||
|
@ -31,15 +31,11 @@
|
|||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||||
check_password/6, try_register/3,
|
get_users/2, init_db/0,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
count_users/2, get_password/2,
|
||||||
get_vh_registered_users/2, init_db/0,
|
remove_user/2, store_type/1, export/1, import/2,
|
||||||
get_vh_registered_users_number/1,
|
plain_password_required/1, use_cache/1]).
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, export/1, import/2,
|
|
||||||
plain_password_required/0]).
|
|
||||||
-export([need_transform/1, transform/1]).
|
-export([need_transform/1, transform/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -52,8 +48,6 @@
|
|||||||
-record(reg_users_counter, {vhost = <<"">> :: binary(),
|
-record(reg_users_counter, {vhost = <<"">> :: binary(),
|
||||||
count = 0 :: integer() | '$1'}).
|
count = 0 :: integer() | '$1'}).
|
||||||
|
|
||||||
-define(SALT_LENGTH, 16).
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% API
|
%%% API
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
@ -67,14 +61,14 @@ stop(_Host) ->
|
|||||||
|
|
||||||
init_db() ->
|
init_db() ->
|
||||||
ejabberd_mnesia:create(?MODULE, passwd,
|
ejabberd_mnesia:create(?MODULE, passwd,
|
||||||
[{disc_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{attributes, record_info(fields, passwd)}]),
|
{attributes, record_info(fields, passwd)}]),
|
||||||
ejabberd_mnesia:create(?MODULE, reg_users_counter,
|
ejabberd_mnesia:create(?MODULE, reg_users_counter,
|
||||||
[{ram_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, reg_users_counter)}]).
|
{attributes, record_info(fields, reg_users_counter)}]).
|
||||||
|
|
||||||
update_reg_users_counter_table(Server) ->
|
update_reg_users_counter_table(Server) ->
|
||||||
Set = get_vh_registered_users(Server),
|
Set = get_users(Server, []),
|
||||||
Size = length(Set),
|
Size = length(Set),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
@ -83,309 +77,153 @@ update_reg_users_counter_table(Server) ->
|
|||||||
end,
|
end,
|
||||||
mnesia:sync_dirty(F).
|
mnesia:sync_dirty(F).
|
||||||
|
|
||||||
plain_password_required() ->
|
use_cache(_) ->
|
||||||
is_scrammed().
|
case mnesia:table_info(passwd, storage_type) of
|
||||||
|
disc_only_copies -> true;
|
||||||
store_type() ->
|
_ -> false
|
||||||
ejabberd_auth:password_format(?MYNAME).
|
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read({passwd, US}) of
|
|
||||||
[#passwd{password = Password}] when is_binary(Password) ->
|
|
||||||
Password /= <<"">>;
|
|
||||||
[#passwd{password = Scram}] when is_record(Scram, scram) ->
|
|
||||||
is_password_scram_valid(Password, Scram);
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password, Digest,
|
plain_password_required(Server) ->
|
||||||
DigestGen) ->
|
store_type(Server) == scram.
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
store_type(Server) ->
|
||||||
true ->
|
ejabberd_auth:password_format(Server).
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read({passwd, US}) of
|
|
||||||
[#passwd{password = Passwd}] when is_binary(Passwd) ->
|
|
||||||
DigRes = if Digest /= <<"">> ->
|
|
||||||
Digest == DigestGen(Passwd);
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
if DigRes -> true;
|
|
||||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
|
||||||
end;
|
|
||||||
[#passwd{password = Scram}] when is_record(Scram, scram) ->
|
|
||||||
Passwd = misc:decode_base64(Scram#scram.storedkey),
|
|
||||||
DigRes = if Digest /= <<"">> ->
|
|
||||||
Digest == DigestGen(Passwd);
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
if DigRes -> true;
|
|
||||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
|
||||||
end;
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
|
||||||
%% ok | {error, invalid_jid}
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
LUser = jid:nodeprep(User),
|
US = {User, Server},
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LPassword = jid:resourceprep(Password),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
{error, invalid_jid};
|
|
||||||
LPassword == error ->
|
|
||||||
{error, invalid_password};
|
|
||||||
true ->
|
|
||||||
F = fun () ->
|
|
||||||
Password2 = case is_scrammed() and is_binary(Password)
|
|
||||||
of
|
|
||||||
true -> password_to_scram(Password);
|
|
||||||
false -> Password
|
|
||||||
end,
|
|
||||||
mnesia:write(#passwd{us = US, password = Password2})
|
|
||||||
end,
|
|
||||||
{atomic, ok} = mnesia:transaction(F),
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} | {error, not_allowed} | {error, Reason}
|
|
||||||
try_register(User, Server, PasswordList) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
Password = if is_list(PasswordList); is_binary(PasswordList) ->
|
|
||||||
iolist_to_binary(PasswordList);
|
|
||||||
true -> PasswordList
|
|
||||||
end,
|
|
||||||
LPassword = jid:resourceprep(Password),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
{error, invalid_jid};
|
|
||||||
(LPassword == error) and not is_record(Password, scram) ->
|
|
||||||
{error, invalid_password};
|
|
||||||
true ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({passwd, US}) of
|
|
||||||
[] ->
|
|
||||||
Password2 = case is_scrammed() and
|
|
||||||
is_binary(Password)
|
|
||||||
of
|
|
||||||
true -> password_to_scram(Password);
|
|
||||||
false -> Password
|
|
||||||
end,
|
|
||||||
mnesia:write(#passwd{us = US,
|
|
||||||
password = Password2}),
|
|
||||||
mnesia:dirty_update_counter(reg_users_counter,
|
|
||||||
LServer, 1),
|
|
||||||
ok;
|
|
||||||
[_E] -> exists
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F)
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Get all registered users in Mnesia
|
|
||||||
dirty_get_registered_users() ->
|
|
||||||
mnesia:dirty_all_keys(passwd).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
mnesia:dirty_select(passwd,
|
|
||||||
[{#passwd{us = '$1', _ = '_'},
|
|
||||||
[{'==', {element, 2, '$1'}, LServer}], ['$1']}]).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{from, Start}, {to, End}])
|
|
||||||
when is_integer(Start) and is_integer(End) ->
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{limit, End - Start + 1}, {offset, Start}]);
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{limit, Limit}, {offset, Offset}])
|
|
||||||
when is_integer(Limit) and is_integer(Offset) ->
|
|
||||||
case get_vh_registered_users(Server) of
|
|
||||||
[] -> [];
|
|
||||||
Users ->
|
|
||||||
Set = lists:keysort(1, Users),
|
|
||||||
L = length(Set),
|
|
||||||
Start = if Offset < 1 -> 1;
|
|
||||||
Offset > L -> L;
|
|
||||||
true -> Offset
|
|
||||||
end,
|
|
||||||
lists:sublist(Set, Start, Limit)
|
|
||||||
end;
|
|
||||||
get_vh_registered_users(Server, [{prefix, Prefix}])
|
|
||||||
when is_binary(Prefix) ->
|
|
||||||
Set = [{U, S}
|
|
||||||
|| {U, S} <- get_vh_registered_users(Server),
|
|
||||||
str:prefix(Prefix, U)],
|
|
||||||
lists:keysort(1, Set);
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{prefix, Prefix}, {from, Start}, {to, End}])
|
|
||||||
when is_binary(Prefix) and is_integer(Start) and
|
|
||||||
is_integer(End) ->
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{prefix, Prefix}, {limit, End - Start + 1},
|
|
||||||
{offset, Start}]);
|
|
||||||
get_vh_registered_users(Server,
|
|
||||||
[{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
|
||||||
when is_binary(Prefix) and is_integer(Limit) and
|
|
||||||
is_integer(Offset) ->
|
|
||||||
case [{U, S}
|
|
||||||
|| {U, S} <- get_vh_registered_users(Server),
|
|
||||||
str:prefix(Prefix, U)]
|
|
||||||
of
|
|
||||||
[] -> [];
|
|
||||||
Users ->
|
|
||||||
Set = lists:keysort(1, Users),
|
|
||||||
L = length(Set),
|
|
||||||
Start = if Offset < 1 -> 1;
|
|
||||||
Offset > L -> L;
|
|
||||||
true -> Offset
|
|
||||||
end,
|
|
||||||
lists:sublist(Set, Start, Limit)
|
|
||||||
end;
|
|
||||||
get_vh_registered_users(Server, _) ->
|
|
||||||
get_vh_registered_users(Server).
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
Query = mnesia:dirty_select(reg_users_counter,
|
|
||||||
[{#reg_users_counter{vhost = LServer,
|
|
||||||
count = '$1'},
|
|
||||||
[], ['$1']}]),
|
|
||||||
case Query of
|
|
||||||
[Count] -> Count;
|
|
||||||
_ -> 0
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server,
|
|
||||||
[{prefix, Prefix}])
|
|
||||||
when is_binary(Prefix) ->
|
|
||||||
Set = [{U, S}
|
|
||||||
|| {U, S} <- get_vh_registered_users(Server),
|
|
||||||
str:prefix(Prefix, U)],
|
|
||||||
length(Set);
|
|
||||||
get_vh_registered_users_number(Server, _) ->
|
|
||||||
get_vh_registered_users_number(Server).
|
|
||||||
|
|
||||||
get_password(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read(passwd, US) of
|
|
||||||
[#passwd{password = Password}]
|
|
||||||
when is_binary(Password) ->
|
|
||||||
Password;
|
|
||||||
[#passwd{password = Scram}]
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
{misc:decode_base64(Scram#scram.storedkey),
|
|
||||||
misc:decode_base64(Scram#scram.serverkey),
|
|
||||||
misc:decode_base64(Scram#scram.salt),
|
|
||||||
Scram#scram.iterationcount};
|
|
||||||
_ -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read(passwd, US) of
|
|
||||||
[#passwd{password = Password}]
|
|
||||||
when is_binary(Password) ->
|
|
||||||
Password;
|
|
||||||
[#passwd{password = Scram}]
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
<<"">>;
|
|
||||||
_ -> <<"">>
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> true | false | {error, Error}
|
|
||||||
is_user_exists(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read({passwd, US}) of
|
|
||||||
[] -> false;
|
|
||||||
[_] -> true;
|
|
||||||
Other -> {error, Other}
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> ok
|
|
||||||
%% @doc Remove user.
|
|
||||||
%% Note: it returns ok even if there was some problem removing the user.
|
|
||||||
remove_user(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
mnesia:delete({passwd, US}),
|
mnesia:write(#passwd{us = US, password = Password})
|
||||||
mnesia:dirty_update_counter(reg_users_counter, LServer,
|
|
||||||
-1)
|
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F),
|
case mnesia:transaction(F) of
|
||||||
ok.
|
{atomic, ok} ->
|
||||||
|
ok;
|
||||||
|
{aborted, Reason} ->
|
||||||
|
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||||
|
{error, db_failure}
|
||||||
|
end.
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request
|
try_register(User, Server, Password) ->
|
||||||
%% @doc Remove user if the provided password is correct.
|
US = {User, Server},
|
||||||
remove_user(User, Server, Password) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:read({passwd, US}) of
|
case mnesia:read({passwd, US}) of
|
||||||
[#passwd{password = Password}]
|
[] ->
|
||||||
when is_binary(Password) ->
|
mnesia:write(#passwd{us = US, password = Password}),
|
||||||
mnesia:delete({passwd, US}),
|
mnesia:dirty_update_counter(reg_users_counter, Server, 1),
|
||||||
mnesia:dirty_update_counter(reg_users_counter, LServer,
|
ok;
|
||||||
-1),
|
[_] ->
|
||||||
ok;
|
{error, exists}
|
||||||
[#passwd{password = Scram}]
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
case is_password_scram_valid(Password, Scram) of
|
|
||||||
true ->
|
|
||||||
mnesia:delete({passwd, US}),
|
|
||||||
mnesia:dirty_update_counter(reg_users_counter,
|
|
||||||
LServer, -1),
|
|
||||||
ok;
|
|
||||||
false -> not_allowed
|
|
||||||
end;
|
|
||||||
_ -> not_exists
|
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case mnesia:transaction(F) of
|
case mnesia:transaction(F) of
|
||||||
{atomic, ok} -> ok;
|
{atomic, Res} ->
|
||||||
{atomic, Res} -> Res;
|
Res;
|
||||||
_ -> bad_request
|
{aborted, Reason} ->
|
||||||
|
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||||
|
{error, db_failure}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_users(Server, []) ->
|
||||||
|
mnesia:dirty_select(passwd,
|
||||||
|
[{#passwd{us = '$1', _ = '_'},
|
||||||
|
[{'==', {element, 2, '$1'}, Server}], ['$1']}]);
|
||||||
|
get_users(Server, [{from, Start}, {to, End}])
|
||||||
|
when is_integer(Start) and is_integer(End) ->
|
||||||
|
get_users(Server, [{limit, End - Start + 1}, {offset, Start}]);
|
||||||
|
get_users(Server, [{limit, Limit}, {offset, Offset}])
|
||||||
|
when is_integer(Limit) and is_integer(Offset) ->
|
||||||
|
case get_users(Server, []) of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
Users ->
|
||||||
|
Set = lists:keysort(1, Users),
|
||||||
|
L = length(Set),
|
||||||
|
Start = if Offset < 1 -> 1;
|
||||||
|
Offset > L -> L;
|
||||||
|
true -> Offset
|
||||||
|
end,
|
||||||
|
lists:sublist(Set, Start, Limit)
|
||||||
|
end;
|
||||||
|
get_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) ->
|
||||||
|
Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)],
|
||||||
|
lists:keysort(1, Set);
|
||||||
|
get_users(Server, [{prefix, Prefix}, {from, Start}, {to, End}])
|
||||||
|
when is_binary(Prefix) and is_integer(Start) and is_integer(End) ->
|
||||||
|
get_users(Server, [{prefix, Prefix}, {limit, End - Start + 1},
|
||||||
|
{offset, Start}]);
|
||||||
|
get_users(Server, [{prefix, Prefix}, {limit, Limit}, {offset, Offset}])
|
||||||
|
when is_binary(Prefix) and is_integer(Limit) and is_integer(Offset) ->
|
||||||
|
case [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)] of
|
||||||
|
[] ->
|
||||||
|
[];
|
||||||
|
Users ->
|
||||||
|
Set = lists:keysort(1, Users),
|
||||||
|
L = length(Set),
|
||||||
|
Start = if Offset < 1 -> 1;
|
||||||
|
Offset > L -> L;
|
||||||
|
true -> Offset
|
||||||
|
end,
|
||||||
|
lists:sublist(Set, Start, Limit)
|
||||||
|
end;
|
||||||
|
get_users(Server, _) ->
|
||||||
|
get_users(Server, []).
|
||||||
|
|
||||||
|
count_users(Server, []) ->
|
||||||
|
case mnesia:dirty_select(
|
||||||
|
reg_users_counter,
|
||||||
|
[{#reg_users_counter{vhost = Server, count = '$1'},
|
||||||
|
[], ['$1']}]) of
|
||||||
|
[Count] -> Count;
|
||||||
|
_ -> 0
|
||||||
|
end;
|
||||||
|
count_users(Server, [{prefix, Prefix}]) when is_binary(Prefix) ->
|
||||||
|
Set = [{U, S} || {U, S} <- get_users(Server, []), str:prefix(Prefix, U)],
|
||||||
|
length(Set);
|
||||||
|
count_users(Server, _) ->
|
||||||
|
count_users(Server, []).
|
||||||
|
|
||||||
|
get_password(User, Server) ->
|
||||||
|
case mnesia:dirty_read(passwd, {User, Server}) of
|
||||||
|
[#passwd{password = Password}] ->
|
||||||
|
{ok, Password};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(User, Server) ->
|
||||||
|
US = {User, Server},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:delete({passwd, US}),
|
||||||
|
mnesia:dirty_update_counter(reg_users_counter, Server, -1),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, ok} ->
|
||||||
|
ok;
|
||||||
|
{aborted, Reason} ->
|
||||||
|
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||||
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
need_transform(#passwd{us = {U, S}, password = Pass}) ->
|
need_transform(#passwd{us = {U, S}, password = Pass}) ->
|
||||||
if is_binary(Pass) ->
|
if is_binary(Pass) ->
|
||||||
IsScrammed = is_scrammed(),
|
case store_type(S) of
|
||||||
if IsScrammed ->
|
scram ->
|
||||||
?INFO_MSG("Passwords in Mnesia table 'passwd' "
|
?INFO_MSG("Passwords in Mnesia table 'passwd' "
|
||||||
"will be SCRAM'ed", []);
|
"will be SCRAM'ed", []),
|
||||||
true ->
|
true;
|
||||||
ok
|
plain ->
|
||||||
end,
|
false
|
||||||
IsScrammed;
|
end;
|
||||||
is_record(Pass, scram) ->
|
is_record(Pass, scram) ->
|
||||||
case is_scrammed() of
|
case store_type(S) of
|
||||||
true ->
|
scram ->
|
||||||
next;
|
false;
|
||||||
false ->
|
plain ->
|
||||||
?WARNING_MSG("Some passwords were stored in the database "
|
?WARNING_MSG("Some passwords were stored in the database "
|
||||||
"as SCRAM, but 'auth_password_format' "
|
"as SCRAM, but 'auth_password_format' "
|
||||||
"is not configured as 'scram'.", []),
|
"is not configured as 'scram': some "
|
||||||
|
"authentication mechanisms such as DIGEST-MD5 "
|
||||||
|
"would *fail*", []),
|
||||||
false
|
false
|
||||||
end;
|
end;
|
||||||
is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
is_list(U) orelse is_list(S) orelse is_list(Pass) ->
|
||||||
@ -410,61 +248,24 @@ transform(#passwd{us = {U, S}, password = Pass} = R)
|
|||||||
transform(R#passwd{us = NewUS, password = NewPass});
|
transform(R#passwd{us = NewUS, password = NewPass});
|
||||||
transform(#passwd{us = {U, S}, password = Password} = P)
|
transform(#passwd{us = {U, S}, password = Password} = P)
|
||||||
when is_binary(Password) ->
|
when is_binary(Password) ->
|
||||||
case is_scrammed() of
|
case store_type(S) of
|
||||||
true ->
|
scram ->
|
||||||
case jid:resourceprep(Password) of
|
case jid:resourceprep(Password) of
|
||||||
error ->
|
error ->
|
||||||
?ERROR_MSG("SASLprep failed for password of user ~s@~s",
|
?ERROR_MSG("SASLprep failed for password of user ~s@~s",
|
||||||
[U, S]),
|
[U, S]),
|
||||||
P;
|
P;
|
||||||
_ ->
|
_ ->
|
||||||
Scram = password_to_scram(Password),
|
Scram = ejabberd_auth:password_to_scram(Password),
|
||||||
P#passwd{password = Scram}
|
P#passwd{password = Scram}
|
||||||
end;
|
end;
|
||||||
false ->
|
plain ->
|
||||||
P
|
P
|
||||||
end;
|
end;
|
||||||
transform(#passwd{password = Password} = P)
|
transform(#passwd{password = Password} = P)
|
||||||
when is_record(Password, scram) ->
|
when is_record(Password, scram) ->
|
||||||
P.
|
P.
|
||||||
|
|
||||||
%%%
|
|
||||||
%%% SCRAM
|
|
||||||
%%%
|
|
||||||
|
|
||||||
is_scrammed() ->
|
|
||||||
scram == store_type().
|
|
||||||
|
|
||||||
password_to_scram(Password) ->
|
|
||||||
password_to_scram(Password,
|
|
||||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
|
||||||
|
|
||||||
password_to_scram(Password, IterationCount) ->
|
|
||||||
Salt = randoms:bytes(?SALT_LENGTH),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
ServerKey = scram:server_key(SaltedPassword),
|
|
||||||
#scram{storedkey = misc:encode_base64(StoredKey),
|
|
||||||
serverkey = misc:encode_base64(ServerKey),
|
|
||||||
salt = misc:encode_base64(Salt),
|
|
||||||
iterationcount = IterationCount}.
|
|
||||||
|
|
||||||
is_password_scram_valid(Password, Scram) ->
|
|
||||||
case jid:resourceprep(Password) of
|
|
||||||
error ->
|
|
||||||
false;
|
|
||||||
_ ->
|
|
||||||
IterationCount = Scram#scram.iterationcount,
|
|
||||||
Salt = misc:decode_base64(Scram#scram.salt),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
misc:decode_base64(Scram#scram.storedkey) == StoredKey
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
export(_Server) ->
|
||||||
[{passwd,
|
[{passwd,
|
||||||
fun(Host, #passwd{us = {LUser, LServer}, password = Password})
|
fun(Host, #passwd{us = {LUser, LServer}, password = Password})
|
||||||
|
@ -30,14 +30,8 @@
|
|||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, check_password/4,
|
||||||
check_password/6, try_register/3,
|
is_user_exists/2, store_type/1, plain_password_required/1,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
|
||||||
get_vh_registered_users/2,
|
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, plain_password_required/0,
|
|
||||||
opt_type/1]).
|
opt_type/1]).
|
||||||
|
|
||||||
start(_Host) ->
|
start(_Host) ->
|
||||||
@ -46,13 +40,6 @@ start(_Host) ->
|
|||||||
stop(_Host) ->
|
stop(_Host) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
set_password(_User, _Server, _Password) ->
|
|
||||||
{error, not_allowed}.
|
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password, _Digest,
|
|
||||||
_DigestGen) ->
|
|
||||||
check_password(User, AuthzId, Server, Password).
|
|
||||||
|
|
||||||
check_password(User, AuthzId, Host, Password) ->
|
check_password(User, AuthzId, Host, Password) ->
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
if AuthzId /= <<>> andalso AuthzId /= User ->
|
||||||
false;
|
false;
|
||||||
@ -70,25 +57,6 @@ check_password(User, AuthzId, Host, Password) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
try_register(_User, _Server, _Password) ->
|
|
||||||
{error, not_allowed}.
|
|
||||||
|
|
||||||
dirty_get_registered_users() -> [].
|
|
||||||
|
|
||||||
get_vh_registered_users(_Host) -> [].
|
|
||||||
|
|
||||||
get_vh_registered_users(_Host, _) -> [].
|
|
||||||
|
|
||||||
get_vh_registered_users_number(_Host) -> 0.
|
|
||||||
|
|
||||||
get_vh_registered_users_number(_Host, _) -> 0.
|
|
||||||
|
|
||||||
get_password(_User, _Server) -> false.
|
|
||||||
|
|
||||||
get_password_s(_User, _Server) -> <<"">>.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> true | false | {error, Error}
|
|
||||||
%% TODO: Improve this function to return an error instead of 'false' when connection to PAM failed
|
|
||||||
is_user_exists(User, Host) ->
|
is_user_exists(User, Host) ->
|
||||||
Service = get_pam_service(Host),
|
Service = get_pam_service(Host),
|
||||||
UserInfo = case get_pam_userinfotype(Host) of
|
UserInfo = case get_pam_userinfotype(Host) of
|
||||||
@ -97,16 +65,13 @@ is_user_exists(User, Host) ->
|
|||||||
end,
|
end,
|
||||||
case catch epam:acct_mgmt(Service, UserInfo) of
|
case catch epam:acct_mgmt(Service, UserInfo) of
|
||||||
true -> true;
|
true -> true;
|
||||||
_ -> false
|
false -> false;
|
||||||
|
_Err -> {error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(_User, _Server) -> {error, not_allowed}.
|
plain_password_required(_) -> true.
|
||||||
|
|
||||||
remove_user(_User, _Server, _Password) -> not_allowed.
|
store_type(_) -> external.
|
||||||
|
|
||||||
plain_password_required() -> true.
|
|
||||||
|
|
||||||
store_type() -> external.
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% Internal functions
|
%% Internal functions
|
||||||
|
@ -32,15 +32,10 @@
|
|||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||||
check_password/6, try_register/3,
|
get_users/2, count_users/2,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
get_password/2, remove_user/2, store_type/1, export/1, import/2,
|
||||||
get_vh_registered_users/2,
|
plain_password_required/1]).
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, export/1, import/2,
|
|
||||||
plain_password_required/0]).
|
|
||||||
-export([passwd_schema/0]).
|
-export([passwd_schema/0]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -49,258 +44,65 @@
|
|||||||
-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
-record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
||||||
password = <<"">> :: binary() | scram() | '_'}).
|
password = <<"">> :: binary() | scram() | '_'}).
|
||||||
|
|
||||||
-define(SALT_LENGTH, 16).
|
|
||||||
|
|
||||||
start(_Host) ->
|
start(_Host) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
stop(_Host) ->
|
stop(_Host) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
plain_password_required() ->
|
plain_password_required(Server) ->
|
||||||
case is_scrammed() of
|
store_type(Server) == scram.
|
||||||
false -> false;
|
|
||||||
true -> true
|
|
||||||
end.
|
|
||||||
|
|
||||||
store_type() ->
|
store_type(Server) ->
|
||||||
case is_scrammed() of
|
ejabberd_auth:password_format(Server).
|
||||||
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
|
|
||||||
true -> scram %% allows: PLAIN SCRAM
|
|
||||||
end.
|
|
||||||
|
|
||||||
passwd_schema() ->
|
passwd_schema() ->
|
||||||
{record_info(fields, passwd), #passwd{}}.
|
{record_info(fields, passwd), #passwd{}}.
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #passwd{password = Password}} when is_binary(Password) ->
|
|
||||||
Password /= <<"">>;
|
|
||||||
{ok, #passwd{password = Scram}} when is_record(Scram, scram) ->
|
|
||||||
is_password_scram_valid(Password, Scram);
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
check_password(User, AuthzId, Server, Password, Digest,
|
|
||||||
DigestGen) ->
|
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #passwd{password = Passwd}} when is_binary(Passwd) ->
|
|
||||||
DigRes = if Digest /= <<"">> ->
|
|
||||||
Digest == DigestGen(Passwd);
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
if DigRes -> true;
|
|
||||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
|
||||||
end;
|
|
||||||
{ok, #passwd{password = Scram}}
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
Passwd = misc:decode_base64(Scram#scram.storedkey),
|
|
||||||
DigRes = if Digest /= <<"">> ->
|
|
||||||
Digest == DigestGen(Passwd);
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
if DigRes -> true;
|
|
||||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
|
||||||
end;
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
LUser = jid:nodeprep(User),
|
ejabberd_riak:put(#passwd{us = {User, Server}, password = Password},
|
||||||
LServer = jid:nameprep(Server),
|
passwd_schema(),
|
||||||
LPassword = jid:resourceprep(Password),
|
[{'2i', [{<<"host">>, Server}]}]).
|
||||||
US = {LUser, LServer},
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
try_register(User, Server, Password) ->
|
||||||
{error, invalid_jid};
|
US = {User, Server},
|
||||||
LPassword == error ->
|
case ejabberd_riak:get(passwd, passwd_schema(), US) of
|
||||||
{error, invalid_password};
|
{error, notfound} ->
|
||||||
true ->
|
ejabberd_riak:put(#passwd{us = US, password = Password},
|
||||||
Password2 = case is_scrammed() and is_binary(Password)
|
passwd_schema(),
|
||||||
of
|
[{'2i', [{<<"host">>, Server}]}]);
|
||||||
true -> password_to_scram(Password);
|
{ok, _} ->
|
||||||
false -> Password
|
{error, exists};
|
||||||
end,
|
{error, _} = Err ->
|
||||||
ok = ejabberd_riak:put(#passwd{us = US, password = Password2},
|
Err
|
||||||
passwd_schema(),
|
|
||||||
[{'2i', [{<<"host">>, LServer}]}])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
try_register(User, Server, PasswordList) ->
|
get_users(Server, _) ->
|
||||||
LUser = jid:nodeprep(User),
|
case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, Server) of
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
Password = if is_list(PasswordList); is_binary(PasswordList) ->
|
|
||||||
iolist_to_binary(PasswordList);
|
|
||||||
true -> PasswordList
|
|
||||||
end,
|
|
||||||
LPassword = jid:resourceprep(Password),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
{error, invalid_jid};
|
|
||||||
LPassword == error and not is_record(Password, scram) ->
|
|
||||||
{error, invalid_password};
|
|
||||||
true ->
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), US) of
|
|
||||||
{error, notfound} ->
|
|
||||||
Password2 = case is_scrammed() and
|
|
||||||
is_binary(Password)
|
|
||||||
of
|
|
||||||
true -> password_to_scram(Password);
|
|
||||||
false -> Password
|
|
||||||
end,
|
|
||||||
{atomic, ejabberd_riak:put(
|
|
||||||
#passwd{us = US,
|
|
||||||
password = Password2},
|
|
||||||
passwd_schema(),
|
|
||||||
[{'2i', [{<<"host">>, LServer}]}])};
|
|
||||||
{ok, _} ->
|
|
||||||
exists;
|
|
||||||
Err ->
|
|
||||||
{atomic, Err}
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
|
||||||
lists:flatmap(
|
|
||||||
fun(Server) ->
|
|
||||||
get_vh_registered_users(Server)
|
|
||||||
end, ejabberd_config:get_vh_by_auth_method(riak)).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, LServer) of
|
|
||||||
{ok, Users} ->
|
{ok, Users} ->
|
||||||
Users;
|
Users;
|
||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vh_registered_users(Server, _) ->
|
count_users(Server, _) ->
|
||||||
get_vh_registered_users(Server).
|
case ejabberd_riak:count_by_index(passwd, <<"host">>, Server) of
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:count_by_index(passwd, <<"host">>, LServer) of
|
|
||||||
{ok, N} ->
|
{ok, N} ->
|
||||||
N;
|
N;
|
||||||
_ ->
|
_ ->
|
||||||
0
|
0
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, _) ->
|
|
||||||
get_vh_registered_users_number(Server).
|
|
||||||
|
|
||||||
get_password(User, Server) ->
|
get_password(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
case ejabberd_riak:get(passwd, passwd_schema(), {User, Server}) of
|
||||||
LServer = jid:nameprep(Server),
|
{ok, Password} ->
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
{ok, Password};
|
||||||
{ok, #passwd{password = Password}}
|
{error, _} ->
|
||||||
when is_binary(Password) ->
|
error
|
||||||
Password;
|
|
||||||
{ok, #passwd{password = Scram}}
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
{misc:decode_base64(Scram#scram.storedkey),
|
|
||||||
misc:decode_base64(Scram#scram.serverkey),
|
|
||||||
misc:decode_base64(Scram#scram.salt),
|
|
||||||
Scram#scram.iterationcount};
|
|
||||||
_ -> false
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #passwd{password = Password}}
|
|
||||||
when is_binary(Password) ->
|
|
||||||
Password;
|
|
||||||
{ok, #passwd{password = Scram}}
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
<<"">>;
|
|
||||||
_ -> <<"">>
|
|
||||||
end.
|
|
||||||
|
|
||||||
is_user_exists(User, Server) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
|
||||||
{error, notfound} -> false;
|
|
||||||
{ok, _} -> true;
|
|
||||||
Err -> Err
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
ejabberd_riak:delete(passwd, {User, Server}).
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
remove_user(User, Server, Password) ->
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
case ejabberd_riak:get(passwd, passwd_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #passwd{password = Password}}
|
|
||||||
when is_binary(Password) ->
|
|
||||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
|
||||||
ok;
|
|
||||||
{ok, #passwd{password = Scram}}
|
|
||||||
when is_record(Scram, scram) ->
|
|
||||||
case is_password_scram_valid(Password, Scram) of
|
|
||||||
true ->
|
|
||||||
ejabberd_riak:delete(passwd, {LUser, LServer}),
|
|
||||||
ok;
|
|
||||||
false -> not_allowed
|
|
||||||
end;
|
|
||||||
_ -> not_exists
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%
|
|
||||||
%%% SCRAM
|
|
||||||
%%%
|
|
||||||
|
|
||||||
is_scrammed() ->
|
|
||||||
scram == ejabberd_auth:password_format(?MYNAME).
|
|
||||||
|
|
||||||
password_to_scram(Password) ->
|
|
||||||
password_to_scram(Password,
|
|
||||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
|
||||||
|
|
||||||
password_to_scram(Password, IterationCount) ->
|
|
||||||
Salt = randoms:bytes(?SALT_LENGTH),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
ServerKey = scram:server_key(SaltedPassword),
|
|
||||||
#scram{storedkey = misc:encode_base64(StoredKey),
|
|
||||||
serverkey = misc:encode_base64(ServerKey),
|
|
||||||
salt = misc:encode_base64(Salt),
|
|
||||||
iterationcount = IterationCount}.
|
|
||||||
|
|
||||||
is_password_scram_valid(Password, Scram) ->
|
|
||||||
case jid:resourceprep(Password) of
|
|
||||||
error ->
|
|
||||||
false;
|
|
||||||
_ ->
|
|
||||||
IterationCount = Scram#scram.iterationcount,
|
|
||||||
Salt = misc:decode_base64(Scram#scram.salt),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
misc:decode_base64(Scram#scram.storedkey) == StoredKey
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
export(_Server) ->
|
||||||
[{passwd,
|
[{passwd,
|
||||||
|
@ -31,14 +31,9 @@
|
|||||||
|
|
||||||
-behaviour(ejabberd_auth).
|
-behaviour(ejabberd_auth).
|
||||||
|
|
||||||
-export([start/1, stop/1, set_password/3, check_password/4,
|
-export([start/1, stop/1, set_password/3, try_register/3,
|
||||||
check_password/6, try_register/3,
|
get_users/2, count_users/2, get_password/2,
|
||||||
dirty_get_registered_users/0, get_vh_registered_users/1,
|
remove_user/2, store_type/1, plain_password_required/1,
|
||||||
get_vh_registered_users/2,
|
|
||||||
get_vh_registered_users_number/1,
|
|
||||||
get_vh_registered_users_number/2, get_password/2,
|
|
||||||
get_password_s/2, is_user_exists/2, remove_user/2,
|
|
||||||
remove_user/3, store_type/0, plain_password_required/0,
|
|
||||||
convert_to_scram/1]).
|
convert_to_scram/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
@ -54,397 +49,82 @@ start(_Host) -> ok.
|
|||||||
|
|
||||||
stop(_Host) -> ok.
|
stop(_Host) -> ok.
|
||||||
|
|
||||||
plain_password_required() ->
|
plain_password_required(Server) ->
|
||||||
case is_scrammed() of
|
store_type(Server) == scram.
|
||||||
false -> false;
|
|
||||||
true -> true
|
|
||||||
end.
|
|
||||||
|
|
||||||
store_type() ->
|
store_type(Server) ->
|
||||||
case is_scrammed() of
|
ejabberd_auth:password_format(Server).
|
||||||
false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM
|
|
||||||
true -> scram %% allows: PLAIN SCRAM
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, AuthzId, Server, Password) -> true | false | {error, Error}
|
|
||||||
check_password(User, AuthzId, Server, Password) ->
|
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
false;
|
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
case is_scrammed() of
|
|
||||||
true ->
|
|
||||||
try sql_queries:get_password_scram(LServer, LUser) of
|
|
||||||
{selected,
|
|
||||||
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
|
||||||
Scram =
|
|
||||||
#scram{storedkey = StoredKey,
|
|
||||||
serverkey = ServerKey,
|
|
||||||
salt = Salt,
|
|
||||||
iterationcount = IterationCount},
|
|
||||||
is_password_scram_valid_stored(Password, Scram, LUser, LServer);
|
|
||||||
{selected, []} ->
|
|
||||||
false; %% Account does not exist
|
|
||||||
{error, _Error} ->
|
|
||||||
false %% Typical error is that table doesn't exist
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
false %% Typical error is database not accessible
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
try sql_queries:get_password(LServer, LUser) of
|
|
||||||
{selected, [{Password}]} ->
|
|
||||||
Password /= <<"">>;
|
|
||||||
{selected, [{_Password2}]} ->
|
|
||||||
false; %% Password is not correct
|
|
||||||
{selected, []} ->
|
|
||||||
false; %% Account does not exist
|
|
||||||
{error, _Error} ->
|
|
||||||
false %% Typical error is that table doesn't exist
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
false %% Typical error is database not accessible
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, AuthzId, Server, Password, Digest, DigestGen) -> true | false | {error, Error}
|
|
||||||
check_password(User, AuthzId, Server, Password, Digest,
|
|
||||||
DigestGen) ->
|
|
||||||
if AuthzId /= <<>> andalso AuthzId /= User ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
false;
|
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
case is_scrammed() of
|
|
||||||
false ->
|
|
||||||
try sql_queries:get_password(LServer, LUser) of
|
|
||||||
%% Account exists, check if password is valid
|
|
||||||
{selected, [{Passwd}]} ->
|
|
||||||
DigRes = if Digest /= <<"">> ->
|
|
||||||
Digest == DigestGen(Passwd);
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
if DigRes -> true;
|
|
||||||
true -> (Passwd == Password) and (Password /= <<"">>)
|
|
||||||
end;
|
|
||||||
{selected, []} ->
|
|
||||||
false; %% Account does not exist
|
|
||||||
{error, _Error} ->
|
|
||||||
false %% Typical error is that table doesn't exist
|
|
||||||
catch
|
|
||||||
_:_ ->
|
|
||||||
false %% Typical error is database not accessible
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User::string(), Server::string(), Password::string()) ->
|
|
||||||
%% ok | {error, invalid_jid}
|
|
||||||
set_password(User, Server, Password) ->
|
set_password(User, Server, Password) ->
|
||||||
LServer = jid:nameprep(Server),
|
Res = if is_record(Password, scram) ->
|
||||||
LUser = jid:nodeprep(User),
|
sql_queries:set_password_scram_t(
|
||||||
LPassword = jid:resourceprep(Password),
|
Server, User,
|
||||||
if (LUser == error) or (LServer == error) ->
|
Password#scram.storedkey, Password#scram.serverkey,
|
||||||
{error, invalid_jid};
|
Password#scram.salt, Password#scram.iterationcount);
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
true ->
|
||||||
{error, invalid_jid};
|
sql_queries:set_password_t(Server, User, Password)
|
||||||
LPassword == error ->
|
end,
|
||||||
{error, invalid_password};
|
case Res of
|
||||||
true ->
|
{atomic, _} ->
|
||||||
case is_scrammed() of
|
ok;
|
||||||
true ->
|
{aborted, Reason} ->
|
||||||
Scram = password_to_scram(Password),
|
?ERROR_MSG("failed to write to SQL table: ~p", [Reason]),
|
||||||
case catch sql_queries:set_password_scram_t(
|
{error, db_failure}
|
||||||
LServer,
|
|
||||||
LUser,
|
|
||||||
Scram#scram.storedkey,
|
|
||||||
Scram#scram.serverkey,
|
|
||||||
Scram#scram.salt,
|
|
||||||
Scram#scram.iterationcount
|
|
||||||
)
|
|
||||||
of
|
|
||||||
{atomic, ok} -> ok;
|
|
||||||
Other -> {error, Other}
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
case catch sql_queries:set_password_t(LServer,
|
|
||||||
LUser, Password)
|
|
||||||
of
|
|
||||||
{atomic, ok} -> ok;
|
|
||||||
Other -> {error, Other}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid}
|
|
||||||
try_register(User, Server, Password) ->
|
try_register(User, Server, Password) ->
|
||||||
LServer = jid:nameprep(Server),
|
Res = if is_record(Password, scram) ->
|
||||||
LUser = jid:nodeprep(User),
|
sql_queries:add_user_scram(
|
||||||
LPassword = jid:resourceprep(Password),
|
Server, User,
|
||||||
if (LUser == error) or (LServer == error) ->
|
Password#scram.storedkey, Password#scram.serverkey,
|
||||||
{error, invalid_jid};
|
Password#scram.salt, Password#scram.iterationcount);
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
true ->
|
||||||
{error, invalid_jid};
|
sql_queries:add_user(Server, User, Password)
|
||||||
LPassword == error and not is_record(Password, scram) ->
|
end,
|
||||||
{error, invalid_password};
|
case Res of
|
||||||
true ->
|
{updated, 1} -> ok;
|
||||||
case is_scrammed() of
|
_ -> {error, exists}
|
||||||
true ->
|
|
||||||
Scram = case is_record(Password, scram) of
|
|
||||||
true -> Password;
|
|
||||||
false -> password_to_scram(Password)
|
|
||||||
end,
|
|
||||||
case catch sql_queries:add_user_scram(
|
|
||||||
LServer,
|
|
||||||
LUser,
|
|
||||||
Scram#scram.storedkey,
|
|
||||||
Scram#scram.serverkey,
|
|
||||||
Scram#scram.salt,
|
|
||||||
Scram#scram.iterationcount
|
|
||||||
) of
|
|
||||||
{updated, 1} -> {atomic, ok};
|
|
||||||
_ -> {atomic, exists}
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
case catch sql_queries:add_user(LServer, LUser,
|
|
||||||
Password) of
|
|
||||||
{updated, 1} -> {atomic, ok};
|
|
||||||
_ -> {atomic, exists}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
dirty_get_registered_users() ->
|
get_users(Server, Opts) ->
|
||||||
Servers = ejabberd_config:get_vh_by_auth_method(sql),
|
case sql_queries:list_users(Server, Opts) of
|
||||||
lists:flatmap(fun (Server) ->
|
{selected, Res} ->
|
||||||
get_vh_registered_users(Server)
|
[{U, Server} || {U} <- Res];
|
||||||
end,
|
_ -> []
|
||||||
Servers).
|
|
||||||
|
|
||||||
get_vh_registered_users(Server) ->
|
|
||||||
case jid:nameprep(Server) of
|
|
||||||
error -> [];
|
|
||||||
<<>> -> [];
|
|
||||||
LServer ->
|
|
||||||
case catch sql_queries:list_users(LServer) of
|
|
||||||
{selected, Res} ->
|
|
||||||
[{U, LServer} || {U} <- Res];
|
|
||||||
_ -> []
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vh_registered_users(Server, Opts) ->
|
count_users(Server, Opts) ->
|
||||||
case jid:nameprep(Server) of
|
case sql_queries:users_number(Server, Opts) of
|
||||||
error -> [];
|
{selected, [{Res}]} ->
|
||||||
<<>> -> [];
|
Res;
|
||||||
LServer ->
|
_Other -> 0
|
||||||
case catch sql_queries:list_users(LServer, Opts) of
|
|
||||||
{selected, Res} ->
|
|
||||||
[{U, LServer} || {U} <- Res];
|
|
||||||
_ -> []
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server) ->
|
|
||||||
case jid:nameprep(Server) of
|
|
||||||
error -> 0;
|
|
||||||
<<>> -> 0;
|
|
||||||
LServer ->
|
|
||||||
case catch sql_queries:users_number(LServer) of
|
|
||||||
{selected, [{Res}]} ->
|
|
||||||
Res;
|
|
||||||
_ -> 0
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_vh_registered_users_number(Server, Opts) ->
|
|
||||||
case jid:nameprep(Server) of
|
|
||||||
error -> 0;
|
|
||||||
<<>> -> 0;
|
|
||||||
LServer ->
|
|
||||||
case catch sql_queries:users_number(LServer, Opts) of
|
|
||||||
{selected, [{Res}]} ->
|
|
||||||
Res;
|
|
||||||
_Other -> 0
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_password(User, Server) ->
|
get_password(User, Server) ->
|
||||||
LServer = jid:nameprep(Server),
|
case sql_queries:get_password_scram(Server, User) of
|
||||||
LUser = jid:nodeprep(User),
|
{selected, [{Password, <<>>, <<>>, 0}]} ->
|
||||||
if (LUser == error) or (LServer == error) ->
|
{ok, Password};
|
||||||
false;
|
{selected, [{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
{ok, #scram{storedkey = StoredKey,
|
||||||
false;
|
serverkey = ServerKey,
|
||||||
true ->
|
salt = Salt,
|
||||||
case is_scrammed() of
|
iterationcount = IterationCount}};
|
||||||
true ->
|
{selected, []} ->
|
||||||
case catch sql_queries:get_password_scram(
|
error;
|
||||||
LServer, LUser) of
|
Err ->
|
||||||
{selected,
|
?ERROR_MSG("Failed to read password for user ~s@~s: ~p",
|
||||||
[{StoredKey, ServerKey, Salt, IterationCount}]} ->
|
[User, Server, Err]),
|
||||||
{misc:decode_base64(StoredKey),
|
error
|
||||||
misc:decode_base64(ServerKey),
|
|
||||||
misc:decode_base64(Salt),
|
|
||||||
IterationCount};
|
|
||||||
_ -> false
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
case catch sql_queries:get_password(LServer, LUser)
|
|
||||||
of
|
|
||||||
{selected, [{Password}]} -> Password;
|
|
||||||
_ -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_password_s(User, Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
<<"">>;
|
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
|
||||||
<<"">>;
|
|
||||||
true ->
|
|
||||||
case is_scrammed() of
|
|
||||||
false ->
|
|
||||||
case catch sql_queries:get_password(LServer, LUser) of
|
|
||||||
{selected, [{Password}]} -> Password;
|
|
||||||
_ -> <<"">>
|
|
||||||
end;
|
|
||||||
true -> <<"">>
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> true | false | {error, Error}
|
|
||||||
is_user_exists(User, Server) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
false;
|
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
|
||||||
false;
|
|
||||||
true ->
|
|
||||||
try sql_queries:get_password(LServer, LUser) of
|
|
||||||
{selected, [{_Password}]} ->
|
|
||||||
true; %% Account exists
|
|
||||||
{selected, []} ->
|
|
||||||
false; %% Account does not exist
|
|
||||||
{error, Error} -> {error, Error}
|
|
||||||
catch
|
|
||||||
_:B -> {error, B}
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server) -> ok | error
|
|
||||||
%% @doc Remove user.
|
|
||||||
%% Note: it may return ok even if there was some problem removing the user.
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LServer = jid:nameprep(Server),
|
case sql_queries:del_user(Server, User) of
|
||||||
LUser = jid:nodeprep(User),
|
{updated, _} ->
|
||||||
if (LUser == error) or (LServer == error) ->
|
ok;
|
||||||
error;
|
Err ->
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
?ERROR_MSG("failed to delete user ~s@~s: ~p",
|
||||||
error;
|
[User, Server, Err]),
|
||||||
true ->
|
{error, db_failure}
|
||||||
catch sql_queries:del_user(LServer, LUser),
|
|
||||||
ok
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% @spec (User, Server, Password) -> ok | error | not_exists | not_allowed
|
|
||||||
%% @doc Remove user if the provided password is correct.
|
|
||||||
remove_user(User, Server, Password) ->
|
|
||||||
LServer = jid:nameprep(Server),
|
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
if (LUser == error) or (LServer == error) ->
|
|
||||||
error;
|
|
||||||
(LUser == <<>>) or (LServer == <<>>) ->
|
|
||||||
error;
|
|
||||||
true ->
|
|
||||||
case is_scrammed() of
|
|
||||||
true ->
|
|
||||||
case check_password(User, <<"">>, Server, Password) of
|
|
||||||
true ->
|
|
||||||
remove_user(User, Server),
|
|
||||||
ok;
|
|
||||||
false -> not_allowed
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
F = fun () ->
|
|
||||||
Result = sql_queries:del_user_return_password(
|
|
||||||
LServer, LUser, Password),
|
|
||||||
case Result of
|
|
||||||
{selected, [{Password}]} -> ok;
|
|
||||||
{selected, []} -> not_exists;
|
|
||||||
_ -> not_allowed
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
{atomic, Result} = sql_queries:sql_transaction(
|
|
||||||
LServer, F),
|
|
||||||
Result
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%
|
|
||||||
%%% SCRAM
|
|
||||||
%%%
|
|
||||||
|
|
||||||
is_scrammed() ->
|
|
||||||
scram == ejabberd_auth:password_format(?MYNAME).
|
|
||||||
|
|
||||||
password_to_scram(Password) ->
|
|
||||||
password_to_scram(Password,
|
|
||||||
?SCRAM_DEFAULT_ITERATION_COUNT).
|
|
||||||
|
|
||||||
password_to_scram(Password, IterationCount) ->
|
|
||||||
Salt = randoms:bytes(?SALT_LENGTH),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
ServerKey = scram:server_key(SaltedPassword),
|
|
||||||
#scram{storedkey = misc:encode_base64(StoredKey),
|
|
||||||
serverkey = misc:encode_base64(ServerKey),
|
|
||||||
salt = misc:encode_base64(Salt),
|
|
||||||
iterationcount = IterationCount}.
|
|
||||||
|
|
||||||
is_password_scram_valid_stored(Pass, {scram,Pass,<<>>,<<>>,0}, LUser, LServer) ->
|
|
||||||
?INFO_MSG("Apparently, SQL auth method and scram password formatting are "
|
|
||||||
"enabled, but the password of user '~s' in the 'users' table is not "
|
|
||||||
"scrammed. You may want to execute this command: "
|
|
||||||
"ejabberdctl convert_to_scram ~s", [LUser, LServer]),
|
|
||||||
false;
|
|
||||||
is_password_scram_valid_stored(Password, Scram, _, _) ->
|
|
||||||
is_password_scram_valid(Password, Scram).
|
|
||||||
|
|
||||||
is_password_scram_valid(Password, Scram) ->
|
|
||||||
case jid:resourceprep(Password) of
|
|
||||||
error ->
|
|
||||||
false;
|
|
||||||
_ ->
|
|
||||||
IterationCount = Scram#scram.iterationcount,
|
|
||||||
Salt = misc:decode_base64(Scram#scram.salt),
|
|
||||||
SaltedPassword = scram:salted_password(Password, Salt,
|
|
||||||
IterationCount),
|
|
||||||
StoredKey =
|
|
||||||
scram:stored_key(scram:client_key(SaltedPassword)),
|
|
||||||
misc:decode_base64(Scram#scram.storedkey) == StoredKey
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-define(BATCH_SIZE, 1000).
|
-define(BATCH_SIZE, 1000).
|
||||||
@ -485,7 +165,7 @@ convert_to_scram(Server) ->
|
|||||||
"password of user ~s@~s",
|
"password of user ~s@~s",
|
||||||
[LUser, LServer]);
|
[LUser, LServer]);
|
||||||
_ ->
|
_ ->
|
||||||
Scram = password_to_scram(Password),
|
Scram = ejabberd_auth:password_to_scram(Password),
|
||||||
set_password_scram_t(
|
set_password_scram_t(
|
||||||
LUser,
|
LUser,
|
||||||
Scram#scram.storedkey,
|
Scram#scram.storedkey,
|
||||||
|
@ -135,7 +135,7 @@ export_host(Dir, FnH, Host) ->
|
|||||||
{ok, Fd} ->
|
{ok, Fd} ->
|
||||||
print(Fd, make_piefxis_xml_head()),
|
print(Fd, make_piefxis_xml_head()),
|
||||||
print(Fd, make_piefxis_host_head(Host)),
|
print(Fd, make_piefxis_host_head(Host)),
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
case export_users(Users, Host, Fd) of
|
case export_users(Users, Host, Fd) of
|
||||||
ok ->
|
ok ->
|
||||||
print(Fd, make_piefxis_host_tail()),
|
print(Fd, make_piefxis_host_tail()),
|
||||||
@ -402,9 +402,9 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
|
|||||||
stop("Invalid 'user': ~s", [Name]);
|
stop("Invalid 'user': ~s", [Name]);
|
||||||
LUser ->
|
LUser ->
|
||||||
case ejabberd_auth:try_register(LUser, LServer, Pass) of
|
case ejabberd_auth:try_register(LUser, LServer, Pass) of
|
||||||
{atomic, _} ->
|
ok ->
|
||||||
process_user_els(Els, State#state{user = LUser});
|
process_user_els(Els, State#state{user = LUser});
|
||||||
Err ->
|
{error, Err} ->
|
||||||
stop("Failed to create user '~s': ~p", [Name, Err])
|
stop("Failed to create user '~s': ~p", [Name, Err])
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
@ -1370,7 +1370,7 @@ list_vhosts2(Lang, Hosts) ->
|
|||||||
OnlineUsers =
|
OnlineUsers =
|
||||||
length(ejabberd_sm:get_vh_session_list(Host)),
|
length(ejabberd_sm:get_vh_session_list(Host)),
|
||||||
RegisteredUsers =
|
RegisteredUsers =
|
||||||
ejabberd_auth:get_vh_registered_users_number(Host),
|
ejabberd_auth:count_users(Host),
|
||||||
?XE(<<"tr">>,
|
?XE(<<"tr">>,
|
||||||
[?XE(<<"td">>,
|
[?XE(<<"td">>,
|
||||||
[?AC(<<"../server/", Host/binary,
|
[?AC(<<"../server/", Host/binary,
|
||||||
@ -1388,7 +1388,7 @@ list_vhosts2(Lang, Hosts) ->
|
|||||||
|
|
||||||
list_users(Host, Query, Lang, URLFunc) ->
|
list_users(Host, Query, Lang, URLFunc) ->
|
||||||
Res = list_users_parse_query(Query, Host),
|
Res = list_users_parse_query(Query, Host),
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||||
FUsers = case length(SUsers) of
|
FUsers = case length(SUsers) of
|
||||||
N when N =< 100 ->
|
N when N =< 100 ->
|
||||||
@ -1469,7 +1469,7 @@ list_users_parse_query(Query, Host) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
|
list_users_in_diapason(Host, Diap, Lang, URLFunc) ->
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||||
[S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
|
[S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
|
||||||
N1 = binary_to_integer(S1),
|
N1 = binary_to_integer(S1),
|
||||||
@ -1565,7 +1565,7 @@ su_to_list({Server, User}) ->
|
|||||||
get_stats(global, Lang) ->
|
get_stats(global, Lang) ->
|
||||||
OnlineUsers = ejabberd_sm:connected_users_number(),
|
OnlineUsers = ejabberd_sm:connected_users_number(),
|
||||||
RegisteredUsers = lists:foldl(fun (Host, Total) ->
|
RegisteredUsers = lists:foldl(fun (Host, Total) ->
|
||||||
ejabberd_auth:get_vh_registered_users_number(Host)
|
ejabberd_auth:count_users(Host)
|
||||||
+ Total
|
+ Total
|
||||||
end,
|
end,
|
||||||
0, ?MYHOSTS),
|
0, ?MYHOSTS),
|
||||||
@ -1589,7 +1589,7 @@ get_stats(Host, Lang) ->
|
|||||||
OnlineUsers =
|
OnlineUsers =
|
||||||
length(ejabberd_sm:get_vh_session_list(Host)),
|
length(ejabberd_sm:get_vh_session_list(Host)),
|
||||||
RegisteredUsers =
|
RegisteredUsers =
|
||||||
ejabberd_auth:get_vh_registered_users_number(Host),
|
ejabberd_auth:count_users(Host),
|
||||||
[?XAE(<<"table">>, [],
|
[?XAE(<<"table">>, [],
|
||||||
[?XE(<<"tbody">>,
|
[?XE(<<"tbody">>,
|
||||||
[?XE(<<"tr">>,
|
[?XE(<<"tr">>,
|
||||||
|
@ -83,7 +83,7 @@ try_register(User, Server, Password) ->
|
|||||||
case call_port(Server,
|
case call_port(Server,
|
||||||
[<<"tryregister">>, User, Server, Password])
|
[<<"tryregister">>, User, Server, Password])
|
||||||
of
|
of
|
||||||
true -> {atomic, ok};
|
true -> ok;
|
||||||
false -> {error, not_allowed}
|
false -> {error, not_allowed}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -810,14 +810,14 @@ histogram([], _Integral, _Current, Count, Hist) ->
|
|||||||
|
|
||||||
delete_old_users(Days) ->
|
delete_old_users(Days) ->
|
||||||
%% Get the list of registered users
|
%% Get the list of registered users
|
||||||
Users = ejabberd_auth:dirty_get_registered_users(),
|
Users = ejabberd_auth:get_users(),
|
||||||
|
|
||||||
{removed, N, UR} = delete_old_users(Days, Users),
|
{removed, N, UR} = delete_old_users(Days, Users),
|
||||||
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
||||||
|
|
||||||
delete_old_users_vhost(Host, Days) ->
|
delete_old_users_vhost(Host, Days) ->
|
||||||
%% Get the list of registered users
|
%% Get the list of registered users
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
|
|
||||||
{removed, N, UR} = delete_old_users(Days, Users),
|
{removed, N, UR} = delete_old_users(Days, Users),
|
||||||
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
{ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}.
|
||||||
@ -1285,7 +1285,7 @@ subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick
|
|||||||
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
|
subscribe_roster({Name1, Server1, Group1, Nick1}, Roster).
|
||||||
|
|
||||||
push_alltoall(S, G) ->
|
push_alltoall(S, G) ->
|
||||||
Users = ejabberd_auth:get_vh_registered_users(S),
|
Users = ejabberd_auth:get_users(S),
|
||||||
Users2 = build_list_users(G, Users, []),
|
Users2 = build_list_users(G, Users, []),
|
||||||
subscribe_all(Users2),
|
subscribe_all(Users2),
|
||||||
ok.
|
ok.
|
||||||
@ -1499,14 +1499,14 @@ stats(Name) ->
|
|||||||
case Name of
|
case Name of
|
||||||
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
|
<<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000);
|
||||||
<<"processes">> -> length(erlang:processes());
|
<<"processes">> -> length(erlang:processes());
|
||||||
<<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:get_vh_registered_users_number(Host) + Sum end, 0, ?MYHOSTS);
|
<<"registeredusers">> -> lists:foldl(fun(Host, Sum) -> ejabberd_auth:count_users(Host) + Sum end, 0, ?MYHOSTS);
|
||||||
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
|
<<"onlineusersnode">> -> length(ejabberd_sm:dirty_get_my_sessions_list());
|
||||||
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
|
<<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list())
|
||||||
end.
|
end.
|
||||||
|
|
||||||
stats(Name, Host) ->
|
stats(Name, Host) ->
|
||||||
case Name of
|
case Name of
|
||||||
<<"registeredusers">> -> ejabberd_auth:get_vh_registered_users_number(Host);
|
<<"registeredusers">> -> ejabberd_auth:count_users(Host);
|
||||||
<<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
|
<<"onlineusers">> -> length(ejabberd_sm:get_vh_session_list(Host))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ announce_all(#message{to = To} = Packet) ->
|
|||||||
Dest = jid:make(User, Server),
|
Dest = jid:make(User, Server),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
xmpp:set_from_to(add_store_hint(Packet), Local, Dest))
|
xmpp:set_from_to(add_store_hint(Packet), Local, Dest))
|
||||||
end, ejabberd_auth:get_vh_registered_users(To#jid.lserver)).
|
end, ejabberd_auth:get_users(To#jid.lserver)).
|
||||||
|
|
||||||
announce_all_hosts_all(#message{to = To} = Packet) ->
|
announce_all_hosts_all(#message{to = To} = Packet) ->
|
||||||
Local = jid:make(To#jid.server),
|
Local = jid:make(To#jid.server),
|
||||||
@ -642,7 +642,7 @@ announce_all_hosts_all(#message{to = To} = Packet) ->
|
|||||||
Dest = jid:make(User, Server),
|
Dest = jid:make(User, Server),
|
||||||
ejabberd_router:route(
|
ejabberd_router:route(
|
||||||
xmpp:set_from_to(add_store_hint(Packet), Local, Dest))
|
xmpp:set_from_to(add_store_hint(Packet), Local, Dest))
|
||||||
end, ejabberd_auth:dirty_get_registered_users()).
|
end, ejabberd_auth:get_users()).
|
||||||
|
|
||||||
announce_online(#message{to = To} = Packet) ->
|
announce_online(#message{to = To} = Packet) ->
|
||||||
announce_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
announce_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver),
|
||||||
|
@ -547,7 +547,7 @@ get_local_items({_, Host}, [<<"all users">>], _Server,
|
|||||||
get_local_items({_, Host},
|
get_local_items({_, Host},
|
||||||
[<<"all users">>, <<$@, Diap/binary>>], _Server,
|
[<<"all users">>, <<$@, Diap/binary>>], _Server,
|
||||||
_Lang) ->
|
_Lang) ->
|
||||||
Users = ejabberd_auth:get_vh_registered_users(Host),
|
Users = ejabberd_auth:get_users(Host),
|
||||||
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
SUsers = lists:sort([{S, U} || {U, S} <- Users]),
|
||||||
try
|
try
|
||||||
[S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
|
[S1, S2] = ejabberd_regexp:split(Diap, <<"-">>),
|
||||||
@ -661,7 +661,7 @@ get_online_vh_users(Host) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
get_all_vh_users(Host) ->
|
get_all_vh_users(Host) ->
|
||||||
case catch ejabberd_auth:get_vh_registered_users(Host)
|
case catch ejabberd_auth:get_users(Host)
|
||||||
of
|
of
|
||||||
{'EXIT', _Reason} -> [];
|
{'EXIT', _Reason} -> [];
|
||||||
Users ->
|
Users ->
|
||||||
@ -1194,7 +1194,7 @@ get_form(_Host, ?NS_ADMINL(<<"user-stats">>), Lang) ->
|
|||||||
required = true}]}};
|
required = true}]}};
|
||||||
get_form(Host,
|
get_form(Host,
|
||||||
?NS_ADMINL(<<"get-registered-users-num">>), Lang) ->
|
?NS_ADMINL(<<"get-registered-users-num">>), Lang) ->
|
||||||
Num = integer_to_binary(ejabberd_auth:get_vh_registered_users_number(Host)),
|
Num = integer_to_binary(ejabberd_auth:count_users(Host)),
|
||||||
{result, completed,
|
{result, completed,
|
||||||
#xdata{type = form,
|
#xdata{type = form,
|
||||||
fields = [?HFIELD(),
|
fields = [?HFIELD(),
|
||||||
|
@ -323,7 +323,7 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
|
|||||||
case ejabberd_auth:try_register(User, Server,
|
case ejabberd_auth:try_register(User, Server,
|
||||||
Password)
|
Password)
|
||||||
of
|
of
|
||||||
{atomic, ok} ->
|
ok ->
|
||||||
send_welcome_message(JID),
|
send_welcome_message(JID),
|
||||||
send_registration_notifications(
|
send_registration_notifications(
|
||||||
?MODULE, JID, Source),
|
?MODULE, JID, Source),
|
||||||
@ -331,7 +331,7 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
|
|||||||
Error ->
|
Error ->
|
||||||
remove_timeout(Source),
|
remove_timeout(Source),
|
||||||
case Error of
|
case Error of
|
||||||
{atomic, exists} ->
|
{error, exists} ->
|
||||||
Txt = <<"User already exists">>,
|
Txt = <<"User already exists">>,
|
||||||
{error, xmpp:err_conflict(Txt, Lang)};
|
{error, xmpp:err_conflict(Txt, Lang)};
|
||||||
{error, invalid_jid} ->
|
{error, invalid_jid} ->
|
||||||
|
@ -510,8 +510,8 @@ register_account2(Username, Host, Password) ->
|
|||||||
case ejabberd_auth:try_register(Username, Host,
|
case ejabberd_auth:try_register(Username, Host,
|
||||||
Password)
|
Password)
|
||||||
of
|
of
|
||||||
{atomic, Res} ->
|
ok ->
|
||||||
{success, Res, {Username, Host, Password}};
|
{success, ok, {Username, Host, Password}};
|
||||||
Other -> Other
|
Other -> Other
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -450,7 +450,7 @@ get_online_users(Host) ->
|
|||||||
get_group_users(Host1, Group1) ->
|
get_group_users(Host1, Group1) ->
|
||||||
{Host, Group} = split_grouphost(Host1, Group1),
|
{Host, Group} = split_grouphost(Host1, Group1),
|
||||||
case get_group_opt(Host, Group, all_users, false) of
|
case get_group_opt(Host, Group, all_users, false) of
|
||||||
true -> ejabberd_auth:get_vh_registered_users(Host);
|
true -> ejabberd_auth:get_users(Host);
|
||||||
false -> []
|
false -> []
|
||||||
end
|
end
|
||||||
++
|
++
|
||||||
@ -462,7 +462,7 @@ get_group_users(Host1, Group1) ->
|
|||||||
|
|
||||||
get_group_users(Host, Group, GroupOpts) ->
|
get_group_users(Host, Group, GroupOpts) ->
|
||||||
case proplists:get_value(all_users, GroupOpts, false) of
|
case proplists:get_value(all_users, GroupOpts, false) of
|
||||||
true -> ejabberd_auth:get_vh_registered_users(Host);
|
true -> ejabberd_auth:get_users(Host);
|
||||||
false -> []
|
false -> []
|
||||||
end
|
end
|
||||||
++
|
++
|
||||||
|
@ -119,7 +119,7 @@ get_local_stat(Server, [], Name)
|
|||||||
get_local_stat(Server, [], Name)
|
get_local_stat(Server, [], Name)
|
||||||
when Name == <<"users/total">> ->
|
when Name == <<"users/total">> ->
|
||||||
case catch
|
case catch
|
||||||
ejabberd_auth:get_vh_registered_users_number(Server)
|
ejabberd_auth:count_users(Server)
|
||||||
of
|
of
|
||||||
{'EXIT', _Reason} ->
|
{'EXIT', _Reason} ->
|
||||||
?STATERR(500, <<"Internal Server Error">>);
|
?STATERR(500, <<"Internal Server Error">>);
|
||||||
@ -134,7 +134,7 @@ get_local_stat(_Server, [], Name)
|
|||||||
get_local_stat(_Server, [], Name)
|
get_local_stat(_Server, [], Name)
|
||||||
when Name == <<"users/all-hosts/total">> ->
|
when Name == <<"users/all-hosts/total">> ->
|
||||||
NumUsers = lists:foldl(fun (Host, Total) ->
|
NumUsers = lists:foldl(fun (Host, Total) ->
|
||||||
ejabberd_auth:get_vh_registered_users_number(Host)
|
ejabberd_auth:count_users(Host)
|
||||||
+ Total
|
+ Total
|
||||||
end,
|
end,
|
||||||
0, ?MYHOSTS),
|
0, ?MYHOSTS),
|
||||||
|
@ -129,7 +129,7 @@ convert_data(Host, "accounts", User, [Data]) ->
|
|||||||
Pass
|
Pass
|
||||||
end,
|
end,
|
||||||
case ejabberd_auth:try_register(User, Host, Password) of
|
case ejabberd_auth:try_register(User, Host, Password) of
|
||||||
{atomic, ok} ->
|
ok ->
|
||||||
ok;
|
ok;
|
||||||
Err ->
|
Err ->
|
||||||
?ERROR_MSG("failed to register user ~s@~s: ~p",
|
?ERROR_MSG("failed to register user ~s@~s: ~p",
|
||||||
|
@ -468,8 +468,7 @@ re_register(Config) ->
|
|||||||
User = ?config(user, Config),
|
User = ?config(user, Config),
|
||||||
Server = ?config(server, Config),
|
Server = ?config(server, Config),
|
||||||
Pass = ?config(password, Config),
|
Pass = ?config(password, Config),
|
||||||
{atomic, ok} = ejabberd_auth:try_register(User, Server, Pass),
|
ok = ejabberd_auth:try_register(User, Server, Pass).
|
||||||
ok.
|
|
||||||
|
|
||||||
match_failure(Received, [Match]) when is_list(Match)->
|
match_failure(Received, [Match]) when is_list(Match)->
|
||||||
ct:fail("Received input:~n~n~p~n~ndon't match expected patterns:~n~n~s", [Received, Match]);
|
ct:fail("Received input:~n~n~p~n~ndon't match expected patterns:~n~n~s", [Received, Match]);
|
||||||
|
Loading…
Reference in New Issue
Block a user