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