Merge pull request #8412 from rabbitmq/fix-8391

Make scopes optional for oauth2 authentication
This commit is contained in:
Michael Klishin 2023-05-30 23:52:56 +04:00 committed by GitHub
commit bcc2bc8525
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 67 additions and 3 deletions

View File

@ -496,7 +496,7 @@ post_process_payload_in_rich_auth_request_format(#{<<"authorization_details">> :
validate_payload(#{?SCOPE_JWT_FIELD := _Scope } = DecodedToken) -> validate_payload(DecodedToken) ->
ResourceServerEnv = application:get_env(?APP, ?RESOURCE_SERVER_ID, <<>>), ResourceServerEnv = application:get_env(?APP, ?RESOURCE_SERVER_ID, <<>>),
ResourceServerId = rabbit_data_coercion:to_binary(ResourceServerEnv), ResourceServerId = rabbit_data_coercion:to_binary(ResourceServerEnv),
ScopePrefix = application:get_env(?APP, ?SCOPE_PREFIX, <<ResourceServerId/binary, ".">>), ScopePrefix = application:get_env(?APP, ?SCOPE_PREFIX, <<ResourceServerId/binary, ".">>),
@ -507,6 +507,11 @@ validate_payload(#{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedTo
ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}; ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}};
{error, Err} -> {refused, {invalid_aud, Err}} {error, Err} -> {refused, {invalid_aud, Err}}
end; end;
validate_payload(#{?AUD_JWT_FIELD := Aud} = DecodedToken, ResourceServerId, _ScopePrefix) ->
case check_aud(Aud, ResourceServerId) of
ok -> {ok, DecodedToken};
{error, Err} -> {refused, {invalid_aud, Err}}
end;
validate_payload(#{?SCOPE_JWT_FIELD := Scope} = DecodedToken, _ResourceServerId, ScopePrefix) -> validate_payload(#{?SCOPE_JWT_FIELD := Scope} = DecodedToken, _ResourceServerId, ScopePrefix) ->
case application:get_env(?APP, ?VERIFY_AUD, true) of case application:get_env(?APP, ?VERIFY_AUD, true) of
true -> {error, {badarg, {aud_field_is_missing}}}; true -> {error, {badarg, {aud_field_is_missing}}};
@ -534,7 +539,8 @@ check_aud(Aud, ResourceServerId) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope. get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope;
get_scopes(#{}) -> [].
-spec get_expanded_scopes(map(), #resource{}) -> [binary()]. -spec get_expanded_scopes(map(), #resource{}) -> [binary()].
get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> get_expanded_scopes(Token, #resource{virtual_host = VHost}) ->

View File

@ -84,6 +84,15 @@ token_with_scopes_and_expiration(Scopes, Expiration) ->
<<"aud">> => [<<"rabbitmq">>], <<"aud">> => [<<"rabbitmq">>],
<<"scope">> => Scopes}. <<"scope">> => Scopes}.
token_without_scopes() ->
%% expiration is a timestamp with precision in seconds
#{
<<"kid">> => <<"token-key">>,
<<"iss">> => <<"unit_test">>,
<<"foo">> => <<"bar">>,
<<"aud">> => [<<"rabbitmq">>]
}.
fixture_token() -> fixture_token() ->
fixture_token([]). fixture_token([]).

View File

@ -19,8 +19,12 @@ all() ->
test_validate_payload_resource_server_id_mismatch, test_validate_payload_resource_server_id_mismatch,
test_validate_payload_with_scope_prefix, test_validate_payload_with_scope_prefix,
test_validate_payload, test_validate_payload,
test_validate_payload_without_scope,
test_validate_payload_when_verify_aud_false, test_validate_payload_when_verify_aud_false,
test_successful_access_with_a_token, test_successful_access_with_a_token,
test_successful_authentication_without_scopes,
test_successful_authorization_without_scopes,
test_unsuccessful_access_without_scopes,
test_successful_access_with_a_token_with_variables_in_scopes, test_successful_access_with_a_token_with_variables_in_scopes,
test_successful_access_with_a_parsed_token, test_successful_access_with_a_parsed_token,
test_successful_access_with_a_token_that_has_tag_scopes, test_successful_access_with_a_token_that_has_tag_scopes,
@ -609,6 +613,30 @@ post_process_payload_with_complex_claim_authorization(Authorization) ->
{true, Payload} = uaa_jwt_jwt:decode_and_verify(Jwk, EncodedToken), {true, Payload} = uaa_jwt_jwt:decode_and_verify(Jwk, EncodedToken),
rabbit_auth_backend_oauth2:post_process_payload(Payload). rabbit_auth_backend_oauth2:post_process_payload(Payload).
test_successful_authentication_without_scopes(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}],
application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv),
application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>),
Username = <<"username">>,
Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk),
{ok, #auth_user{username = Username} } =
rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]).
test_successful_authorization_without_scopes(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}],
application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv),
application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>),
Username = <<"username">>,
Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk),
{ok, _ } =
rabbit_auth_backend_oauth2:user_login_authorization(Username, [{password, Token}]).
test_successful_access_with_a_token(_) -> test_successful_access_with_a_token(_) ->
%% Generate a token with JOSE %% Generate a token with JOSE
%% Check authorization with the token %% Check authorization with the token
@ -980,6 +1008,21 @@ test_unsuccessful_access_with_a_bogus_token(_) ->
?assertMatch({refused, _, _}, ?assertMatch({refused, _, _},
rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, <<"not a token">>}])). rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, <<"not a token">>}])).
test_unsuccessful_access_without_scopes(_) ->
Username = <<"username">>,
application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>),
Jwk = ?UTIL_MOD:fixture_jwk(),
Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:token_without_scopes(), Username), Jwk),
UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}],
application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv),
{ok, #auth_user{username = Username, tags = [], impl = CredentialsFun } = AuthUser} =
rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]),
ct:log("authuser ~p ~p ", [AuthUser, CredentialsFun()]),
assert_vhost_access_denied(AuthUser, <<"vhost">>).
test_restricted_vhost_access_with_a_valid_token(_) -> test_restricted_vhost_access_with_a_valid_token(_) ->
Username = <<"username">>, Username = <<"username">>,
application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>),
@ -1277,6 +1320,12 @@ test_validate_payload(_) ->
<<"scope">> => [<<"bar">>, <<"other.third">>]}}, <<"scope">> => [<<"bar">>, <<"other.third">>]}},
rabbit_auth_backend_oauth2:validate_payload(KnownResourceServerId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)). rabbit_auth_backend_oauth2:validate_payload(KnownResourceServerId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)).
test_validate_payload_without_scope(_) ->
KnownResourceServerId = #{<<"aud">> => [?RESOURCE_SERVER_ID]
},
?assertEqual({ok, #{<<"aud">> => [?RESOURCE_SERVER_ID] }},
rabbit_auth_backend_oauth2:validate_payload(KnownResourceServerId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)).
test_validate_payload_when_verify_aud_false(_) -> test_validate_payload_when_verify_aud_false(_) ->
WithoutAud = #{ WithoutAud = #{
<<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>,