1: -module(auth_internal_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: -include_lib("eunit/include/eunit.hrl"). 5: -include("scram.hrl"). 6: 7: all() -> 8: [passwords_as_records_are_still_supported, 9: passwords_in_plain_can_be_converted_to_scram]. 10: 11: init_per_suite(C) -> 12: application:ensure_all_started(jid), 13: ok = mnesia:create_schema([node()]), 14: ok = mnesia:start(), 15: AuthOpts = #{methods => [internal], 16: internal => #{}, 17: password => #{format => scram, scram_iterations => 10}}, 18: mongoose_config:set_opts(#{{auth, host_type()} => AuthOpts}), 19: ejabberd_auth_internal:start(host_type()), 20: C. 21: 22: end_per_suite(_C) -> 23: ejabberd_auth_internal:stop(host_type()), 24: mongoose_config:erase_opts(), 25: mnesia:stop(), 26: mnesia:delete_schema([node()]). 27: 28: init_per_testcase(_TC, Config) -> 29: mongoose_domain_core:start_link([{domain(), host_type()}], []), 30: Config. 31: 32: end_per_testcase(_TC, _Config) -> 33: ok. 34: 35: passwords_as_records_are_still_supported(_C) -> 36: %% given in mnesia there is a user with password in old scram format 37: {U, S, P} = gen_user(), 38: OldScramFormat = old_password_to_scram(P, 340), 39: UserRecord = {passwd, {U, S}, OldScramFormat}, 40: mnesia:dirty_write(UserRecord), 41: %% when we read the password via ejabberd_auth_internal:get_password/3 42: NewScramMap = ejabberd_auth_internal:get_password(host_type(), U, S), 43: %% then new map with sha key is returned 44: ?assertMatch(#{iteration_count := _, 45: sha := #{salt := _, 46: server_key := _, 47: stored_key := _}}, NewScramMap), 48: %% even though in db there is old record stored 49: OldScram = mnesia:dirty_read({passwd, {U, S}}), 50: ?assertMatch([{passwd, _, {scram, _, _, _, _}}], OldScram). 51: 52: passwords_in_plain_can_be_converted_to_scram(_C) -> 53: %% Given there in mnesia there are users 54: %% with plain text password 55: {U, S, P} = gen_user(), 56: UserRecord = {passwd, {U, S}, P}, 57: mnesia:dirty_write(UserRecord), 58: %% and password in the old record format 59: {U2, S2, P2} = gen_user(), 60: OldScramFormat = old_password_to_scram(P2, 340), 61: UserRecord2 = {passwd, {U2, S2}, OldScramFormat}, 62: mnesia:dirty_write(UserRecord2), 63: %% when the migration function is run 64: ejabberd_auth_internal:scram_passwords(), 65: AfterMigrationPlain = mnesia:dirty_read({passwd, {U, S}}), 66: %% then plain text passwords are converted to new map with sha and sha256 67: ?assertMatch([{passwd, _, 68: #{iteration_count := _, 69: sha := #{salt := _, 70: server_key := _, 71: stored_key := _}, 72: sha224 := #{salt := _, 73: server_key := _, 74: stored_key := _}, 75: sha256 := #{salt := _, 76: server_key := _, 77: stored_key := _}, 78: sha384 := #{salt := _, 79: server_key := _, 80: stored_key := _}, 81: sha512 := #{salt := _, 82: server_key := _, 83: stored_key := _}}}], AfterMigrationPlain), 84: %% and the old scram format remains the same 85: AfterMigrationScram = mnesia:dirty_read({passwd, {U2, S2}}), 86: ?assertMatch([{passwd, _, 87: #{iteration_count := _, 88: sha := #{salt := _, 89: server_key := _, 90: stored_key := _}}}], AfterMigrationScram). 91: 92: gen_user() -> 93: {base64:encode(crypto:strong_rand_bytes(5)), 94: domain(), 95: base64:encode(crypto:strong_rand_bytes(6))}. 96: 97: old_password_to_scram(Password, IterationCount) -> 98: Salt = crypto:strong_rand_bytes(16), 99: SaltedPassword = fast_scram:salted_password(sha, jid:resourceprep(Password), Salt, IterationCount), 100: ClientKey = fast_scram:client_key(sha, SaltedPassword), 101: StoredKey = fast_scram:stored_key(sha, ClientKey), 102: ServerKey = fast_scram:server_key(sha, SaltedPassword), 103: #scram{storedkey = base64:encode(StoredKey), 104: serverkey = base64:encode(ServerKey), 105: salt = base64:encode(Salt), 106: iterationcount = IterationCount}. 107: 108: domain() -> 109: <<"server">>. 110: 111: host_type() -> 112: <<"test host type">>.