1: -module(mim_c2s_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include_lib("common_test/include/ct.hrl"). 6: -include_lib("eunit/include/eunit.hrl"). 7: -include_lib("exml/include/exml_stream.hrl"). 8: -include_lib("escalus/include/escalus.hrl"). 9: -include_lib("escalus/include/escalus_xmlns.hrl"). 10: -define(BAD_RESOURCE, <<"\x{EFBB}"/utf8>>). 11: 12: -import(distributed_helper, [mim/0]). 13: 14: %%-------------------------------------------------------------------- 15: %% Suite configuration 16: %%-------------------------------------------------------------------- 17: 18: all() -> 19: [ 20: {group, basic}, 21: {group, backwards_compatible_session} 22: ]. 23: 24: groups() -> 25: [ 26: {basic, [parallel], 27: [ 28: client_sets_stream_from_server_answers_with_to, 29: stream_from_does_not_match_sasl_jid_results_in_stream_error, 30: two_users_can_log_and_chat, 31: too_big_stanza_rejected, 32: message_sent_to_malformed_jid_results_in_error, 33: verify_session_establishment_is_not_announced, 34: invalid_resource_fails_to_log 35: ]}, 36: {backwards_compatible_session, [parallel], 37: [ 38: verify_session_establishment_is_announced 39: ]} 40: ]. 41: 42: %%-------------------------------------------------------------------- 43: %% Init & teardown 44: %%-------------------------------------------------------------------- 45: init_per_suite(Config) -> 46: HostType = domain_helper:host_type(), 47: Config1 = dynamic_modules:save_modules(HostType, Config), 48: dynamic_modules:ensure_stopped(HostType, [mod_presence]), 49: EscalusOverrides = [{initial_activity, fun(_) -> ok end}, 50: {start_ready_clients, fun ?MODULE:escalus_start/2}], 51: escalus:init_per_suite([{escalus_overrides, EscalusOverrides} | Config1 ]). 52: 53: end_per_suite(Config) -> 54: dynamic_modules:restore_modules(Config), 55: mongoose_helper:restore_config(Config), 56: escalus:end_per_suite(Config). 57: 58: init_per_group(basic, Config) -> 59: Steps = [start_stream, stream_features, maybe_use_ssl, authenticate, bind], 60: Config1 = save_c2s_listener(Config), 61: Config2 = escalus_users:update_userspec(Config1, alice, connection_steps, Steps), 62: Config3 = escalus_users:update_userspec(Config2, bob, connection_steps, Steps), 63: configure_c2s_listener(Config3, #{backwards_compatible_session => false, max_stanza_size => 1024}), 64: Config3; 65: init_per_group(backwards_compatible_session, Config) -> 66: Config. 67: 68: end_per_group(basic, Config) -> 69: escalus_fresh:clean(), 70: restore_c2s_listener(Config), 71: Config; 72: end_per_group(backwards_compatible_session, Config) -> 73: escalus_fresh:clean(), 74: Config. 75: 76: init_per_testcase(Name, Config) -> 77: escalus:init_per_testcase(Name, Config). 78: 79: end_per_testcase(Name, Config) -> 80: escalus:end_per_testcase(Name, Config). 81: 82: %%-------------------------------------------------------------------- 83: %% tests 84: %%-------------------------------------------------------------------- 85: client_sets_stream_from_server_answers_with_to(Config) -> 86: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 87: Alice = escalus_connection:connect(AliceSpec), 88: escalus_client:send(Alice, stream_start(Alice)), 89: [StreamStartAnswer, _StreamFeatures] = escalus_client:wait_for_stanzas(Alice, 2, 500), 90: #xmlstreamstart{name = <<"stream:stream">>, attrs = Attrs} = StreamStartAnswer, 91: FromClient = jid:from_binary(escalus_utils:get_jid(Alice)), 92: {_, FromServerBin} = lists:keyfind(<<"to">>, 1, Attrs), 93: FromServer = jid:from_binary(FromServerBin), 94: ?assert(jid:are_equal(FromClient, FromServer)), 95: escalus_connection:stop(Alice). 96: 97: stream_from_does_not_match_sasl_jid_results_in_stream_error(Config) -> 98: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 99: Alice = escalus_connection:connect(AliceSpec), 100: Server = escalus_utils:get_server(Alice), 101: escalus_client:send(Alice, stream_start(Server, <<"not_alice@", Server/binary>>)), 102: [_StreamStartAnswer, _StreamFeatures] = escalus_client:wait_for_stanzas(Alice, 2, 500), 103: try escalus_auth:auth_plain(Alice, AliceSpec) of 104: _ -> error(authentication_with_inconsistent_jid_succeeded) 105: catch 106: throw:{auth_failed, _User, AuthReply} -> 107: escalus:assert(is_stream_error, [<<"invalid-from">>, <<>>], AuthReply), 108: escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)), 109: true = escalus_connection:wait_for_close(Alice, timer:seconds(1)) 110: end. 111: 112: two_users_can_log_and_chat(Config) -> 113: AliceHost = escalus_users:get_server(Config, alice), 114: HostType = domain_helper:domain_to_host_type(mim(), AliceHost), 115: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 116: MongooseMetrics = [{[global, data, xmpp, received, xml_stanza_size], changed}, 117: {[global, data, xmpp, sent, xml_stanza_size], changed}, 118: {[global, data, xmpp, received, c2s, tcp], changed}, 119: {[global, data, xmpp, sent, c2s, tcp], changed}, 120: {[HostTypePrefix, data, xmpp, c2s, message, processing_time], changed}, 121: {[global, data, xmpp, received, c2s, tls], 0}, 122: {[global, data, xmpp, sent, c2s, tls], 0}], 123: escalus:fresh_story([{mongoose_metrics, MongooseMetrics} | Config], 124: [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 125: escalus_client:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)), 126: escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Bob)), 127: escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Hi!">>)), 128: escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Alice)) 129: end). 130: 131: too_big_stanza_rejected(Config) -> 132: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 133: {ok, Alice, _Features} = escalus_connection:start(AliceSpec), 134: BigBody = base16:encode(crypto:strong_rand_bytes(1024)), 135: escalus_client:send(Alice, escalus_stanza:chat_to(Alice, BigBody)), 136: escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], escalus_client:wait_for_stanza(Alice)), 137: escalus:assert(is_stream_end, escalus_client:wait_for_stanza(Alice)), 138: true = escalus_connection:wait_for_close(Alice, timer:seconds(1)). 139: 140: message_sent_to_malformed_jid_results_in_error(Config) -> 141: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 142: % Alice sends message with malformed "to" 143: Stanza = escalus_client:send_and_wait(Alice, 144: escalus_stanza:chat_to(<<"@invalid">>, <<"Hi!">>)), 145: % Alice receives error 146: escalus_assert:is_error(Stanza, <<"modify">>, <<"jid-malformed">>), 147: % Alice resends message with proper "to" 148: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)), 149: % Bob gets the message 150: escalus_assert:is_chat_message(<<"Hi!">>, escalus_client:wait_for_stanza(Bob)) 151: end). 152: 153: invalid_resource_fails_to_log(Config) -> 154: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 155: Steps = [start_stream, stream_features, authenticate], 156: {ok, Alice, _Features} = escalus_connection:start(AliceSpec, Steps), 157: BindStanza = escalus_stanza:bind(?BAD_RESOURCE), 158: escalus_connection:send(Alice, BindStanza), 159: Response = escalus_client:wait_for_stanza(Alice), 160: escalus_assert:is_error(Response, <<"modify">>, <<"bad-request">>), 161: escalus_connection:stop(Alice). 162: 163: verify_session_establishment_is_not_announced(Config) -> 164: MaybeSessionFeature = start_connection_maybe_get_session_feature(Config), 165: ?assertEqual(undefined, MaybeSessionFeature). 166: 167: verify_session_establishment_is_announced(Config) -> 168: MaybeSessionFeature = start_connection_maybe_get_session_feature(Config), 169: ?assertNotEqual(undefined, MaybeSessionFeature). 170: 171: start_connection_maybe_get_session_feature(Config) -> 172: Steps = [start_stream, stream_features], 173: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 174: {ok, Client = #client{props = Props}, _} = escalus_connection:start(AliceSpec, Steps), 175: ok = escalus_auth:auth_plain(Client, Props), 176: escalus_connection:reset_parser(Client), 177: Client1 = escalus_session:start_stream(Client), 178: Features = escalus_connection:get_stanza(Client1, wait_for_features), 179: escalus_connection:stop(Client1), 180: exml_query:path(Features, [{element_with_ns, <<"session">>, ?NS_SESSION}]). 181: 182: %%-------------------------------------------------------------------- 183: %% helpers 184: %%-------------------------------------------------------------------- 185: 186: stream_start(Client) -> 187: Server = escalus_utils:get_server(Client), 188: From = escalus_utils:get_jid(Client), 189: stream_start(Server, From). 190: 191: stream_start(Server, From) -> 192: #xmlstreamstart{name = <<"stream:stream">>, 193: attrs = [{<<"to">>, Server}, 194: {<<"from">>, From}, 195: {<<"version">>, <<"1.0">>}, 196: {<<"xml:lang">>, <<"en">>}, 197: {<<"xmlns">>, <<"jabber:client">>}, 198: {<<"xmlns:stream">>, <<"http://etherx.jabber.org/streams">>}]}. 199: 200: save_c2s_listener(Config) -> 201: C2SPort = ct:get_config({hosts, mim, c2s_port}), 202: [C2SListener] = mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => mongoose_c2s_listener}), 203: [{c2s_listener, C2SListener} | Config]. 204: 205: restore_c2s_listener(Config) -> 206: C2SListener = ?config(c2s_listener, Config), 207: mongoose_helper:restart_listener(mim(), C2SListener). 208: 209: configure_c2s_listener(Config, ExtraC2SOpts) -> 210: C2SListener = ?config(c2s_listener, Config), 211: NewC2SListener = maps:merge(C2SListener, ExtraC2SOpts), 212: mongoose_helper:restart_listener(mim(), NewC2SListener). 213: 214: escalus_start(Cfg, FlatCDs) -> 215: {_, RClients} = lists:foldl( 216: fun({UserSpec, BaseResource}, {N, Acc}) -> 217: Resource = escalus_overridables:do(Cfg, modify_resource, [BaseResource], 218: {escalus_utils, identity}), 219: {ok, Client} = escalus_client:start(Cfg, UserSpec, Resource), 220: {N+1, [Client|Acc]} 221: end, {1, []}, FlatCDs), 222: Clients = lists:reverse(RClients), 223: [ escalus_assert:has_no_stanzas(Client) || Client <- Clients ], 224: Clients.