From 826adacf1154546088c13ca751635b25b25d236f Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 10 Feb 2022 10:23:31 +0100 Subject: [PATCH 1/4] Calculate a list of auth modules once on creds initialization --- src/auth/ejabberd_auth.erl | 13 ++++++++++--- src/mongoose_credentials.erl | 13 ++++++++++--- test/auth_jwt_SUITE.erl | 4 ++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/auth/ejabberd_auth.erl b/src/auth/ejabberd_auth.erl index abbc75866b..f4a276bbe0 100644 --- a/src/auth/ejabberd_auth.erl +++ b/src/auth/ejabberd_auth.erl @@ -53,7 +53,8 @@ -export([check_digest/4]). -export([auth_modules/1, - auth_methods/1]). + auth_methods/1, + auth_modules_for_host_type/1]). %% Library functions for reuse in ejabberd_auth_* modules -export([authorize_with_check_password/2]). @@ -132,7 +133,6 @@ supports_sasl_module(HostType, SASLModule) -> -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()} | {error, not_authorized}. authorize(Creds) -> - HostType = mongoose_credentials:host_type(Creds), F = fun(Mod, {_CurResult, CurCreds}) -> case mongoose_gen_auth:authorize(Mod, CurCreds) of {ok, NewCreds} -> @@ -143,7 +143,7 @@ authorize(Creds) -> end end, Opts = #{default => {not_authorized, Creds}, metric => authorize}, - case call_auth_modules_for_host_type(HostType, F, Opts) of + case call_auth_modules_with_creds(Creds, F, Opts) of Res = {ok, _Creds} -> Res; {not_authorized, _Creds} -> {error, not_authorized} end. @@ -508,6 +508,13 @@ call_auth_modules_for_host_type(HostType, F, Opts) -> Modules = auth_modules_for_host_type(HostType), call_auth_modules(Modules, F, Opts). +-spec call_auth_modules_with_creds(mongoose_credentials:t(), + mod_fun() | mod_fold_fun(), call_opts()) -> + mod_res() | [mod_res()]. +call_auth_modules_with_creds(Creds, F, Opts) -> + Modules = mongoose_credentials:auth_modules(Creds), + call_auth_modules(Modules, F, Opts). + %% @doc Perform a map or a fold operation with function F over the provided Modules -spec call_auth_modules([authmodule()], mod_fun() | mod_fold_fun(), call_opts()) -> mod_res() | [mod_res()]. diff --git a/src/mongoose_credentials.erl b/src/mongoose_credentials.erl index bb5ac75a0b..0fedc48c1b 100644 --- a/src/mongoose_credentials.erl +++ b/src/mongoose_credentials.erl @@ -3,6 +3,7 @@ -export([new/2, lserver/1, host_type/1, + auth_modules/1, get/2, get/3, set/3, extend/2, @@ -10,7 +11,7 @@ -export_type([t/0]). --record(mongoose_credentials, {lserver, host_type, registry = [], extra = []}). +-record(mongoose_credentials, {lserver, host_type, registry = [], extra = [], modules}). -type auth_event() :: any(). @@ -22,11 +23,14 @@ registry :: [{ejabberd_gen_auth:t(), auth_event()}], %% These values are dependent on the ejabberd_auth backend in use. %% Each backend may require different values to be present. - extra :: [proplists:property()] }. + extra :: [proplists:property()], + modules :: [ejabberd_auth:authmodule()] }. -spec new(jid:lserver(), binary()) -> mongoose_credentials:t(). new(LServer, HostType) when is_binary(LServer), is_binary(HostType) -> - #mongoose_credentials{lserver = LServer, host_type = HostType}. + Modules = ejabberd_auth:auth_modules_for_host_type(HostType), + #mongoose_credentials{lserver = LServer, host_type = HostType, + modules = Modules}. -spec host_type(t()) -> mongooseim:host_type(). host_type(#mongoose_credentials{host_type = HostType}) -> HostType. @@ -34,6 +38,9 @@ host_type(#mongoose_credentials{host_type = HostType}) -> HostType. -spec lserver(t()) -> jid:lserver(). lserver(#mongoose_credentials{lserver = S}) -> S. +-spec auth_modules(t()) -> [ejabberd_auth:authmodule()]. +auth_modules(#mongoose_credentials{modules = Modules}) -> Modules. + %% @doc Calls erlang:error/2 when Key is not found! -spec get(t(), Key) -> Value when Key :: any(), diff --git a/test/auth_jwt_SUITE.erl b/test/auth_jwt_SUITE.erl index fb10f4a410..f7b3df781b 100644 --- a/test/auth_jwt_SUITE.erl +++ b/test/auth_jwt_SUITE.erl @@ -45,10 +45,14 @@ suite() -> init_per_suite(Config) -> application:ensure_all_started(jid), + meck:new(ejabberd_auth, [no_link, passthrough]), + meck:expect(ejabberd_auth, auth_modules_for_host_type, + fun(_) -> [] end), Config. end_per_suite(Config) -> unset_auth_opts(), + meck:unload(ejabberd_auth), Config. init_per_group(public_key, Config) -> From fce8eb81ad302b63bd603d408d4cce6980ca28a3 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 10 Feb 2022 13:11:40 +0100 Subject: [PATCH 2/4] Fix types for c2s options --- include/ejabberd_c2s.hrl | 3 ++- src/ejabberd_c2s.erl | 28 ++++++++++++++-------------- src/ejabberd_listener.erl | 4 ++++ src/ejabberd_socket.erl | 8 ++++---- src/mongoose_credentials.erl | 11 +++++++++-- src/mongoose_listener_config.erl | 2 +- src/mongoose_tcp_listener.erl | 8 ++++---- 7 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/ejabberd_c2s.hrl b/include/ejabberd_c2s.hrl index 39b076476c..b556a1251f 100644 --- a/include/ejabberd_c2s.hrl +++ b/include/ejabberd_c2s.hrl @@ -78,7 +78,8 @@ csi_buffer = [] :: [mongoose_acc:t()], hibernate_after = 0 :: non_neg_integer(), replaced_pids = [] :: [{MonitorRef :: reference(), ReplacedPid :: pid()}], - handlers = #{} :: #{ term() => {module(), atom(), term()} } + handlers = #{} :: #{ term() => {module(), atom(), term()} }, + cred_opts :: mongoose_credentials:opts() }). -type aux_key() :: atom(). -type aux_value() :: any(). diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 3eee329d70..e3c7d14004 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -78,24 +78,27 @@ -export_type([broadcast/0, packet/0, state/0]). -type packet() :: {jid:jid(), jid:jid(), exml:element()}. +-type sock_data() :: term(). +-type start_result() :: {error, _} + | {ok, undefined | pid()} + | {ok, undefined | pid(), _}. %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- --spec start(_, list()) --> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. +-spec start(sock_data(), ejabberd_listener:opts()) -> start_result(). start(SockData, Opts) -> ?SUPERVISOR_START(SockData, Opts). +-spec start_link(sock_data(), ejabberd_listener:opts()) -> start_result(). start_link(SockData, Opts) -> - p1_fsm_old:start_link( - ejabberd_c2s, [SockData, Opts], ?FSMOPTS ++ fsm_limit_opts(Opts)). + p1_fsm_old:start_link(ejabberd_c2s, {SockData, Opts}, + ?FSMOPTS ++ fsm_limit_opts(Opts)). socket_type() -> xml_stream. - %% @doc Return Username, Resource and presence information get_presence(FsmRef) -> p1_fsm_old:sync_send_all_state_event(FsmRef, get_presence, 1000). @@ -174,14 +177,9 @@ run_remote_hook_after(Delay, Pid, HandlerName, Args) -> %%% Callback functions from gen_fsm %%%---------------------------------------------------------------------- -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, StateName, StateData} | -%% {ok, StateName, StateData, Timeout} | -%% ignore | -%% {stop, StopReason} -%%---------------------------------------------------------------------- -init([{SockMod, Socket}, Opts]) -> +-spec init({sock_data(), ejabberd_listener:opts()}) -> + {stop, normal} | {ok, wait_for_stream, state(), non_neg_integer()}. +init({{SockMod, Socket}, Opts}) -> Access = case lists:keyfind(access, 1, Opts) of {_, A} -> A; _ -> all @@ -238,6 +236,7 @@ init([{SockMod, Socket}, Opts]) -> false -> Socket end, SocketMonitor = mongoose_transport:monitor(SockMod, Socket1), + CredOpts = mongoose_credentials:make_opts(Opts), {ok, wait_for_stream, #state{server = ?MYNAME, socket = Socket1, sockmod = SockMod, @@ -254,7 +253,8 @@ init([{SockMod, Socket}, Opts]) -> shaper = Shaper, ip = IP, lang = ?MYLANG, - hibernate_after= HibernateAfter + hibernate_after= HibernateAfter, + cred_opts = CredOpts }, ?C2S_OPEN_TIMEOUT} end. diff --git a/src/ejabberd_listener.erl b/src/ejabberd_listener.erl index fad3941620..93ca4afd73 100644 --- a/src/ejabberd_listener.erl +++ b/src/ejabberd_listener.erl @@ -38,6 +38,10 @@ -export([format_error/1, socket_error/6]). -ignore_xref([start_link/0, init/1, start_listener/1, stop_listener/1]). +-export_type([opts/0, mod/0]). + +-type opts() :: list(). +-type mod() :: module(). -include("mongoose.hrl"). diff --git a/src/ejabberd_socket.erl b/src/ejabberd_socket.erl index e42d349ff7..0b1da51f6b 100644 --- a/src/ejabberd_socket.erl +++ b/src/ejabberd_socket.erl @@ -66,8 +66,8 @@ %% Function: %% Description: %%-------------------------------------------------------------------- --spec start(atom() | tuple(), ejabberd:sockmod(), - Socket :: port(), Opts :: [{atom(), _}]) -> ok. +-spec start(ejabberd_listener:mod(), ejabberd:sockmod(), + Socket :: port(), Opts :: ejabberd_listener:opts()) -> ok. start(Module, SockMod, Socket, Opts) -> case Module:socket_type() of xml_stream -> @@ -78,8 +78,8 @@ start(Module, SockMod, Socket, Opts) -> start_raw_stream(Module, SockMod, Socket, Opts) end. --spec start_raw_stream(atom() | tuple(), ejabberd:sockmod(), - Socket :: port(), Opts :: [{atom(), _}]) -> ok. +-spec start_raw_stream(ejabberd_listener:mod(), ejabberd:sockmod(), + Socket :: port(), Opts :: ejabberd_listener:opts()) -> ok. start_raw_stream(Module, SockMod, Socket, Opts) -> case Module:start({SockMod, Socket}, Opts) of {ok, Pid} -> diff --git a/src/mongoose_credentials.erl b/src/mongoose_credentials.erl index 0fedc48c1b..c653fa605b 100644 --- a/src/mongoose_credentials.erl +++ b/src/mongoose_credentials.erl @@ -1,6 +1,7 @@ -module(mongoose_credentials). --export([new/2, +-export([make_opts/1, + new/2, lserver/1, host_type/1, auth_modules/1, @@ -9,7 +10,7 @@ extend/2, register/3]). --export_type([t/0]). +-export_type([t/0, opts/0]). -record(mongoose_credentials, {lserver, host_type, registry = [], extra = [], modules}). @@ -26,6 +27,12 @@ extra :: [proplists:property()], modules :: [ejabberd_auth:authmodule()] }. +-type opts() :: #{}. + +-spec make_opts(ejabberd_listener:opts()) -> opts(). +make_opts(Opts) -> + #{}. + -spec new(jid:lserver(), binary()) -> mongoose_credentials:t(). new(LServer, HostType) when is_binary(LServer), is_binary(HostType) -> Modules = ejabberd_auth:auth_modules_for_host_type(HostType), diff --git a/src/mongoose_listener_config.erl b/src/mongoose_listener_config.erl index 84d40cce25..df16b1a7f2 100644 --- a/src/mongoose_listener_config.erl +++ b/src/mongoose_listener_config.erl @@ -53,7 +53,7 @@ verify_unique_listeners(Listeners) -> end. %% @doc Convert listener configuration to options that can be passed to listener modules --spec prepare_opts(listener()) -> proplists:proplist(). +-spec prepare_opts(listener()) -> ejabberd_listener:opts(). prepare_opts(Listener) -> lists:flatmap(fun prepare_opt/1, maps:to_list(Listener)). diff --git a/src/mongoose_tcp_listener.erl b/src/mongoose_tcp_listener.erl index bf99570d44..5bf8894ef0 100644 --- a/src/mongoose_tcp_listener.erl +++ b/src/mongoose_tcp_listener.erl @@ -76,16 +76,16 @@ init({Id, Module, Opts, SockOpts, Port, IPS}) -> %%-------------------------------------------------------------------- -spec start_accept_loop(Socket :: port(), - Module :: atom(), - Opts :: [any(), ...]) -> {ok, pid()}. + Module :: ejabberd_listener:mod(), + Opts :: ejabberd_listener:opts()) -> {ok, pid()}. start_accept_loop(ListenSock, Module, Opts) -> ProxyProtocol = proplists:get_value(proxy_protocol, Opts, false), Pid = proc_lib:spawn_link(?MODULE, accept_loop, [ListenSock, Module, Opts, ProxyProtocol]), {ok, Pid}. -spec accept_loop(Socket :: port(), - Module :: atom(), - Opts :: [any(), ...], + Module :: ejabberd_listener:mod(), + Opts :: ejabberd_listener:opts(), ProxyProtocol :: boolean()) -> no_return(). accept_loop(ListenSocket, Module, Opts, ProxyProtocol) -> case do_accept(ListenSocket, ProxyProtocol) of From 6caaae5711aeca018cfdb711090b9bec307ed87e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Fri, 11 Feb 2022 16:30:40 +0100 Subject: [PATCH 3/4] Introduce allowed_auth_methods for listeners --- big_tests/default.spec | 1 + big_tests/dynamic_domains.spec | 1 + .../tests/auth_methods_for_c2s_SUITE.erl | 82 +++++++++++++++++++ rel/files/mongooseim.toml | 3 + src/auth/ejabberd_auth.erl | 7 +- src/config/mongoose_config_spec.erl | 2 + src/ejabberd_c2s.erl | 5 +- .../mongoose_client_api.erl | 2 +- src/mongoose_credentials.erl | 24 ++++-- test/auth_dummy_SUITE.erl | 4 +- test/auth_http_SUITE.erl | 2 +- test/auth_jwt_SUITE.erl | 2 +- test/ejabberd_c2s_SUITE_mocks.erl | 4 +- 13 files changed, 124 insertions(+), 15 deletions(-) create mode 100644 big_tests/tests/auth_methods_for_c2s_SUITE.erl diff --git a/big_tests/default.spec b/big_tests/default.spec index 24ed972044..5d79ee0252 100644 --- a/big_tests/default.spec +++ b/big_tests/default.spec @@ -89,6 +89,7 @@ {suites, "tests", domain_removal_SUITE}. {suites, "tests", mam_send_message_SUITE}. {suites, "tests", dynamic_domains_SUITE}. +{suites, "tests", auth_methods_for_c2s_SUITE}. {config, ["test.config"]}. {logdir, "ct_report"}. diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 85ad2a1762..539b4eec12 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -131,6 +131,7 @@ {suites, "tests", xep_0352_csi_SUITE}. {suites, "tests", domain_removal_SUITE}. +{suites, "tests", auth_methods_for_c2s_SUITE}. {config, ["dynamic_domains.config", "test.config"]}. diff --git a/big_tests/tests/auth_methods_for_c2s_SUITE.erl b/big_tests/tests/auth_methods_for_c2s_SUITE.erl new file mode 100644 index 0000000000..2cbcf3892d --- /dev/null +++ b/big_tests/tests/auth_methods_for_c2s_SUITE.erl @@ -0,0 +1,82 @@ +-module(auth_methods_for_c2s_SUITE). + +-compile([export_all, nowarn_export_all]). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("exml/include/exml.hrl"). + +-import(distributed_helper, [mim/0, rpc/4]). + +all() -> + [{group, two_methods_enabled}]. + +groups() -> + [{two_methods_enabled, [parallel], + [can_login_with_allowed_method, + cannot_login_with_not_allowed_method, + can_login_to_another_listener]}]. + +init_per_suite(Config) -> + Config0 = escalus:init_per_suite(Config), + Config1 = ejabberd_node_utils:init(Config0), + ejabberd_node_utils:backup_config_file(Config1), + Config1. + +end_per_suite(Config) -> + ejabberd_node_utils:restore_config_file(Config), + ejabberd_node_utils:restart_application(mongooseim), + escalus:end_per_suite(Config). + +init_per_group(_, Config) -> + modify_config_and_restart(Config), + escalus_cleaner:start(Config). + +end_per_group(_, _Config) -> + escalus_fresh:clean(). + +init_per_testcase(TC, Config) -> + Spec = escalus_fresh:freshen_spec(Config, alice), + Clean = register_internal_user(Spec), + [{clean_fn, Clean}, {spec, Spec}|escalus:init_per_testcase(TC, Config)]. + +end_per_testcase(TC, Config) -> + Clean = proplists:get_value(clean_fn, Config), + Clean(), + escalus:end_per_testcase(TC, Config). + +modify_config_and_restart(Config) -> + NewConfigValues = [{auth_method, "internal]\n [auth.dummy"}, + {auth_method_opts, false}, + {allowed_auth_methods, "\"internal\""}], + ejabberd_node_utils:modify_config_file(NewConfigValues, Config), + ejabberd_node_utils:restart_application(mongooseim). + +can_login_with_allowed_method(Config) -> + Spec = proplists:get_value(spec, Config), + {ok, _, _} = escalus_connection:start(Spec). + +cannot_login_with_not_allowed_method(Config) -> + Spec = proplists:get_value(spec, Config), + {error, _} = escalus_connection:start([{password, <<"wrong">>}|Spec]). + +can_login_to_another_listener(Config) -> + Spec = proplists:get_value(spec, Config), + Spec2 = [{port, ct:get_config({hosts, mim, c2s_tls_port})}, + {password, <<"wrong">>}|Spec], + {ok, _, _} = escalus_connection:start(Spec2). + +%% Helpers + +%% If dummy backend is enabled, it is not possible to create new users +%% (we check if an user does exist before registering the user). +register_internal_user(Spec) -> + #{username := User, server := Server, + password := Password} = maps:from_list(Spec), + LUser = jid:nodeprep(User), + LServer = escalus_utils:jid_to_lower(Server), + HostType = domain_helper:host_type(), + rpc(mim(), ejabberd_auth_internal, try_register, + [HostType, LUser, LServer, Password]), + fun() -> rpc(mim(), ejabberd_auth_internal, remove_user, + [HostType, LUser, LServer]) end. diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index 5fea8a6326..6cdb6c5cf5 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -144,6 +144,9 @@ {{#c2s_dhfile}} tls.dhfile = {{{c2s_dhfile}}} {{/c2s_dhfile}} + {{#allowed_auth_methods}} + allowed_auth_methods = [{{{allowed_auth_methods}}}] + {{/allowed_auth_methods}} {{#secondary_c2s}} {{{secondary_c2s}}} diff --git a/src/auth/ejabberd_auth.erl b/src/auth/ejabberd_auth.erl index f4a276bbe0..7ca4eb146a 100644 --- a/src/auth/ejabberd_auth.erl +++ b/src/auth/ejabberd_auth.erl @@ -54,7 +54,8 @@ -export([auth_modules/1, auth_methods/1, - auth_modules_for_host_type/1]). + auth_modules_for_host_type/1, + methods_to_modules/1]). %% Library functions for reuse in ejabberd_auth_* modules -export([authorize_with_check_password/2]). @@ -413,6 +414,10 @@ auth_modules(LServer) -> -spec auth_modules_for_host_type(HostType :: mongooseim:host_type()) -> [authmodule()]. auth_modules_for_host_type(HostType) -> Methods = auth_methods(HostType), + methods_to_modules(Methods). + +-spec methods_to_modules([atom()]) -> [authmodule()]. +methods_to_modules(Methods) -> [auth_method_to_module(M) || M <- Methods]. -spec auth_methods(mongooseim:host_type()) -> [atom()]. diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index 4bd0eaec76..d44e2399b6 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -310,6 +310,8 @@ xmpp_listener_items(<<"c2s">>) -> validate = positive}, <<"max_fsm_queue">> => #option{type = integer, validate = positive}, + <<"allowed_auth_methods">> => #list{items = #option{type = atom, + validate = {module, ejabberd_auth}}}, <<"tls">> => c2s_tls()}; xmpp_listener_items(<<"s2s">>) -> #{<<"shaper">> => #option{type = atom, diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index e3c7d14004..d43b5eb8cb 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -344,8 +344,9 @@ stream_start_negotiate_features(#state{} = S) -> end. stream_start_features_before_auth(#state{server = Server, - host_type = HostType} = S) -> - Creds0 = mongoose_credentials:new(Server, HostType), + host_type = HostType, + cred_opts = CredOpts} = S) -> + Creds0 = mongoose_credentials:new(Server, HostType, CredOpts), Creds = maybe_add_cert(Creds0, S), SASLState = cyrsasl:server_new(<<"jabber">>, Server, HostType, <<>>, [], Creds), SockMod = (S#state.sockmod):get_sockmod(S#state.socket), diff --git a/src/mongoose_client_api/mongoose_client_api.erl b/src/mongoose_client_api/mongoose_client_api.erl index 4dea861bc2..c005d6859b 100644 --- a/src/mongoose_client_api/mongoose_client_api.erl +++ b/src/mongoose_client_api/mongoose_client_api.erl @@ -130,7 +130,7 @@ check_password(JID, Password) -> {LUser, LServer} = jid:to_lus(JID), case mongoose_domain_api:get_domain_host_type(LServer) of {ok, HostType} -> - Creds0 = mongoose_credentials:new(LServer, HostType), + Creds0 = mongoose_credentials:new(LServer, HostType, #{}), Creds1 = mongoose_credentials:set(Creds0, username, LUser), Creds2 = mongoose_credentials:set(Creds1, password, Password), case ejabberd_auth:authorize(Creds2) of diff --git a/src/mongoose_credentials.erl b/src/mongoose_credentials.erl index c653fa605b..bde448c88a 100644 --- a/src/mongoose_credentials.erl +++ b/src/mongoose_credentials.erl @@ -1,7 +1,7 @@ -module(mongoose_credentials). -export([make_opts/1, - new/2, + new/3, lserver/1, host_type/1, auth_modules/1, @@ -31,14 +31,28 @@ -spec make_opts(ejabberd_listener:opts()) -> opts(). make_opts(Opts) -> - #{}. + case lists:keyfind(allowed_auth_methods, 1, Opts) of + {allowed_auth_methods, Methods} -> + #{allowed_modules => ejabberd_auth:methods_to_modules(Methods)}; + _ -> + #{} + end. --spec new(jid:lserver(), binary()) -> mongoose_credentials:t(). -new(LServer, HostType) when is_binary(LServer), is_binary(HostType) -> - Modules = ejabberd_auth:auth_modules_for_host_type(HostType), +-spec new(jid:lserver(), binary(), opts()) -> mongoose_credentials:t(). +new(LServer, HostType, Opts) when is_binary(LServer), is_binary(HostType) -> + HostTypeModules = ejabberd_auth:auth_modules_for_host_type(HostType), + Modules = filter_modules(HostTypeModules, Opts), #mongoose_credentials{lserver = LServer, host_type = HostType, modules = Modules}. +%% Allows to enable only some modules for some listeners +-spec filter_modules([ejabberd_auth:authmodule()], opts()) -> + [ejabberd_auth:authmodule()]. +filter_modules(Modules, #{allowed_modules := AllowedModules}) -> + [M || M <- Modules, lists:member(M, AllowedModules)]; +filter_modules(Modules, _) -> + Modules. + -spec host_type(t()) -> mongooseim:host_type(). host_type(#mongoose_credentials{host_type = HostType}) -> HostType. diff --git a/test/auth_dummy_SUITE.erl b/test/auth_dummy_SUITE.erl index 4faf0a2657..96f8b46162 100644 --- a/test/auth_dummy_SUITE.erl +++ b/test/auth_dummy_SUITE.erl @@ -48,7 +48,7 @@ end_per_suite(_C) -> %%-------------------------------------------------------------------- authorize(_Config) -> - Creds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE), + Creds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE, #{}), {ok, Creds2} = ejabberd_auth_dummy:authorize(Creds), ejabberd_auth_dummy = mongoose_credentials:get(Creds2, auth_module). @@ -61,7 +61,7 @@ ejabberd_auth_interfaces(_Config) -> fun(?DOMAIN) -> {ok, ?HOST_TYPE} end), meck:expect(mongoose_metrics, update, fun(_, _, _) -> ok end), - Creds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE), + Creds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE, #{}), {ok, Creds2} = ejabberd_auth:authorize(Creds), ejabberd_auth_dummy = mongoose_credentials:get(Creds2, auth_module), diff --git a/test/auth_http_SUITE.erl b/test/auth_http_SUITE.erl index 7e45b401ad..2f6486fad7 100644 --- a/test/auth_http_SUITE.erl +++ b/test/auth_http_SUITE.erl @@ -231,7 +231,7 @@ cert_auth_nonexistent(Config) -> %%-------------------------------------------------------------------- creds_with_cert(Config, Username) -> Cert = proplists:get_value(der_cert, Config), - NewCreds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE), + NewCreds = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE, #{}), mongoose_credentials:extend(NewCreds, [{der_cert, Cert}, {username, Username}]). diff --git a/test/auth_jwt_SUITE.erl b/test/auth_jwt_SUITE.erl index f7b3df781b..71829c6cc1 100644 --- a/test/auth_jwt_SUITE.erl +++ b/test/auth_jwt_SUITE.erl @@ -96,7 +96,7 @@ check_password_fails_for_correct_token_but_wrong_username(_C) -> generate_token(hs256, 0, ?JWT_KEY)). authorize(_C) -> - Creds0 = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE), + Creds0 = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE, #{}), Creds = mongoose_credentials:extend(Creds0, [{username, ?USERNAME}, {password, generate_token(hs256, 0, ?JWT_KEY)}, {digest, fake}, diff --git a/test/ejabberd_c2s_SUITE_mocks.erl b/test/ejabberd_c2s_SUITE_mocks.erl index 1c198bf569..7b7e771c4a 100644 --- a/test/ejabberd_c2s_SUITE_mocks.erl +++ b/test/ejabberd_c2s_SUITE_mocks.erl @@ -28,8 +28,8 @@ setup() -> meck:expect(cyrsasl, server_start, fun(_, _, _, _) -> {ok, dummy_creds} end), meck:expect(cyrsasl, listmech, fun(_) -> [] end), - meck:new(mongoose_credentials), - meck:expect(mongoose_credentials, new, fun(_, _) -> ok end), + meck:new(mongoose_credentials, [passthrough]), + meck:expect(mongoose_credentials, new, fun(_, _, _) -> ok end), meck:expect(mongoose_credentials, get, fun(dummy_creds, sasl_success_response, undefined) -> undefined end), From 48e9f2f759355c4d93da2e832d1a8968acfae5c2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Fri, 11 Feb 2022 17:22:29 +0100 Subject: [PATCH 4/4] Add docs --- doc/configuration/listen.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/configuration/listen.md b/doc/configuration/listen.md index d8ac0463b2..41feaf4777 100644 --- a/doc/configuration/listen.md +++ b/doc/configuration/listen.md @@ -130,6 +130,17 @@ The value of the access rule needs to be either the shaper name or the string `" Enables ZLIB support, the integer value is a limit for a decompressed output size in bytes (to prevent a successful [ZLIB bomb attack](https://xmpp.org/community/security-notices/uncontrolled-resource-consumption-with-highly-compressed-xmpp-stanzas.html)). +### `listen.c2s.allowed_auth_methods` + +* **Syntax:** array of strings. Allowed values: `"internal"`, `"rdbms"`, `"external"`, `"anonymous"`, `"ldap"`, `"jwt"`, `"riak"`, `"http"`, `"pki"`, `"dummy"` +* **Default:** not set +* **Example:** `allowed_auth_methods = ["internal"]` + +A subset of enabled methods to login with for this listener. +This option allows to enable only some backends. +It is useful, if you want to have several listeners for different type of users (for example, some users use PKI while other users use LDAP auth). +Same syntax as for `auth.methods` option. + ## TLS options for C2S The following options allow enabling and configuring TLS which makes the client-to-server conenctions secure.