Skip to content

Commit

Permalink
Merge pull request #3132 from esl/multitenancy/inbox
Browse files Browse the repository at this point in the history
Multitenancy/inbox
  • Loading branch information
chrzaszcz authored May 31, 2021
2 parents cf515ef + 661edf9 commit fa8766c
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 118 deletions.
12 changes: 11 additions & 1 deletion big_tests/dynamic_domains.config
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,15 @@
{username, <<"bOb">>},
{server, <<"domain.example.com">>},
{host, <<"localhost">>},
{password, <<"makrolika">>}]}
{password, <<"makrolika">>}]},
{kate, [
{username, <<"kate">>},
{server, <<"domain.example.com">>},
{host, <<"localhost">>},
{password, <<"makrowe;p">>}]},
{mike, [
{username, <<"mike">>},
{server, <<"domain.example.com">>},
{host, <<"localhost">>},
{password, <<"nicniema">>}]}
]}.
12 changes: 12 additions & 0 deletions big_tests/dynamic_domains.spec
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
{skip_cases, "tests", carboncopy_SUITE, [discovering_support],
"at the moment mod_disco doesn't support dynamic domains"}.

{suites, "tests", inbox_SUITE}.
{skip_cases, "tests", inbox_SUITE, [disco_service],
"at the moment mod_disco doesn't support dynamic domains"}.
{skip_cases, "tests", inbox_SUITE, [msg_sent_to_offline_user],
"at the moment mod_offline doesn't support dynamic domains"}.
{skip_groups, "tests", inbox_SUITE, [muclight, muc],
"at the moment muc/muclight doesn't support dynamic domains"}.

{suites, "tests", inbox_extensions_SUITE}.
{skip_groups, "tests", inbox_extensions_SUITE, [muclight],
"at the moment muclight doesn't support dynamic domains"}.

{config, ["dynamic_domains.config", "test.config"]}.

{logdir, "ct_report"}.
Expand Down
71 changes: 35 additions & 36 deletions big_tests/tests/dynamic_modules.erl
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
-import(distributed_helper, [mim/0,
rpc/4]).

save_modules(Domain, Config) ->
[{saved_modules, get_current_modules(Domain)} | Config].
save_modules(HostType, Config) ->
[{saved_modules, get_current_modules(HostType)} | Config].

ensure_modules(Domain, RequiredModules) ->
CurrentModules = get_current_modules(Domain),
ensure_modules(HostType, RequiredModules) ->
CurrentModules = get_current_modules(HostType),
{ToReplace, ReplaceWith} = to_replace(RequiredModules, CurrentModules, [], []),
ok = rpc(mim(), gen_mod_deps, replace_modules, [Domain, ToReplace, ReplaceWith]).
ok = rpc(mim(), gen_mod_deps, replace_modules, [HostType, ToReplace, ReplaceWith]).

to_replace([], _CurrentModules, ReplaceAcc, ReplaceWithAcc) ->
{lists:usort(ReplaceAcc), ReplaceWithAcc};
Expand All @@ -30,74 +30,73 @@ to_replace([RequiredModule | Rest], CurrentModules, ReplaceAcc, ReplaceWithAcc)
end,
to_replace(Rest, CurrentModules, NewReplaceAcc, NewReplaceWithAcc).

restore_modules(Domain, Config) ->
restore_modules(HostType, Config) ->
SavedModules = ?config(saved_modules, Config),
CurrentModules = get_current_modules(Domain),
rpc(mim(), gen_mod_deps, replace_modules, [Domain, CurrentModules, SavedModules]).
CurrentModules = get_current_modules(HostType),
rpc(mim(), gen_mod_deps, replace_modules, [HostType, CurrentModules, SavedModules]).

get_current_modules(Domain) ->
rpc(mim(), gen_mod, loaded_modules_with_opts, [Domain]).
get_current_modules(HostType) ->
rpc(mim(), gen_mod, loaded_modules_with_opts, [HostType]).

stop(Domain, Mod) ->
stop(HostType, Mod) ->
Node = escalus_ct:get_config(ejabberd_node),
stop(Node, Domain, Mod).
stop(Node, HostType, Mod).

