1: -module(gdpr_SUITE). 2: 3: %% Tests for features related to GDPR compliance. 4: 5: -include_lib("common_test/include/ct.hrl"). 6: -include_lib("escalus/include/escalus.hrl"). 7: -include_lib("exml/include/exml.hrl"). 8: -include("muc_light.hrl"). 9: 10: -export([suite/0, all/0, groups/0]). 11: -export([init_per_suite/1, end_per_suite/1]). 12: -export([init_per_group/2, end_per_group/2]). 13: -export([init_per_testcase/2, end_per_testcase/2]). 14: -export([ 15: retrieve_vcard/1, 16: remove_vcard/1, 17: remove_private/1, 18: remove_multiple_private_xmls/1, 19: dont_remove_other_user_private_xml/1, 20: retrieve_roster/1, 21: retrieve_mam_pm/1, 22: retrieve_mam_muc/1, 23: retrieve_mam_muc_private_msg/1, 24: retrieve_mam_muc_store_pm/1, 25: remove_mam_pm/1, 26: retrieve_mam_muc_light/1, 27: retrieve_mam_pm_and_muc_light_interfere/1, 28: retrieve_mam_pm_and_muc_light_dont_interfere/1, 29: remove_roster/1, 30: retrieve_offline/1, 31: remove_offline/1, 32: retrieve_pubsub_payloads/1, 33: retrieve_created_pubsub_nodes/1, 34: retrieve_all_pubsub_data/1, 35: dont_retrieve_other_user_pubsub_payload/1, 36: retrieve_pubsub_subscriptions/1, 37: retrieve_private_xml/1, 38: dont_retrieve_other_user_private_xml/1, 39: retrieve_multiple_private_xmls/1, 40: retrieve_inbox/1, 41: remove_inbox/1, 42: retrieve_inbox_for_multiple_messages/1, 43: retrieve_inbox_muclight/1, 44: retrieve_inbox_muc/1, 45: remove_inbox_muclight/1, 46: remove_inbox_muc/1, 47: retrieve_logs/1, 48: remove_pubsub_all_data/1, 49: remove_pubsub_dont_remove_node_when_only_publisher/1, 50: remove_pubsub_subscriptions/1, 51: remove_pubsub_dont_remove_flat_pubsub_node/1, 52: remove_pubsub_push_node/1, 53: remove_pubsub_pep_node/1 54: ]). 55: -export([ 56: data_is_not_retrieved_for_missing_user/1 57: ]). 58: 59: -import(mongooseimctl_helper, [mongooseimctl/3]). 60: -import(distributed_helper, [mim/0, subhost_pattern/1, rpc/4]). 61: -import(muc_light_helper, [room_bin_jid/1]). 62: -import(domain_helper, [host_type/0]). 63: 64: -define(ROOM, <<"tt1">>). 65: 66: %% ------------------------------------------------------------- 67: %% Common Test stuff 68: %% ------------------------------------------------------------- 69: 70: suite() -> 71: escalus:suite(). 72: 73: all() -> 74: [ 75: {group, retrieve_personal_data}, 76: {group, retrieve_negative}, 77: {group, remove_personal_data} 78: ]. 79: 80: groups() -> 81: %% **DON'T** make any of these groups parallel, because calling mongooseimctl 82: %% in parallel is broken! 83: [ 84: {retrieve_personal_data, [], [ 85: retrieve_vcard, 86: retrieve_roster, 87: retrieve_offline, 88: retrieve_inbox, 89: retrieve_logs, 90: {group, retrieve_personal_data_pubsub}, 91: {group, retrieve_personal_data_private_xml}, 92: {group, retrieve_personal_data_mam}, 93: {group, retrieve_personal_data_inbox} 94: ]}, 95: {retrieve_personal_data_inbox, [],[ 96: retrieve_inbox, 97: retrieve_inbox_for_multiple_messages, 98: retrieve_inbox_muclight, 99: retrieve_inbox_muc 100: ]}, 101: {retrieve_personal_data_pubsub, [], [ 102: retrieve_pubsub_payloads, 103: dont_retrieve_other_user_pubsub_payload, 104: retrieve_pubsub_subscriptions, 105: retrieve_created_pubsub_nodes, 106: retrieve_all_pubsub_data 107: ]}, 108: {retrieve_personal_data_private_xml, [], [ 109: retrieve_private_xml, 110: dont_retrieve_other_user_private_xml, 111: retrieve_multiple_private_xmls 112: ]}, 113: {retrieve_negative, [], [ 114: data_is_not_retrieved_for_missing_user 115: ]}, 116: {retrieve_personal_data_mam, [], [ 117: {group, retrieve_personal_data_mam_rdbms}, 118: {group, retrieve_personal_data_mam_riak}, 119: {group, retrieve_personal_data_mam_cassandra}, 120: {group, retrieve_personal_data_mam_elasticsearch} 121: ]}, 122: {retrieve_personal_data_mam_rdbms, [], all_mam_testcases()}, 123: {retrieve_personal_data_mam_riak, [], all_mam_testcases()}, 124: {retrieve_personal_data_mam_cassandra, [], all_mam_testcases()}, 125: {retrieve_personal_data_mam_elasticsearch, [], all_mam_testcases()}, 126: {remove_personal_data, [], removal_testcases()}, 127: {remove_personal_data_inbox, [], [remove_inbox, remove_inbox_muclight, remove_inbox_muc]}, 128: {remove_personal_data_mam, [], [ 129: {group, remove_personal_data_mam_rdbms}, 130: {group, remove_personal_data_mam_riak}, 131: {group, remove_personal_data_mam_cassandra}, 132: {group, remove_personal_data_mam_elasticsearch} 133: ]}, 134: {remove_personal_data_mam_rdbms, [], mam_removal_testcases()}, 135: {remove_personal_data_mam_riak, [], mam_removal_testcases()}, 136: {remove_personal_data_mam_cassandra, [], mam_removal_testcases()}, 137: {remove_personal_data_mam_elasticsearch, [], mam_removal_testcases()}, 138: {remove_personal_data_pubsub, [], [ 139: remove_pubsub_subscriptions, 140: remove_pubsub_dont_remove_node_when_only_publisher, 141: remove_pubsub_dont_remove_flat_pubsub_node, 142: remove_pubsub_push_node, 143: remove_pubsub_pep_node, 144: remove_pubsub_all_data 145: ]} 146: ]. 147: 148: removal_testcases() -> 149: [ 150: remove_vcard, 151: remove_roster, 152: remove_offline, 153: remove_private, 154: remove_multiple_private_xmls, 155: dont_remove_other_user_private_xml, 156: {group, remove_personal_data_inbox}, 157: {group, remove_personal_data_pubsub}, 158: {group, remove_personal_data_mam} 159: ]. 160: 161: mam_removal_testcases() -> 162: [ 163: remove_mam_pm 164: ]. 165: 166: 167: mam_testcases() -> 168: [ 169: retrieve_mam_pm, 170: retrieve_mam_muc_light, 171: retrieve_mam_pm_and_muc_light_interfere, 172: retrieve_mam_pm_and_muc_light_dont_interfere 173: ]. 174: 175: all_mam_testcases() -> 176: [ 177: retrieve_mam_muc, 178: retrieve_mam_muc_private_msg, 179: retrieve_mam_muc_store_pm 180: | mam_testcases() 181: ]. 182: 183: init_per_suite(Config) -> 184: #{node := MimNode} = distributed_helper:mim(), 185: Config1 = [{{ejabberd_cwd, MimNode}, get_mim_cwd()} | dynamic_modules:save_modules(host_type(), Config)], 186: muc_helper:load_muc(), 187: escalus:init_per_suite(Config1). 188: 189: end_per_suite(Config) -> 190: delete_files(), 191: escalus_fresh:clean(), 192: dynamic_modules:restore_modules(Config), 193: escalus:end_per_suite(Config). 194: 195: init_per_group(GN, Config) when GN =:= remove_personal_data_mam_rdbms; 196: GN =:= retrieve_personal_data_mam_rdbms -> 197: try_backend_for_mam(Config, rdbms); 198: init_per_group(GN, Config) when GN =:= retrieve_personal_data_pubsub; 199: GN =:= remove_personal_data_pubsub -> 200: [{group, GN} | Config]; 201: init_per_group(GN, Config) when GN =:= retrieve_personal_data_mam_riak; 202: GN =:= remove_personal_data_mam_riak -> 203: try_backend_for_mam(Config, riak); 204: init_per_group(GN, Config) when GN =:= retrieve_personal_data_mam_cassandra; 205: GN =:= remove_personal_data_mam_cassandra-> 206: try_backend_for_mam(Config, cassandra); 207: init_per_group(GN, Config) when GN =:= retrieve_personal_data_mam_elasticsearch; 208: GN =:= remove_personal_data_mam_elasticsearch -> 209: try_backend_for_mam(Config, elasticsearch); 210: init_per_group(retrieve_personal_data_inbox = GN, Config) -> 211: init_inbox(GN, Config, muclight); 212: init_per_group(remove_personal_data_inbox = GN, Config) -> 213: init_inbox(GN, Config, muclight); 214: init_per_group(retrieve_personal_data_private_xml, Config) -> 215: private_started(), 216: Config; 217: init_per_group(_GN, Config) -> 218: Config. 219: 220: end_per_group(_GN, Config) -> 221: Config. 222: 223: try_backend_for_mam(Config, Backend) -> 224: case is_backend_enabled(Backend) of 225: true -> [{mam_backend, Backend} | Config]; 226: false -> {skip, backend_is_not_configured} 227: end. 228: 229: is_backend_enabled(rdbms) -> mongoose_helper:is_rdbms_enabled(host_type()); 230: is_backend_enabled(riak) -> mam_helper:is_riak_enabled(host_type()); 231: is_backend_enabled(cassandra) -> mam_helper:is_cassandra_enabled(host_type()); 232: is_backend_enabled(elasticsearch) -> mam_helper:is_elasticsearch_enabled(host_type()). 233: 234: 235: init_per_testcase(retrieve_logs = CN, Config) -> 236: case is_mim2_started() of 237: false -> {skip, not_running_in_distributed}; 238: _ -> escalus:init_per_testcase(CN, Config) 239: end; 240: init_per_testcase(CN, Config) when CN =:= remove_offline; 241: CN =:= retrieve_offline -> 242: offline_started(), 243: escalus:init_per_testcase(CN, Config); 244: init_per_testcase(CN, Config) when 245: CN =:= remove_inbox; 246: CN =:= retrieve_inbox; 247: CN =:= remove_inbox_muclight; 248: CN =:= retrieve_inbox_muclight -> 249: Config1 = init_inbox(CN, Config, muclight), 250: Config1; 251: init_per_testcase(CN, Config) when CN =:= retrieve_inbox_muc; 252: CN =:= remove_inbox_muc -> 253: muc_helper:load_muc(), 254: Config0 = init_inbox(CN, Config, muc), 255: Config0; 256: 257: init_per_testcase(retrieve_vcard = CN, Config) -> 258: case vcard_helper:is_vcard_ldap() of 259: true -> 260: {skip, skipped_for_simplicity_for_now}; % TODO: Fix the case for LDAP as well 261: _ -> 262: escalus:init_per_testcase(CN, Config) 263: end; 264: init_per_testcase(remove_vcard = CN, Config) -> 265: case vcard_helper:is_vcard_ldap() of 266: true -> 267: {skip, skipped_for_simplicity_for_now}; % TODO: Fix the case for LDAP as well 268: _ -> 269: vcard_started(), 270: escalus:init_per_testcase(CN, Config) 271: end; 272: init_per_testcase(CN, Config) when CN =:= remove_private; 273: CN =:= dont_remove_other_user_private_xml; 274: CN =:= remove_multiple_private_xmls -> 275: private_started(), 276: escalus:init_per_testcase(CN, Config); 277: 278: init_per_testcase(CN, Config) when CN =:= retrieve_mam_muc; 279: CN =:= retrieve_mam_muc_private_msg; 280: CN =:= retrieve_mam_muc_store_pm; 281: CN =:= retrieve_mam_muc_light; 282: CN =:= retrieve_mam_pm_and_muc_light_interfere; 283: CN =:= retrieve_mam_pm_and_muc_light_dont_interfere; 284: CN =:= retrieve_mam_pm; 285: CN =:= remove_mam_pm -> 286: case proplists:get_value(mam_backend, Config, skip) of 287: skip -> 288: {skip, no_mam_backend_configured}; 289: Backend -> 290: dynamic_modules:restore_modules(Config), 291: RequiredModules = mam_required_modules(CN, Backend), 292: dynamic_modules:ensure_modules(host_type(), RequiredModules), 293: ct:log("required modules: ~p~n", [RequiredModules]), 294: escalus:init_per_testcase(CN, [{mam_modules, RequiredModules} | Config]) 295: end; 296: init_per_testcase(remove_roster = CN, Config) -> 297: roster_started(), 298: escalus:init_per_testcase(CN, Config); 299: init_per_testcase(CN, Config) -> 300: GN = proplists:get_value(group, Config), 301: IsPubSub = lists:member(GN, [retrieve_personal_data_pubsub, remove_personal_data_pubsub]), 302: case IsPubSub of 303: true -> 304: dynamic_modules:ensure_modules(host_type(), pubsub_required_modules()); 305: _ -> 306: ok 307: end, 308: escalus:init_per_testcase(CN, Config). 309: 310: 311: end_per_testcase(CN, Config) when CN =:= retrieve_mam_muc_light; 312: CN =:= retrieve_mam_pm_and_muc_light_interfere; 313: CN =:= retrieve_mam_pm_and_muc_light_dont_interfere -> 314: muc_light_helper:clear_db(host_type()), 315: escalus:end_per_testcase(CN, Config); 316: %% mod_inbox 317: end_per_testcase(CN, Config) when 318: CN =:= remove_inbox; 319: CN =:= retrieve_inbox; 320: CN =:= remove_inbox_muclight; 321: CN =:= retrieve_inbox_muclight -> 322: muc_light_helper:clear_db(host_type()), 323: escalus:end_per_testcase(CN, Config); 324: end_per_testcase(CN, Config) when CN =:= retrieve_inbox_muc; 325: CN =:= remove_inbox_muc -> 326: muc_helper:unload_muc(), 327: escalus:end_per_testcase(CN, Config); 328: end_per_testcase(CN, Config) -> 329: escalus_fresh:clean(), 330: escalus:end_per_testcase(CN, Config). 331: 332: init_inbox(CN, Config, GroupChatType) -> 333: case (not ct_helper:is_ct_running()) 334: orelse mongoose_helper:is_rdbms_enabled(host_type()) of 335: true -> 336: dynamic_modules:ensure_modules(host_type(), inbox_required_modules(GroupChatType)), 337: escalus:init_per_testcase(CN, Config); 338: false -> 339: {skip, require_rdbms} 340: end. 341: inbox_required_modules(Type) -> 342: GroupChatModules = groupchat_module(Type), 343: InboxOpts = (inbox_helper:inbox_opts())#{groupchat => [Type]}, 344: Inbox = {mod_inbox, InboxOpts}, 345: GroupChatModules ++ [Inbox] . 346: 347: groupchat_module(muc) -> 348: []; 349: groupchat_module(muclight) -> 350: [{mod_muc_light, 351: [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, 352: {backend, mongoose_helper:mnesia_or_rdbms_backend()}, 353: {rooms_in_rosters, true}]}]. 354: 355: mam_required_modules(CN, Backend) when CN =:= remove_mam_pm; 356: CN =:= retrieve_mam_pm -> 357: [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, pm => #{}})}]; 358: mam_required_modules(CN, Backend) when CN =:= retrieve_mam_pm_and_muc_light_dont_interfere; 359: CN =:= retrieve_mam_muc_light -> 360: HostPattern = subhost_pattern(muc_light_helper:muc_host_pattern()), 361: [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, 362: pm => #{}, 363: muc => #{host => HostPattern}})}, 364: {mod_muc_light, [{host, HostPattern}]}]; 365: mam_required_modules(retrieve_mam_pm_and_muc_light_interfere, Backend) -> 366: HostPattern = subhost_pattern(muc_light_helper:muc_host_pattern()), 367: [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, 368: db_message_format => mam_message_xml, 369: pm => #{archive_groupchats => true}, 370: muc => #{host => HostPattern}})}, 371: {mod_muc_light, [{host, HostPattern}]}]; 372: mam_required_modules(CN, Backend) when CN =:= retrieve_mam_muc_private_msg; 373: CN =:= retrieve_mam_muc -> 374: HostPattern = subhost_pattern(muc_helper:muc_host_pattern()), 375: [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, 376: pm => #{}, 377: muc => #{host => HostPattern}})}, 378: {mod_muc, [{host, HostPattern}]}]; 379: mam_required_modules(retrieve_mam_muc_store_pm, Backend) -> 380: HostPattern = subhost_pattern(muc_helper:muc_host_pattern()), 381: [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, 382: pm => #{archive_groupchats => true}, 383: muc => #{host => HostPattern}})}, 384: {mod_muc, [{host, HostPattern}]}]. 385: 386: pick_enabled_backend() -> 387: BackendsList = [ 388: {mam_helper:is_riak_enabled(host_type()), riak}, 389: {mongoose_helper:is_rdbms_enabled(host_type()), rdbms} 390: ], 391: proplists:get_value(true, BackendsList, mnesia). 392: 393: roster_required_modules() -> 394: Backend = pick_enabled_backend(), 395: [{mod_roster, roster_backend_opts(Backend)}]. 396: 397: roster_backend_opts(riak) -> 398: RiakDefaults = config_parser_helper:default_config([modules, mod_roster, riak]), 399: config_parser_helper:mod_config(mod_roster, #{backend => riak, riak => RiakDefaults}); 400: roster_backend_opts(Backend) -> 401: config_parser_helper:mod_config(mod_roster, #{backend => Backend}). 402: 403: vcard_required_modules() -> 404: Backend = pick_enabled_backend(), 405: [{mod_vcard, config_parser_helper:mod_config(mod_vcard, vcard_backend_opts(Backend))}]. 406: 407: vcard_backend_opts(riak) -> 408: #{backend => riak, riak => #{bucket_type => <<"vcard">>, 409: search_index => <<"vcard">>}}; 410: vcard_backend_opts(Backend) -> 411: #{backend => Backend}. 412: 413: offline_required_modules() -> 414: [{mod_offline, [{backend, pick_enabled_backend()}]}]. 415: 416: pubsub_required_modules() -> 417: pubsub_required_modules([<<"flat">>, <<"pep">>, <<"push">>]). 418: pubsub_required_modules(Plugins) -> 419: HostPattern = subhost_pattern("pubsub.@HOST@"), 420: [{mod_caps, []}, {mod_pubsub, [ 421: {backend, mongoose_helper:mnesia_or_rdbms_backend()}, 422: {host, HostPattern}, 423: {nodetree, <<"tree">>}, 424: {plugins, Plugins} 425: ] 426: }]. 427: 428: is_mim2_started() -> 429: #{node := Node} = distributed_helper:mim2(), 430: case net_adm:ping(Node) of 431: pong -> true; 432: _ -> false 433: end. 434: 435: roster_started() -> 436: dynamic_modules:ensure_modules(host_type(), roster_required_modules()). 437: 438: vcard_started() -> 439: dynamic_modules:ensure_modules(host_type(), vcard_required_modules()). 440: 441: offline_started() -> 442: dynamic_modules:ensure_modules(host_type(), offline_required_modules()). 443: 444: private_required_modules() -> 445: [{mod_private, create_private_config(pick_enabled_backend())}]. 446: 447: create_private_config(riak) -> 448: config_parser_helper:mod_config(mod_private, #{backend => riak, riak => #{bucket_type => <<"private">>}}); 449: create_private_config(Backend) -> 450: config_parser_helper:mod_config(mod_private, #{backend => Backend}). 451: 452: private_started() -> 453: dynamic_modules:ensure_modules(host_type(), private_required_modules()). 454: 455: %% ------------------------------------------------------------- 456: %% Test cases 457: %% ------------------------------------------------------------- 458: 459: %% ------------------------- Data retrieval - per type verification ------------------------- 460: 461: retrieve_vcard(Config) -> 462: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 463: AliceFields = [{<<"FN">>, <<"Alice">>}, {<<"LN">>, <<"Ecila">>}], 464: AliceSetResultStanza 465: = escalus:send_and_wait(Alice, escalus_stanza:vcard_update(AliceFields)), 466: escalus:assert(is_iq_result, AliceSetResultStanza), 467: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 468: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 469: ExpectedHeader = ["jid", "vcard"], 470: ExpectedItems = [ 471: #{ "jid" => [{contains, AliceU}, 472: {contains, AliceS}], 473: "vcard" => [{contains, "Alice"}, 474: {contains, "Ecila"}] } 475: ], 476: retrieve_and_validate_personal_data( 477: Alice, Config, "vcard", ExpectedHeader, ExpectedItems) 478: end). 479: 480: remove_vcard(Config) -> 481: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 482: AliceFields = [{<<"FN">>, <<"Alice">>}, {<<"LN">>, <<"Ecila">>}], 483: AliceSetResultStanza 484: = escalus:send_and_wait(Alice, escalus_stanza:vcard_update(AliceFields)), 485: escalus:assert(is_iq_result, AliceSetResultStanza), 486: 487: {0, _} = unregister(Alice, Config), 488: 489: assert_personal_data_via_rpc(Alice, [{vcard,["jid","vcard"],[]}]) 490: 491: end). 492: 493: remove_private(Config) -> 494: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 495: %% Add some private data for Alice 496: Element = #xmlel{name = <<"item">>, 497: attrs = [{<<"xmlns">>, <<"alice:private_remove:ns">>}], 498: children = [#xmlcdata{ content = <<"Something to declare">> }]}, 499: SetPrivateResult = escalus:send_and_wait(Alice, 500: escalus_stanza:private_set(Element)), 501: escalus:assert(is_iq_result, SetPrivateResult), 502: 503: %% Verify the data is stored 504: assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], 505: [{<<"alice:private_remove:ns">>, 506: <<"<item xmlns='alice:private_remove:ns'>Something to declare</item>">>}]}]), 507: 508: %% Remove Alice 509: {0, _} = unregister(Alice, Config), 510: 511: %% Expect Alice's data to be gone 512: assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]) 513: 514: end). 515: 516: dont_remove_other_user_private_xml(Config) -> 517: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 518: %% Add some private data for Alice and Bob 519: AliceNS = <<"alice:private:ns">>, 520: AliceContent = <<"To be or not to be">>, 521: BobNS = <<"bob:private:ns">>, 522: BobContent = <<"This is the winter of our discontent">>, 523: send_and_assert_private_stanza(Alice, AliceNS, AliceContent), 524: send_and_assert_private_stanza(Bob, BobNS, BobContent), 525: 526: %% Remove Alice 527: {0, _} = unregister(Alice, Config), 528: 529: %% Expect Alice's data to be gone 530: assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]), 531: 532: %% Verify that Bob's data is left intact 533: ExpectedHeader = ["ns", "xml"], 534: ExpectedItems = [#{ "ns" => binary_to_list(BobNS), 535: "xml" => [{contains, binary_to_list(BobNS)}, 536: {contains, binary_to_list(BobContent)}] } 537: ], 538: retrieve_and_validate_personal_data( 539: Bob, Config, "private", ExpectedHeader, ExpectedItems) 540: 541: end). 542: 543: remove_multiple_private_xmls(Config) -> 544: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 545: %% Add some private data for Alice for multiple keys 546: NSsAndContents = [ 547: {<<"alice:private:ns1">>, <<"Some text">>}, 548: {<<"alice:private:ns2">>, <<"Other text for another key">>}, 549: {<<"alice:private:ns3">>, <<"Even more of text">>} 550: ], 551: lists:foreach( 552: fun({NS, Content}) -> 553: send_and_assert_private_stanza(Alice, NS, Content) 554: end, NSsAndContents), 555: ExpectedHeader = ["ns", "xml"], 556: ExpectedItems = lists:map( 557: fun({NS, Content}) -> 558: #{ "ns" => binary_to_list(NS), 559: "xml" => [{contains, binary_to_list(NS)}, 560: {contains, binary_to_list(Content)}]} 561: end, NSsAndContents), 562: 563: %% Verify the data is stored 564: retrieve_and_validate_personal_data( 565: Alice, Config, "private", ExpectedHeader, ExpectedItems), 566: 567: %% Remove Alice 568: {0, _} = unregister(Alice, Config), 569: 570: %% Expect all of Alice's data to be gone 571: assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]) 572: 573: end). 574: 575: retrieve_roster(Config) -> 576: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 577: escalus_story:make_all_clients_friends([Alice, Bob]), 578: BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)), 579: BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)), 580: ExpectedItems = [ 581: #{ "jid" => [{contains, BobU}, {contains, BobS}] } 582: ], 583: retrieve_and_validate_personal_data( 584: Alice, Config, "roster", expected_header(mod_roster), ExpectedItems) 585: end). 586: 587: remove_roster(Config) -> 588: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 589: escalus_story:make_all_clients_friends([Alice, Bob]), 590: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 591: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 592: ExpectedItems = [ 593: #{ "jid" => [{contains, AliceU}, {contains, AliceS}] } 594: ], 595: 596: {0, _} = unregister(Alice, Config), 597: 598: assert_personal_data_via_rpc(Alice, [{roster, expected_header(mod_roster), []}]), 599: retrieve_and_validate_personal_data( 600: Bob, Config, "roster", expected_header(mod_roster), ExpectedItems) 601: 602: end). 603: 604: retrieve_mam_pm(Config) -> 605: F = fun(Alice, Bob) -> 606: Msg1 = <<"1some simple pm message">>, 607: Msg2 = <<"2another simple pm message">>, 608: Msg3 = <<"3third simple pm message">>, 609: escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg1)), 610: escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg2)), 611: escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg3)), 612: [mam_helper:wait_for_archive_size(User, 3) || User <- [Alice, Bob]], 613: AliceJID = escalus_client:full_jid(Alice), 614: BobJID = escalus_client:full_jid(Bob), 615: 616: ExpectedHeader = ["id", "from", "message"], 617: ExpectedItems = [ 618: #{"message" => [{contains, Msg1}], "from" => [{jid, AliceJID}]}, 619: #{"message" => [{contains, Msg3}], "from" => [{jid, AliceJID}]}, 620: #{"message" => [{contains, Msg2}], "from" => [{jid, BobJID}]} 621: ], 622: 623: retrieve_and_validate_personal_data( 624: Alice, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"]), 625: retrieve_and_validate_personal_data( 626: Bob, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"]) 627: end, 628: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 629: 630: retrieve_mam_muc(Config) -> 631: F = fun(Alice, Bob, Kate) -> 632: AliceUserCfg = escalus_users:get_user_by_name(alice), 633: RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []), 634: [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]], 635: AllRoomMembers = [Alice, Bob, Kate], 636: 637: muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>}, 638: {Bob, <<"Sid">>}, 639: {Kate, <<"Johnny">>}]), 640: 641: Body1 = <<"1some simple muc message">>, 642: Body2 = <<"2another one">>, 643: Body3 = <<"3third message">>, 644: muc_helper:send_to_room(RoomCfg, Alice, Body1), 645: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body1), 646: muc_helper:send_to_room(RoomCfg, Alice, Body2), 647: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body2), 648: muc_helper:send_to_room(RoomCfg, Bob, Body3), 649: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Sid">>, Body3), 650: 651: mam_helper:wait_for_room_archive_size(Domain, Room, 3), 652: 653: ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]}, 654: #{"message" => [{contains, binary_to_list(Body2)}]}], 655: 656: ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}], 657: 658: AliceDir = retrieve_all_personal_data(Alice, Config), 659: BobDir = retrieve_all_personal_data(Bob, Config), 660: KateDir = retrieve_all_personal_data(Kate, Config), 661: 662: validate_personal_data( 663: AliceDir, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]), 664: validate_personal_data( 665: BobDir, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]), 666: refute_personal_data(KateDir, "mam_muc"), 667: 668: [refute_personal_data(Dir, "mam_pm") || Dir <- [AliceDir, BobDir, KateDir]], 669: 670: muc_helper:destroy_room(RoomCfg) 671: end, 672: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F). 673: 674: retrieve_mam_muc_private_msg(Config) -> 675: F = fun(Alice, Bob) -> 676: AliceUserCfg = escalus_users:get_user_by_name(alice), 677: RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []), 678: [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]], 679: 680: muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>}, {Bob, <<"Sid">>}]), 681: 682: PMBody = <<"Hi, Bob!">>, 683: {PrivAddrAlice, _} = send_receive_muc_private_message( 684: Room, Domain, {Alice, <<"Nancy">>}, {Bob, <<"Sid">>}, PMBody), 685: 686: [mam_helper:wait_for_archive_size(User, 1) || User <- [Alice, Bob]], 687: 688: PMExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(PMBody)}], 689: "from" => [{jid, escalus_client:full_jid(Alice)}]}], 690: PMExpectedItemsBob = [#{"message" => [{contains, binary_to_list(PMBody)}], 691: "from" => [{jid, PrivAddrAlice}]}], 692: 693: AliceDir = retrieve_all_personal_data(Alice, Config), 694: BobDir = retrieve_all_personal_data(Bob, Config), 695: 696: validate_personal_data( 697: AliceDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsAlice, []), 698: validate_personal_data( 699: BobDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsBob, []), 700: 701: refute_personal_data(AliceDir, "mam_muc"), 702: refute_personal_data(BobDir, "mam_muc"), 703: 704: muc_helper:destroy_room(RoomCfg) 705: end, 706: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 707: 708: 709: 710: retrieve_mam_muc_store_pm(Config) -> 711: F = fun(Alice, Bob, Kate) -> 712: AliceUserCfg = escalus_users:get_user_by_name(alice), 713: RoomCfg = muc_helper:start_fresh_room([], AliceUserCfg, <<"someroom">>, []), 714: [Room, Domain] = [proplists:get_value(Key, RoomCfg) || Key <- [room, muc_host]], 715: AllRoomMembers = [Alice, Bob, Kate], 716: 717: muc_helper:enter_room(RoomCfg, [{Alice, <<"Nancy">>}, 718: {Bob, <<"Sid">>}, 719: {Kate, <<"Johnny">>}]), 720: 721: Body1 = <<"1some simple muc message">>, 722: Body2 = <<"2another one">>, 723: Body3 = <<"3third message">>, 724: muc_helper:send_to_room(RoomCfg, Alice, Body1), 725: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body1), 726: muc_helper:send_to_room(RoomCfg, Alice, Body2), 727: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Nancy">>, Body2), 728: muc_helper:send_to_room(RoomCfg, Bob, Body3), 729: muc_helper:verify_message_received(RoomCfg, AllRoomMembers, <<"Sid">>, Body3), 730: 731: PMBody = <<"4Hi, Bob!">>, 732: {PrivAddrAlice, PrivAddrBob} = send_receive_muc_private_message( 733: Room, Domain, {Alice, <<"Nancy">>}, {Bob, <<"Sid">>}, PMBody), 734: 735: mam_helper:wait_for_room_archive_size(Domain, Room, 3), 736: mam_helper:wait_for_archive_size(Kate, 4), 737: [mam_helper:wait_for_archive_size(User, 5) || User <- [Alice, Bob]], 738: 739: AliceDir = retrieve_all_personal_data(Alice, Config), 740: BobDir = retrieve_all_personal_data(Bob, Config), 741: KateDir = retrieve_all_personal_data(Kate, Config), 742: 743: ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]}, 744: #{"message" => [{contains, binary_to_list(Body2)}]}], 745: ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}], 746: 747: validate_personal_data( 748: AliceDir, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]), 749: validate_personal_data( 750: BobDir, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]), 751: refute_personal_data(KateDir, "mam_muc"), 752: 753: RoomJID = <<Room/binary, "@", Domain/binary>>, 754: MsgFromAliceToRoom = #{"message" => [{contains, "<body>[1,2]"}], 755: "from" => [{jid, PrivAddrAlice}]}, 756: PMExpectedItemsKate = [#{"message" => [{contains, "<body/>"}], 757: "from" => [{jid, RoomJID}]}, 758: MsgFromAliceToRoom, MsgFromAliceToRoom, 759: #{"message" => [{contains, binary_to_list(Body3)}], 760: "from" => [{jid, PrivAddrBob}]} 761: ], 762: PMExpectedItemsAlice = PMExpectedItemsKate ++ 763: [#{"message" => [{contains, binary_to_list(PMBody)}], 764: "from" => [{jid, escalus_client:full_jid(Alice)}]}], 765: MsgFromAlice = #{"message" => [{contains, "<body>[1,2,4]"}], 766: "from" => [{jid, PrivAddrAlice}]}, 767: PMExpectedItemsBob = [#{"message" => [{contains, "<body/>"}], 768: "from" => [{jid, RoomJID}]}, 769: MsgFromAlice, MsgFromAlice, MsgFromAlice, 770: #{"message" => [{contains, binary_to_list(Body3)}], 771: "from" => [{jid, PrivAddrBob}]} 772: ], 773: SortFn = muc_msg_first(RoomJID), 774: validate_personal_data( 775: KateDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsKate, SortFn), 776: validate_personal_data( 777: AliceDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsAlice, SortFn), 778: validate_personal_data( 779: BobDir, "mam_pm", ["id", "from", "message"], PMExpectedItemsBob, SortFn), 780: 781: muc_helper:destroy_room(RoomCfg) 782: end, 783: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F). 784: 785: remove_mam_pm(Config) -> 786: F = fun(Alice, Bob) -> 787: Msg1 = <<"1remove_mam_pm message">>, 788: Msg2 = <<"2remove_mam_pm message message">>, 789: Msg3 = <<"3remove_mam_pm message message">>, 790: escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg1)), 791: escalus:send(Bob, escalus_stanza:chat_to(Alice, Msg2)), 792: escalus:send(Alice, escalus_stanza:chat_to(Bob, Msg3)), 793: [mam_helper:wait_for_archive_size(User, 3) || User <- [Alice, Bob]], 794: AliceJID = escalus_client:full_jid(Alice), 795: BobJID = escalus_client:full_jid(Bob), 796: 797: ExpectedHeader = ["id", "from", "message"], 798: ExpectedItems = [ 799: #{"message" => [{contains, Msg1}], "from" => [{jid, AliceJID}]}, 800: #{"message" => [{contains, Msg3}], "from" => [{jid, AliceJID}]}, 801: #{"message" => [{contains, Msg2}], "from" => [{jid, BobJID}]} 802: ], 803: 804: {0, _} = unregister(Alice, Config), 805: 806: assert_personal_data_via_rpc(Alice, [{mam_pm, ExpectedHeader, []}]), 807: 808: retrieve_and_validate_personal_data( 809: Bob, Config, "mam_pm", ExpectedHeader, ExpectedItems, ["from", "message"]) 810: end, 811: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). 812: 813: retrieve_mam_muc_light(Config) -> 814: F = fun(Alice, Bob, Kate) -> 815: RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice, [{Bob, member}, {Kate, member}]), 816: [Room, Domain] = binary:split(RoomJid, <<"@">>), 817: Body1 = <<"1some simple muc message">>, 818: Body2 = <<"2another one">>, 819: Body3 = <<"3third message">>, 820: 821: M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, Body1, <<"Id1">>), 822: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1), 823: M2 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, Body2, <<"Id2">>), 824: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2), 825: M3 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, Body3, <<"Id3">>), 826: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M3), 827: 828: mam_helper:wait_for_room_archive_size(Domain, Room, 4), 829: 830: ExpectedItemsAlice = [#{"message" => [{contains, binary_to_list(Body1)}]}, 831: #{"message" => [{contains, binary_to_list(Body2)}]}], 832: 833: ExpectedItemsBob = [#{"message" => [{contains, binary_to_list(Body3)}]}], 834: 835: retrieve_and_validate_personal_data( 836: Alice, Config, "mam_muc", ["id", "message"], ExpectedItemsAlice, ["message"]), 837: retrieve_and_validate_personal_data( 838: Bob, Config, "mam_muc", ["id", "message"], ExpectedItemsBob, ["message"]), 839: refute_personal_data(Kate, Config, "mam_muc") 840: end, 841: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F). 842: 843: retrieve_mam_pm_and_muc_light_dont_interfere(Config) -> 844: F = fun(Alice, Bob, Kate) -> 845: RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice, 846: [{Bob, member}, {Kate, member}]), 847: [Room, Domain] = binary:split(RoomJid, <<"@">>), 848: BodyMucAlice = <<"some simple muc message from Alice">>, 849: BodyMucBob = <<"some simple muc message from Bob">>, 850: BodyPmAlice = <<"some simple pm message from Alice">>, 851: BodyPmBob = <<"some simple pm message from Bob">>, 852: 853: M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, BodyMucAlice, <<"Id1">>), 854: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1), 855: M2 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, BodyMucBob, <<"Id2">>), 856: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2), 857: 858: mam_helper:wait_for_room_archive_size(Domain, Room, 3), 859: 860: escalus:send(Alice, escalus_stanza:chat_to(Bob, BodyPmAlice)), 861: escalus:send(Bob, escalus_stanza:chat_to(Alice, BodyPmBob)), 862: 863: [mam_helper:wait_for_archive_size(User, 2) || User <- [Alice, Bob]], 864: 865: false = mongoose_helper:successful_rpc(gen_mod, get_module_opt, 866: [host_type(), mod_mam, archive_groupchats, undefined]), 867: 868: AliceDir = retrieve_all_personal_data(Alice, Config), 869: BobDir = retrieve_all_personal_data(Bob, Config), 870: KateDir = retrieve_all_personal_data(Kate, Config), 871: 872: validate_personal_data( 873: AliceDir, "mam_muc", ["id", "message"], 874: [#{"message" => [{contains, binary_to_list(BodyMucAlice)}]}], []), 875: validate_personal_data( 876: BobDir, "mam_muc", ["id", "message"], 877: [#{"message" => [{contains, binary_to_list(BodyMucBob)}]}], []), 878: 879: PM = [#{"message" => [{contains, BodyPmAlice}], 880: "from" => [{jid, escalus_client:full_jid(Alice)}]}, 881: #{"message" => [{contains, BodyPmBob}], 882: "from" => [{jid, escalus_client:full_jid(Bob)}]}], 883: validate_personal_data(AliceDir, "mam_pm", ["id", "from", "message"], PM, ["from", "message"]), 884: validate_personal_data(BobDir, "mam_pm", ["id", "from", "message"], PM, ["from", "message"]), 885: refute_personal_data(KateDir, "mam_pm"), 886: refute_personal_data(KateDir, "mam_muc") 887: end, 888: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F). 889: 890: retrieve_mam_pm_and_muc_light_interfere(Config) -> 891: F = fun(Alice, Bob, Kate) -> 892: RoomJid = muc_light_helper:given_muc_light_room(undefined, Alice, 893: [{Bob, member}, {Kate, member}]), 894: [Room, Domain] = binary:split(RoomJid, <<"@">>), 895: BodyMucAlice = <<"some simple muc message from Alice">>, 896: BodyMucBob = <<"some simple muc message from Bob">>, 897: BodyPmAlice = <<"some simple pm message from Alice">>, 898: BodyPmBob = <<"some simple pm message from Bob">>, 899: 900: M1 = muc_light_helper:when_muc_light_message_is_sent(Alice, Room, BodyMucAlice, <<"Id1">>), 901: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M1), 902: M2 = muc_light_helper:when_muc_light_message_is_sent(Bob, Room, BodyMucBob, <<"Id2">>), 903: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob, Kate], M2), 904: 905: mam_helper:wait_for_room_archive_size(Domain, Room, 3), 906: 907: escalus:send(Alice, escalus_stanza:chat_to(Bob, BodyPmAlice)), 908: escalus:send(Bob, escalus_stanza:chat_to(Alice, BodyPmBob)), 909: 910: [mam_helper:wait_for_archive_size(User, 5) || User <- [Alice, Bob]], 911: mam_helper:wait_for_archive_size(Kate, 3), 912: 913: true = mongoose_helper:successful_rpc(gen_mod, get_module_opt, 914: [host_type(), mod_mam, archive_groupchats, undefined]), 915: 916: AliceDir = retrieve_all_personal_data(Alice, Config), 917: BobDir = retrieve_all_personal_data(Bob, Config), 918: KateDir = retrieve_all_personal_data(Kate, Config), 919: 920: validate_personal_data( 921: AliceDir, "mam_muc", ["id", "message"], 922: [#{"message" => [{contains, binary_to_list(BodyMucAlice)}]}], []), 923: validate_personal_data( 924: BobDir, "mam_muc", ["id", "message"], 925: [#{"message" => [{contains, binary_to_list(BodyMucBob)}]}], []), 926: 927: AliceRoomJid = <<RoomJid/binary, "/", (escalus_client:short_jid(Alice))/binary>>, 928: BobRoomJid = <<RoomJid/binary, "/", (escalus_client:short_jid(Bob))/binary>>, 929: 930: MucPM = [#{"message" => [{contains, "urn:xmpp:muclight:0#affiliations"}], 931: "from" => [{jid, RoomJid}]}, 932: #{"message" => [{contains, BodyMucAlice}], "from" => [{jid, AliceRoomJid}]}, 933: #{"message" => [{contains, BodyMucBob}], "from" => [{jid, BobRoomJid}]}], 934: AllPM = MucPM ++ [#{"message" => [{contains, BodyPmAlice}], 935: "from" => [{jid, escalus_client:full_jid(Alice)}]}, 936: #{"message" => [{contains, BodyPmBob}], 937: "from" => [{jid, escalus_client:full_jid(Bob)}]}], 938: SortFn = muc_msg_first(RoomJid), 939: validate_personal_data(AliceDir, "mam_pm", ["id", "from", "message"], AllPM, SortFn), 940: validate_personal_data(BobDir, "mam_pm", ["id", "from", "message"], AllPM, SortFn), 941: validate_personal_data(KateDir, "mam_pm", ["id", "from", "message"], MucPM, SortFn), 942: refute_personal_data(KateDir, "mam_muc") 943: end, 944: escalus_fresh:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], F). 945: 946: retrieve_offline(Config) -> 947: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 948: mongoose_helper:logout_user(Config, Alice), 949: Body1 = <<"1Hey!">>, 950: Body2 = <<"2Here is Johnny!">>, 951: Body3 = <<"3Where is Johnny ?">>, 952: escalus:send(Bob, escalus_stanza:chat_to(Alice, Body1)), 953: escalus:send(Bob, escalus_stanza:chat_to(Alice, Body2)), 954: escalus:send(Kate, escalus_stanza:chat_to(Alice, Body3)), 955: %% Well, jid_to_lower works for any binary :) 956: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 957: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 958: mongoose_helper:wait_until( 959: fun() -> 960: mongoose_helper:successful_rpc(mod_offline_backend, count_offline_messages, 961: [host_type(), AliceU, AliceS, 10]) 962: end, 3), 963: 964: BobJid = escalus_client:full_jid(Bob), 965: AliceJid = escalus_client:short_jid(Alice), 966: KateJid = escalus_client:full_jid(Kate), 967: ExpectedHeader = ["timestamp", "from", "to", "packet"], 968: Expected = [{Body1, BobJid, AliceJid}, {Body2, BobJid, AliceJid}, {Body3, KateJid, AliceJid}], 969: 970: ExpectedItems = lists:map(fun({Body, From ,To}) -> 971: #{ "packet" => [{contains, Body}], 972: "from" => binary_to_list(From), 973: "to" => binary_to_list(To), 974: "timestamp" => [{validate, fun validate_datetime/1}]} 975: end, Expected), 976: 977: retrieve_and_validate_personal_data( 978: Alice, Config, "offline", ExpectedHeader, ExpectedItems, ["packet"]) 979: end). 980: 981: remove_offline(Config) -> 982: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 983: mongoose_helper:logout_user(Config, Alice), 984: Body1 = <<"Hey!">>, 985: Body2 = <<"Here is Johnny!">>, 986: Body3 = <<"Where is Johnny ?">>, 987: escalus:send(Bob, escalus_stanza:chat_to(Alice, Body1)), 988: escalus:send(Bob, escalus_stanza:chat_to(Alice, Body2)), 989: escalus:send(Kate, escalus_stanza:chat_to(Alice, Body3)), 990: %% Well, jid_to_lower works for any binary :) 991: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 992: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 993: mongoose_helper:wait_until( 994: fun() -> 995: mongoose_helper:successful_rpc(mod_offline_backend, count_offline_messages, 996: [host_type(), AliceU, AliceS, 10]) 997: end, 3), 998: 999: {0, _} = unregister(Alice, Config), 1000: 1001: assert_personal_data_via_rpc( 1002: Alice, [{offline, ["timestamp","from", "to", "packet"],[]}]) 1003: end). 1004: 1005: retrieve_pubsub_payloads(Config) -> 1006: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 1007: [Node1={_,NodeName1}, Node2={_,NodeName2}] = pubsub_tools:create_node_names(2), 1008: {BinItem1, StringItem1} = item_content(<<"Item1Data">>), 1009: {BinItem2, StringItem2} = item_content(<<"Item2Data">>), 1010: {BinItem3, StringItem3} = item_content(<<"Item3Data">>), 1011: {BinOther, StringOther} = item_content(<<"OtherItemData">>), 1012: 1013: pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, BinItem1}]), 1014: pubsub_tools:publish(Alice, <<"Item2">>, Node1, [{with_payload, BinItem2}]), 1015: pubsub_tools:publish(Alice, <<"Item3">>, Node1, [{with_payload, BinItem3}]), 1016: pubsub_tools:publish(Alice, <<"OtherItem">>, Node2, [{with_payload, BinOther}]), 1017: 1018: ExpectedItems = [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1), 1019: pubsub_payloads_row_map(NodeName1, "Item2", StringItem2), 1020: pubsub_payloads_row_map(NodeName1, "Item3", StringItem3), 1021: pubsub_payloads_row_map(NodeName2, "OtherItem", StringOther)], 1022: 1023: retrieve_and_validate_personal_data(Alice, Config, "pubsub_payloads", 1024: ["node_name", "item_id", "payload"], 1025: ExpectedItems, ["item_id"]) 1026: end). 1027: 1028: dont_retrieve_other_user_pubsub_payload(Config) -> 1029: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1030: [Node1={_,NodeName1}] = pubsub_tools:create_node_names(1), 1031: pubsub_tools:create_nodes([{Alice, Node1, []}]), 1032: 1033: {BinItem1, StringItem1} = item_content(<<"Item1Data">>), 1034: {BinItem2, StringItem2} = item_content(<<"Item2Data">>), 1035: 1036: AffChange = [{Bob, <<"publish-only">>}], 1037: pubsub_tools:set_affiliations(Alice, Node1, AffChange, []), 1038: pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, {true, BinItem1}}]), 1039: pubsub_tools:publish(Bob, <<"Item2">>, Node1, [{with_payload, {true, BinItem2}}]), 1040: 1041: retrieve_and_validate_personal_data( 1042: Alice, Config, "pubsub_payloads", ["node_name", "item_id", "payload"], 1043: [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1)]), 1044: 1045: retrieve_and_validate_personal_data( 1046: Bob, Config, "pubsub_payloads", ["node_name","item_id", "payload"], 1047: [pubsub_payloads_row_map(NodeName1, "Item2", StringItem2)]), 1048: 1049: pubsub_tools:delete_node(Alice, Node1, []) 1050: end). 1051: 1052: retrieve_created_pubsub_nodes(Config) -> 1053: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1054: [Node1={_,NodeName1}, Node2={_,NodeName2}, Node3={_,NodeName3}] = 1055: pubsub_tools:create_node_names(3), 1056: 1057: NodeNS = <<"myns">>, 1058: PepNode = make_pep_node_info(Alice, NodeNS), 1059: AccessModel = {<<"pubsub#access_model">>, <<"authorize">>}, 1060: 1061: pubsub_tools:create_nodes([ 1062: {Alice, Node1, []}, 1063: {Alice, Node2, []}, 1064: {Alice, PepNode, [{config, [AccessModel]}]}, 1065: {Bob, Node3, [{type, <<"push">>}]} 1066: ]), 1067: 1068: ExpectedHeader = ["node_name", "type"], 1069: 1070: retrieve_and_validate_personal_data( 1071: Alice, Config, "pubsub_nodes", ExpectedHeader, 1072: [pubsub_nodes_row_map(NodeNS, "pep"), 1073: pubsub_nodes_row_map(NodeName1, "flat"), 1074: pubsub_nodes_row_map(NodeName2, "flat")]), 1075: 1076: retrieve_and_validate_personal_data( 1077: Bob, Config, "pubsub_nodes", ExpectedHeader, 1078: [pubsub_nodes_row_map(NodeName3, "push")]), 1079: 1080: 1081: Nodes = [{Alice, PepNode}, {Alice, Node1}, {Alice, Node2}, {Bob, Node3}], 1082: [pubsub_tools:delete_node(User, Node, []) || {User, Node} <- Nodes] 1083: end). 1084: 1085: remove_pubsub_subscriptions(Config) -> 1086: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1087: Node = pubsub_tools:pubsub_node(), 1088: pubsub_tools:create_node(Alice, Node, []), 1089: pubsub_tools:subscribe(Bob, Node, []), 1090: 1091: {0, _} = unregister(Bob, Config), 1092: 1093: assert_personal_data_via_rpc(Bob, 1094: [{pubsub_payloads,["node_name","item_id","payload"],[]}, 1095: {pubsub_nodes,["node_name","type"],[]}, 1096: {pubsub_subscriptions,["node_name"],[]}]) 1097: end). 1098: 1099: retrieve_pubsub_subscriptions(Config) -> 1100: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1101: Node = {_Domain, NodeName} = pubsub_tools:pubsub_node(), 1102: pubsub_tools:create_node(Alice, Node, []), 1103: pubsub_tools:subscribe(Bob, Node, []), 1104: retrieve_and_validate_personal_data(Bob, Config, "pubsub_subscriptions", ["node_name"], 1105: [pubsub_subscription_row_map(NodeName)]), 1106: 1107: pubsub_tools:delete_node(Alice, Node, []) 1108: end). 1109: 1110: remove_pubsub_dont_remove_flat_pubsub_node(Config) -> 1111: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 1112: Node1 = {_,NodeName} = pubsub_tools:pubsub_node_with_num(1), 1113: pubsub_tools:create_nodes([{Alice, Node1, []}]), 1114: 1115: {0, _} = unregister(Alice, Config), 1116: 1117: assert_personal_data_via_rpc(Alice, 1118: [{pubsub_payloads,["node_name","item_id","payload"],[]}, 1119: {pubsub_nodes,["node_name","type"],[[NodeName, <<"flat">>]]}, 1120: {pubsub_subscriptions,["node_name"],[]}]) 1121: end). 1122: 1123: remove_pubsub_push_node(Config) -> 1124: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1125: [Node] = pubsub_tools:create_node_names(1), 1126: pubsub_tools:create_nodes([{Alice, Node, [{type, <<"push">>}]}]), 1127: 1128: Content = [ 1129: {<<"message-count">>, <<"1">>}, 1130: {<<"last-message-sender">>, <<"senderId">>}, 1131: {<<"last-message-body">>, <<"message body">>} 1132: ], 1133: Options = [ 1134: {<<"device_id">>, <<"sometoken">>}, 1135: {<<"service">>, <<"apns">>} 1136: ], 1137: 1138: PublishIQ = push_pubsub_SUITE:publish_iq(Bob, Node, Content, Options), 1139: escalus:send(Bob, PublishIQ), 1140: escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)), 1141: 1142: {0, _} = unregister(Alice, Config), 1143: 1144: assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, 1145: {pubsub_nodes,["node_name","type"],[]}, 1146: {pubsub_subscriptions,["node_name"],[]}]) 1147: end). 1148: 1149: remove_pubsub_pep_node(Config) -> 1150: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 1151: NodeName = <<"myns">>, 1152: PepNode = make_pep_node_info(Alice, NodeName), 1153: 1154: pubsub_tools:create_nodes([ 1155: {Alice, PepNode, []} 1156: ]), 1157: 1158: {0, _} = unregister(Alice, Config), 1159: 1160: assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, 1161: {pubsub_nodes,["node_name","type"],[]}, 1162: {pubsub_subscriptions,["node_name"],[]}]) 1163: end). 1164: 1165: remove_pubsub_dont_remove_node_when_only_publisher(Config) -> 1166: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1167: Node1 = {_,NodeName} = pubsub_tools:pubsub_node_with_num(1), 1168: pubsub_tools:create_nodes([{Alice, Node1, []}]), 1169: 1170: AffChange = [{Bob, <<"publish-only">>}], 1171: pubsub_tools:set_affiliations(Alice, Node1, AffChange, []), 1172: 1173: {0, _} = unregister(Bob, Config), 1174: 1175: assert_personal_data_via_rpc(Alice, 1176: [{pubsub_payloads,["node_name","item_id","payload"],[]}, 1177: {pubsub_nodes,["node_name","type"],[[NodeName, <<"flat">>]]}, 1178: {pubsub_subscriptions,["node_name"],[]}]) 1179: end). 1180: 1181: remove_pubsub_all_data(Config) -> 1182: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1183: [Node1={_,Name1}, Node2={_,Name2}, Node3={_,Name3}, Node4={_,Name4}] 1184: = pubsub_tools:create_node_names(4), 1185: PepNode = make_pep_node_info(Alice, <<"myns">>), 1186: pubsub_tools:create_nodes([ 1187: {Alice, Node1, []}, 1188: {Alice, Node2, []}, 1189: {Alice, PepNode, []}, 1190: {Bob, Node3, []}, 1191: {Bob, Node4, [{type, <<"push">>}]} 1192: ]), 1193: 1194: AffChange = [{Bob, <<"publish-only">>}], 1195: pubsub_tools:set_affiliations(Alice, Node1, AffChange, []), 1196: pubsub_tools:subscribe(Bob, Node2, []), 1197: pubsub_tools:subscribe(Alice, Node3, []), 1198: 1199: {BinItem1, _} = item_content(<<"Item1Data">>), 1200: {BinItem2, _} = item_content(<<"Item2Data">>), 1201: {BinItem3, _} = item_content(<<"Item3Data">>), 1202: {BinItem4, _} = item_content(<<"Item4Data">>), 1203: AliceToNode1 = <<"Alice publishes to Node1, but nobody is subscribed">>, 1204: AliceToNode2 = <<"Alice published to Node2, so Bob receives it">>, 1205: BobToNode1 = <<"Bob publishes to Node1, but nobody is subscribed">>, 1206: BobToNode3 = <<"Bob publishes to Node3, so Alice receives it">>, 1207: 1208: pubsub_tools:publish(Alice, AliceToNode1, Node1, [{with_payload, {true, BinItem1}}]), 1209: 1210: pubsub_tools:publish(Alice, AliceToNode2, Node2, [{with_payload, {true, BinItem2}}]), 1211: pubsub_tools:receive_item_notification(Bob, AliceToNode2, Node2, []), 1212: 1213: pubsub_tools:publish(Bob, BobToNode1, Node1, [{with_payload, {true, BinItem3}}]), 1214: 1215: pubsub_tools:publish(Bob, BobToNode3, Node3, [{with_payload, {true, BinItem4}}]), 1216: pubsub_tools:receive_item_notification(Alice, BobToNode3, Node3, []), 1217: 1218: {0, _} = unregister(Alice, Config), 1219: 1220: [{pubsub_payloads,["node_name","item_id","payload"], AlicePayloads}, 1221: {pubsub_nodes,["node_name","type"], AliceNodes}, 1222: {pubsub_subscriptions, ["node_name"], []}] 1223: = get_personal_data_via_rpc( 1224: Alice, [pubsub_payloads, pubsub_nodes, pubsub_subscriptions]), 1225: XmlBinItem1 = exml:to_binary(BinItem1), 1226: XmlBinItem2 = exml:to_binary(BinItem2), 1227: [[Name1, AliceToNode1, XmlBinItem1], 1228: [Name2, AliceToNode2, XmlBinItem2]] = lists:sort(AlicePayloads), 1229: [[Name1, <<"flat">>], [Name2, <<"flat">>]] = lists:sort(AliceNodes), 1230: 1231: [{pubsub_payloads,["node_name","item_id","payload"], Payloads}, 1232: {pubsub_nodes,["node_name","type"], Nodes}, 1233: {pubsub_subscriptions, ["node_name"], Subs}] 1234: = get_personal_data_via_rpc( 1235: Bob, [pubsub_payloads, pubsub_nodes, pubsub_subscriptions]), 1236: XmlBinItem3 = exml:to_binary(BinItem3), 1237: XmlBinItem4 = exml:to_binary(BinItem4), 1238: [[Name1, BobToNode1, XmlBinItem3], 1239: [Name3, BobToNode3, XmlBinItem4]] = lists:sort(Payloads), 1240: [[Name3, <<"flat">>], [Name4, <<"push">>]] = lists:sort(Nodes), 1241: [[Name2]] = Subs 1242: end). 1243: 1244: retrieve_all_pubsub_data(Config) -> 1245: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1246: [Node1={_,NodeName1}, Node2={_,NodeName2}, Node3={_,NodeName3}] = 1247: pubsub_tools:create_node_names(3), 1248: pubsub_tools:create_nodes([{Alice, Node1, []}, {Alice, Node2, []}, {Bob, Node3, []}]), 1249: 1250: AffChange = [{Bob, <<"publish-only">>}], 1251: pubsub_tools:set_affiliations(Alice, Node1, AffChange, []), 1252: pubsub_tools:subscribe(Bob, Node2, []), 1253: 1254: {BinItem1, StringItem1} = item_content(<<"Item1Data">>), 1255: {BinItem2, StringItem2} = item_content(<<"Item2Data">>), 1256: {BinItem3, StringItem3} = item_content(<<"Item3Data">>), 1257: 1258: pubsub_tools:publish(Alice, <<"Item1">>, Node1, [{with_payload, {true, BinItem1}}]), 1259: pubsub_tools:publish(Alice, <<"Item2">>, Node2, [{with_payload, {true, BinItem2}}]), 1260: pubsub_tools:receive_item_notification(Bob, <<"Item2">>, Node2, []), 1261: pubsub_tools:publish(Bob, <<"Item3">>, Node1, [{with_payload, {true, BinItem3}}]), 1262: 1263: %% Bob has one subscription, one node created and one payload sent 1264: retrieve_and_validate_personal_data( 1265: Bob, Config, "pubsub_subscriptions", ["node_name"], 1266: [pubsub_subscription_row_map(NodeName2)]), 1267: 1268: retrieve_and_validate_personal_data( 1269: Bob, Config, "pubsub_nodes", ["node_name", "type"], 1270: [pubsub_nodes_row_map(NodeName3, "flat")]), 1271: 1272: retrieve_and_validate_personal_data( 1273: Bob, Config, "pubsub_payloads", ["node_name", "item_id", "payload"], 1274: [pubsub_payloads_row_map(NodeName1, "Item3", StringItem3)]), 1275: 1276: %% Alice has two nodes created and two payloads sent 1277: retrieve_and_validate_personal_data( 1278: Alice, Config, "pubsub_nodes", ["node_name", "type"], 1279: [pubsub_nodes_row_map(NodeName1, "flat"), 1280: pubsub_nodes_row_map(NodeName2, "flat")]), 1281: retrieve_and_validate_personal_data( 1282: Alice, Config, "pubsub_payloads", ["node_name", "item_id","payload"], 1283: [pubsub_payloads_row_map(NodeName1, "Item1", StringItem1), 1284: pubsub_payloads_row_map(NodeName2, "Item2", StringItem2)]), 1285: 1286: dynamic_modules:ensure_modules(host_type(), pubsub_required_modules()), 1287: Nodes = [{Alice, Node1}, {Alice, Node2}, {Bob, Node3}], 1288: [pubsub_tools:delete_node(User, Node, []) || {User, Node} <- Nodes] 1289: end). 1290: 1291: 1292: retrieve_private_xml(Config) -> 1293: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 1294: NS = <<"alice:gdpr:ns">>, 1295: Content = <<"dGhlcmUgYmUgZHJhZ29ucw==">>, 1296: send_and_assert_private_stanza(Alice, NS, Content), 1297: ExpectedHeader = ["ns", "xml"], 1298: ExpectedItems = [#{ "ns" => binary_to_list(NS), 1299: "xml" => [{contains, binary_to_list(NS)}, 1300: {contains, binary_to_list(Content)}] } 1301: ], 1302: retrieve_and_validate_personal_data( 1303: Alice, Config, "private", ExpectedHeader, ExpectedItems) 1304: end). 1305: 1306: dont_retrieve_other_user_private_xml(Config) -> 1307: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1308: AliceNS = <<"alice:gdpr:ns">>, 1309: AliceContent = <<"To be or not to be">>, 1310: BobNS = <<"bob:gdpr:ns">>, 1311: BobContent = <<"This is the winter of our discontent">>, 1312: send_and_assert_private_stanza(Alice, AliceNS, AliceContent), 1313: send_and_assert_private_stanza(Bob, BobNS, BobContent), 1314: ExpectedHeader = ["ns", "xml"], 1315: ExpectedItems = [#{ "ns" => binary_to_list(AliceNS), 1316: "xml" => [{contains, binary_to_list(AliceNS)}, 1317: {contains, binary_to_list(AliceContent)}] } 1318: ], 1319: retrieve_and_validate_personal_data( 1320: Alice, Config, "private", ExpectedHeader, ExpectedItems) 1321: end). 1322: 1323: retrieve_multiple_private_xmls(Config) -> 1324: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 1325: NSsAndContents = [ 1326: {<<"alice:gdpr:ns1">>, <<"You do not talk about FIGHT CLUB.">>}, 1327: {<<"alice:gdpr:ns2">>, <<"You do not talk about FIGHT CLUB.">>}, 1328: {<<"alice:gdpr:ns3">>, <<"If someone says stop or goes limp," 1329: " taps out the fight is over.">>}, 1330: {<<"alice:gdpr:ns4">>, <<"Only two guys to a fight.">>}, 1331: {<<"alice:gdpr:ns5">>, <<"One fight at a time.">>} 1332: ], 1333: lists:foreach( 1334: fun({NS, Content}) -> 1335: send_and_assert_private_stanza(Alice, NS, Content) 1336: end, NSsAndContents), 1337: ExpectedHeader = ["ns", "xml"], 1338: ExpectedItems = lists:map( 1339: fun({NS, Content}) -> 1340: #{ "ns" => binary_to_list(NS), 1341: "xml" => [{contains, binary_to_list(NS)}, 1342: {contains, binary_to_list(Content)}]} 1343: end, NSsAndContents), 1344: 1345: retrieve_and_validate_personal_data( 1346: Alice, Config, "private", ExpectedHeader, ExpectedItems) 1347: end). 1348: 1349: retrieve_inbox_muclight(Config) -> 1350: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1351: muc_light_helper:given_muc_light_room(?ROOM, Alice, [{Bob, member}]), 1352: Domain = muc_light_helper:muc_host(), 1353: 1354: Body = <<"Are you sure?">>, 1355: Res = muc_light_helper:when_muc_light_message_is_sent(Alice, ?ROOM, Body, <<"9128">>), 1356: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob], Res), 1357: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1358: ExpectedAliceItems = [#{ "jid" => [{contains, <<?ROOM/binary, $@, Domain/binary>>}], 1359: "unread_count" => "0" }], 1360: %% MUC Light affiliations are also stored in inbox 1361: ExpectedBobItems = [#{ "jid" => [{contains, <<?ROOM/binary, $@, Domain/binary>>}], 1362: "unread_count" => "2" }], 1363: 1364: retrieve_and_validate_personal_data( 1365: Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems), 1366: retrieve_and_validate_personal_data( 1367: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems), 1368: 1369: StanzaDestroy = escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []), room_bin_jid(?ROOM)), 1370: escalus:send(Alice, StanzaDestroy), 1371: ok 1372: end). 1373: 1374: retrieve_inbox_muc(Config) -> 1375: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1376: {ok, Room} = given_fresh_muc_room(Alice#client.props, []), 1377: Users = [Alice, Bob], 1378: Msg = <<"Hi Room!">>, 1379: Id = <<"MyID">>, 1380: RoomAddr = muc_helper:room_address(Room), 1381: 1382: inbox_helper:enter_room(Room, Users), 1383: inbox_helper:make_members(Room, Alice, [Bob]), 1384: Stanza = escalus_stanza:set_id( 1385: escalus_stanza:groupchat_to(RoomAddr, Msg), Id), 1386: escalus:send(Bob, Stanza), 1387: inbox_helper:wait_for_groupchat_msg(Users), 1388: 1389: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1390: 1391: ExpectedBobItems = [#{ 1392: "content" => [{contains, Msg}], 1393: "jid" => [{contains, RoomAddr}], 1394: "unread_count" => "0" }], 1395: 1396: retrieve_and_validate_personal_data( 1397: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems), 1398: ExpectedAliceItems = [#{ 1399: "content" => [{contains, Msg}], 1400: "jid" => [{contains, RoomAddr}], 1401: "unread_count" => "1" }], 1402: 1403: retrieve_and_validate_personal_data( 1404: Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems), 1405: ok 1406: end). 1407: 1408: retrieve_inbox(Config) -> 1409: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1410: BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)), 1411: BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)), 1412: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 1413: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 1414: Body = <<"With spam?">>, 1415: send_and_assert_is_chat_message(Bob, Alice, Body), 1416: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1417: ExpectedAliceItems = [#{ "content" => [{contains, Body}], 1418: "jid" => [{contains, BobS}, 1419: {contains, BobU}], 1420: "unread_count" => "1" }], 1421: ExpectedBobItems = [#{ "content" => [{contains, Body}], 1422: "jid" => [{contains, AliceS}, 1423: {contains, AliceU}], 1424: "unread_count" => "0" }], 1425: retrieve_and_validate_personal_data( 1426: Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems), 1427: retrieve_and_validate_personal_data( 1428: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems) 1429: end). 1430: 1431: remove_inbox(Config) -> 1432: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1433: AliceU = escalus_utils:jid_to_lower(escalus_client:username(Alice)), 1434: AliceS = escalus_utils:jid_to_lower(escalus_client:server(Alice)), 1435: Body = <<"With spam?">>, 1436: send_and_assert_is_chat_message(Bob, Alice, Body), 1437: 1438: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1439: 1440: {0, _} = unregister(Alice, Config), 1441: 1442: assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]), 1443: 1444: ExpectedBobItems = [ 1445: #{ "content" => [{contains, Body}], 1446: "jid" => [{contains, AliceS}, 1447: {contains, AliceU}], 1448: "unread_count" => "0" } 1449: ], 1450: retrieve_and_validate_personal_data( 1451: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems) 1452: end). 1453: 1454: remove_inbox_muclight(Config) -> 1455: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1456: Domain = muc_light_helper:muc_host(), 1457: Room = <<"ttt2">>, 1458: muc_light_helper:given_muc_light_room(Room, Alice, [{Bob, member}]), 1459: 1460: Body = <<"Are you sure?">>, 1461: Res = muc_light_helper:when_muc_light_message_is_sent(Alice, Room , Body, <<"9128">>), 1462: muc_light_helper:then_muc_light_message_is_received_by([Alice, Bob], Res), 1463: 1464: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1465: 1466: {0, _} = unregister(Alice, Config), 1467: 1468: %% MUC Light affiliations are also stored in inbox 1469: %% 1. Added to the room 1470: %% 2. Message 1471: %% 3. Aff change: Alice -> none, Bob -> owner 1472: %% Writing aff changes to inbox is enabled by default 1473: ExpectedBobItems = [#{ 1474: "jid" => [{contains, <<Room/binary, $@, Domain/binary>>}], 1475: "unread_count" => "3" } 1476: ], 1477: 1478: retrieve_and_validate_personal_data( 1479: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems), 1480: 1481: assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]), 1482: 1483: timer:sleep(5000), 1484: StanzaDestroy = escalus_stanza:to(escalus_stanza:iq_set(?NS_MUC_LIGHT_DESTROY, []), 1485: room_bin_jid(Room)), 1486: escalus:send(Alice, StanzaDestroy), 1487: ok 1488: end). 1489: 1490: remove_inbox_muc(Config) -> 1491: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1492: {ok, Room} = given_fresh_muc_room(Alice#client.props, []), 1493: 1494: Users = [Alice, Bob], 1495: Msg = <<"Hi Room!">>, 1496: Id = <<"MyID">>, 1497: RoomAddr = muc_helper:room_address(Room), 1498: 1499: inbox_helper:enter_room(Room, Users), 1500: inbox_helper:make_members(Room, Alice, [Bob]), 1501: Stanza = escalus_stanza:set_id( 1502: escalus_stanza:groupchat_to(RoomAddr, Msg), Id), 1503: escalus:send(Bob, Stanza), 1504: inbox_helper:wait_for_groupchat_msg(Users), 1505: 1506: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1507: 1508: {0, _} = unregister(Alice, Config), 1509: 1510: escalus:wait_for_stanza(Bob), 1511: assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]), 1512: 1513: ExpectedBobItems = [#{ 1514: "content" => [{contains, Msg}], 1515: "jid" => [{contains, RoomAddr}], 1516: "unread_count" => "0" }], 1517: 1518: retrieve_and_validate_personal_data( 1519: Bob, Config, "inbox", ExpectedHeader, ExpectedBobItems), 1520: ok 1521: end). 1522: 1523: retrieve_inbox_for_multiple_messages(Config) -> 1524: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1525: Bodies = [ <<"Nobody exists on purpose.">>, 1526: <<"Nobody belongs anywhere.">>, 1527: <<"We're all going to die.">>, 1528: <<"Come watch TV.">>], 1529: lists:foreach(fun(Body) -> send_and_assert_is_chat_message(Bob, Alice, Body) end, Bodies), 1530: BobU = escalus_utils:jid_to_lower(escalus_client:username(Bob)), 1531: BobS = escalus_utils:jid_to_lower(escalus_client:server(Bob)), 1532: 1533: ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], 1534: ExpectedAliceItems = [#{ "content" => [{contains, lists:last(Bodies)}], 1535: "jid" => [{contains, BobS}, 1536: {contains, BobU}], 1537: "unread_count" => integer_to_list(length(Bodies)) }], 1538: retrieve_and_validate_personal_data( 1539: Alice, Config, "inbox", ExpectedHeader, ExpectedAliceItems) 1540: end). 1541: 1542: retrieve_logs(Config) -> 1543: escalus:fresh_story(Config, [{alice, 1}], 1544: fun(Alice) -> 1545: User = string:to_lower(binary_to_list(escalus_client:username(Alice))), 1546: Domain = string:to_lower(binary_to_list(escalus_client:server(Alice))), 1547: JID = string:to_upper(binary_to_list(escalus_client:short_jid(Alice))), 1548: #{node := MIM2NodeName} = MIM2Node = distributed_helper:mim2(), 1549: mongoose_helper:successful_rpc(net_kernel, connect_node, [MIM2NodeName]), 1550: mongoose_helper:successful_rpc(MIM2Node, error_logger, error_msg, 1551: ["event=disturbance_in_the_force, jid=~s", [JID]]), 1552: Dir = request_and_unzip_personal_data(User, Domain, Config), 1553: Filename = filename:join(Dir, "logs-" ++ atom_to_list(MIM2NodeName) ++ ".txt"), 1554: {ok, Content} = file:read_file(Filename), 1555: {match, _} = re:run(Content, "disturbance_in_the_force") 1556: end). 1557: 1558: %% ------------------------- Data retrieval - Negative case ------------------------- 1559: 1560: data_is_not_retrieved_for_missing_user(Config) -> 1561: {Filename, 1, _} = retrieve_personal_data("non-person", "oblivion", Config), 1562: {error, _} = file:read_file_info(Filename). 1563: 1564: %% ------------------------------------------------------------- 1565: %% Internal functions 1566: %% ------------------------------------------------------------- 1567: 1568: assert_personal_data_via_rpc(Client, ExpectedPersonalDataEntries) -> 1569: ExpectedKeys = [ Key || {Key, _, _} <- ExpectedPersonalDataEntries ], 1570: 1571: %% We use wait_until here, because e.g. the deletion in ElasticSearch 1572: %% sometimes is applied with a delay (i.e. immediately after successful deletion 1573: %% the data retrieval still returned valid entries) 1574: mongoose_helper:wait_until( 1575: fun() -> 1576: get_personal_data_via_rpc(Client, ExpectedKeys) 1577: end, ExpectedPersonalDataEntries). 1578: 1579: get_personal_data_via_rpc(Client, ExpectedKeys) -> 1580: ClientU = escalus_utils:jid_to_lower(escalus_client:username(Client)), 1581: ClientS = escalus_utils:jid_to_lower(escalus_client:server(Client)), 1582: AllPersonalData = mongoose_helper:successful_rpc( 1583: service_admin_extra_gdpr, get_data_from_modules, [ClientU, ClientS]), 1584: %% We don't use lists:filter/2 because this line also ensures order 1585: [ lists:keyfind(Key, 1, AllPersonalData) || Key <- ExpectedKeys ]. 1586: 1587: retrieve_and_validate_personal_data(User, Config, FilePrefix, ExpectedHeader, ExpectedItems) -> 1588: Dir = retrieve_all_personal_data(User, Config), 1589: validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, ExpectedHeader). 1590: 1591: retrieve_and_validate_personal_data(User, Config, FilePrefix, ExpectedHeader, ExpectedItems, SortBy) -> 1592: Dir = retrieve_all_personal_data(User, Config), 1593: validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, SortBy). 1594: 1595: validate_personal_data(Dir, FilePrefix, ExpectedHeader, ExpectedItems, SortBy) -> 1596: PersonalCSV = decode_personal_data(Dir, FilePrefix), 1597: UnsortedMaps = csv_to_maps(ExpectedHeader, PersonalCSV), 1598: PersonalMaps = lists:sort(get_sort_fn(SortBy), UnsortedMaps), 1599: try validate_personal_maps(PersonalMaps, ExpectedItems) of 1600: _ -> ok 1601: catch 1602: C:R:S -> 1603: ct:fail(#{ 1604: class => C, 1605: reason => R, 1606: stacktrace => S, 1607: sorted_by => SortBy, 1608: personal_maps => PersonalMaps, 1609: expected_items => ExpectedItems 1610: }) 1611: end. 1612: 1613: 1614: get_sort_fn(SortBy) when is_list(SortBy) -> 1615: %% if SortBy is [], than original list remains unsorted. 1616: fun(Map1, Map2) -> compare_maps(SortBy, Map1, Map2) end; 1617: get_sort_fn(SortFn) when is_function(SortFn, 2) -> 1618: SortFn. 1619: 1620: compare_maps([], _, _) -> true; 1621: compare_maps([Key | T], Map1, Map2) -> 1622: #{Key:=Val1} = Map1, 1623: #{Key:=Val2} = Map2, 1624: if 1625: Val1 =:= Val2 -> compare_maps(T, Map1, Map2); 1626: Val1 > Val2 -> false; 1627: Val1 < Val2 -> true 1628: end. 1629: 1630: muc_msg_first(MucJid) -> 1631: MucJidNormalized = escalus_utils:jid_to_lower(to_binary(MucJid)), 1632: N = erlang:byte_size(MucJidNormalized), 1633: fun(#{"from" := JID1}, #{"from" := JID2}) -> 1634: Jid1Normalized = escalus_utils:jid_to_lower(to_binary(JID1)), 1635: Jid2Normalized = escalus_utils:jid_to_lower(to_binary(JID2)), 1636: case {Jid1Normalized, Jid2Normalized} of 1637: {<<MucJidNormalized:N/binary, _/binary>>, <<MucJidNormalized:N/binary, _/binary>>} -> 1638: Jid1Normalized =< Jid2Normalized; 1639: {<<MucJidNormalized:N/binary, _/binary>>, _} -> 1640: true; 1641: {_, <<MucJidNormalized:N/binary, _/binary>>} -> 1642: false; 1643: {_, _} -> 1644: Jid1Normalized =< Jid2Normalized 1645: end 1646: end. 1647: 1648: csv_to_maps(ExpectedHeader, [ExpectedHeader | Rows]) -> 1649: lists:foldl(fun(Row, Maps) -> [ csv_row_to_map(ExpectedHeader, Row) | Maps ] end, [], Rows). 1650: 1651: csv_row_to_map(Header, Row) -> 1652: maps:from_list(lists:zip(Header, Row)). 1653: 1654: validate_personal_maps(PersonalMaps, ExpectedItems) -> 1655: validate_sorted_personal_maps(PersonalMaps, ExpectedItems). 1656: 1657: validate_sorted_personal_maps([], []) -> ok; 1658: validate_sorted_personal_maps(UnexpectedRecords, []) -> 1659: erlang:error("Unexpected records left ~p", [UnexpectedRecords]); 1660: validate_sorted_personal_maps([Map | RMaps], [Checks | RChecks]) -> 1661: maps:fold(fun(K, Conditions, _) -> 1662: validate_personal_item(maps:get(K, Map), Conditions) 1663: end, ok, Checks), 1664: validate_sorted_personal_maps(RMaps, RChecks). 1665: 1666: validate_personal_item(_Value, []) -> 1667: ok; 1668: validate_personal_item(ExactValue, ExactValue) -> 1669: ok; 1670: validate_personal_item(Value, [{jid, ExpectedValue} | RConditions]) -> 1671: JID = escalus_utils:jid_to_lower(to_binary(Value)), 1672: JID = escalus_utils:jid_to_lower(to_binary(ExpectedValue)), 1673: validate_personal_item(Value, RConditions); 1674: validate_personal_item(Value, [{contains, String} | RConditions]) -> 1675: {match, _} = re:run(Value, String), 1676: validate_personal_item(Value, RConditions); 1677: validate_personal_item(Value, [{validate, Validator} | RConditions]) when is_function(Validator) -> 1678: true = Validator(Value), 1679: validate_personal_item(Value, RConditions). 1680: 1681: to_binary(List) when is_list(List) -> list_to_binary(List); 1682: to_binary(Binary) when is_binary(Binary) -> Binary. 1683: 1684: decode_personal_data(Dir, FilePrefix) -> 1685: CSVPath = filename:join(Dir, FilePrefix ++ ".csv"), 1686: {ok, Content} = file:read_file(CSVPath), 1687: % We expect non-empty list because it must contain at least header with columns names 1688: [_ | _] = csv:decode_binary(Content). 1689: 1690: refute_personal_data(Client, Config, FilePrefix) -> 1691: Dir = retrieve_all_personal_data(Client, Config), 1692: refute_personal_data(Dir, FilePrefix). 1693: 1694: refute_personal_data(Dir, FilePrefix) -> 1695: CSVPath = filename:join(Dir, FilePrefix ++ ".csv"), 1696: false = filelib:is_regular(CSVPath). 1697: 1698: retrieve_all_personal_data(Client, Config) -> 1699: User = escalus_client:username(Client), 1700: Domain = escalus_client:server(Client), 1701: request_and_unzip_personal_data(User, Domain, Config). 1702: 1703: request_and_unzip_personal_data(User, Domain, Config) -> 1704: {Filename, 0, _} = retrieve_personal_data(User, Domain, Config), 1705: FullPath = get_mim_cwd() ++ "/" ++ Filename, 1706: Dir = make_dir_name(Filename, User), 1707: ct:log("extracting logs ~s", [Dir]), 1708: {ok, _} = zip:extract(FullPath, [{cwd, Dir}]), 1709: Dir. 1710: 1711: make_dir_name(Filename, User) when is_binary(User) -> 1712: make_dir_name(Filename, binary_to_list(User)); 1713: make_dir_name(Filename, User) when is_list(User) -> 1714: Filename ++ "." ++ User ++ ".unzipped". 1715: 1716: retrieve_personal_data(User, Domain, Config) -> 1717: Filename = random_filename(Config), 1718: {CommandOutput, Code} = mongooseimctl("retrieve_personal_data", 1719: [User, Domain, Filename], Config), 1720: {Filename, Code, CommandOutput}. 1721: 1722: unregister(Client, Config) -> 1723: User = escalus_client:username(Client), 1724: Domain = escalus_client:server(Client), 1725: {CommandOutput, Code} = mongooseimctl("unregister", [User, Domain], Config), 1726: {Code, CommandOutput}. 1727: 1728: random_filename(Config) -> 1729: TCName = atom_to_list(?config(tc_name, Config)), 1730: TCName ++ "." ++ integer_to_list(erlang:system_time()) ++ ".zip". 1731: 1732: get_mim_cwd() -> 1733: {ok, Cwd} = rpc(mim(), file, get_cwd, []), 1734: Cwd. 1735: 1736: delete_files() -> 1737: Cwd = get_mim_cwd(), 1738: {ok, Filenames} = rpc(mim(), file, list_dir, [Cwd]), 1739: FilteredFilenames = lists:filter( 1740: fun is_file_to_be_deleted/1, 1741: Filenames), 1742: lists:foreach( 1743: fun(Filename) -> rpc(mim(), file, delete, [Cwd ++ "/" ++ Filename]) end, 1744: FilteredFilenames), 1745: ok. 1746: 1747: is_file_to_be_deleted(Filename) -> 1748: DeletableRegexes = ["\.csv", "\.zip"], 1749: lists:any( 1750: fun(Regex) -> 1751: re:run(Filename, Regex) =/= nomatch 1752: end, 1753: DeletableRegexes). 1754: 1755: pubsub_payloads_row_map(Node, ItemId, Payload) -> 1756: #{"node_name" => binary_to_list(Node), "item_id" => ItemId, "payload" => Payload}. 1757: 1758: pubsub_nodes_row_map(Node, Type) -> 1759: #{"node_name" => binary_to_list(Node), "type" => Type}. 1760: 1761: pubsub_subscription_row_map(Node) -> 1762: #{"node_name" => binary_to_list(Node)}. 1763: 1764: make_pep_node_info(Client, NodeName) -> 1765: {escalus_utils:jid_to_lower(escalus_utils:get_short_jid(Client)), NodeName}. 1766: 1767: item_content(Data) -> 1768: Bin = item_content_xml(Data), 1769: {Bin, binary_to_list(exml:to_binary(Bin))}. 1770: 1771: item_content_xml(Data) -> 1772: #xmlel{name = <<"entry">>, 1773: attrs = [{<<"xmlns">>, <<"http://www.w3.org/2005/Atom">>}], 1774: children = [#xmlcdata{content = Data}]}. 1775: 1776: send_and_assert_private_stanza(User, NS, Content) -> 1777: XML = #xmlel{ name = <<"fingerprint">>, 1778: attrs = [{<<"xmlns">>, NS}], 1779: children = [#xmlcdata{ content = Content }]}, 1780: PrivateStanza = escalus_stanza:private_set(XML), 1781: escalus_client:send(User, PrivateStanza), 1782: escalus:assert(is_iq_result, [PrivateStanza], escalus_client:wait_for_stanza(User)). 1783: 1784: send_and_assert_is_chat_message(UserFrom, UserTo, Body) -> 1785: escalus:send(UserFrom, escalus_stanza:chat_to(UserTo, Body)), 1786: Msg = escalus:wait_for_stanza(UserTo), 1787: escalus:assert(is_chat_message, [Body], Msg). 1788: 1789: validate_datetime(TimeStr) -> 1790: [Date, Time] = string:tokens(TimeStr, "T"), 1791: validate_date(Date), 1792: validate_time(Time). 1793: 1794: validate_date(Date) -> 1795: [Y, M, D] = string:tokens(Date, "-"), 1796: Date1 = {list_to_integer(Y), list_to_integer(M), list_to_integer(D)}, 1797: calendar:valid_date(Date1). 1798: 1799: validate_time(Time) -> 1800: [T | _] = string:tokens(Time, "Z"), 1801: validate_time1(T). 1802: 1803: 1804: validate_time1(Time) -> 1805: [H, M, S] = string:tokens(Time, ":"), 1806: check_list([{H, 24}, {M, 60}, {S, 60}]). 1807: 1808: check_list(List) -> 1809: lists:all(fun({V, L}) -> I = list_to_integer(V), I >= 0 andalso I < L end, List). 1810: 1811: expected_header(mod_roster) -> ["jid", "name", "subscription", 1812: "ask", "groups", "askmessage", "xs"]. 1813: 1814: given_fresh_muc_room(UserSpec, RoomOpts) -> 1815: Username = proplists:get_value(username, UserSpec), 1816: RoomName = muc_helper:fresh_room_name(Username), 1817: From = muc_helper:generate_rpc_jid({user, UserSpec}), 1818: muc_helper:create_instant_room(RoomName, From, Username, RoomOpts), 1819: {ok, RoomName}. 1820: 1821: send_receive_muc_private_message(Room, Domain, {User1, Nickname1}, {User2, Nickname2}, Text) -> 1822: RoomPrivAddrUser1 = <<Room/binary, "@", Domain/binary, "/", Nickname1/binary>>, 1823: RoomPrivAddrUser2 = <<Room/binary, "@", Domain/binary, "/", Nickname2/binary>>, 1824: Msg = escalus_stanza:chat_to(RoomPrivAddrUser2, Text), 1825: escalus:send(User1, Msg), 1826: PMStanza = escalus:wait_for_stanza(User2), 1827: escalus:assert(is_chat_message_from_to, 1828: [RoomPrivAddrUser1, escalus_client:full_jid(User2), Text], PMStanza), 1829: {RoomPrivAddrUser1, RoomPrivAddrUser2}.