1: -module(bind2_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include_lib("stdlib/include/assert.hrl"). 6: -include_lib("exml/include/exml.hrl"). 7: -include_lib("escalus/include/escalus.hrl"). 8: -include_lib("escalus/include/escalus_xmlns.hrl"). 9: 10: -define(NS_CSI, <<"urn:xmpp:csi:0">>). 11: -define(NS_SASL_2, <<"urn:xmpp:sasl:2">>). 12: -define(NS_BIND_2, <<"urn:xmpp:bind:0">>). 13: -define(BAD_TAG, <<"\x{EFBB}"/utf8>>). 14: 15: %%-------------------------------------------------------------------- 16: %% Suite configuration 17: %%-------------------------------------------------------------------- 18: 19: all() -> 20: [ 21: {group, basic} 22: ]. 23: 24: groups() -> 25: [ 26: {basic, [parallel], 27: [ 28: server_announces_bind2_with_features, 29: auth_and_bind_ignores_invalid_resource_and_generates_a_new_one, 30: auth_and_bind_to_random_resource, 31: auth_and_bind_do_not_expose_user_agent_id_in_client, 32: auth_and_bind_contains_client_tag, 33: carbons_are_enabled_with_bind_inline_request, 34: csi_is_active_with_bind_inline_request, 35: csi_is_inactive_with_bind_inline_request, 36: stream_resumption_enable_sm_on_bind, 37: stream_resumption_enable_sm_on_bind_with_resume, 38: stream_resumption_failing_does_bind_and_contains_sm_status, 39: stream_resumption_overrides_bind_request 40: ]} 41: ]. 42: 43: %%-------------------------------------------------------------------- 44: %% Init & teardown 45: %%-------------------------------------------------------------------- 46: 47: init_per_suite(Config) -> 48: Config1 = load_modules(Config), 49: escalus:init_per_suite(Config1). 50: 51: end_per_suite(Config) -> 52: escalus_fresh:clean(), 53: dynamic_modules:restore_modules(Config), 54: escalus:end_per_suite(Config). 55: 56: init_per_group(_GroupName, Config) -> 57: Config. 58: 59: end_per_group(_GroupName, Config) -> 60: Config. 61: 62: init_per_testcase(Name, Config) -> 63: escalus:init_per_testcase(Name, Config). 64: 65: end_per_testcase(Name, Config) -> 66: escalus:end_per_testcase(Name, Config). 67: 68: load_modules(Config) -> 69: HostType = domain_helper:host_type(), 70: Config1 = dynamic_modules:save_modules(HostType, Config), 71: sasl2_helper:load_all_sasl2_modules(HostType), 72: Config1. 73: 74: %%-------------------------------------------------------------------- 75: %% tests 76: %%-------------------------------------------------------------------- 77: 78: server_announces_bind2_with_features(Config) -> 79: Steps = [create_connect_tls, start_stream_get_features], 80: #{features := Features} = sasl2_helper:apply_steps(Steps, Config), 81: Bind2 = exml_query:path(Features, [{element_with_ns, <<"authentication">>, ?NS_SASL_2}, 82: {element, <<"inline">>}, 83: {element_with_ns, <<"bind">>, ?NS_BIND_2}]), 84: ?assertNotEqual(undefined, Bind2), 85: InlineFeatures = exml_query:path(Bind2, [{element, <<"inline">>}]), 86: check_var(InlineFeatures, ?NS_STREAM_MGNT_3), 87: check_var(InlineFeatures, ?NS_CARBONS_2), 88: check_var(InlineFeatures, ?NS_CSI). 89: 90: auth_and_bind_ignores_invalid_resource_and_generates_a_new_one(Config) -> 91: Steps = [start_new_user, {?MODULE, auth_and_bind_wrong_resource}, receive_features, has_no_more_stanzas], 92: #{answer := Response} = sasl2_helper:apply_steps(Steps, Config), 93: Success = exml_query:path(Response, [{element_with_ns, <<"bound">>, ?NS_BIND_2}]), 94: ?assertNotEqual(undefined, Success). 95: 96: auth_and_bind_to_random_resource(Config) -> 97: Steps = [start_new_user, {?MODULE, auth_and_bind}, receive_features, has_no_more_stanzas], 98: #{answer := Success} = sasl2_helper:apply_steps(Steps, Config), 99: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 100: Bound = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}]), 101: ?assertNotEqual(undefined, Bound), 102: Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata]), 103: #jid{resource = LResource} = jid:from_binary(Identifier), 104: ?assert(0 =< byte_size(LResource), LResource). 105: 106: auth_and_bind_do_not_expose_user_agent_id_in_client(Config) -> 107: Steps = [start_new_user, {?MODULE, auth_and_bind_with_user_agent_uuid}, receive_features, has_no_more_stanzas], 108: #{answer := Success, uuid := Uuid} = sasl2_helper:apply_steps(Steps, Config), 109: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 110: Bound = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}]), 111: ?assertNotEqual(undefined, Bound), 112: Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata]), 113: #jid{resource = LResource} = jid:from_binary(Identifier), 114: ?assertNotEqual(Uuid, LResource). 115: 116: auth_and_bind_contains_client_tag(Config) -> 117: Steps = [start_new_user, {?MODULE, auth_and_bind_with_tag}, receive_features, has_no_more_stanzas], 118: #{answer := Success, tag := Tag} = sasl2_helper:apply_steps(Steps, Config), 119: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 120: Bound = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}]), 121: ?assertNotEqual(undefined, Bound), 122: Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata]), 123: #jid{resource = LResource} = jid:from_binary(Identifier), 124: ResourceParts = binary:split(LResource, <<"/">>, [global]), 125: ?assertMatch([Tag, _], ResourceParts). 126: 127: carbons_are_enabled_with_bind_inline_request(Config) -> 128: Steps = [start_new_user, 129: {?MODULE, start_peer}, 130: {?MODULE, auth_and_bind_with_carbon_copies}, receive_features, 131: {?MODULE, receive_message_carbon_arrives}, has_no_more_stanzas], 132: sasl2_helper:apply_steps(Steps, Config). 133: 134: csi_is_active_with_bind_inline_request(Config) -> 135: Steps = [start_new_user, 136: {?MODULE, start_peer}, 137: {?MODULE, auth_and_bind_with_csi_active}, receive_features, 138: {?MODULE, inactive_csi_msg_wont_arrive}, has_no_more_stanzas], 139: sasl2_helper:apply_steps(Steps, Config). 140: 141: csi_is_inactive_with_bind_inline_request(Config) -> 142: Steps = [start_new_user, 143: {?MODULE, start_peer}, 144: {?MODULE, auth_and_bind_with_csi_inactive}, has_no_more_stanzas, 145: {?MODULE, inactive_csi_msgs_do_not_arrive}, 146: {?MODULE, activate_csi}, receive_features, 147: {?MODULE, receive_csi_msgs}, has_no_more_stanzas], 148: sasl2_helper:apply_steps(Steps, Config). 149: 150: stream_resumption_enable_sm_on_bind(Config) -> 151: Steps = [start_new_user, 152: {?MODULE, start_peer}, 153: {?MODULE, auth_and_bind_with_sm_enabled}, 154: receive_features, has_no_more_stanzas], 155: #{answer := Success} = sasl2_helper:apply_steps(Steps, Config), 156: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 157: Enabled = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}, 158: {element_with_ns, <<"enabled">>, ?NS_STREAM_MGNT_3}]), 159: ?assertNotEqual(undefined, Enabled). 160: 161: stream_resumption_enable_sm_on_bind_with_resume(Config) -> 162: Steps = [start_new_user, 163: {?MODULE, start_peer}, 164: {?MODULE, auth_and_bind_with_sm_resume_enabled}, 165: receive_features, has_no_more_stanzas], 166: #{answer := Success} = sasl2_helper:apply_steps(Steps, Config), 167: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 168: Enabled = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}, 169: {element_with_ns, <<"enabled">>, ?NS_STREAM_MGNT_3}]), 170: ?assertNotEqual(undefined, Enabled). 171: 172: stream_resumption_failing_does_bind_and_contains_sm_status(Config) -> 173: Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features, 174: {?MODULE, auth_and_bind_with_resumption_unknown_smid}, 175: receive_features, has_no_more_stanzas], 176: #{answer := Success, tag := Tag} = sasl2_helper:apply_steps(Steps, Config), 177: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 178: Bound = exml_query:path(Success, [{element_with_ns, <<"bound">>, ?NS_BIND_2}]), 179: ?assertNotEqual(undefined, Bound), 180: Resumed = exml_query:path(Success, [{element_with_ns, <<"failed">>, ?NS_STREAM_MGNT_3}]), 181: escalus:assert(is_sm_failed, [<<"item-not-found">>], Resumed), 182: Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata]), 183: #jid{resource = LResource} = jid:from_binary(Identifier), 184: ResourceParts = binary:split(LResource, <<"/">>, [global]), 185: ?assertMatch([Tag, _], ResourceParts). 186: 187: stream_resumption_overrides_bind_request(Config) -> 188: Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features, 189: {?MODULE, auth_and_bind_with_resumption}, has_no_more_stanzas], 190: #{answer := Success, smid := SMID} = sasl2_helper:apply_steps(Steps, Config), 191: ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success), 192: Resumed = exml_query:path(Success, [{element_with_ns, <<"resumed">>, ?NS_STREAM_MGNT_3}]), 193: ?assert(escalus_pred:is_sm_resumed(SMID, Resumed)). 194: 195: 196: %% Step helpers 197: auth_and_bind(Config, Client, Data) -> 198: plain_auth(Config, Client, Data, [], []). 199: 200: auth_and_bind_wrong_resource(Config, Client, Data) -> 201: Tag = ?BAD_TAG, 202: plain_auth(Config, Client, Data#{tag => Tag}, [bind_tag(Tag)], []). 203: 204: auth_and_bind_with_user_agent_uuid(Config, Client, Data) -> 205: Uuid = uuid:uuid_to_string(uuid:get_v4(), binary_standard), 206: plain_auth(Config, Client, Data#{uuid => Uuid}, [], [good_user_agent_elem(Uuid)]). 207: 208: auth_and_bind_with_tag(Config, Client, Data) -> 209: Tag = uuid:uuid_to_string(uuid:get_v4(), binary_standard), 210: plain_auth(Config, Client, Data#{tag => Tag}, [bind_tag(Tag)], []). 211: 212: auth_and_bind_with_resumption_unknown_smid(Config, Client, Data) -> 213: Tag = uuid:uuid_to_string(uuid:get_v4(), binary_standard), 214: Resume = escalus_stanza:resume(<<"123456">>, 1), 215: plain_auth(Config, Client, Data#{tag => Tag}, [bind_tag(Tag)], [Resume]). 216: 217: auth_and_bind_with_resumption(Config, Client, #{smid := SMID, texts := Texts} = Data) -> 218: Tag = uuid:uuid_to_string(uuid:get_v4(), binary_standard), 219: Resume = escalus_stanza:resume(SMID, 1), 220: {Client1, Data1} = plain_auth(Config, Client, Data#{tag => Tag}, [bind_tag(Tag)], [Resume]), 221: Msgs = sm_helper:wait_for_messages(Client, Texts), 222: {Client1, Data1#{sm_storage => Msgs}}. 223: 224: auth_and_bind_with_carbon_copies(Config, Client, #{spec := Spec} = Data) -> 225: CarbonEnable = enable_carbons_el(), 226: {Client1, Data1} = plain_auth(Config, Client, Data, [CarbonEnable], []), 227: Resource = <<"second_resource">>, 228: {ok, Client2, _} = escalus_connection:start( 229: [{carbons, true}, {resource, Resource} | Spec]), 230: Jid = <<(escalus_client:short_jid(Client2))/binary, "/", Resource/binary>>, 231: {Client1, Data1#{client_2 => Client2, client_2_jid => Jid}}. 232: 233: auth_and_bind_with_csi_active(Config, Client, Data) -> 234: CsiActive = csi_helper:csi_stanza(<<"active">>), 235: plain_auth(Config, Client, Data, [CsiActive], []). 236: 237: inactive_csi_msg_wont_arrive(_Config, Client, #{peer := Bob} = Data) -> 238: escalus_client:send(Bob, escalus_stanza:chat_to(Client, <<"hello 1">>)), 239: AliceReceived = escalus_client:wait_for_stanza(Client), 240: escalus:assert(is_message, AliceReceived), 241: csi_helper:given_client_is_inactive_and_no_messages_arrive(Client), 242: csi_helper:given_messages_are_sent(Client, Bob, 1), 243: csi_helper:then_client_does_not_receive_any_message(Client), 244: {Client, Data}. 245: 246: auth_and_bind_with_csi_inactive(Config, Client, Data) -> 247: CsiInactive = csi_helper:csi_stanza(<<"inactive">>), 248: plain_auth(Config, Client, Data, [CsiInactive], []). 249: 250: inactive_csi_msgs_do_not_arrive(_Config, Client, #{peer := Bob} = Data) -> 251: Msgs = csi_helper:given_messages_are_sent(Client, Bob, 2), 252: csi_helper:then_client_does_not_receive_any_message(Client), 253: {Client, Data#{msgs => Msgs}}. 254: 255: activate_csi(_Config, Client, Data) -> 256: csi_helper:given_client_is_active(Client), 257: {Client, Data}. 258: 259: receive_csi_msgs(_Config, Client, #{msgs := Msgs} = Data) -> 260: csi_helper:then_client_receives_message(Client, Msgs), 261: {Client, Data}. 262: 263: auth_and_bind_with_sm_enabled(Config, Client, Data) -> 264: SmEnable = escalus_stanza:enable_sm(), 265: plain_auth(Config, Client, Data, [SmEnable], []). 266: 267: auth_and_bind_with_sm_resume_enabled(Config, Client, Data) -> 268: SmEnable = escalus_stanza:enable_sm([{resume, true}]), 269: plain_auth(Config, Client, Data, [SmEnable], []). 270: 271: receive_message_carbon_arrives( 272: _Config, Client1, #{client_1_jid := Jid1, client_2_jid := Jid2, 273: client_2 := Client2, peer := Bob} = Data) -> 274: escalus_client:send(Bob, escalus_stanza:chat_to(Jid1, <<"hello 1">>)), 275: Answers1 = [ escalus_client:wait_for_stanza(C) || C <- [Client1, Client2]], 276: escalus_client:send(Bob, escalus_stanza:chat_to(Jid2, <<"hello 2">>)), 277: Answers2 = [ escalus_client:wait_for_stanza(C) || C <- [Client1, Client2]], 278: {Client1, Data#{answers_1 => Answers1, answers_2 => Answers2}}. 279: 280: plain_auth(_Config, Client, Data, BindElems, Extra) -> 281: InitEl = sasl2_helper:plain_auth_initial_response(Client), 282: BindEl = #xmlel{name = <<"bind">>, 283: attrs = [{<<"xmlns">>, ?NS_BIND_2}], 284: children = BindElems}, 285: Authenticate = auth_elem(<<"PLAIN">>, [InitEl, BindEl | Extra]), 286: escalus:send(Client, Authenticate), 287: Answer = escalus_client:wait_for_stanza(Client), 288: Identifier = exml_query:path(Answer, [{element, <<"authorization-identifier">>}, cdata]), 289: #jid{resource = LResource} = jid:from_binary(Identifier), 290: {Client, Data#{answer => Answer, client_1_jid => Identifier, bind2_resource => LResource}}. 291: 292: start_peer(Config, Client, Data) -> 293: BobSpec = escalus_fresh:create_fresh_user(Config, bob), 294: {ok, Bob, _} = escalus_connection:start(BobSpec), 295: {Client, Data#{peer => Bob}}. 296: 297: %% XML helpers 298: auth_elem(Mech, Children) -> 299: #xmlel{name = <<"authenticate">>, 300: attrs = [{<<"xmlns">>, ?NS_SASL_2}, {<<"mechanism">>, Mech}], 301: children = Children}. 302: 303: bind_tag(Tag) -> 304: #xmlel{name = <<"tag">>, children = [#xmlcdata{content = Tag}]}. 305: 306: enable_carbons_el() -> 307: #xmlel{name = <<"enable">>, 308: attrs = [{<<"xmlns">>, ?NS_CARBONS_2}]}. 309: 310: good_user_agent_elem(Uuid) -> 311: user_agent_elem(Uuid, <<"cool-xmpp-client">>, <<"latest-and-greatest-device">>). 312: 313: user_agent_elem(Id, Software, Device) -> 314: SoftEl = [#xmlel{name = <<"software">>, children = [#xmlcdata{content = Value}]} 315: || Value <- [Software], Value =/= undefined ], 316: DeviEl = [#xmlel{name = <<"device">>, children = [#xmlcdata{content = Value}]} 317: || Value <- [Device], Value =/= undefined ], 318: Attrs = [ {<<"id">>, Value} || Value <- [Id], Value =/= undefined ], 319: #xmlel{name = <<"user-agent">>, attrs = Attrs, children = SoftEl ++ DeviEl}. 320: 321: check_var(InlineFeatures, NS) -> 322: Var = exml_query:subelement_with_attr(InlineFeatures, <<"var">>, NS), 323: ?assertNotEqual(undefined, Var).