%%%---------------------------------------------------------------------- %%% File : ejabberd_auth.erl %%% Author : Alexey Shchepin %%% Purpose : Authentification %%% Created : 23 Nov 2002 by Alexey Shchepin %%% %%% %%% ejabberd, Copyright (C) 2002-2009 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as %%% published by the Free Software Foundation; either version 2 of the %%% License, or (at your option) any later version. %%% %%% This program is distributed in the hope that it will be useful, %%% but WITHOUT ANY WARRANTY; without even the implied warranty of %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %%% General Public License for more details. %%% %%% You should have received a copy of the GNU General Public License %%% along with this program; if not, write to the Free Software %%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA %%% 02111-1307 USA %%% %%%---------------------------------------------------------------------- %% TODO: Use the functions in ejabberd auth to add and remove users. -module(ejabberd_auth). -author('alexey@process-one.net'). %% External exports -export([start/0, set_password/3, check_password/3, check_password/5, check_password_with_authmodule/3, check_password_with_authmodule/5, 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, 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 ]). -export([auth_modules/1]). -include("ejabberd.hrl"). %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- start() -> lists:foreach( fun(Host) -> lists:foreach( fun(M) -> M:start(Host) end, auth_modules(Host)) end, ?MYHOSTS). plain_password_required(Server) -> lists:any( fun(M) -> M:plain_password_required() end, auth_modules(Server)). %% @doc Check if the user and password can login in server. %% @spec (User::string(), Server::string(), Password::string()) -> %% true | false check_password(User, Server, Password) -> lists:any( fun(M) -> M:check_password(User, Server, Password) end, auth_modules(Server)). %% @doc Check if the user and password can login in server. %% @spec (User::string(), Server::string(), Password::string(), %% StreamID::string(), Digest::string()) -> %% true | false check_password(User, Server, Password, StreamID, Digest) -> lists:any( fun(M) -> M:check_password(User, Server, Password, StreamID, Digest) end, auth_modules(Server)). %% @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(), Server::string(), Password::string()) -> %% {true, AuthModule} | false %% where %% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external %% | ejabberd_auth_internal | ejabberd_auth_ldap %% | ejabberd_auth_odbc | ejabberd_auth_pam check_password_with_authmodule(User, Server, Password) -> Res = lists:dropwhile( fun(M) -> not apply(M, check_password, [User, Server, Password]) end, auth_modules(Server)), case Res of [] -> false; [AuthMod | _] -> {true, AuthMod} end. check_password_with_authmodule(User, Server, Password, StreamID, Digest) -> Res = lists:dropwhile( fun(M) -> not apply(M, check_password, [User, Server, Password, StreamID, Digest]) end, auth_modules(Server)), case Res of [] -> false; [AuthMod | _] -> {true, AuthMod} end. %% @spec (User::string(), Server::string(), Password::string()) -> %% ok | {error, ErrorType} %% where ErrorType = empty_password | not_allowed | invalid_jid set_password(_User, _Server, "") -> %% We do not allow empty password {error, empty_password}; set_password(User, Server, Password) -> lists:foldl( fun(M, {error, _}) -> M:set_password(User, Server, Password); (_M, Res) -> Res end, {error, not_allowed}, auth_modules(Server)). %% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed} try_register(_User, _Server, "") -> %% We do not allow empty password {error, not_allowed}; try_register(User, Server, Password) -> case is_user_exists(User,Server) of true -> {atomic, exists}; false -> case lists:member(jlib:nameprep(Server), ?MYHOSTS) of true -> Res = lists:foldl( fun(_M, {atomic, ok} = Res) -> Res; (M, _) -> M:try_register(User, Server, Password) end, {error, not_allowed}, auth_modules(Server)), case Res of {atomic, ok} -> ejabberd_hooks:run(register_user, Server, [User, Server]), {atomic, ok}; _ -> Res end; false -> {error, not_allowed} end end. %% Registered users list do not include anonymous users logged dirty_get_registered_users() -> lists:flatmap( fun(M) -> M:dirty_get_registered_users() end, auth_modules()). %% 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)). get_vh_registered_users(Server, Opts) -> lists:flatmap( fun(M) -> M:get_vh_registered_users(Server, Opts) 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))). get_vh_registered_users_number(Server, Opts) -> 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))). %% @doc Get the password of the user. %% @spec (User::string(), Server::string()) -> Password::string() get_password(User, Server) -> lists:foldl( fun(M, false) -> M:get_password(User, Server); (_M, Password) -> Password end, false, auth_modules(Server)). 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} get_password_with_authmodule(User, Server) -> lists:foldl( fun(M, {false, _}) -> {M:get_password(User, Server), M}; (_M, {Password, AuthModule}) -> {Password, AuthModule} end, {false, none}, auth_modules(Server)). %% Returns true if the user exists in the DB or if an anonymous user is logged %% under the given name is_user_exists(User, Server) -> lists:any( fun(M) -> M:is_user_exists(User, Server) end, auth_modules(Server)). %% Check if the user exists in all authentications module except the module %% passed as parameter is_user_exists_in_other_modules(Module, User, Server) -> lists:any( fun(M) -> M:is_user_exists(User, Server) end, auth_modules(Server)--[Module]). %% @spec (User, Server) -> ok | error | {error, not_allowed} %% @doc Remove user. %% Note: it may return ok even if there was some problem removing the user. remove_user(User, Server) -> R = lists:foreach( fun(M) -> M:remove_user(User, Server) end, auth_modules(Server)), case R of ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]); _ -> none end, R. %% @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. 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, jlib:nameprep(Server), [User, Server]); _ -> none end, R. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- %% Return the lists of all the auth modules actually used in the %% configuration auth_modules() -> lists:usort( lists:flatmap( fun(Server) -> auth_modules(Server) end, ?MYHOSTS)). %% Return the list of authenticated modules for a given host auth_modules(Server) -> LServer = jlib:nameprep(Server), Method = ejabberd_config:get_local_option({auth_method, LServer}), Methods = if Method == undefined -> []; is_list(Method) -> Method; is_atom(Method) -> [Method] end, [list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods].