Skip to content

Oauth2: Support variable expansion when checking resource access #13941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,21 @@ user_login_authorization(Username, AuthProps) ->
check_vhost_access(#auth_user{impl = DecodedTokenFun},
VHost, _AuthzData) ->
with_decoded_token(DecodedTokenFun(),
fun(_Token) ->
DecodedToken = DecodedTokenFun(),
Scopes = get_scope(DecodedToken),
ScopeString = rabbit_oauth2_scope:concat_scopes(Scopes, ","),
rabbit_log:debug("Matching virtual host '~ts' against the following scopes: ~ts", [VHost, ScopeString]),
fun(Token) ->
Scopes = get_expanded_scopes(Token, #resource{virtual_host = VHost}),
rabbit_oauth2_scope:vhost_access(VHost, Scopes)
end).

check_resource_access(#auth_user{impl = DecodedTokenFun},
Resource, Permission, _AuthzContext) ->
with_decoded_token(DecodedTokenFun(),
fun(Token) ->
Scopes = get_scope(Token),
Scopes = get_expanded_scopes(Token, Resource),
rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes)
end).

check_topic_access(#auth_user{impl = DecodedTokenFun},
Resource, Permission, Context) ->
Resource, Permission, Context) ->
with_decoded_token(DecodedTokenFun(),
fun(Token) ->
Scopes = get_expanded_scopes(Token, Resource),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ resolve_resource_server_from_audience(Audience) ->
case find_audience(Audience, AllowedResourceServerIds) of
{error, aud_matched_many_resource_servers_only_one_allowed} = Error ->
Error;
{error, no_matching_aud_found} ->
{error, no_matching_aud_found} ->
translate_error_if_any(
find_unique_resource_server_without_verify_aud(),
true);
Expand Down
10 changes: 6 additions & 4 deletions deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ resource_access(#resource{virtual_host = VHost, name = Name},
end,
get_scope_permissions(Scopes)).

-spec topic_access(rabbit_types:r(atom()), permission(), map(), [binary()]) -> boolean().
topic_access(#resource{virtual_host = VHost, name = ExchangeName},
Permission,
#{routing_key := RoutingKey},
Scopes) ->
lists:any(
fun({VHostPattern, ExchangeNamePattern, RoutingKeyPattern, ScopeGrantedPermission}) ->
fun({VHostPattern, ExchangeNamePattern, RoutingKeyPattern, ScopeGrantedPermission}) ->
is_binary(RoutingKeyPattern) andalso
wildcard:match(VHost, VHostPattern) andalso
wildcard:match(ExchangeName, ExchangeNamePattern) andalso
Expand All @@ -61,10 +62,11 @@ topic_access(#resource{virtual_host = VHost, name = ExchangeName},
get_scope_permissions(Scopes) when is_list(Scopes) ->
lists:filtermap(
fun(ScopeEl) ->
case parse_permission_pattern(ScopeEl) of
Ret = case parse_permission_pattern(ScopeEl) of
ignore -> false;
Perm -> {true, Perm}
end
end,
Ret
end,
Scopes).

Expand All @@ -87,7 +89,7 @@ parse_permission_pattern(_Other) ->

-spec parse_resource_pattern(binary(), permission()) ->
{rabbit_types:vhost(), binary(), binary() | none, permission()} | 'ignore'.
parse_resource_pattern(Pattern, Permission) ->
parse_resource_pattern(Pattern, Permission) ->
case binary:split(Pattern, <<"/">>, [global]) of
[VhostPattern, NamePattern] ->
{VhostPattern, NamePattern, none, Permission};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ fixture_token() ->

token_with_sub(TokenFixture, Sub) ->
maps:put(<<"sub">>, Sub, TokenFixture).
token_with_claim(TokenFixture, Name, Value) ->
maps:put(Name, Value, TokenFixture).
token_with_scopes(TokenFixture, Scopes) ->
maps:put(<<"scope">>, Scopes, TokenFixture).

Expand Down
63 changes: 49 additions & 14 deletions deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ all() ->
test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_field,
test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field,
test_username_from,
{group, with_rabbitmq_node}
{group, with_rabbitmq_node},
{group, with_resource_server_id}

].
groups() ->
Expand All @@ -62,11 +63,11 @@ groups() ->
},
{with_resource_server_id, [], [
test_successful_access_with_a_token,
test_validate_payload_resource_server_id_mismatch,
test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field,
test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_field,
test_successful_authorization_without_scopes,
test_successful_authentication_without_scopes,
test_successful_access_with_a_token_that_uses_single_scope_alias_with_var_expansion,
test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_source_field,
test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_scope_source_field,
normalize_token_scope_with_additional_scopes_complex_claims,
Expand Down Expand Up @@ -634,7 +635,7 @@ normalize_token_scope_with_additional_scopes_complex_claims(_) ->
<<"rabbitmq3">> =>
[<<"rabbitmq-resource.write:*/*">>,
<<"rabbitmq-resource-write">>]},
[<<"read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>]
[<<"read:*/*">>]
},
{
"claims are map with list content - empty result",
Expand All @@ -647,7 +648,7 @@ normalize_token_scope_with_additional_scopes_complex_claims(_) ->
"claims are map with binary content",
#{ <<"rabbitmq">> => <<"rabbitmq-resource.read:*/* rabbitmq-resource-read">>,
<<"rabbitmq3">> => <<"rabbitmq-resource.write:*/* rabbitmq-resource-write">>},
[<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>]
[<<"read:*/*">>]
},
{
"claims are map with binary content - empty result",
Expand Down Expand Up @@ -777,6 +778,45 @@ test_successful_access_with_a_token_that_has_tag_scopes(_) ->
{ok, #auth_user{username = Username, tags = [management, policymaker]}} =
user_login_authentication(Username, [{password, Token}]).

test_successful_access_with_a_token_that_uses_single_scope_alias_with_var_expansion(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}],
set_env(key_config, UaaEnv),
Alias = <<"client-alias-1">>,
set_env(scope_aliases, #{
Alias => [
<<"rabbitmq.configure:{vhost}/q-{sub}/rk-{client_id}**">>
]
}),

VHost = <<"vhost">>,
Username = <<"bob">>,
ClientId = <<"rmq">>,
Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(
?UTIL_MOD:token_with_claim(
?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), <<"client_id">>, ClientId),
Username), Jwk),

