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