Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic domains support in mod_disco #3151

Merged
merged 34 commits into from
Jun 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
445ccf2
Register disco IQ handlers with the new API
chrzaszcz Jun 9, 2021
98864dc
Use host types in disco_local_items and disco_sm_items
chrzaszcz Jun 9, 2021
b7bd0fb
Remove unnecessary get_local_features from mod_vcard
chrzaszcz Jun 10, 2021
7e6b95d
Remove unnecessary get_local_features from mod_version
chrzaszcz Jun 10, 2021
6d55030
Test server features added by mod_disco
chrzaszcz Jun 10, 2021
4c1451c
Use host types in get_local_features and get_sm_features
chrzaszcz Jun 10, 2021
e407994
Use host types when handling disco_local_features in ejabberd_local
chrzaszcz Jun 10, 2021
0fd8381
Rework disco_local_features in mod_inbox
chrzaszcz Jun 10, 2021
2ead628
Rework disco_local_features in mod_mam
chrzaszcz Jun 10, 2021
b2295f8
Rework disco_*_features in mod_adhoc
chrzaszcz Jun 10, 2021
faf39ed
Rework disco_local_features in mod_amp
chrzaszcz Jun 10, 2021
a85a167
Rework disco_local_features in mod_auth_token
chrzaszcz Jun 10, 2021
edcf0ed
Rework disco_local_features in mod_blocking
chrzaszcz Jun 10, 2021
1d5eeb2
Rework disco_local_features in mod_caps
chrzaszcz Jun 10, 2021
6e8c44b
Rework disco_local_features in mod_carboncopy
chrzaszcz Jun 10, 2021
f2e493c
Rework disco_*_features in mod_disco
chrzaszcz Jun 10, 2021
ae768f1
Rework disco_features in mod_offline
chrzaszcz Jun 10, 2021
859f78f
Rework disco_*_features in mod_pubsub
chrzaszcz Jun 10, 2021
263a6e2
Use a separate hook for MUC feature discovery
chrzaszcz Jun 10, 2021
621fb2f
Test disco for SM
chrzaszcz Jun 10, 2021
d3022ff
Use host types in disco_local_identity and disco_sm_identity
chrzaszcz Jun 10, 2021
d2d90ef
Use host types in disco_info
chrzaszcz Jun 11, 2021
9acad5f
Make mongoose_disco responsible for running all disco hooks
chrzaszcz Jun 11, 2021
1b0f915
Update disco hooks in mod_caps
chrzaszcz Jun 11, 2021
9d064e9
Update disco hooks in mod_disco
chrzaszcz Jun 11, 2021
5942326
Update disco hooks in MUC(Light) modules
chrzaszcz Jun 11, 2021
4a6fee2
Get local domains using the new domain API
chrzaszcz Jun 14, 2021
ec0caff
Add tests for XEP-0157 Contact Addresses
chrzaszcz Jun 14, 2021
122fac4
Add dynamic_domains to supported_features of mod_disco
chrzaszcz Jun 14, 2021
cfb763b
Test mod_disco with dynamic domains
chrzaszcz Jun 14, 2021
bc02e78
Remove unnecessary white spaces
chrzaszcz Jun 15, 2021
2777a29
Make mod_disco exports more readable
chrzaszcz Jun 15, 2021
ddcc5f7
Extract result construction to a new function
chrzaszcz Jun 15, 2021
836d2ad
Minor code style change
chrzaszcz Jun 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 14 additions & 33 deletions big_tests/dynamic_domains.spec
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,19 @@
{suites, "tests", acc_e2e_SUITE}.

{suites, "tests", carboncopy_SUITE}.
{skip_cases, "tests", carboncopy_SUITE, [discovering_support],
"at the moment mod_disco doesn't support dynamic domains"}.

{suites, "tests", offline_stub_SUITE}.
{suites, "tests", disco_and_caps_SUITE}.
{skip_cases, "tests", disco_and_caps_SUITE,
[caps_feature_is_advertised,
user_can_query_server_caps_via_disco],
"at the moment mod_caps doesn't support dynamic domains"}.
{skip_cases, "tests", disco_and_caps_SUITE,
[user_can_query_friend_resources,
user_can_query_friend_features,
user_cannot_query_friend_resources_with_unknown_node],
"at the moment mod_roster 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"}.

Expand All @@ -28,54 +33,29 @@
{suites, "tests", mam_SUITE}.
{skip_cases, "tests", mam_SUITE,
[muc_service_discovery, mam_service_discovery],
"at the moment mod_disco doesn't support dynamic domains"}.
"at the moment mod_mam config options don't support dynamic domains"}.
{skip_cases, "tests", mam_SUITE,
[messages_filtered_when_prefs_default_policy_is_roster],
"at the moment mod_roster doesn't support dynamic domains"}.

{suites, "tests", mod_ping_SUITE}.
{skip_cases, "tests", mod_ping_SUITE, [disco],
"at the moment mod_disco doesn't support dynamic domains"}.

{suites, "tests", muc_SUITE}.
{skip_groups, "tests", muc_SUITE,
[disco, disco_non_parallel, disco_rsm, disco_rsm_with_offline],
"at the moment mod_disco doesn't support dynamic domains"}.
{skip_groups, "tests", muc_SUITE,
[register_over_s2s],
"at the moment S2S doesn't support dynamic domains "
"(requires mod_register creating CT users)"}.

{suites, "tests", muc_light_SUITE}.
{skip_cases, "tests", muc_light_SUITE,
[disco_service,
disco_features,
disco_features_with_mam,
disco_info,
disco_info_with_mam,
disco_rooms,
disco_rooms_rsm,
disco_rooms_created_page_1,
disco_rooms_created_page_infinity,
disco_rooms_empty_page_infinity,
disco_rooms_empty_page_1],
"at the moment mod_disco doesn't support dynamic domains"}.
{skip_cases, "tests", muc_light_SUITE,
[rooms_in_rosters,
rooms_in_rosters_doesnt_break_disco_info,
no_roomname_in_schema_doesnt_break_disco_and_roster],
"at the moment mod_roster doesn't support dynamic domains"}.

{suites, "tests", muc_light_legacy_SUITE}.
{skip_cases, "tests", muc_light_legacy_SUITE,
[disco_service,
disco_features,
disco_features_with_mam,
disco_info,
disco_info_with_mam,
disco_rooms,
disco_rooms_rsm],
"at the moment mod_disco doesn't support dynamic domains"}.

{suites, "tests", offline_stub_SUITE}.

{suites, "tests", sm_SUITE}.
{skip_cases, "tests", sm_SUITE,
Expand All @@ -93,6 +73,7 @@
resume_expired_session_returns_correct_h,
unacknowledged_message_hook_offline],
"at the moment mod_offline doesn't support dynamic domains"}.

{suites, "tests", rest_client_SUITE}.
{skip_groups, "tests", rest_client_SUITE, [roster],
"at the moment mod_roster doesn't support dynamic domains"}.
Expand Down
127 changes: 114 additions & 13 deletions big_tests/tests/disco_and_caps_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,54 @@
-compile(export_all).

-include_lib("eunit/include/eunit.hrl").
-include_lib("escalus/include/escalus_xmlns.hrl").

-import(domain_helper, [host_type/0]).

all() ->
[{group, all_tests}].
[{group, disco_with_caps},
{group, disco_with_extra_features}].

groups() ->
G = [{all_tests, [parallel], all_test_cases()}],
G = [{disco_with_caps, [parallel], basic_test_cases() ++ caps_test_cases()},
{disco_with_extra_features, [parallel], basic_test_cases() ++ extra_feature_test_cases()}],
ct_helper:repeat_all_until_all_ok(G).

all_test_cases() ->
[caps_feature_is_advertised,
user_can_query_server_caps_via_disco,
user_cannot_query_stranger_resources,
basic_test_cases() ->
[user_cannot_query_stranger_resources,
user_cannot_query_stranger_features,
user_can_query_friend_resources,
user_can_query_friend_features,
user_cannot_query_own_resources_with_unknown_node,
user_cannot_query_friend_resources_with_unknown_node,
extra_domains_are_advertised].
user_can_query_server_features].

caps_test_cases() ->
[caps_feature_is_advertised,
user_can_query_server_caps_via_disco].

extra_feature_test_cases() ->
[user_can_query_extra_domains,
user_can_query_server_info].

domain() ->
ct:get_config({hosts, mim, domain}).

init_per_suite(C) ->
C2 = escalus:init_per_suite(dynamic_modules:save_modules(domain(), C)),
dynamic_modules:ensure_modules(domain(), [{mod_caps, []},
{mod_disco, [{extra_domains, [extra_domain()]}]}]),
C2.
C.

end_per_suite(C) ->
escalus_fresh:clean(),
dynamic_modules:restore_modules(domain(), C),
escalus:end_per_suite(C).

init_per_group(Name, C) ->
C2 = escalus:init_per_suite(dynamic_modules:save_modules(host_type(), C)),
dynamic_modules:ensure_modules(host_type(), required_modules(Name)),
C2.

end_per_group(Name, C) ->
dynamic_modules:restore_modules(host_type(), C).

init_per_testcase(Name, C) ->
escalus:init_per_testcase(Name, C).

Expand Down Expand Up @@ -74,6 +91,17 @@ user_cannot_query_stranger_resources(Config) ->
escalus:assert(is_stanza_from, [BobJid], Stanza)
end).

user_cannot_query_stranger_features(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
BobJid = escalus_client:short_jid(Bob),
Request = escalus_stanza:disco_info(BobJid),
escalus:send(Alice, Request),
Stanza = escalus:wait_for_stanza(Alice),
escalus:assert(is_iq_error, [Request], Stanza),
escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Stanza),
escalus:assert(is_stanza_from, [BobJid], Stanza)
end).

user_can_query_friend_resources(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
escalus_story:make_all_clients_friends([Alice, Bob]),
Expand All @@ -88,6 +116,16 @@ user_can_query_friend_resources(Config) ->
escalus:assert(is_stanza_from, [BobJid], Stanza)
end).

user_can_query_friend_features(Config) ->
escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
escalus_story:make_all_clients_friends([Alice, Bob]),
BobJid = escalus_client:short_jid(Bob),
escalus:send(Alice, escalus_stanza:disco_info(BobJid)),
Stanza = escalus:wait_for_stanza(Alice),
escalus:assert(has_identity, [<<"account">>, <<"registered">>], Stanza),
escalus:assert(is_stanza_from, [BobJid], Stanza)
end).

user_cannot_query_own_resources_with_unknown_node(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
AliceJid = escalus_client:short_jid(Alice),
Expand All @@ -111,7 +149,7 @@ user_cannot_query_friend_resources_with_unknown_node(Config) ->
escalus:assert(is_stanza_from, [BobJid], Stanza)
end).

extra_domains_are_advertised(Config) ->
user_can_query_extra_domains(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
Server = escalus_client:server(Alice),
escalus:send(Alice, escalus_stanza:service_discovery(Server)),
Expand All @@ -120,5 +158,68 @@ extra_domains_are_advertised(Config) ->
escalus:assert(is_stanza_from, [domain()], Stanza)
end).

user_can_query_server_features(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
Server = escalus_client:server(Alice),
escalus:send(Alice, escalus_stanza:disco_info(Server)),
Stanza = escalus:wait_for_stanza(Alice),
escalus:assert(has_identity, [<<"server">>, <<"im">>], Stanza),
escalus:assert(has_feature, [<<"iq">>], Stanza),
escalus:assert(has_feature, [<<"presence">>], Stanza),
escalus:assert(has_feature, [<<"presence-invisible">>], Stanza),
escalus:assert(is_stanza_from, [domain()], Stanza)
end).

%% XEP-0157: Contact Addresses for XMPP Services
user_can_query_server_info(Config) ->
escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
Server = escalus_client:server(Alice),
escalus:send(Alice, escalus_stanza:disco_info(Server)),
Stanza = escalus:wait_for_stanza(Alice),
escalus:assert(is_stanza_from, [domain()], Stanza),

%% 'sales' is hidden for mod_disco, so only 'abuse' and 'admin' are expected
[HiddenField, AbuseField, AdminField] = get_form_fields(Stanza),
?assertEqual(<<"FORM_TYPE">>, exml_query:attr(HiddenField, <<"var">>)),
?assertEqual(<<"hidden">>, exml_query:attr(HiddenField, <<"type">>)),
?assertEqual([?NS_SERVERINFO], exml_query:paths(HiddenField, [{element, <<"value">>},
cdata])),
?assertEqual(name(abuse), exml_query:attr(AbuseField, <<"var">>)),
?assertEqual(urls(abuse), exml_query:paths(AbuseField, [{element, <<"value">>},
cdata])),
?assertEqual(name(admin), exml_query:attr(AdminField, <<"var">>)),
?assertEqual(urls(admin), exml_query:paths(AdminField, [{element, <<"value">>},
cdata]))
end).

%% Helpers

required_modules(disco_with_caps) ->
[{mod_caps, []},
{mod_disco, []}];
required_modules(disco_with_extra_features) ->
[{mod_disco, [{extra_domains, [extra_domain()]},
{server_info, [server_info(abuse, []),
server_info(admin, [{modules, [mod_disco]}]),
server_info(sales, [{modules, [mod_pubsub]}])]
}]
}].

get_form_fields(Stanza) ->
exml_query:paths(Stanza, [{element_with_ns, <<"query">>, ?NS_DISCO_INFO},
{element_with_ns, <<"x">>, ?NS_DATA_FORMS},
{element, <<"field">>}]).

extra_domain() ->
<<"eXtra.example.com">>.

server_info(Type, Extra) ->
[{name, name(Type)}, {urls, urls(Type)} | Extra].

name(abuse) -> <<"abuse-addresses">>;
name(admin) -> <<"admin-addresses">>;
name(sales) -> <<"sales-addresses">>.

