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