From 633b68db1130c81551b063f3aa15d599b0d355e5 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Thu, 11 May 2017 14:37:21 +0300 Subject: [PATCH] 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`. --- rebar.config | 2 +- src/cyrsasl_scram.erl | 6 +- src/ejabberd_admin.erl | 6 +- src/ejabberd_auth.erl | 850 ++++++++++++++++++++++---------- src/ejabberd_auth_anonymous.erl | 86 +--- src/ejabberd_auth_external.erl | 263 +--------- src/ejabberd_auth_ldap.erl | 64 +-- src/ejabberd_auth_mnesia.erl | 475 ++++++------------ src/ejabberd_auth_pam.erl | 47 +- src/ejabberd_auth_riak.erl | 264 ++-------- src/ejabberd_auth_sql.erl | 448 +++-------------- src/ejabberd_piefxis.erl | 6 +- src/ejabberd_web_admin.erl | 10 +- src/extauth.erl | 2 +- src/mod_admin_extra.erl | 10 +- src/mod_announce.erl | 4 +- src/mod_configure.erl | 6 +- src/mod_register.erl | 4 +- src/mod_register_web.erl | 4 +- src/mod_shared_roster.erl | 4 +- src/mod_stats.erl | 4 +- src/prosody2ejabberd.erl | 2 +- test/suite.erl | 3 +- 23 files changed, 905 insertions(+), 1665 deletions(-) diff --git a/rebar.config b/rebar.config index b553931e6..7def98c79 100644 --- a/rebar.config +++ b/rebar.config @@ -20,7 +20,7 @@ {deps, [{lager, ".*", {git, "https://github.com/basho/lager", {tag, "3.2.1"}}}, {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"}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.8"}}}, {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.21"}}}, diff --git a/src/cyrsasl_scram.erl b/src/cyrsasl_scram.erl index 8cbc821c7..b496be0a4 100644 --- a/src/cyrsasl_scram.erl +++ b/src/cyrsasl_scram.erl @@ -111,7 +111,11 @@ mech_step(#state{step = 2} = State, ClientIn) -> {error, saslprep_failed, UserName}; true -> {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 -> TempSalt = randoms:bytes(?SALT_LENGTH), diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index a615b2fee..c0429fb0c 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -478,9 +478,9 @@ update_module(ModuleNameString) -> register(User, Host, Password) -> case ejabberd_auth:try_register(User, Host, Password) of - {atomic, ok} -> + ok -> {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]), {error, conflict, 10090, Msg}; {error, Reason} -> @@ -494,7 +494,7 @@ unregister(User, Host) -> {ok, ""}. registered_users(Host) -> - Users = ejabberd_auth:get_vh_registered_users(Host), + Users = ejabberd_auth:get_users(Host), SUsers = lists:sort(Users), lists:map(fun({U, _S}) -> U end, SUsers). diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 9751142a5..23ed0eeae 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -22,9 +22,6 @@ %%% 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). -behaviour(gen_server). @@ -37,10 +34,10 @@ set_password/3, check_password/4, check_password/6, check_password_with_authmodule/4, check_password_with_authmodule/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, export/1, import_info/0, - get_vh_registered_users_number/1, import/5, import_start/2, - get_vh_registered_users_number/2, get_password/2, + get_users/0, get_users/1, password_to_scram/1, + get_users/2, export/1, import_info/0, + count_users/1, import/5, import_start/2, + count_users/2, get_password/2, get_password_s/2, get_password_with_authmodule/2, is_user_exists/2, is_user_exists_in_other_modules/3, remove_user/2, remove_user/3, plain_password_required/1, @@ -54,10 +51,13 @@ -include("ejabberd.hrl"). -include("logger.hrl"). +-define(AUTH_CACHE, auth_cache). +-define(SALT_LENGTH, 16). + -record(state, {host_modules = #{} :: map()}). --type scrammed_password() :: {binary(), binary(), binary(), non_neg_integer()}. --type password() :: binary() | scrammed_password(). +-type password() :: binary() | #scram{}. +-type digest_fun() :: fun((binary()) -> binary()). -export_type([password/0]). %%%---------------------------------------------------------------------- @@ -69,24 +69,29 @@ -callback start(binary()) -> any(). -callback stop(binary()) -> any(). --callback plain_password_required() -> boolean(). --callback store_type() -> plain | external | scram. +-callback plain_password_required(binary()) -> boolean(). +-callback store_type(binary()) -> plain | external | scram. -callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}. --callback remove_user(binary(), binary()) -> any(). --callback remove_user(binary(), binary(), binary()) -> any(). +-callback remove_user(binary(), binary()) -> ok | {error, any()}. -callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}. -callback check_password(binary(), binary(), binary(), binary()) -> boolean(). --callback check_password(binary(), binary(), binary(), binary(), binary(), - fun((binary()) -> binary())) -> boolean(). --callback try_register(binary(), binary(), binary()) -> {atomic, atom()} | - {error, atom()}. --callback dirty_get_registered_users() -> [{binary(), binary()}]. --callback get_vh_registered_users(binary()) -> [{binary(), binary()}]. --callback get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}]. --callback get_vh_registered_users_number(binary()) -> number(). --callback get_vh_registered_users_number(binary(), opts()) -> number(). --callback get_password(binary(), binary()) -> false | password(). --callback get_password_s(binary(), binary()) -> password(). +-callback try_register(binary(), binary(), password()) -> ok | {error, atom()}. +-callback get_users(binary(), opts()) -> [{binary(), binary()}]. +-callback count_users(binary(), opts()) -> number(). +-callback get_password(binary(), binary()) -> {ok, password()} | error. +-callback use_cache(binary()) -> boolean(). +-callback cache_nodes(binary()) -> boolean(). + +-optional_callbacks([set_password/3, + remove_user/2, + is_user_exists/2, + 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()}. start_link() -> @@ -99,9 +104,13 @@ init([]) -> HostModules = lists:foldl( fun(Host, Acc) -> Modules = auth_modules(Host), - start(Host, Modules), maps:put(Host, Modules, Acc) end, #{}, ?MYHOSTS), + lists:foreach( + fun({Host, Modules}) -> + start(Host, Modules) + end, maps:to_list(HostModules)), + init_cache(HostModules), {ok, #state{host_modules = HostModules}}. handle_call(_Request, _From, State) -> @@ -112,11 +121,13 @@ handle_cast({host_up, Host}, #state{host_modules = HostModules} = State) -> Modules = auth_modules(Host), start(Host, Modules), NewHostModules = maps:put(Host, Modules, HostModules), + init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast({host_down, Host}, #state{host_modules = HostModules} = State) -> Modules = maps:get(Host, HostModules, []), stop(Host, Modules), NewHostModules = maps:remove(Host, HostModules), + init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast(config_reloaded, #state{host_modules = HostModules} = State) -> NewHostModules = lists:foldl( @@ -127,6 +138,7 @@ handle_cast(config_reloaded, #state{host_modules = HostModules} = State) -> stop(Host, OldModules -- NewModules), maps:put(Host, NewModules, Acc) end, HostModules, ?MYHOSTS), + init_cache(NewHostModules), {noreply, State#state{host_modules = NewHostModules}}; handle_cast(Msg, State) -> ?WARNING_MSG("unexpected cast: ~p", [Msg]), @@ -162,306 +174,266 @@ host_down(Host) -> config_reloaded() -> gen_server:cast(?MODULE, config_reloaded). +-spec plain_password_required(binary()) -> boolean(). 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)). +-spec store_type(binary()) -> plain | scram | external. store_type(Server) -> -%% @doc Check if the user and password can login in server. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% true | false - lists:foldl(fun (_, external) -> external; - (M, scram) -> - case M:store_type() of - external -> external; - _Else -> scram - end; - (M, plain) -> M:store_type() - end, - plain, auth_modules(Server)). + lists:foldl( + fun(_, external) -> external; + (M, scram) -> + case M:store_type(Server) of + external -> external; + _ -> scram + end; + (M, plain) -> + M:store_type(Server) + end, plain, auth_modules(Server)). -spec check_password(binary(), binary(), binary(), binary()) -> boolean(). - check_password(User, AuthzId, Server, Password) -> - case check_password_with_authmodule(User, AuthzId, Server, - Password) - of - {true, _AuthModule} -> true; - false -> false - end. + check_password(User, AuthzId, Server, Password, <<"">>, undefined). -%% @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(), - fun((binary()) -> binary())) -> boolean(). - -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - case check_password_with_authmodule(User, AuthzId, Server, - Password, Digest, DigestGen) - of - {true, _AuthModule} -> true; - false -> false + digest_fun() | undefined) -> boolean(). +check_password(User, AuthzId, Server, Password, Digest, DigestGen) -> + case check_password_with_authmodule( + User, AuthzId, Server, Password, Digest, DigestGen) of + {true, _AuthModule} -> true; + false -> false end. -%% @doc Check if the user and password can login in server. -%% The user can login if at least an authentication method accepts the user -%% and the password. -%% The first authentication method that accepts the credentials is returned. -%% @spec (User::string(), AuthzId::string(), Server::string(), Password::string()) -> -%% {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()}. +-spec check_password_with_authmodule(binary(), binary(), + binary(), binary()) -> false | {true, atom()}. +check_password_with_authmodule(User, AuthzId, Server, Password) -> + check_password_with_authmodule( + User, AuthzId, Server, Password, <<"">>, undefined). -check_password_with_authmodule(User, AuthzId, Server, - Password) -> - check_password_loop(auth_modules(Server), - [User, AuthzId, Server, Password]). - --spec check_password_with_authmodule(binary(), binary(), binary(), binary(), binary(), - fun((binary()) -> binary())) -> false | - {true, atom()}. - -check_password_with_authmodule(User, AuthzId, Server, Password, - Digest, DigestGen) -> - check_password_loop(auth_modules(Server), - [User, AuthzId, Server, Password, Digest, DigestGen]). - -check_password_loop([], _Args) -> false; -check_password_loop([AuthModule | AuthModules], Args) -> - case apply(AuthModule, check_password, Args) of - true -> {true, AuthModule}; - false -> check_password_loop(AuthModules, Args) +-spec check_password_with_authmodule( + binary(), binary(), binary(), binary(), binary(), + digest_fun() | undefined) -> false | {true, atom()}. +check_password_with_authmodule(User, AuthzId, Server, Password, Digest, DigestGen) -> + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + lists:foldl( + fun(Mod, false) -> + case db_check_password( + LUser, AuthzId, LServer, Password, + Digest, DigestGen, Mod) of + true -> {true, Mod}; + false -> false + end; + (_, Acc) -> + Acc + end, false, auth_modules(LServer)); + _ -> + false end. --spec set_password(binary(), binary(), binary()) -> ok | - {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}; +-spec set_password(binary(), binary(), password()) -> ok | {error, atom()}. set_password(User, Server, Password) -> -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed} - lists:foldl(fun (M, {error, _}) -> - M:set_password(User, Server, Password); - (_M, Res) -> Res - end, - {error, not_allowed}, auth_modules(Server)). - --spec try_register(binary(), binary(), binary()) -> {atomic, atom()} | - {error, atom()}. - -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 + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + lists:foldl( + fun(M, {error, _}) -> + db_set_password(LUser, LServer, Password, M); + (_, ok) -> + ok + end, {error, not_allowed}, auth_modules(LServer)); + Err -> + Err end. -%% Registered users list do not include anonymous users logged --spec dirty_get_registered_users() -> [{binary(), binary()}]. +-spec try_register(binary(), binary(), password()) -> ok | {error, atom()}. +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() -> - lists:flatmap(fun (M) -> M:dirty_get_registered_users() - end, - auth_modules()). +-spec get_users() -> [{binary(), binary()}]. +get_users() -> + lists:flatmap( + 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 -get_vh_registered_users(Server) -> - lists:flatmap(fun (M) -> - M:get_vh_registered_users(Server) - end, - auth_modules(Server)). +-spec get_users(binary(), opts()) -> [{binary(), binary()}]. +get_users(Server, Opts) -> + case jid:nameprep(Server) of + error -> []; + LServer -> + 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) -> - lists:flatmap(fun (M) -> - case erlang:function_exported(M, - get_vh_registered_users, - 2) - of - true -> M:get_vh_registered_users(Server, Opts); - false -> M:get_vh_registered_users(Server) - 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 count_users(binary(), opts()) -> non_neg_integer(). +count_users(Server, Opts) -> + case jid:nameprep(Server) of + error -> 0; + LServer -> + lists:sum( + lists:map( + fun(M) -> db_count_users(LServer, Opts, M) end, + auth_modules(LServer))) + end. -spec get_password(binary(), binary()) -> false | password(). - get_password(User, Server) -> - lists:foldl(fun (M, false) -> - M:get_password(User, Server); - (_M, Password) -> Password - end, - false, auth_modules(Server)). + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + case lists:foldl( + fun(M, error) -> db_get_password(LUser, LServer, M); + (_M, Acc) -> Acc + end, error, auth_modules(LServer)) of + {ok, Password} -> + Password; + error -> + false + end; + _ -> + false + end. -spec get_password_s(binary(), binary()) -> password(). - get_password_s(User, Server) -> case get_password(User, Server) of false -> <<"">>; Password -> Password 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()}. - get_password_with_authmodule(User, Server) -> -%% Returns true if the user exists in the DB or if an anonymous user is logged -%% under the given name - lists:foldl(fun (M, {false, _}) -> - {M:get_password(User, Server), M}; - (_M, {Password, AuthModule}) -> {Password, AuthModule} - end, - {false, none}, auth_modules(Server)). + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + case lists:foldl( + fun(M, {error, _}) -> + {db_get_password(LUser, LServer, M), M}; + (_M, Acc) -> + 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(). - is_user_exists(_User, <<"">>) -> false; - is_user_exists(User, Server) -> -%% Check if the user exists in all authentications module except the module -%% passed as parameter -%% @spec (Module::atom(), User, Server) -> true | false | maybe - lists:any(fun (M) -> - case M:is_user_exists(User, Server) of - {error, Error} -> - ?ERROR_MSG("The authentication module ~p returned " - "an error~nwhen checking user ~p in server " - "~p~nError message: ~p", - [M, User, Server, Error]), - false; - Else -> Else + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + lists:any( + fun(M) -> + case db_is_user_exists(LUser, LServer, M) of + {error, _} -> + false; + Else -> + Else end - end, - auth_modules(Server)). + end, auth_modules(LServer)); + _ -> + false + end. -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_loop(auth_modules(Server) - -- [Module], - User, Server). + is_user_exists_in_other_modules_loop( + auth_modules(Server) -- [Module], User, Server). -is_user_exists_in_other_modules_loop([], _User, - _Server) -> +is_user_exists_in_other_modules_loop([], _User, _Server) -> false; -is_user_exists_in_other_modules_loop([AuthModule - | AuthModules], - User, Server) -> - case AuthModule:is_user_exists(User, Server) of - true -> true; - false -> - is_user_exists_in_other_modules_loop(AuthModules, User, - Server); - {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 +is_user_exists_in_other_modules_loop([AuthModule | AuthModules], User, Server) -> + case db_is_user_exists(User, Server, AuthModule) of + true -> + true; + false -> + is_user_exists_in_other_modules_loop(AuthModules, User, Server); + {error, _} -> + maybe end. -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) -> - lists:foreach(fun (M) -> M:remove_user(User, Server) - end, - auth_modules(Server)), - ejabberd_hooks:run(remove_user, jid:nameprep(Server), - [User, Server]), - ok. - -%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error -%% @doc Try to remove user if the provided password is correct. -%% 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(). + case validate_credentials(User, Server) of + {ok, LUser, LServer} -> + lists:foreach( + fun(Mod) -> db_remove_user(LUser, LServer, Mod) end, + auth_modules(LServer)), + ejabberd_hooks:run(remove_user, LServer, [LUser, LServer]); + _Err -> + ok + end. +-spec remove_user(binary(), binary(), password()) -> ok | {error, atom()}. remove_user(User, Server, Password) -> - R = lists:foldl(fun (_M, ok = Res) -> Res; - (M, _) -> M:remove_user(User, Server, Password) - end, - error, auth_modules(Server)), - case R of - ok -> - ejabberd_hooks:run(remove_user, jid:nameprep(Server), - [User, Server]); - _ -> none - end, - R. + case validate_credentials(User, Server, Password) of + {ok, LUser, LServer} -> + case lists:foldl( + fun (_, ok) -> + ok; + (Mod, _) -> + case db_check_password( + LUser, <<"">>, LServer, Password, + <<"">>, undefined, Mod) of + true -> + 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. +-spec entropy(iodata()) -> float(). entropy(B) -> case binary_to_list(B) of "" -> 0.0; @@ -497,15 +469,266 @@ backend_type(Mod) -> _ -> Mod end. +-spec password_format(binary() | global) -> plain | scram. password_format(LServer) -> 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 %%%---------------------------------------------------------------------- --spec auth_modules() -> [module()]. +-spec auth_modules() -> [{binary(), module()}]. 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()]. auth_modules(Server) -> @@ -516,6 +739,65 @@ auth_modules(Server) -> (misc:atom_to_binary(M))/binary>>) || 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) -> ejabberd_auth_mnesia:export(Server). @@ -536,6 +818,10 @@ import(_LServer, {sql, _}, sql, <<"users">>, _) -> -spec opt_type(auth_method) -> fun((atom() | [atom()]) -> [atom()]); (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()]. opt_type(auth_method) -> fun (V) when is_list(V) -> @@ -546,4 +832,20 @@ opt_type(auth_password_format) -> fun (plain) -> plain; (scram) -> scram 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]. diff --git a/src/ejabberd_auth_anonymous.erl b/src/ejabberd_auth_anonymous.erl index 5bb2daed7..b3ae1f6dd 100644 --- a/src/ejabberd_auth_anonymous.erl +++ b/src/ejabberd_auth_anonymous.erl @@ -40,15 +40,9 @@ unregister_connection/3 ]). --export([login/2, set_password/3, check_password/4, - check_password/6, try_register/3, - 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_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]). +-export([login/2, check_password/4, is_user_exists/2, + get_users/2, count_users/2, store_type/1, + plain_password_required/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -139,15 +133,7 @@ unregister_connection(_SID, %% --------------------------------- %% Specific anonymous auth functions %% --------------------------------- - -%% 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) -> +check_password(User, _AuthzId, Server, _Password) -> case ejabberd_auth:is_user_exists_in_other_modules(?MODULE, User, Server) @@ -173,68 +159,20 @@ login(User, Server) -> end end. -%% When anonymous login is enabled, check that the user is permanent before -%% changing its password -set_password(User, Server, _Password) -> - case anonymous_user_exist(User, Server) of - true -> ok; - false -> {error, not_allowed} - end. +get_users(Server, _) -> + [{U, S} || {U, S, _R} <- ejabberd_sm:get_vh_session_list(Server)]. -%% When anonymous login is enabled, check if permanent users are allowed on -%% the server: -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. +count_users(Server, Opts) -> + length(get_users(Server, Opts)). is_user_exists(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. - -plain_password_required() -> false. - -store_type() -> - plain. +store_type(_) -> + external. -spec opt_type(allow_multiple_connection) -> fun((boolean()) -> boolean()); (anonymous_protocol) -> fun((sasl_anon | login_anon | both) -> diff --git a/src/ejabberd_auth_external.erl b/src/ejabberd_auth_external.erl index 5bdd704a0..5b2b26c1a 100644 --- a/src/ejabberd_auth_external.erl +++ b/src/ejabberd_auth_external.erl @@ -32,14 +32,8 @@ -behaviour(ejabberd_auth). -export([start/1, stop/1, set_password/3, check_password/4, - check_password/6, try_register/3, - 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]). + try_register/3, is_user_exists/2, remove_user/2, + store_type/1, plain_password_required/1, opt_type/1]). -include("ejabberd.hrl"). -include("logger.hrl"). @@ -49,275 +43,60 @@ %%%---------------------------------------------------------------------- start(Host) -> Cmd = ejabberd_config:get_option({extauth_program, Host}, "extauth"), - extauth:start(Host, Cmd), - check_cache_last_options(Host), - ejabberd_auth_mnesia:start(Host). + extauth:start(Host, Cmd). stop(Host) -> - extauth:stop(Host), - ejabberd_auth_mnesia:stop(Host). + extauth:stop(Host). -check_cache_last_options(Server) -> - 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. -plain_password_required() -> true. - -store_type() -> external. +store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> false; true -> - case get_cache_option(Server) of - false -> - check_password_extauth(User, AuthzId, Server, Password); - {true, CacheTime} -> - check_password_cache(User, AuthzId, Server, Password, - CacheTime) - end + check_password_extauth(User, AuthzId, Server, Password) end. -check_password(User, AuthzId, Server, Password, _Digest, - _DigestGen) -> - check_password(User, AuthzId, Server, Password). - set_password(User, Server, Password) -> case extauth:set_password(User, Server, Password) of - true -> - set_password_mnesia(User, Server, Password), ok; - _ -> {error, unknown_problem} + true -> ok; + _ -> {error, db_failure} end. try_register(User, Server, Password) -> - case get_cache_option(Server) of - false -> try_register_extauth(User, Server, Password); - {true, _CacheTime} -> - try_register_external_cache(User, Server, Password) - end. + extauth:try_register(User, Server, Password). -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) -> try extauth:is_user_exists(User, Server) of - Res -> Res + Res -> Res catch - _:Error -> {error, Error} + _:Error -> + ?ERROR_MSG("external authentication program failure: ~p", + [Error]), + {error, db_failure} end. remove_user(User, Server) -> case extauth:remove_user(User, Server) of - false -> false; - true -> - case get_cache_option(Server) of - false -> false; - {true, _CacheTime} -> - ejabberd_auth_mnesia:remove_user(User, Server) - end + false -> {error, not_allowed}; + true -> ok 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) -> extauth:check_password(User, Server, Password) andalso 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()) -> false | non_neg_integer()); (extauth_program) -> fun((binary()) -> string()); (atom()) -> [atom()]. 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; (I) when is_integer(I), I >= 0 -> I end; diff --git a/src/ejabberd_auth_ldap.erl b/src/ejabberd_auth_ldap.erl index 8a4532e38..15abebedc 100644 --- a/src/ejabberd_auth_ldap.erl +++ b/src/ejabberd_auth_ldap.erl @@ -37,13 +37,9 @@ handle_cast/2, terminate/2, code_change/3]). -export([start/1, stop/1, start_link/1, set_password/3, - check_password/4, check_password/6, try_register/3, - 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, + check_password/4, is_user_exists/2, + get_users/2, count_users/2, + store_type/1, plain_password_required/1, opt_type/1]). -include("ejabberd.hrl"). @@ -112,9 +108,9 @@ init(Host) -> State#state.password, State#state.tls_options), {ok, State}. -plain_password_required() -> true. +plain_password_required(_) -> true. -store_type() -> external. +store_type(_) -> external. check_password(User, AuthzId, Server, Password) -> if AuthzId /= <<>> andalso AuthzId /= User -> @@ -129,60 +125,34 @@ check_password(User, AuthzId, Server, Password) -> end end. -check_password(User, AuthzId, Server, Password, _Digest, - _DigestGen) -> - check_password(User, AuthzId, Server, Password). - set_password(User, Server, Password) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), case find_user_dn(User, State) of - false -> {error, user_not_found}; + false -> {error, notfound}; DN -> - eldap_pool:modify_passwd(State#state.eldap_id, DN, - Password) + case eldap_pool:modify_passwd(State#state.eldap_id, DN, + Password) of + ok -> ok; + _Err -> {error, db_failure} + end end. -%% @spec (User, Server, Password) -> {error, not_allowed} -try_register(_User, _Server, _Password) -> - {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 +get_users(Server, []) -> + case catch get_users_ldap(Server) of {'EXIT', _} -> []; Result -> Result end. -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). - -get_password(_User, _Server) -> false. - -get_password_s(_User, _Server) -> <<"">>. +count_users(Server, Opts) -> + length(get_users(Server, Opts)). %% @spec (User, Server) -> true | false | {error, Error} is_user_exists(User, Server) -> case catch is_user_exists_ldap(User, Server) of - {'EXIT', Error} -> {error, Error}; + {'EXIT', _Error} -> {error, db_failure}; Result -> Result end. -remove_user(_User, _Server) -> {error, not_allowed}. - -remove_user(_User, _Server, _Password) -> not_allowed. - %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- @@ -199,7 +169,7 @@ check_password_ldap(User, Server, Password) -> end end. -get_vh_registered_users_ldap(Server) -> +get_users_ldap(Server) -> {ok, State} = eldap_utils:get_state(Server, ?MODULE), UIDs = State#state.uids, Eldap_ID = State#state.eldap_id, diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl index 592b9c566..02c22f9d5 100644 --- a/src/ejabberd_auth_mnesia.erl +++ b/src/ejabberd_auth_mnesia.erl @@ -31,15 +31,11 @@ -behaviour(ejabberd_auth). --export([start/1, stop/1, set_password/3, check_password/4, - check_password/6, try_register/3, - dirty_get_registered_users/0, get_vh_registered_users/1, - get_vh_registered_users/2, init_db/0, - 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([start/1, stop/1, set_password/3, try_register/3, + get_users/2, init_db/0, + count_users/2, get_password/2, + remove_user/2, store_type/1, export/1, import/2, + plain_password_required/1, use_cache/1]). -export([need_transform/1, transform/1]). -include("ejabberd.hrl"). @@ -52,8 +48,6 @@ -record(reg_users_counter, {vhost = <<"">> :: binary(), count = 0 :: integer() | '$1'}). --define(SALT_LENGTH, 16). - %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- @@ -67,14 +61,14 @@ stop(_Host) -> init_db() -> ejabberd_mnesia:create(?MODULE, passwd, - [{disc_copies, [node()]}, + [{disc_only_copies, [node()]}, {attributes, record_info(fields, passwd)}]), ejabberd_mnesia:create(?MODULE, reg_users_counter, [{ram_copies, [node()]}, {attributes, record_info(fields, reg_users_counter)}]). update_reg_users_counter_table(Server) -> - Set = get_vh_registered_users(Server), + Set = get_users(Server, []), Size = length(Set), LServer = jid:nameprep(Server), F = fun () -> @@ -83,309 +77,153 @@ update_reg_users_counter_table(Server) -> end, mnesia:sync_dirty(F). -plain_password_required() -> - is_scrammed(). - -store_type() -> - 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 +use_cache(_) -> + case mnesia:table_info(passwd, storage_type) of + disc_only_copies -> true; + _ -> false end. -check_password(User, AuthzId, Server, Password, Digest, - DigestGen) -> - 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 = 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. +plain_password_required(Server) -> + store_type(Server) == scram. + +store_type(Server) -> + ejabberd_auth:password_format(Server). -%% @spec (User::string(), Server::string(), Password::string()) -> -%% ok | {error, invalid_jid} set_password(User, Server, Password) -> - LUser = jid:nodeprep(User), - 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}, + US = {User, Server}, F = fun () -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, LServer, - -1) + mnesia:write(#passwd{us = US, password = Password}) end, - mnesia:transaction(F), - ok. + case mnesia:transaction(F) of + {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 -%% @doc Remove user if the provided password is correct. -remove_user(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - US = {LUser, LServer}, +try_register(User, Server, Password) -> + US = {User, Server}, F = fun () -> case mnesia:read({passwd, US}) of - [#passwd{password = Password}] - when is_binary(Password) -> - mnesia:delete({passwd, US}), - mnesia:dirty_update_counter(reg_users_counter, LServer, - -1), - ok; - [#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 + [] -> + mnesia:write(#passwd{us = US, password = Password}), + mnesia:dirty_update_counter(reg_users_counter, Server, 1), + ok; + [_] -> + {error, exists} end end, case mnesia:transaction(F) of - {atomic, ok} -> ok; - {atomic, Res} -> Res; - _ -> bad_request + {atomic, Res} -> + Res; + {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. need_transform(#passwd{us = {U, S}, password = Pass}) -> if is_binary(Pass) -> - IsScrammed = is_scrammed(), - if IsScrammed -> + case store_type(S) of + scram -> ?INFO_MSG("Passwords in Mnesia table 'passwd' " - "will be SCRAM'ed", []); - true -> - ok - end, - IsScrammed; + "will be SCRAM'ed", []), + true; + plain -> + false + end; is_record(Pass, scram) -> - case is_scrammed() of - true -> - next; - false -> + case store_type(S) of + scram -> + false; + plain -> ?WARNING_MSG("Some passwords were stored in the database " "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 end; 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(#passwd{us = {U, S}, password = Password} = P) when is_binary(Password) -> - case is_scrammed() of - true -> + case store_type(S) of + scram -> case jid:resourceprep(Password) of error -> ?ERROR_MSG("SASLprep failed for password of user ~s@~s", [U, S]), P; _ -> - Scram = password_to_scram(Password), + Scram = ejabberd_auth:password_to_scram(Password), P#passwd{password = Scram} end; - false -> + plain -> P end; transform(#passwd{password = Password} = P) when is_record(Password, scram) -> 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) -> [{passwd, fun(Host, #passwd{us = {LUser, LServer}, password = Password}) diff --git a/src/ejabberd_auth_pam.erl b/src/ejabberd_auth_pam.erl index 9d2fc819b..f865f36f6 100644 --- a/src/ejabberd_auth_pam.erl +++ b/src/ejabberd_auth_pam.erl @@ -30,14 +30,8 @@ -behaviour(ejabberd_auth). --export([start/1, stop/1, set_password/3, check_password/4, - check_password/6, try_register/3, - 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, +-export([start/1, stop/1, check_password/4, + is_user_exists/2, store_type/1, plain_password_required/1, opt_type/1]). start(_Host) -> @@ -46,13 +40,6 @@ start(_Host) -> stop(_Host) -> 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) -> if AuthzId /= <<>> andalso AuthzId /= User -> false; @@ -70,25 +57,6 @@ check_password(User, AuthzId, Host, Password) -> 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) -> Service = get_pam_service(Host), UserInfo = case get_pam_userinfotype(Host) of @@ -97,16 +65,13 @@ is_user_exists(User, Host) -> end, case catch epam:acct_mgmt(Service, UserInfo) of true -> true; - _ -> false + false -> false; + _Err -> {error, db_failure} end. -remove_user(_User, _Server) -> {error, not_allowed}. +plain_password_required(_) -> true. -remove_user(_User, _Server, _Password) -> not_allowed. - -plain_password_required() -> true. - -store_type() -> external. +store_type(_) -> external. %%==================================================================== %% Internal functions diff --git a/src/ejabberd_auth_riak.erl b/src/ejabberd_auth_riak.erl index 74f0f73ca..fccaba102 100644 --- a/src/ejabberd_auth_riak.erl +++ b/src/ejabberd_auth_riak.erl @@ -32,15 +32,10 @@ -behaviour(ejabberd_auth). %% External exports --export([start/1, stop/1, set_password/3, check_password/4, - check_password/6, try_register/3, - 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, export/1, import/2, - plain_password_required/0]). +-export([start/1, stop/1, set_password/3, try_register/3, + get_users/2, count_users/2, + get_password/2, remove_user/2, store_type/1, export/1, import/2, + plain_password_required/1]). -export([passwd_schema/0]). -include("ejabberd.hrl"). @@ -49,258 +44,65 @@ -record(passwd, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', password = <<"">> :: binary() | scram() | '_'}). --define(SALT_LENGTH, 16). - start(_Host) -> ok. stop(_Host) -> ok. -plain_password_required() -> - case is_scrammed() of - false -> false; - true -> true - end. +plain_password_required(Server) -> + store_type(Server) == scram. -store_type() -> - case is_scrammed() of - false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM - true -> scram %% allows: PLAIN SCRAM - end. +store_type(Server) -> + ejabberd_auth:password_format(Server). passwd_schema() -> {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) -> - LUser = jid:nodeprep(User), - 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 -> - Password2 = case is_scrammed() and is_binary(Password) - of - true -> password_to_scram(Password); - false -> Password - end, - ok = ejabberd_riak:put(#passwd{us = US, password = Password2}, - passwd_schema(), - [{'2i', [{<<"host">>, LServer}]}]) + ejabberd_riak:put(#passwd{us = {User, Server}, password = Password}, + passwd_schema(), + [{'2i', [{<<"host">>, Server}]}]). + +try_register(User, Server, Password) -> + US = {User, Server}, + case ejabberd_riak:get(passwd, passwd_schema(), US) of + {error, notfound} -> + ejabberd_riak:put(#passwd{us = US, password = Password}, + passwd_schema(), + [{'2i', [{<<"host">>, Server}]}]); + {ok, _} -> + {error, exists}; + {error, _} = Err -> + Err end. -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 -> - 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 +get_users(Server, _) -> + case ejabberd_riak:get_keys_by_index(passwd, <<"host">>, Server) of {ok, Users} -> Users; _ -> [] end. -get_vh_registered_users(Server, _) -> - get_vh_registered_users(Server). - -get_vh_registered_users_number(Server) -> - LServer = jid:nameprep(Server), - case ejabberd_riak:count_by_index(passwd, <<"host">>, LServer) of +count_users(Server, _) -> + case ejabberd_riak:count_by_index(passwd, <<"host">>, Server) of {ok, N} -> N; _ -> 0 end. -get_vh_registered_users_number(Server, _) -> - get_vh_registered_users_number(Server). - get_password(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) -> - {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 + case ejabberd_riak:get(passwd, passwd_schema(), {User, Server}) of + {ok, Password} -> + {ok, Password}; + {error, _} -> + error end. remove_user(User, Server) -> - LUser = jid:nodeprep(User), - 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. + ejabberd_riak:delete(passwd, {User, Server}). export(_Server) -> [{passwd, diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 8514b9cf1..d682634f0 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -31,14 +31,9 @@ -behaviour(ejabberd_auth). --export([start/1, stop/1, set_password/3, check_password/4, - check_password/6, try_register/3, - 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, +-export([start/1, stop/1, set_password/3, try_register/3, + get_users/2, count_users/2, get_password/2, + remove_user/2, store_type/1, plain_password_required/1, convert_to_scram/1]). -include("ejabberd.hrl"). @@ -54,397 +49,82 @@ start(_Host) -> ok. stop(_Host) -> ok. -plain_password_required() -> - case is_scrammed() of - false -> false; - true -> true - end. +plain_password_required(Server) -> + store_type(Server) == scram. -store_type() -> - case is_scrammed() of - false -> plain; %% allows: PLAIN DIGEST-MD5 SCRAM - true -> scram %% allows: PLAIN SCRAM - end. +store_type(Server) -> + ejabberd_auth:password_format(Server). -%% @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) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - LPassword = jid:resourceprep(Password), - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - (LUser == <<>>) or (LServer == <<>>) -> - {error, invalid_jid}; - LPassword == error -> - {error, invalid_password}; - true -> - case is_scrammed() of - true -> - Scram = password_to_scram(Password), - case catch sql_queries:set_password_scram_t( - 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 + Res = if is_record(Password, scram) -> + sql_queries:set_password_scram_t( + Server, User, + Password#scram.storedkey, Password#scram.serverkey, + Password#scram.salt, Password#scram.iterationcount); + true -> + sql_queries:set_password_t(Server, User, Password) + end, + case Res of + {atomic, _} -> + ok; + {aborted, Reason} -> + ?ERROR_MSG("failed to write to SQL table: ~p", [Reason]), + {error, db_failure} end. -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, invalid_jid} try_register(User, Server, Password) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - LPassword = jid:resourceprep(Password), - if (LUser == error) or (LServer == error) -> - {error, invalid_jid}; - (LUser == <<>>) or (LServer == <<>>) -> - {error, invalid_jid}; - LPassword == error and not is_record(Password, scram) -> - {error, invalid_password}; - true -> - case is_scrammed() of - 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 + Res = if is_record(Password, scram) -> + sql_queries:add_user_scram( + Server, User, + Password#scram.storedkey, Password#scram.serverkey, + Password#scram.salt, Password#scram.iterationcount); + true -> + sql_queries:add_user(Server, User, Password) + end, + case Res of + {updated, 1} -> ok; + _ -> {error, exists} end. -dirty_get_registered_users() -> - Servers = ejabberd_config:get_vh_by_auth_method(sql), - lists:flatmap(fun (Server) -> - get_vh_registered_users(Server) - 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 +get_users(Server, Opts) -> + case sql_queries:list_users(Server, Opts) of + {selected, Res} -> + [{U, Server} || {U} <- Res]; + _ -> [] end. -get_vh_registered_users(Server, Opts) -> - case jid:nameprep(Server) of - error -> []; - <<>> -> []; - LServer -> - 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 +count_users(Server, Opts) -> + case sql_queries:users_number(Server, Opts) of + {selected, [{Res}]} -> + Res; + _Other -> 0 end. get_password(User, Server) -> - 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 -> - case catch sql_queries:get_password_scram( - LServer, LUser) of - {selected, - [{StoredKey, ServerKey, Salt, IterationCount}]} -> - {misc:decode_base64(StoredKey), - 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 + case sql_queries:get_password_scram(Server, User) of + {selected, [{Password, <<>>, <<>>, 0}]} -> + {ok, Password}; + {selected, [{StoredKey, ServerKey, Salt, IterationCount}]} -> + {ok, #scram{storedkey = StoredKey, + serverkey = ServerKey, + salt = Salt, + iterationcount = IterationCount}}; + {selected, []} -> + error; + Err -> + ?ERROR_MSG("Failed to read password for user ~s@~s: ~p", + [User, Server, Err]), + error 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) -> - LServer = jid:nameprep(Server), - LUser = jid:nodeprep(User), - if (LUser == error) or (LServer == error) -> - error; - (LUser == <<>>) or (LServer == <<>>) -> - error; - true -> - 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 + case sql_queries:del_user(Server, User) of + {updated, _} -> + ok; + Err -> + ?ERROR_MSG("failed to delete user ~s@~s: ~p", + [User, Server, Err]), + {error, db_failure} end. -define(BATCH_SIZE, 1000). @@ -485,7 +165,7 @@ convert_to_scram(Server) -> "password of user ~s@~s", [LUser, LServer]); _ -> - Scram = password_to_scram(Password), + Scram = ejabberd_auth:password_to_scram(Password), set_password_scram_t( LUser, Scram#scram.storedkey, diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index aea280591..ecb4908a7 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -135,7 +135,7 @@ export_host(Dir, FnH, Host) -> {ok, Fd} -> print(Fd, make_piefxis_xml_head()), 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 ok -> print(Fd, make_piefxis_host_tail()), @@ -402,9 +402,9 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els}, stop("Invalid 'user': ~s", [Name]); LUser -> case ejabberd_auth:try_register(LUser, LServer, Pass) of - {atomic, _} -> + ok -> process_user_els(Els, State#state{user = LUser}); - Err -> + {error, Err} -> stop("Failed to create user '~s': ~p", [Name, Err]) end end. diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 5050ac095..6fdac1971 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -1370,7 +1370,7 @@ list_vhosts2(Lang, Hosts) -> OnlineUsers = length(ejabberd_sm:get_vh_session_list(Host)), RegisteredUsers = - ejabberd_auth:get_vh_registered_users_number(Host), + ejabberd_auth:count_users(Host), ?XE(<<"tr">>, [?XE(<<"td">>, [?AC(<<"../server/", Host/binary, @@ -1388,7 +1388,7 @@ list_vhosts2(Lang, Hosts) -> list_users(Host, Query, Lang, URLFunc) -> 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]), FUsers = case length(SUsers) of N when N =< 100 -> @@ -1469,7 +1469,7 @@ list_users_parse_query(Query, Host) -> end. 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]), [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), N1 = binary_to_integer(S1), @@ -1565,7 +1565,7 @@ su_to_list({Server, User}) -> get_stats(global, Lang) -> OnlineUsers = ejabberd_sm:connected_users_number(), RegisteredUsers = lists:foldl(fun (Host, Total) -> - ejabberd_auth:get_vh_registered_users_number(Host) + ejabberd_auth:count_users(Host) + Total end, 0, ?MYHOSTS), @@ -1589,7 +1589,7 @@ get_stats(Host, Lang) -> OnlineUsers = length(ejabberd_sm:get_vh_session_list(Host)), RegisteredUsers = - ejabberd_auth:get_vh_registered_users_number(Host), + ejabberd_auth:count_users(Host), [?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, diff --git a/src/extauth.erl b/src/extauth.erl index 54f44953c..70f32bf8f 100644 --- a/src/extauth.erl +++ b/src/extauth.erl @@ -83,7 +83,7 @@ try_register(User, Server, Password) -> case call_port(Server, [<<"tryregister">>, User, Server, Password]) of - true -> {atomic, ok}; + true -> ok; false -> {error, not_allowed} end. diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index 133b2fd29..982772af0 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -810,14 +810,14 @@ histogram([], _Integral, _Current, Count, Hist) -> delete_old_users(Days) -> %% 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), {ok, io_lib:format("Deleted ~p users: ~p", [N, UR])}. delete_old_users_vhost(Host, Days) -> %% 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), {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). push_alltoall(S, G) -> - Users = ejabberd_auth:get_vh_registered_users(S), + Users = ejabberd_auth:get_users(S), Users2 = build_list_users(G, Users, []), subscribe_all(Users2), ok. @@ -1499,14 +1499,14 @@ stats(Name) -> case Name of <<"uptimeseconds">> -> trunc(element(1, erlang:statistics(wall_clock))/1000); <<"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()); <<"onlineusers">> -> length(ejabberd_sm:dirty_get_sessions_list()) end. stats(Name, Host) -> 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)) end. diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 35d179c0c..379cd3ca0 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -633,7 +633,7 @@ announce_all(#message{to = To} = Packet) -> Dest = jid:make(User, Server), ejabberd_router:route( 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) -> Local = jid:make(To#jid.server), @@ -642,7 +642,7 @@ announce_all_hosts_all(#message{to = To} = Packet) -> Dest = jid:make(User, Server), ejabberd_router:route( 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_online1(ejabberd_sm:get_vh_session_list(To#jid.lserver), diff --git a/src/mod_configure.erl b/src/mod_configure.erl index b9db1e511..f3eb496d8 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -547,7 +547,7 @@ get_local_items({_, Host}, [<<"all users">>], _Server, get_local_items({_, Host}, [<<"all users">>, <<$@, Diap/binary>>], _Server, _Lang) -> - Users = ejabberd_auth:get_vh_registered_users(Host), + Users = ejabberd_auth:get_users(Host), SUsers = lists:sort([{S, U} || {U, S} <- Users]), try [S1, S2] = ejabberd_regexp:split(Diap, <<"-">>), @@ -661,7 +661,7 @@ get_online_vh_users(Host) -> end. get_all_vh_users(Host) -> - case catch ejabberd_auth:get_vh_registered_users(Host) + case catch ejabberd_auth:get_users(Host) of {'EXIT', _Reason} -> []; Users -> @@ -1194,7 +1194,7 @@ get_form(_Host, ?NS_ADMINL(<<"user-stats">>), Lang) -> required = true}]}}; get_form(Host, ?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, #xdata{type = form, fields = [?HFIELD(), diff --git a/src/mod_register.erl b/src/mod_register.erl index af4ba02f4..5167370c1 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -323,7 +323,7 @@ try_register(User, Server, Password, SourceRaw, Lang) -> case ejabberd_auth:try_register(User, Server, Password) of - {atomic, ok} -> + ok -> send_welcome_message(JID), send_registration_notifications( ?MODULE, JID, Source), @@ -331,7 +331,7 @@ try_register(User, Server, Password, SourceRaw, Lang) -> Error -> remove_timeout(Source), case Error of - {atomic, exists} -> + {error, exists} -> Txt = <<"User already exists">>, {error, xmpp:err_conflict(Txt, Lang)}; {error, invalid_jid} -> diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index cd19bb65a..d5e4112c3 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -510,8 +510,8 @@ register_account2(Username, Host, Password) -> case ejabberd_auth:try_register(Username, Host, Password) of - {atomic, Res} -> - {success, Res, {Username, Host, Password}}; + ok -> + {success, ok, {Username, Host, Password}}; Other -> Other end. diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 67b5870a2..c25b13f66 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -450,7 +450,7 @@ get_online_users(Host) -> get_group_users(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), 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 -> [] end ++ @@ -462,7 +462,7 @@ get_group_users(Host1, Group1) -> get_group_users(Host, Group, GroupOpts) -> case proplists:get_value(all_users, GroupOpts, false) of - true -> ejabberd_auth:get_vh_registered_users(Host); + true -> ejabberd_auth:get_users(Host); false -> [] end ++ diff --git a/src/mod_stats.erl b/src/mod_stats.erl index 92a6627c5..2bdbdbd33 100644 --- a/src/mod_stats.erl +++ b/src/mod_stats.erl @@ -119,7 +119,7 @@ get_local_stat(Server, [], Name) get_local_stat(Server, [], Name) when Name == <<"users/total">> -> case catch - ejabberd_auth:get_vh_registered_users_number(Server) + ejabberd_auth:count_users(Server) of {'EXIT', _Reason} -> ?STATERR(500, <<"Internal Server Error">>); @@ -134,7 +134,7 @@ get_local_stat(_Server, [], Name) get_local_stat(_Server, [], Name) when Name == <<"users/all-hosts/total">> -> NumUsers = lists:foldl(fun (Host, Total) -> - ejabberd_auth:get_vh_registered_users_number(Host) + ejabberd_auth:count_users(Host) + Total end, 0, ?MYHOSTS), diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 17bbc1d0a..f575724c6 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -129,7 +129,7 @@ convert_data(Host, "accounts", User, [Data]) -> Pass end, case ejabberd_auth:try_register(User, Host, Password) of - {atomic, ok} -> + ok -> ok; Err -> ?ERROR_MSG("failed to register user ~s@~s: ~p", diff --git a/test/suite.erl b/test/suite.erl index 76be5f806..6bd96002a 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -468,8 +468,7 @@ re_register(Config) -> User = ?config(user, Config), Server = ?config(server, Config), Pass = ?config(password, Config), - {atomic, ok} = ejabberd_auth:try_register(User, Server, Pass), - ok. + ok = ejabberd_auth:try_register(User, Server, Pass). 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]);