urls(abuse) -> [<<"abuse@example.com">>];
urls(admin) -> [<<"admin@example.com">>, <<"operations@example.com">>];
urls(sales) -> [<<"sales@example.com">>].
3 changes: 2 additions & 1 deletion rel/mim1.vars-toml.config
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
auth.dummy.base_time = 1
auth.dummy.variance = 5
[host_config.modules.mod_carboncopy]
[host_config.modules.mod_stream_management]"}.
[host_config.modules.mod_stream_management]
[host_config.modules.mod_disco]"}.
{password_format, "password.format = \"scram\"
password.hash = [\"sha256\"]"}.
{scram_iterations, 64}.
Expand Down
1 change: 1 addition & 0 deletions src/config/mongoose_config_validator.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
-spec validate(mongoose_config_parser_toml:option_value(),
mongoose_config_spec:option_type(), validator()) -> any().
validate(V, binary, domain) -> validate_binary_domain(V);
validate(V, binary, url) -> validate_non_empty_binary(V);
validate(V, binary, non_empty) -> validate_non_empty_binary(V);
validate(V, binary, {module, Prefix}) ->
validate_module(list_to_atom(atom_to_list(Prefix) ++ "_" ++ binary_to_list(V)));
Expand Down
8 changes: 7 additions & 1 deletion src/domain/mongoose_domain_api.erl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
-export([register_subdomain/3,
unregister_subdomain/2,
get_subdomain_host_type/1,
get_subdomain_info/1]).
get_subdomain_info/1,
get_all_subdomains_for_domain/1]).

-type domain() :: jid:lserver().
-type host_type() :: mongooseim:host_type().
Expand Down Expand Up @@ -168,3 +169,8 @@ register_subdomain(HostType, SubdomainPattern, PacketHandler) ->
-spec unregister_subdomain(host_type(), subdomain_pattern()) -> ok.
unregister_subdomain(HostType, SubdomainPattern) ->
mongoose_subdomain_core:unregister_subdomain(HostType, SubdomainPattern).

-spec get_all_subdomains_for_domain(domain()) ->
[mongoose_subdomain_core:subdomain_info()].
get_all_subdomains_for_domain(Domain) ->
mongoose_subdomain_core:get_all_subdomains_for_domain(Domain).
20 changes: 9 additions & 11 deletions src/ejabberd_local.erl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
%% Hooks callbacks

-export([node_cleanup/2,
add_local_features/5]).
disco_local_features/1]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
Expand Down Expand Up @@ -237,13 +237,11 @@ register_host(Host) ->
unregister_host(Host) ->
gen_server:call(?MODULE, {unregister_host, Host}).

-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(),
ejabberd:lang()) ->
mongoose_disco:feature_acc().
add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) ->
-spec disco_local_features(mongoose_disco:feature_acc()) -> mongoose_disco:feature_acc().
disco_local_features(Acc = #{to_jid := #jid{lserver = LServer}, node := <<>>}) ->
Features = ets:lookup_element(?NSTABLE, LServer, 2),
mongoose_disco:add_features(Features, Acc);
add_local_features(Acc, _From, _To, _Node, _Lang) ->
disco_local_features(Acc) ->
Acc.

%%====================================================================
Expand Down Expand Up @@ -371,7 +369,9 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------

hooks() ->
[{node_cleanup, global, ?MODULE, node_cleanup, 50}].
[{node_cleanup, global, ?MODULE, node_cleanup, 50} |
[{disco_local_features, HostType, ?MODULE, disco_local_features, 99} ||
HostType <- ?ALL_HOST_TYPES]].

-spec do_route(Acc :: mongoose_acc:t(),
From :: jid:jid(),
Expand Down Expand Up @@ -464,9 +464,7 @@ cancel_timer(TRef) ->
end.

do_register_host(Host) ->
ejabberd_router:register_route(Host, mongoose_packet_handler:new(?MODULE)),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, add_local_features, 99).
ejabberd_router:register_route(Host, mongoose_packet_handler:new(?MODULE)).

do_unregister_host(Host) ->
ejabberd_router:unregister_route(Host),
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, add_local_features, 99).
ejabberd_router:unregister_route(Host).
8 changes: 8 additions & 0 deletions src/ejabberd_router.erl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
dirty_get_all_domains/0,
dirty_get_all_routes/1,
dirty_get_all_domains/1,
dirty_get_all_components/1,
register_components/2,
register_components/3,
register_components/4,
Expand Down Expand Up @@ -402,6 +403,13 @@ all_routes(only_public) ->
++
mnesia:dirty_select(external_component_global, [MatchNonHidden]).

-spec dirty_get_all_components(return_hidden()) -> [jid:lserver()].
dirty_get_all_components(all) ->
mnesia:dirty_all_keys(external_component_global);
dirty_get_all_components(only_public) ->
MatchNonHidden = {#external_component{ domain = '$1', is_hidden = false, _ = '_' }, [], ['$1']},
mnesia:dirty_select(external_component_global, [MatchNonHidden]).


%%====================================================================
%% gen_server callbacks
Expand Down
Loading