1: -module(disco_and_caps_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("eunit/include/eunit.hrl"). 5: -include_lib("escalus/include/escalus_xmlns.hrl"). 6: 7: -import(domain_helper, [host_type/0, domain/0]). 8: -import(config_parser_helper, [default_mod_config/1, mod_config/2]). 9: 10: all() -> 11: [{group, disco_with_caps}, 12: {group, disco_with_extra_features}]. 13: 14: groups() -> 15: G = [{disco_with_caps, [parallel], basic_test_cases() ++ caps_test_cases()}, 16: {disco_with_extra_features, [parallel], basic_test_cases() ++ extra_feature_test_cases()}], 17: ct_helper:repeat_all_until_all_ok(G). 18: 19: basic_test_cases() -> 20: [user_cannot_query_stranger_resources, 21: user_cannot_query_stranger_features, 22: user_can_query_friend_resources, 23: user_can_query_friend_features, 24: user_cannot_query_own_resources_with_unknown_node, 25: user_cannot_query_friend_resources_with_unknown_node, 26: user_can_query_server_features]. 27: 28: caps_test_cases() -> 29: [caps_feature_is_advertised, 30: user_can_query_server_caps_via_disco]. 31: 32: extra_feature_test_cases() -> 33: [user_can_query_extra_domains, 34: user_can_query_server_info]. 35: 36: init_per_suite(C) -> 37: C. 38: 39: end_per_suite(C) -> 40: escalus_fresh:clean(), 41: escalus:end_per_suite(C). 42: 43: init_per_group(Name, C) -> 44: C2 = escalus:init_per_suite(dynamic_modules:save_modules(host_type(), C)), 45: dynamic_modules:ensure_modules(host_type(), required_modules(Name)), 46: C2. 47: 48: end_per_group(_Name, C) -> 49: dynamic_modules:restore_modules(C). 50: 51: init_per_testcase(Name, C) -> 52: escalus:init_per_testcase(Name, C). 53: 54: end_per_testcase(Name, C) -> 55: escalus:end_per_testcase(Name, C). 56: 57: caps_feature_is_advertised(Config) -> 58: Spec = escalus_users:get_userspec(Config, alice), 59: {ok, Connection, Features} = escalus_connection:start(Spec, [start_stream, stream_features]), 60: true = is_map(proplists:get_value(caps, Features)), 61: escalus_connection:stop(Connection). 62: 63: user_can_query_server_caps_via_disco(Config) -> 64: NewConfig = escalus_fresh:create_users(Config, [{alice, 1}]), 65: Spec = escalus_users:get_userspec(NewConfig, alice), 66: {ok, Alice, Features} = escalus_connection:start(Spec), 67: #{<<"node">> := Node, 68: <<"ver">> := Ver} = proplists:get_value(caps, Features), 69: NodeVer = <<Node/binary, $#, Ver/binary>>, 70: Server = proplists:get_value(server, Spec), 71: Disco = escalus_stanza:disco_info(Server, NodeVer), 72: escalus:send(Alice, Disco), 73: DiscoResp = escalus:wait_for_stanza(Alice), 74: escalus:assert(is_iq_result, [Disco], DiscoResp), 75: Identity = exml_query:path(DiscoResp, [{element, <<"query">>}, 76: {element, <<"identity">>}, 77: {attr, <<"name">>}]), 78: <<"MongooseIM">> = Identity, 79: escalus_connection:stop(Alice). 80: 81: user_cannot_query_stranger_resources(Config) -> 82: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 83: BobJid = escalus_client:short_jid(Bob), 84: Request = escalus_stanza:disco_items(BobJid), 85: escalus:send(Alice, Request), 86: Stanza = escalus:wait_for_stanza(Alice), 87: escalus:assert(is_iq_error, [Request], Stanza), 88: escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Stanza), 89: escalus:assert(is_stanza_from, [BobJid], Stanza) 90: end). 91: 92: user_cannot_query_stranger_features(Config) -> 93: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 94: BobJid = escalus_client:short_jid(Bob), 95: Request = escalus_stanza:disco_info(BobJid), 96: escalus:send(Alice, Request), 97: Stanza = escalus:wait_for_stanza(Alice), 98: escalus:assert(is_iq_error, [Request], Stanza), 99: escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Stanza), 100: escalus:assert(is_stanza_from, [BobJid], Stanza) 101: end). 102: 103: user_can_query_friend_resources(Config) -> 104: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 105: escalus_story:make_all_clients_friends([Alice, Bob]), 106: BobJid = escalus_client:short_jid(Bob), 107: escalus:send(Alice, escalus_stanza:disco_items(BobJid)), 108: Stanza = escalus:wait_for_stanza(Alice), 109: Query = exml_query:subelement(Stanza, <<"query">>), 110: BobFullJid = escalus_client:full_jid(Bob), 111: BobName = escalus_client:username(Bob), 112: Item = exml_query:subelement_with_attr(Query, <<"jid">>, BobFullJid), 113: ?assertEqual(BobName, exml_query:attr(Item, <<"name">>)), 114: escalus:assert(is_stanza_from, [BobJid], Stanza) 115: end). 116: 117: user_can_query_friend_features(Config) -> 118: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 119: escalus_story:make_all_clients_friends([Alice, Bob]), 120: BobJid = escalus_client:short_jid(Bob), 121: escalus:send(Alice, escalus_stanza:disco_info(BobJid)), 122: Stanza = escalus:wait_for_stanza(Alice), 123: escalus:assert(has_identity, [<<"account">>, <<"registered">>], Stanza), 124: escalus:assert(is_stanza_from, [BobJid], Stanza) 125: end). 126: 127: user_cannot_query_own_resources_with_unknown_node(Config) -> 128: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 129: AliceJid = escalus_client:short_jid(Alice), 130: Request = escalus_stanza:disco_items(AliceJid, <<"unknown-node">>), 131: escalus:send(Alice, Request), 132: Stanza = escalus:wait_for_stanza(Alice), 133: escalus:assert(is_iq_error, [Request], Stanza), 134: escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], Stanza), 135: escalus:assert(is_stanza_from, [AliceJid], Stanza) 136: end). 137: 138: user_cannot_query_friend_resources_with_unknown_node(Config) -> 139: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 140: escalus_story:make_all_clients_friends([Alice, Bob]), 141: BobJid = escalus_client:short_jid(Bob), 142: Request = escalus_stanza:disco_items(BobJid, <<"unknown-node">>), 143: escalus:send(Alice, Request), 144: Stanza = escalus:wait_for_stanza(Alice), 145: escalus:assert(is_iq_error, [Request], Stanza), 146: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], Stanza), 147: escalus:assert(is_stanza_from, [BobJid], Stanza) 148: end). 149: 150: user_can_query_extra_domains(Config) -> 151: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 152: Server = escalus_client:server(Alice), 153: escalus:send(Alice, escalus_stanza:service_discovery(Server)), 154: Stanza = escalus:wait_for_stanza(Alice), 155: escalus:assert(has_service, [extra_domain()], Stanza), 156: escalus:assert(is_stanza_from, [domain()], Stanza) 157: end). 158: 159: user_can_query_server_features(Config) -> 160: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 161: Server = escalus_client:server(Alice), 162: escalus:send(Alice, escalus_stanza:disco_info(Server)), 163: Stanza = escalus:wait_for_stanza(Alice), 164: escalus:assert(has_identity, [<<"server">>, <<"im">>], Stanza), 165: escalus:assert(has_feature, [<<"iq">>], Stanza), 166: escalus:assert(has_feature, [<<"presence">>], Stanza), 167: escalus:assert(has_feature, [<<"presence-invisible">>], Stanza), 168: escalus:assert(is_stanza_from, [domain()], Stanza) 169: end). 170: 171: %% XEP-0157: Contact Addresses for XMPP Services 172: user_can_query_server_info(Config) -> 173: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 174: Server = escalus_client:server(Alice), 175: escalus:send(Alice, escalus_stanza:disco_info(Server)), 176: Stanza = escalus:wait_for_stanza(Alice), 177: escalus:assert(is_stanza_from, [domain()], Stanza), 178: 179: %% 'sales' is hidden for mod_disco, so only 'abuse' and 'admin' are expected 180: [HiddenField, AbuseField, AdminField] = get_form_fields(Stanza), 181: ?assertEqual(<<"FORM_TYPE">>, exml_query:attr(HiddenField, <<"var">>)), 182: ?assertEqual(<<"hidden">>, exml_query:attr(HiddenField, <<"type">>)), 183: ?assertEqual([?NS_SERVERINFO], exml_query:paths(HiddenField, [{element, <<"value">>}, 184: cdata])), 185: ?assertEqual(name(abuse), exml_query:attr(AbuseField, <<"var">>)), 186: ?assertEqual(urls(abuse), exml_query:paths(AbuseField, [{element, <<"value">>}, 187: cdata])), 188: ?assertEqual(name(admin), exml_query:attr(AdminField, <<"var">>)), 189: ?assertEqual(urls(admin), exml_query:paths(AdminField, [{element, <<"value">>}, 190: cdata])) 191: end). 192: 193: %% Helpers 194: 195: required_modules(disco_with_caps) -> 196: [{mod_caps, []}, 197: {mod_disco, default_mod_config(mod_disco)}]; 198: required_modules(disco_with_extra_features) -> 199: [{mod_disco, mod_config(mod_disco, extra_disco_opts())}]. 200: 201: extra_disco_opts() -> 202: #{extra_domains => [extra_domain()], 203: server_info => [server_info(abuse, #{}), 204: server_info(admin, #{modules => [mod_disco]}), 205: server_info(sales, #{modules => [mod_pubsub]})]}. 206: 207: get_form_fields(Stanza) -> 208: exml_query:paths(Stanza, [{element_with_ns, <<"query">>, ?NS_DISCO_INFO}, 209: {element_with_ns, <<"x">>, ?NS_DATA_FORMS}, 210: {element, <<"field">>}]). 211: 212: extra_domain() -> 213: <<"eXtra.example.com">>. 214: 215: server_info(Type, Extra) -> 216: maps:merge(#{name => name(Type), urls => urls(Type)}, Extra). 217: 218: name(abuse) -> <<"abuse-addresses">>; 219: name(admin) -> <<"admin-addresses">>; 220: name(sales) -> <<"sales-addresses">>. 221: 222: urls(abuse) -> [<<"abuse@example.com">>]; 223: urls(admin) -> [<<"admin@example.com">>, <<"operations@example.com">>]; 224: urls(sales) -> [<<"sales@example.com">>].