stop(#{node := Node}, Domain, Mod) ->
stop(Node, Domain, Mod);
stop(Node, Domain, Mod) ->
stop(#{node := Node}, HostType, Mod) ->
stop(Node, HostType, Mod);
stop(Node, HostType, Mod) ->
Cookie = escalus_ct:get_config(ejabberd_cookie),
IsLoaded = escalus_rpc:call(Node, gen_mod, is_loaded, [Domain, Mod], 5000, Cookie),
IsLoaded = escalus_rpc:call(Node, gen_mod, is_loaded, [HostType, Mod], 5000, Cookie),
case IsLoaded of
true -> unsafe_stop(Node, Cookie, Domain, Mod);
true -> unsafe_stop(Node, Cookie, HostType, Mod);
false -> {error, stopped}
end.

unsafe_stop(Node, Cookie, Domain, Mod) ->
case escalus_rpc:call(Node, gen_mod, stop_module, [Domain, Mod], 5000, Cookie) of
unsafe_stop(Node, Cookie, HostType, Mod) ->
case escalus_rpc:call(Node, gen_mod, stop_module, [HostType, Mod], 5000, Cookie) of
{badrpc, Reason} ->
ct:fail("Cannot stop module ~p reason ~p", [Mod, Reason]);
R -> R
end.

start(Domain, Mod, Args) ->
start(HostType, Mod, Args) ->
Node = escalus_ct:get_config(ejabberd_node),
start(Node, Domain, Mod, Args).
start(Node, HostType, Mod, Args).

start(#{node := Node}, Domain, Mod, Args) ->
start(Node, Domain, Mod, Args);
start(Node, Domain, Mod, Args) ->
start(#{node := Node}, HostType, Mod, Args) ->
start(Node, HostType, Mod, Args);
start(Node, HostType, Mod, Args) ->
Cookie = escalus_ct:get_config(ejabberd_cookie),
case escalus_rpc:call(Node, gen_mod, start_module, [Domain, Mod, Args], 5000, Cookie) of
case escalus_rpc:call(Node, gen_mod, start_module, [HostType, Mod, Args], 5000, Cookie) of
{badrpc, Reason} ->
ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]);
R -> R
end.

restart(Domain, Mod, Args) ->
stop(Domain, Mod),
restart(HostType, Mod, Args) ->
stop(HostType, Mod),
ModStr = atom_to_list(Mod),
case lists:reverse(ModStr) of
"smbdr_" ++ Str -> %%check if we need to start rdbms module or regular
stop(Domain, list_to_atom(lists:reverse(Str)));
stop(HostType, list_to_atom(lists:reverse(Str)));
Str ->
stop(Domain, list_to_atom(lists:reverse(Str)++"_rdbms"))
stop(HostType, list_to_atom(lists:reverse(Str)++"_rdbms"))
end,
start(Domain, Mod, Args).
start(HostType, Mod, Args).

start_running(Config) ->
Domain = ct:get_config({hosts, mim, domain}),
HostType = domain_helper:host_type(mim),
case ?config(running, Config) of
List when is_list(List) ->
_ = [start(Domain, Mod, Args) || {Mod, Args} <- List];
_ = [start(HostType, Mod, Args) || {Mod, Args} <- List];
_ ->
ok
end.

stop_running(Mod, Config) ->
ModL = atom_to_list(Mod),
Domain = escalus_ejabberd:unify_str_arg(
ct:get_config({hosts, mim, domain})),
Modules = rpc(mim(), ejabberd_config, get_local_option, [{modules, Domain}]),
HostType = domain_helper:host_type(mim),
Modules = rpc(mim(), ejabberd_config, get_local_option, [{modules, HostType}]),
Filtered = lists:filter(fun({Module, _}) ->
ModuleL = atom_to_list(Module),
case lists:sublist(ModuleL, 1, length(ModL)) of
Expand All @@ -110,6 +109,6 @@ stop_running(Mod, Config) ->
[] ->
Config;
[{Module,_Args}=Head|_] ->
stop(Domain, Module),
stop(HostType, Module),
[{running, [Head]} | Config]
end.
14 changes: 8 additions & 6 deletions big_tests/tests/inbox_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@
-import(muc_light_helper, [room_bin_jid/1]).
-import(inbox_helper, [
muclight_domain/0,
required_modules/0,
inbox_modules/0,
muclight_modules/0,
inbox_opts/0,
muc_domain/0,
parse_form_iq/1,
Expand Down Expand Up @@ -190,23 +191,22 @@ suite() ->
%%--------------------------------------------------------------------

init_per_suite(Config) ->
ok = dynamic_modules:ensure_modules(inbox_helper:domain(), required_modules()),
ok = dynamic_modules:ensure_modules(domain_helper:host_type(mim), inbox_modules()),
InboxOptions = inbox_opts(),
Config1 = escalus:init_per_suite(Config),
Config2 = [{inbox_opts, InboxOptions} | Config1],
escalus:create_users(Config2, escalus:get_users([alice, bob, kate, mike])).

end_per_suite(Config) ->
Host = ct:get_config({hosts, mim, domain}),
Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])),
dynamic_modules:stop(Host, mod_inbox),
dynamic_modules:stop(Host, mod_muc_light),
muc_light_helper:clear_db(),
HostType = domain_helper:host_type(mim),
dynamic_modules:stop(HostType, mod_inbox),
escalus:end_per_suite(Config1).

init_per_group(one_to_one, Config) ->
inbox_helper:reload_inbox_option(Config, groupchat, []);
init_per_group(muclight, Config) ->
ok = dynamic_modules:ensure_modules(domain_helper:host_type(mim), muclight_modules()),
inbox_helper:reload_inbox_option(Config, groupchat, [muclight]),
muc_light_helper:create_room(?ROOM, muclight_domain(), alice,
[bob, kate], Config, muc_light_helper:ver(1));
Expand All @@ -218,6 +218,8 @@ init_per_group(_GroupName, Config) ->

end_per_group(muclight, Config) ->
muc_light_helper:clear_db(),
HostType = domain_helper:host_type(mim),
dynamic_modules:stop(HostType, mod_muc_light),
Config;
end_per_group(_GroupName, Config) ->
Config.
Expand Down
13 changes: 8 additions & 5 deletions big_tests/tests/inbox_extensions_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@

-import(inbox_helper, [
muclight_domain/0,
required_modules/0,
inbox_modules/0,
muclight_modules/0,
inbox_opts/0
]).

Expand Down Expand Up @@ -144,28 +145,30 @@ inbox_extensions_tests() ->
].

init_per_suite(Config) ->
ok = dynamic_modules:ensure_modules(inbox_helper:domain(), required_modules()),
ok = dynamic_modules:ensure_modules(domain_helper:host_type(mim), inbox_modules()),
InboxOptions = inbox_opts(),
Config1 = escalus:init_per_suite(Config),
Config2 = [{inbox_opts, InboxOptions} | Config1],
escalus:create_users(Config2, escalus:get_users([alice, bob, kate, mike])).

end_per_suite(Config) ->
Host = ct:get_config({hosts, mim, domain}),
Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])),
dynamic_modules:stop(Host, mod_inbox),
dynamic_modules:stop(Host, mod_muc_light),
HostType = domain_helper:host_type(mim),
dynamic_modules:stop(HostType, mod_inbox),
muc_light_helper:clear_db(),
escalus:end_per_suite(Config1).

