1: -module(accounts_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("escalus/include/escalus.hrl"). 5: -include_lib("escalus/include/escalus_xmlns.hrl"). 6: 7: -include_lib("common_test/include/ct.hrl"). 8: -include_lib("eunit/include/eunit.hrl"). 9: 10: -include_lib("exml/include/exml.hrl"). 11: 12: -import(distributed_helper, [mim/0, 13: require_rpc_nodes/1, 14: rpc/4]). 15: 16: -import(mongoose_helper, [wait_for_user/3]). 17: 18: -import(domain_helper, [domain/0, host_type/0]). 19: 20: %%-------------------------------------------------------------------- 21: %% Suite configuration 22: %%-------------------------------------------------------------------- 23: 24: -define(REGISTRATION_TIMEOUT, 2). %% seconds 25: 26: all() -> 27: [ 28: {group, register}, 29: {group, registration_watchers}, 30: {group, bad_registration}, 31: {group, bad_cancelation}, 32: {group, registration_timeout}, 33: {group, change_account_details}, 34: {group, change_account_details_store_plain}, 35: {group, utilities} 36: ]. 37: 38: groups() -> 39: [{register, [parallel], [register, 40: already_registered, 41: registration_conflict, 42: check_unregistered]}, 43: {registration_watchers, [sequence], [admin_notify]}, 44: {bad_registration, [sequence], [null_password]}, 45: {bad_cancelation, [sequence], [bad_request_registration_cancelation, 46: not_allowed_registration_cancelation]}, 47: {registration_timeout, [sequence], [registration_timeout, 48: registration_failure_timeout]}, 49: {change_account_details, [parallel], change_password_tests()}, 50: {change_account_details_store_plain, [parallel], change_password_tests()}, 51: {utilities, [{group, user_info}, 52: {group, users_number_estimate}]}, 53: {user_info, [parallel], [list_users, 54: list_selected_users, 55: count_users, 56: count_selected_users]}, 57: {users_number_estimate, [], [count_users_estimate]} 58: ]. 59: 60: suite() -> 61: require_rpc_nodes([mim]) ++ escalus:suite(). 62: 63: change_password_tests() -> 64: [change_password, 65: change_password_to_null]. 66: %%-------------------------------------------------------------------- 67: %% Init & teardown 68: %%-------------------------------------------------------------------- 69: 70: init_per_suite(Config1) -> 71: ok = dynamic_modules:ensure_modules(host_type(), required_modules()), 72: Config2 = [{mod_register_options, mod_register_options()} | Config1], 73: escalus:init_per_suite([{escalus_user_db, xmpp} | Config2]). 74: 75: end_per_suite(Config) -> 76: escalus_fresh:clean(), 77: escalus:end_per_suite(Config). 78: 79: required_modules() -> 80: [{mod_register, mod_register_options()}]. 81: 82: mod_register_options() -> 83: [{welcome_message, {"", ""}}, 84: {ip_access, [{allow, "127.0.0.0/8"}, 85: {deny, "0.0.0.0/0"}]}, 86: {access, register}, 87: {registration_watchers, []}]. 88: 89: init_per_group(bad_cancelation, Config) -> 90: escalus:create_users(Config, escalus:get_users([alice])); 91: init_per_group(change_account_details, Config) -> 92: [{escalus_user_db, {module, escalus_ejabberd}} |Config]; 93: init_per_group(change_account_details_store_plain, Config) -> 94: AuthOpts = mongoose_helper:auth_opts_with_password_format(plain), 95: Config1 = mongoose_helper:backup_and_set_config_option(Config, auth, AuthOpts), 96: [{escalus_user_db, {module, escalus_ejabberd}} |Config1]; 97: init_per_group(registration_timeout, Config) -> 98: set_registration_timeout(Config); 99: init_per_group(utilities, Config) -> 100: escalus:create_users(Config, escalus:get_users([alice, bob])); 101: init_per_group(users_number_estimate, Config) -> 102: AuthOpts = get_auth_opts(), 103: NewAuthOpts = AuthOpts#{rdbms => #{users_number_estimate => true}}, 104: set_auth_opts(Config, NewAuthOpts); 105: init_per_group(_GroupName, Config) -> 106: Config. 107: 108: end_per_group(change_account_details, Config) -> 109: escalus_fresh:clean(), 110: [{escalus_user_db, xmpp} | Config]; 111: end_per_group(change_account_details_store_plain, Config) -> 112: escalus_fresh:clean(), 113: mongoose_helper:restore_config(Config), 114: [{escalus_user_db, xmpp} | Config]; 115: end_per_group(bad_cancelation, Config) -> 116: escalus:delete_users(Config, escalus:get_users([alice])); 117: end_per_group(registration_timeout, Config) -> 118: restore_registration_timeout(Config); 119: end_per_group(utilities, Config) -> 120: escalus:delete_users(Config, escalus:get_users([alice, bob])); 121: end_per_group(users_number_estimate, Config) -> 122: mongoose_helper:restore_config(Config); 123: end_per_group(_GroupName, Config) -> 124: Config. 125: 126: get_auth_opts() -> 127: rpc(mim(), mongoose_config, get_opt, [{auth, host_type()}]). 128: 129: set_auth_opts(Config, AuthOpts) -> 130: rpc(mim(), ejabberd_auth, stop, [host_type()]), 131: Config1 = mongoose_helper:backup_and_set_config_option(Config, {auth, host_type()}, AuthOpts), 132: rpc(mim(), ejabberd_auth, start, [host_type()]), 133: Config1. 134: 135: init_per_testcase(admin_notify, Config) -> 136: [{_, AdminSpec}] = escalus_users:get_users([admin]), 137: [AdminU, AdminS, _AdminP] = escalus_users:get_usp(Config, AdminSpec), 138: AdminJid = <<AdminU/binary, "@", AdminS/binary>>, 139: enable_watcher(Config, AdminJid), 140: escalus:init_per_testcase(admin_notify, Config); 141: init_per_testcase(not_allowed_registration_cancelation, Config) -> 142: %% Use a configuration that will not allow inband cancelation (and registration). 143: reload_mod_register_option(Config, access, {access, none}), 144: escalus:init_per_testcase(not_allowed_registration_cancelation, Config); 145: init_per_testcase(registration_failure_timeout, Config) -> 146: Config1 = deny_everyone_registration(Config), 147: escalus:init_per_testcase(registration_failure_timeout, Config1); 148: init_per_testcase(CaseName, Config) when CaseName =:= list_selected_users; 149: CaseName =:= count_selected_users -> 150: case mongoose_helper:auth_modules() of 151: [Mod | _] when Mod =:= ejabberd_auth_rdbms; 152: Mod =:= ejabberd_auth_internal -> 153: escalus:init_per_testcase(CaseName, Config); 154: Modules -> 155: {skip, {"Queries for selected users not supported", Modules}} 156: end; 157: init_per_testcase(CaseName, Config) -> 158: escalus:init_per_testcase(CaseName, Config). 159: 160: end_per_testcase(register, Config) -> 161: escalus:end_per_testcase(register, Config); 162: end_per_testcase(admin_notify, Config) -> 163: disable_watcher(Config), 164: escalus:delete_users(Config, escalus:get_users([alice, bob, admin])), 165: escalus:end_per_testcase(admin_notify, Config); 166: end_per_testcase(registration_conflict, Config) -> 167: escalus_users:delete_users(Config, escalus:get_users([alice])), 168: escalus:end_per_testcase(registration_conflict, Config); 169: end_per_testcase(not_allowed_registration_cancelation, Config) -> 170: restore_mod_register_options(Config), 171: escalus:end_per_testcase(not_allowed_registration_cancelation, Config); 172: end_per_testcase(registration_timeout, Config) -> 173: escalus:delete_users(Config, escalus:get_users([alice, bob])), 174: escalus:end_per_testcase(registration_timeout, Config); 175: end_per_testcase(registration_failure_timeout, Config) -> 176: mongoose_helper:restore_config_option(Config, [{access, host_type()}, register]), 177: escalus:end_per_testcase(registration_failure_timeout, Config); 178: end_per_testcase(CaseName, Config) -> 179: escalus:end_per_testcase(CaseName, Config). 180: 181: register(Config) -> 182: [{Name1, _UserSpec1}, {Name2, _UserSpec2}] = escalus_users:get_users([alice, bob]), 183: escalus_fresh:create_users(Config, escalus:get_users([Name1, Name2])). 184: 185: already_registered(Config) -> 186: escalus_fresh:story(Config, [{alice, 1}], fun(Alice) -> 187: escalus:send(Alice, escalus_stanza:get_registration_fields()), 188: Stanza = escalus:wait_for_stanza(Alice), 189: escalus:assert(is_iq_result, Stanza), 190: true = has_registered_element(Stanza) 191: 192: end). 193: registration_conflict(Config) -> 194: [Alice] = escalus_users:get_users([alice]), 195: {ok, result, _Stanza} = escalus_users:create_user(Config, Alice), 196: {ok, conflict, _Raw} = escalus_users:create_user(Config, Alice). 197: 198: 199: 200: admin_notify(Config) -> 201: [{Name1, UserSpec1}, {Name2, UserSpec2}] = escalus_users:get_users([alice, bob]), 202: [{_, AdminSpec}] = escalus_users:get_users([admin]), 203: [Username1, _Server1, _Pass1] = escalus_users:get_usp(Config, UserSpec1), 204: [Username2, _Server2, _Pass2] = escalus_users:get_usp(Config, UserSpec2), 205: [AdminU, AdminS, AdminP] = escalus_users:get_usp(Config, AdminSpec), 206: 207: rpc(mim(), ejabberd_auth, try_register, [mongoose_helper:make_jid(AdminU, AdminS), AdminP]), 208: escalus:story(Config, [{admin, 1}], fun(Admin) -> 209: escalus:create_users(Config, escalus:get_users([Name1, Name2])), 210: 211: Predicates = [ 212: fun(Stanza) -> 213: Body = exml_query:path(Stanza, [{element, <<"body">>}, cdata]), 214: escalus_pred:is_chat_message(Stanza) 215: andalso 216: re:run(Body, <<"registered">>, []) =/= nomatch 217: andalso 218: re:run(Body, Username, []) =/= nomatch 219: end 220: || Username <- [Username1, Username2] 221: ], 222: escalus:assert_many(Predicates, escalus:wait_for_stanzas(Admin, 2)) 223: end). 224: 225: 226: 227: null_password(Config) -> 228: Details = escalus_fresh:freshen_spec(Config, alice), 229: Alice = {alice, lists:keyreplace(password, 1, Details, {password, <<>>})}, 230: {error, _, Response} = escalus_users:create_user(Config, Alice), 231: escalus:assert(is_iq_error, Response), 232: %% This error response means there was no character data, 233: %% i.e. elements `<password\>' or `<password></password>' where 234: %% indeed present. 235: {username, Name} = lists:keyfind(username, 1, Details), 236: {server, Server} = lists:keyfind(server, 1, Details), 237: escalus:assert(is_error, [<<"modify">>, <<"not-acceptable">>], Response), 238: false = rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Name, Server)]). 239: 240: check_unregistered(Config) -> 241: [{_, UserSpec}] = escalus_users:get_users([bob]), 242: [Username, Server, _Pass] = escalus_users:get_usp(Config, UserSpec), 243: false = rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Username, Server)]). 244: 245: bad_request_registration_cancelation(Config) -> 246: 247: %% To quote XEP 0077, section 3.2, table 1 (unregister error 248: %% cases): "The <remove/> element [is] not the only child element 249: %% of the <query/> element." 250: escalus:story(Config, [{alice, 1}], fun(Alice) -> 251: 252: %% Alice sends bad cancelation request 253: escalus:send(Alice, bad_cancelation_stanza()), 254: 255: %% Alice receives failure response 256: Stanza = escalus:wait_for_stanza(Alice), 257: escalus:assert(is_iq_error, Stanza), 258: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], Stanza) 259: 260: end). 261: 262: not_allowed_registration_cancelation(Config) -> 263: 264: %% To quote XEP 0077, section 3.2, table 1 (unregister error 265: %% cases): "No sender is allowed to cancel registrations in-band." 266: 267: escalus:story(Config, [{alice, 1}], fun(Alice) -> 268: 269: %% Alice sends cancelation request 270: escalus:send(Alice, escalus_stanza:remove_account()), 271: 272: %% Alice receives failure response 273: Stanza = escalus:wait_for_stanza(Alice), 274: escalus:assert(is_iq_error, Stanza), 275: escalus:assert(is_error, [<<"cancel">>, <<"not-allowed">>], Stanza) 276: 277: end). 278: 279: registration_timeout(Config) -> 280: [Alice, Bob] = escalus_users:get_users([alice, bob]), 281: 282: %% The first user should be created successfully 283: wait_for_user(Config, Alice, ?REGISTRATION_TIMEOUT), 284: 285: %% Creation of the second one should err because of not timing out yet 286: {error, failed_to_register, Stanza} = escalus_users:create_user(Config, Bob), 287: escalus:assert(is_iq_error, Stanza), 288: %% Something else may be more acceptable for the assertion 289: %% below... 2nd paragraph, section 3.1.1, XEP 0077: [...] a server 290: %% MAY return a `<not-acceptable/>' stanza error if [...] an 291: %% entity attempts to register a second identity after 292: %% successfully completing the registration use case. 293: escalus:assert(is_error, [<<"wait">>, <<"resource-constraint">>], Stanza), 294: 295: %% After timeout, the user should be registered successfully 296: wait_for_user(Config, Alice, erlang:round(?REGISTRATION_TIMEOUT * 1.5 * 1000)). 297: 298: registration_failure_timeout(Config) -> 299: timer:sleep(timer:seconds(?REGISTRATION_TIMEOUT + 1)), 300: [Alice] = escalus_users:get_users([alice]), 301: 302: %% Registration of the first user should fail because of access denial 303: {error,failed_to_register,R} = escalus_users:create_user(Config, Alice), 304: escalus:assert(is_iq_error, R), 305: escalus:assert(is_error, [<<"auth">>, <<"forbidden">>], R), 306: 307: %% Registration of a second one should fail because requests were 308: %% made in quick succession 309: {error,failed_to_register,S} = escalus_users:create_user(Config, Alice), 310: escalus:assert(is_iq_error, S), 311: escalus:assert(is_error, [<<"wait">>, <<"resource-constraint">>], S). 312: 313: change_password(Config) -> 314: 315: escalus_fresh:story(Config, [{alice, 1}], fun(Alice) -> 316: Username = escalus_client:username(Alice), 317: escalus:send(Alice, 318: Q = escalus_stanza:iq_set(?NS_INBAND_REGISTER, 319: [#xmlel{name = <<"username">>, 320: children = [#xmlcdata{content = Username}]}, 321: #xmlel{name = <<"password">>, 322: children = [#xmlcdata{content = strong_pwd()}]}])), 323: 324: R = escalus:wait_for_stanza(Alice), 325: 326: escalus:assert(is_iq_result, [Q], R) 327: 328: end). 329: 330: change_password_to_null(Config) -> 331: 332: %% Section 3.3, XEP 0077: If the user provides an empty password 333: %% element or a password element that contains no XML character 334: %% data (i.e., either <password/> or <password></password>), the 335: %% server or service MUST NOT change the password to a null value, 336: %% but instead MUST maintain the existing password. 337: 338: %% By the above, `end_per_testcase' should succeed. XEP 0077 339: %% doesn't say how how an XMPP sever should respond, but since 340: %% this is in IQ, it must: so we choose to require a `not-allowed' 341: %% response. 342: 343: escalus_fresh:story(Config, [{alice, 1}], fun(Alice) -> 344: Username = escalus_client:username(Alice), 345: escalus:send(Alice, 346: escalus_stanza:iq_set(?NS_INBAND_REGISTER, 347: [#xmlel{name = <<"username">>, 348: children = [#xmlcdata{content = Username}]}, 349: #xmlel{name = <<"password">>, 350: children = [#xmlcdata{content = <<"">>}]}])), 351: 352: R = escalus:wait_for_stanza(Alice), 353: 354: escalus:assert(is_iq_error, R), 355: escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], R) 356: 357: end). 358: 359: %% Tests for utility functions currently accessible only from the Erlang shell 360: 361: list_users(_Config) -> 362: Users = [{<<"alice">>, domain()}, {<<"bob">>, domain()}], 363: ?assertEqual(Users, lists:sort(rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()]))). 364: 365: list_selected_users(_Config) -> 366: Alice = {<<"alice">>, domain()}, 367: Bob = {<<"bob">>, domain()}, 368: ?assertEqual([Alice], rpc(mim(), ejabberd_auth, get_vh_registered_users, 369: [domain(), [{from, 1}, {to, 1}]])), 370: ?assertEqual([Bob], rpc(mim(), ejabberd_auth, get_vh_registered_users, 371: [domain(), [{from, 2}, {to, 10}]])), 372: ?assertEqual([Alice], rpc(mim(), ejabberd_auth, get_vh_registered_users, 373: [domain(), [{prefix, <<"a">>}]])), 374: ?assertEqual([Alice], rpc(mim(), ejabberd_auth, get_vh_registered_users, 375: [domain(), [{prefix, <<"a">>}, {from, 1}, {to, 10}]])), 376: ?assertEqual([Bob], rpc(mim(), ejabberd_auth, get_vh_registered_users, 377: [domain(), [{prefix, <<>>}, {from, 2}, {to, 10}]])). 378: 379: count_users(_Config) -> 380: ?assertEqual(2, rpc(mim(), ejabberd_auth, get_vh_registered_users_number, [domain()])). 381: 382: count_users_estimate(_Config) -> 383: Count = rpc(mim(), ejabberd_auth, get_vh_registered_users_number, [domain()]), 384: ?assert(is_integer(Count) andalso Count >= 0). 385: 386: count_selected_users(_Config) -> 387: ?assertEqual(1, rpc(mim(), ejabberd_auth, get_vh_registered_users_number, 388: [domain(), [{prefix, <<"a">>}]])). 389: 390: %%-------------------------------------------------------------------- 391: %% Helpers 392: %%-------------------------------------------------------------------- 393: 394: skip_if_mod_register_not_enabled(Config) -> 395: case escalus_users:is_mod_register_enabled(Config) of 396: true -> 397: Config; % will create users inside test case 398: _ -> 399: {skip, mod_register_disabled} 400: end. 401: 402: strong_pwd() -> 403: <<"Sup3r","c4li","fr4g1","l1571c","3xp1","4l1","d0c10u5">>. 404: 405: set_registration_timeout(Config) -> 406: mongoose_helper:backup_and_set_config_option(Config, registration_timeout, 407: ?REGISTRATION_TIMEOUT). 408: 409: restore_registration_timeout(Config) -> 410: mongoose_helper:restore_config_option(Config, registration_timeout). 411: 412: deny_everyone_registration(Config) -> 413: mongoose_helper:backup_and_set_config_option(Config, [{access, host_type()}, register], 414: [#{acl => all, value => deny}]). 415: 416: has_registered_element(Stanza) -> 417: [#xmlel{name = <<"registered">>}] =:= exml_query:paths(Stanza, 418: [{element, <<"query">>}, {element, <<"registered">>}]). 419: 420: bad_cancelation_stanza() -> 421: escalus_stanza:iq(<<"set">>, [#xmlel{name = <<"query">>, 422: attrs = [{<<"xmlns">>, <<"jabber:iq:register">>}], 423: children = [#xmlel{name = <<"remove">>}, 424: %% The <remove/> element is not the only child element of the 425: %% <query/> element. 426: #xmlel{name = <<"foo">>}]}]). 427: 428: user_exists(Name, Config) -> 429: {Name, Client} = escalus_users:get_user_by_name(Name), 430: [Username, Server, _Pass] = escalus_users:get_usp(Config, Client), 431: rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Username, Server)]). 432: 433: reload_mod_register_option(Config, Key, Value) -> 434: Host = host_type(), 435: Args = proplists:get_value(mod_register_options, Config), 436: Args1 = lists:keyreplace(Key, 1, Args, {Key, Value}), 437: dynamic_modules:restart(Host, mod_register, Args1). 438: 439: restore_mod_register_options(Config) -> 440: Host = host_type(), 441: Args = proplists:get_value(mod_register_options, Config), 442: dynamic_modules:restart(Host, mod_register, Args). 443: 444: enable_watcher(Config, Watcher) -> 445: reload_mod_register_option(Config, registration_watchers, [Watcher]). 446: 447: disable_watcher(Config) -> 448: restore_mod_register_options(Config).