{ok, #auth_user{username = Username} = AuthUser} =
user_login_authentication(Username, [{password, Token}]),

%% vhost access
assert_vhost_access_granted(AuthUser, ClientId),

%% resource access
assert_resource_access_denied(AuthUser, VHost, <<"none">>, read),
assert_resource_access_granted(AuthUser, VHost, <<"q-bob">>, configure),

%% topic access
assert_topic_access_refused(AuthUser, VHost, <<"q-bob">>, configure,
#{routing_key => <<"rk-r2mq/#">>}),
assert_topic_access_granted(AuthUser, VHost, <<"q-bob">>, configure,
#{routing_key => <<"rk-rmq/#">>}),


application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config).

test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}],
Expand Down Expand Up @@ -813,8 +853,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field(
assert_resource_access_denied(AuthUser, VHost, <<"three">>, write),

application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id).
application:unset_env(rabbitmq_auth_backend_oauth2, key_config).


test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_and_custom_scope_prefix(_) ->
Expand Down Expand Up @@ -855,8 +894,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_

application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix),
application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id).
application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix).

test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_field(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
Expand Down Expand Up @@ -901,8 +939,7 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi
assert_resource_access_denied(AuthUser, VHost, <<"three">>, write),

application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id).
application:unset_env(rabbitmq_auth_backend_oauth2, key_config).

test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_field(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
Expand Down Expand Up @@ -976,8 +1013,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_
assert_resource_access_denied(AuthUser, VHost, <<"three">>, write),

application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id).
application:unset_env(rabbitmq_auth_backend_oauth2, key_config).

test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_scope_source_field(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
Expand Down Expand Up @@ -1021,8 +1057,7 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc
assert_resource_access_denied(AuthUser, VHost, <<"three">>, write),

application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases),
application:unset_env(rabbitmq_auth_backend_oauth2, key_config),
application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id).
application:unset_env(rabbitmq_auth_backend_oauth2, key_config).

test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field(_) ->
Jwk = ?UTIL_MOD:fixture_jwk(),
Expand Down