init_per_group(muclight, Config) ->
ok = dynamic_modules:ensure_modules(domain_helper:host_type(mim), muclight_modules()),
inbox_helper:reload_inbox_option(Config, groupchat, [muclight]),
Config;
init_per_group(_GroupName, Config) ->
Config.

end_per_group(muclight, Config) ->
muc_light_helper:clear_db(),
HostType = domain_helper:host_type(mim),
dynamic_modules:stop(HostType, mod_muc_light),
Config;
end_per_group(_GroupName, Config) ->
Config.
Expand Down
41 changes: 24 additions & 17 deletions big_tests/tests/inbox_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
-export([
skip_or_run_inbox_tests/1,
inbox_opts/0,
required_modules/0,
inbox_modules/0,
muclight_modules/0,
clear_inbox_all/0,
foreach_check_inbox/4,
check_inbox/2, check_inbox/4,
Expand Down Expand Up @@ -126,18 +127,22 @@ inbox_opts() ->

skip_or_run_inbox_tests(TestCases) ->
case (not ct_helper:is_ct_running())
orelse mongoose_helper:is_rdbms_enabled(inbox_helper:domain()) of
orelse mongoose_helper:is_rdbms_enabled(domain()) of
true -> TestCases;
false -> {skip, require_rdbms}
end.

required_modules() ->
inbox_modules() ->
[
{mod_muc_light, [{host, subhost_pattern(muclight_config_domain())},
{backend, rdbms}]},
{mod_inbox, inbox_opts()}
].

muclight_modules() ->
[
{mod_muc_light, [{host, subhost_pattern(muclight_config_domain())},
{backend, rdbms}]}
].

foreach_check_inbox(Users, Unread, SenderJid, Msg) ->
[begin
UserJid = to_bare_lower(U),
Expand Down Expand Up @@ -241,32 +246,34 @@ timestamp_from_item(Item) ->
calendar:rfc3339_to_system_time(binary_to_list(ISOTStamp), [{unit, microsecond}]).

clear_inbox_all() ->
clear_inboxes([alice, bob, kate, mike], domain()).
clear_inboxes([alice, bob, kate, mike]).

clear_inboxes(UserList, Host) ->
Usernames = [escalus_users:get_username(escalus_users:get_users(UserList),U) || U <- UserList],
[escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [Username,Host]) || Username <- Usernames].
clear_inboxes(UserList) ->
Usernames = [{escalus_users:get_username(escalus_users:get_users(UserList),U),
escalus_users:get_server(escalus_users:get_users(UserList),U)}
|| U <- UserList],
[escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [User, Server]) || {User, Server} <- Usernames].

reload_inbox_option(Config, KeyValueList) ->
Host = domain(),
HostType = domain_helper:host_type(mim),
Args = proplists:get_value(inbox_opts, Config),
Args2 = lists:foldl(fun({K, V}, AccIn) ->
lists:keyreplace(K, 1, AccIn, {K, V})
end, Args, KeyValueList),
dynamic_modules:restart(Host, mod_inbox, Args2),
dynamic_modules:restart(HostType, mod_inbox, Args2),
lists:keyreplace(inbox_opts, 1, Config, {inbox_opts, Args2}).

reload_inbox_option(Config, Key, Value) ->
Host = domain(),
HostType = domain_helper:host_type(mim),
Args = proplists:get_value(inbox_opts, Config),
Args1 = lists:keyreplace(Key, 1, Args, {Key, Value}),
dynamic_modules:restart(Host, mod_inbox, Args1),
dynamic_modules:restart(HostType, mod_inbox, Args1),
lists:keyreplace(inbox_opts, 1, Config, {inbox_opts, Args1}).

restore_inbox_option(Config) ->
Host = domain(),
HostType = domain_helper:host_type(mim),
Args = proplists:get_value(inbox_opts, Config),
dynamic_modules:restart(Host, mod_inbox, Args).
dynamic_modules:restart(HostType, mod_inbox, Args).

get_inner_msg(Msg) ->
exml_query:paths(Msg, [{element, <<"result">>}, {element, <<"forwarded">>},
Expand Down Expand Up @@ -639,7 +646,7 @@ muclight_config_domain() ->

-spec muc_domain() -> binary().
muc_domain() ->
Domain = inbox_helper:domain(),
Domain = domain(),
<<"muc.", Domain/binary>>.

-spec domain() -> binary().
Expand Down Expand Up @@ -714,7 +721,7 @@ assert_invalid_reset_inbox(From, To, Field, Value) ->
assert_invalid_form(From, ResetStanza, Field, Value).

assert_invalid_inbox_form_value_error(User, Field, Value) ->
Stanza = inbox_helper:make_inbox_stanza( #{ Field => Value }, false),
Stanza = make_inbox_stanza( #{ Field => Value }, false),
assert_invalid_form(User, Stanza, Field, Value).

assert_invalid_form(User, Stanza, Field, Value) ->
Expand Down
8 changes: 5 additions & 3 deletions src/ejabberd_c2s.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2396,12 +2396,13 @@ process_unauthenticated_stanza(StateData, El) ->
<<>> ->
case StateData#state.lang of
<<>> -> El;
Lang ->
xml:replace_tag_attr(<<"xml:lang">>, Lang, El)
L ->
xml:replace_tag_attr(<<"xml:lang">>, L, El)
end;
_ ->
El
end,
Lang = xml:get_tag_attr_s(<<"xml:lang">>, NewEl),
case jlib:iq_query_info(NewEl) of
#iq{} = IQ ->
Res = mongoose_hooks:c2s_unauthenticated_iq(
Expand All @@ -2412,8 +2413,9 @@ process_unauthenticated_stanza(StateData, El) ->
empty ->
% The only reasonable IQ's here are auth and register IQ's
% They contain secrets, so don't include subelements to response
Text = <<"Forbidden unauthenticated stanza">>,
ResIQ = IQ#iq{type = error,
sub_el = [mongoose_xmpp_errors:service_unavailable()]},
sub_el = [mongoose_xmpp_errors:service_unavailable(Lang, Text)]},
Res1 = jlib:replace_from_to(
jid:make_noprep(<<>>, StateData#state.server, <<>>),
jid:make_noprep(<<>>, <<>>, <<>>),
Expand Down
2 changes: 1 addition & 1 deletion src/ejabberd_sm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,7 @@ process_iq(#iq{xmlns = XMLNS} = IQ, From, To, Acc, Packet) ->
gen_iq_component:handle(IQHandler, Acc, From, To, IQ);
[] ->
{Acc1, Err} = jlib:make_error_reply(
Acc, Packet, mongoose_xmpp_errors:service_unavailable()),
Acc, Packet, mongoose_xmpp_errors:service_unavailable(<<"en">>, <<"Unknown xmlns">>)),
ejabberd_router:route(To, From, Acc1, Err)
end;
process_iq(_, From, To, Acc, Packet) ->
Expand Down
Loading

0 comments on commit fa8766c

Please sign in to comment.