1: -module(auth_jwt_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("common_test/include/ct.hrl"). 5: 6: -define(DOMAIN, <<"localhost">>). 7: -define(HOST_TYPE, <<"test host type">>). 8: -define(USERNAME, <<"10857839">>). 9: -define(WRONG_USERNAME, <<"alice">>). 10: -define(JWT_KEY, <<"testtesttest">>). 11: %%-------------------------------------------------------------------- 12: %% Suite configuration 13: %%-------------------------------------------------------------------- 14: 15: all() -> 16: [{group, generic}, {group, public_key}]. 17: 18: groups() -> 19: [ 20: {generic, [parallel], generic_tests()}, 21: {public_key, [], public_key_tests()} 22: ]. 23: 24: generic_tests() -> 25: [ 26: check_password_succeeds_for_correct_token, 27: check_password_fails_for_wrong_token, 28: check_password_fails_for_correct_token_but_wrong_username, 29: authorize, 30: does_user_exist, 31: supported_sasl_mechanisms 32: ]. 33: 34: public_key_tests() -> 35: [ 36: check_password_succeeds_for_pubkey_signed_token 37: ]. 38: 39: suite() -> 40: []. 41: 42: %%-------------------------------------------------------------------- 43: %% Init & teardown 44: %%-------------------------------------------------------------------- 45: 46: init_per_suite(Config) -> 47: application:ensure_all_started(jid), 48: meck:new(ejabberd_auth, [no_link, passthrough]), 49: meck:expect(ejabberd_auth, auth_modules_for_host_type, 50: fun(_) -> [] end), 51: Config. 52: 53: end_per_suite(Config) -> 54: unset_auth_opts(), 55: meck:unload(ejabberd_auth), 56: Config. 57: 58: init_per_group(public_key, Config) -> 59: Root = small_path_helper:repo_dir(Config), 60: PrivkeyPath = filename:join([Root, "tools", "ssl", "mongooseim", "privkey.pem"]), 61: PubkeyPath = filename:join([Root, "tools", "ssl", "mongooseim", "pubkey.pem"]), 62: {ok, PrivKey} = file:read_file(PrivkeyPath), 63: set_auth_opts({file, PubkeyPath}, "RS256", bookingNumber), 64: ok = ejabberd_auth_jwt:start(?HOST_TYPE), 65: [{priv_key, PrivKey} | Config]; 66: init_per_group(_, Config) -> 67: set_auth_opts({value, ?JWT_KEY}, "HS256", bookingNumber), 68: ok = ejabberd_auth_jwt:start(?HOST_TYPE), 69: Config. 70: 71: end_per_group(_, Config) -> 72: ok = ejabberd_auth_jwt:stop(?HOST_TYPE), 73: Config. 74: 75: 76: init_per_testcase(_CaseName, Config) -> 77: Config. 78: 79: end_per_testcase(_CaseName, Config) -> 80: Config. 81: 82: %%-------------------------------------------------------------------- 83: %% Authentication tests 84: %%-------------------------------------------------------------------- 85: 86: check_password_succeeds_for_correct_token(_Config) -> 87: true = ejabberd_auth_jwt:check_password(?HOST_TYPE, ?USERNAME, ?DOMAIN, 88: generate_token(hs256, 0, ?JWT_KEY)). 89: 90: check_password_fails_for_wrong_token(_C) -> 91: false = ejabberd_auth_jwt:check_password(?HOST_TYPE, ?USERNAME, ?DOMAIN, 92: generate_token(hs256, 60, ?JWT_KEY)). 93: 94: check_password_fails_for_correct_token_but_wrong_username(_C) -> 95: false = ejabberd_auth_jwt:check_password(?HOST_TYPE, ?WRONG_USERNAME, ?DOMAIN, 96: generate_token(hs256, 0, ?JWT_KEY)). 97: 98: authorize(_C) -> 99: Creds0 = mongoose_credentials:new(?DOMAIN, ?HOST_TYPE, #{}), 100: Creds = mongoose_credentials:extend(Creds0, [{username, ?USERNAME}, 101: {password, generate_token(hs256, 0, ?JWT_KEY)}, 102: {digest, fake}, 103: {digest_gen, fun(A) -> A end}]), 104: {ok, Creds2} = ejabberd_auth_jwt:authorize(Creds), 105: ejabberd_auth_jwt = mongoose_credentials:get(Creds2, auth_module). 106: 107: does_user_exist(_Config) -> 108: true = ejabberd_auth_jwt:does_user_exist(?HOST_TYPE, <<"madhatter">>, ?DOMAIN). 109: 110: supported_sasl_mechanisms(_C) -> 111: Modules = [cyrsasl_plain, cyrsasl_digest, cyrsasl_external, 112: cyrsasl_scram_sha1, cyrsasl_scram_sha224, cyrsasl_scram_sha256, 113: cyrsasl_scram_sha384, cyrsasl_scram_sha512], 114: [true, false, false, false, false, false, false, false] = 115: [ejabberd_auth_jwt:supports_sasl_module(?DOMAIN, Mod) || Mod <- Modules]. 116: 117: check_password_succeeds_for_pubkey_signed_token(C) -> 118: Key = proplists:get_value(priv_key, C), 119: true = ejabberd_auth_jwt:check_password(?HOST_TYPE, ?USERNAME, ?DOMAIN, 120: generate_token(rs256, 0, Key)). 121: 122: %%-------------------------------------------------------------------- 123: %% Helpers 124: %%-------------------------------------------------------------------- 125: 126: set_auth_opts(Secret, Algorithm, Key) -> 127: mongoose_config:set_opts(#{{auth, ?HOST_TYPE} => #{jwt => #{secret => Secret, 128: algorithm => Algorithm, 129: username_key => Key}}}). 130: 131: unset_auth_opts() -> 132: mongoose_config:erase_opts(). 133: 134: generate_token(Alg, NbfDelta, Key) -> 135: Now = erlang:system_time(second), 136: Data = #{bookingNumber => ?USERNAME, 137: exp => Now + 60, 138: nbf => Now + NbfDelta, 139: iat => Now}, 140: jwerl:sign(Data, Alg, Key).