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