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