Support keycloak custom format via configuration
This commit is contained in:
parent
55ae918094
commit
1179d3a3ec
|
@ -13,7 +13,6 @@ def all_beam_files(name = "all_beam_files"):
|
|||
"src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl",
|
||||
"src/rabbit_auth_backend_oauth2.erl",
|
||||
"src/rabbit_auth_backend_oauth2_app.erl",
|
||||
"src/rabbit_oauth2_keycloak.erl",
|
||||
"src/rabbit_oauth2_provider.erl",
|
||||
"src/rabbit_oauth2_rar.erl",
|
||||
"src/rabbit_oauth2_resource_server.erl",
|
||||
|
@ -51,7 +50,6 @@ def all_test_beam_files(name = "all_test_beam_files"):
|
|||
"src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl",
|
||||
"src/rabbit_auth_backend_oauth2.erl",
|
||||
"src/rabbit_auth_backend_oauth2_app.erl",
|
||||
"src/rabbit_oauth2_keycloak.erl",
|
||||
"src/rabbit_oauth2_provider.erl",
|
||||
"src/rabbit_oauth2_rar.erl",
|
||||
"src/rabbit_oauth2_resource_server.erl",
|
||||
|
@ -101,7 +99,6 @@ def all_srcs(name = "all_srcs"):
|
|||
"src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl",
|
||||
"src/rabbit_auth_backend_oauth2.erl",
|
||||
"src/rabbit_auth_backend_oauth2_app.erl",
|
||||
"src/rabbit_oauth2_keycloak.erl",
|
||||
"src/rabbit_oauth2_provider.erl",
|
||||
"src/rabbit_oauth2_rar.erl",
|
||||
"src/rabbit_oauth2_resource_server.erl",
|
||||
|
|
|
@ -28,8 +28,7 @@
|
|||
get_scope/1, set_scope/2,
|
||||
resolve_resource_server/1]).
|
||||
|
||||
-import(rabbit_oauth2_keycloak, [has_keycloak_scopes/1, extract_scopes_from_keycloak_format/1]).
|
||||
-import(rabbit_oauth2_rar, [extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]).
|
||||
-import(rabbit_oauth2_rar, [extract_scopes_from_rich_auth_request/2]).
|
||||
|
||||
-import(rabbit_oauth2_scope, [filter_matching_scope_prefix_and_drop_it/2]).
|
||||
|
||||
|
@ -229,79 +228,142 @@ check_token(Token, {ResourceServer, InternalOAuthProvider}) ->
|
|||
{false, _} -> {refused, signature_invalid}
|
||||
end.
|
||||
|
||||
extract_scopes_from_scope_claim(Payload) ->
|
||||
case maps:find(?SCOPE_JWT_FIELD, Payload) of
|
||||
{ok, Bin} when is_binary(Bin) ->
|
||||
maps:put(?SCOPE_JWT_FIELD,
|
||||
binary:split(Bin, <<" ">>, [global, trim_all]),
|
||||
Payload);
|
||||
_ -> Payload
|
||||
end.
|
||||
|
||||
-spec normalize_token_scope(
|
||||
ResourceServer :: resource_server(), DecodedToken :: decoded_jwt_token()) -> map().
|
||||
normalize_token_scope(ResourceServer, Payload) ->
|
||||
Payload0 = maps:map(fun(K, V) ->
|
||||
case K of
|
||||
?SCOPE_JWT_FIELD when is_binary(V) ->
|
||||
binary:split(V, <<" ">>, [global, trim_all]);
|
||||
_ -> V
|
||||
end
|
||||
end, Payload),
|
||||
|
||||
Payload1 = case has_additional_scopes_key(ResourceServer, Payload0) of
|
||||
true -> extract_scopes_from_additional_scopes_key(ResourceServer, Payload0);
|
||||
false -> Payload0
|
||||
end,
|
||||
|
||||
Payload2 = case has_keycloak_scopes(Payload1) of
|
||||
true -> extract_scopes_from_keycloak_format(Payload1);
|
||||
false -> Payload1
|
||||
end,
|
||||
|
||||
Payload3 = case ResourceServer#resource_server.scope_aliases of
|
||||
undefined -> Payload2;
|
||||
ScopeAliases -> extract_scopes_using_scope_aliases(ScopeAliases, Payload2)
|
||||
end,
|
||||
|
||||
Payload4 = case has_rich_auth_request_scopes(Payload3) of
|
||||
true -> extract_scopes_from_rich_auth_request(ResourceServer, Payload3);
|
||||
false -> Payload3
|
||||
end,
|
||||
|
||||
Payload1 = extract_scopes_from_rich_auth_request(ResourceServer,
|
||||
extract_scopes_using_scope_aliases(ResourceServer,
|
||||
extract_scopes_from_additional_scopes_key(ResourceServer,
|
||||
extract_scopes_from_scope_claim(Payload)))),
|
||||
|
||||
FilteredScopes = filter_matching_scope_prefix_and_drop_it(
|
||||
get_scope(Payload4), ResourceServer#resource_server.scope_prefix),
|
||||
set_scope(FilteredScopes, Payload4).
|
||||
|
||||
get_scope(Payload1), ResourceServer#resource_server.scope_prefix),
|
||||
set_scope(FilteredScopes, Payload1).
|
||||
|
||||
-spec extract_scopes_using_scope_aliases(
|
||||
ScopeAliasMapping :: map(), Payload :: map()) -> map().
|
||||
extract_scopes_using_scope_aliases(ScopeAliasMapping, Payload) ->
|
||||
Scopes0 = get_scope(Payload),
|
||||
Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0),
|
||||
%% for all scopes, look them up in the scope alias map, and if they are
|
||||
%% present, add the alias to the final scope list. Note that we also preserve
|
||||
%% the original scopes, it should not hurt.
|
||||
ExpandedScopes =
|
||||
lists:foldl(fun(ScopeListItem, Acc) ->
|
||||
case maps:get(ScopeListItem, ScopeAliasMapping, undefined) of
|
||||
undefined ->
|
||||
Acc;
|
||||
MappedList when is_list(MappedList) ->
|
||||
Binaries = rabbit_data_coercion:to_list_of_binaries(MappedList),
|
||||
Acc ++ Binaries;
|
||||
Value ->
|
||||
Binaries = rabbit_data_coercion:to_list_of_binaries(Value),
|
||||
Acc ++ Binaries
|
||||
end
|
||||
end, Scopes, Scopes),
|
||||
set_scope(ExpandedScopes, Payload).
|
||||
ResourceServer :: resource_server(), Payload :: map()) -> map().
|
||||
extract_scopes_using_scope_aliases(
|
||||
#resource_server{scope_aliases = ScopeAliasMapping}, Payload)
|
||||
when is_map(ScopeAliasMapping) ->
|
||||
Scopes0 = get_scope(Payload),
|
||||
Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0),
|
||||
%% for all scopes, look them up in the scope alias map, and if they are
|
||||
%% present, add the alias to the final scope list. Note that we also preserve
|
||||
%% the original scopes, it should not hurt.
|
||||
ExpandedScopes =
|
||||
lists:foldl(fun(ScopeListItem, Acc) ->
|
||||
case maps:get(ScopeListItem, ScopeAliasMapping, undefined) of
|
||||
undefined ->
|
||||
Acc;
|
||||
MappedList when is_list(MappedList) ->
|
||||
Binaries = rabbit_data_coercion:to_list_of_binaries(MappedList),
|
||||
Acc ++ Binaries;
|
||||
Value ->
|
||||
Binaries = rabbit_data_coercion:to_list_of_binaries(Value),
|
||||
Acc ++ Binaries
|
||||
end
|
||||
end, Scopes, Scopes),
|
||||
set_scope(ExpandedScopes, Payload);
|
||||
extract_scopes_using_scope_aliases(_, Payload) -> Payload.
|
||||
|
||||
%% Path is a binary expression which is a plain word like <<"roles">>
|
||||
%% or +1 word separated by . like <<"authorization.permissions.scopes">>
|
||||
%% The Payload is a map.
|
||||
%% Using the path <<"authorization.permissions.scopes">> as an example
|
||||
%% 1. lookup the key <<"authorization">> in the Payload
|
||||
%% 2. if it is found, the next map to use as payload is the value found from the key <<"authorization">>
|
||||
%% 3. lookup the key <<"permissions">> in the previous map
|
||||
%% 4. if it is found, it may be a map or a list of maps.
|
||||
%% 5. if it is a list of maps, iterate each element in the list
|
||||
%% 6. for each element in the list, which should be a map, find the key <<"scopes">>
|
||||
%% 7. because there are no more words/keys, return a list of all the values found
|
||||
%% associated to the word <<"scopes">>
|
||||
extract_token_value(R, Payload, Path, ValueMapperFun)
|
||||
when is_map(Payload), is_binary(Path), is_function(ValueMapperFun) ->
|
||||
extract_token_value_from_map(R, Payload, [], split_path(Path), ValueMapperFun);
|
||||
extract_token_value(_, _, _, _) ->
|
||||
[].
|
||||
|
||||
extract_scope_list_from_token_value(_R, List) when is_list(List) -> List;
|
||||
extract_scope_list_from_token_value(_R, Binary) when is_binary(Binary) ->
|
||||
binary:split(Binary, <<" ">>, [global, trim_all]);
|
||||
extract_scope_list_from_token_value(#resource_server{id = ResourceServerId}, Map) when is_map(Map) ->
|
||||
case maps:get(ResourceServerId, Map, undefined) of
|
||||
undefined -> [];
|
||||
Ks when is_list(Ks) ->
|
||||
[erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- Ks];
|
||||
ClaimBin when is_binary(ClaimBin) ->
|
||||
UnprefixedClaims = binary:split(ClaimBin, <<" ">>, [global, trim_all]),
|
||||
[erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- UnprefixedClaims];
|
||||
_ -> []
|
||||
end;
|
||||
extract_scope_list_from_token_value(_, _) -> [].
|
||||
|
||||
extract_token_value_from_map(_, _Map, Acc, [], _Mapper) ->
|
||||
Acc;
|
||||
extract_token_value_from_map(R, Map, Acc, [KeyStr], Mapper) when is_map(Map) ->
|
||||
case maps:find(KeyStr, Map) of
|
||||
{ok, Value} -> Acc ++ Mapper(R, Value);
|
||||
error -> Acc
|
||||
end;
|
||||
extract_token_value_from_map(R, Map, Acc, [KeyStr | Rest], Mapper) when is_map(Map) ->
|
||||
case maps:find(KeyStr, Map) of
|
||||
{ok, M} when is_map(M) -> extract_token_value_from_map(R, M, Acc, Rest, Mapper);
|
||||
{ok, L} when is_list(L) -> extract_token_value_from_list(R, L, Acc, Rest, Mapper);
|
||||
{ok, Value} when Rest =:= [] -> Acc ++ Mapper(R, Value);
|
||||
_ -> Acc
|
||||
end;
|
||||
extract_token_value_from_map(_, _, Acc, _, _Mapper) ->
|
||||
Acc.
|
||||
|
||||
extract_token_value_from_list(_, [], Acc, [], _Mapper) ->
|
||||
Acc;
|
||||
extract_token_value_from_list(_, [], Acc, [_KeyStr | _Rest], _Mapper) ->
|
||||
Acc;
|
||||
extract_token_value_from_list(R, [H | T], Acc, [KeyStr | Rest] = KeyList, Mapper) when is_map(H) ->
|
||||
NewAcc = case maps:find(KeyStr, H) of
|
||||
{ok, Map} when is_map(Map) -> extract_token_value_from_map(R, Map, Acc, Rest, Mapper);
|
||||
{ok, List} when is_list(List) -> extract_token_value_from_list(R, List, Acc, Rest, Mapper);
|
||||
{ok, Value} -> Acc++Mapper(R, Value);
|
||||
_ -> Acc
|
||||
end,
|
||||
extract_token_value_from_list(R, T, NewAcc, KeyList, Mapper);
|
||||
|
||||
extract_token_value_from_list(R, [E | T], Acc, [], Mapper) ->
|
||||
extract_token_value_from_list(R, T, Acc++Mapper(R, E), [], Mapper);
|
||||
extract_token_value_from_list(R, [E | _T] = L, Acc, KeyList, Mapper) when is_map(E) ->
|
||||
extract_token_value_from_list(R, L, Acc, KeyList, Mapper);
|
||||
extract_token_value_from_list(R, [_ | T], Acc, KeyList, Mapper) ->
|
||||
extract_token_value_from_list(R, T, Acc, KeyList, Mapper).
|
||||
|
||||
|
||||
split_path(Path) when is_binary(Path) ->
|
||||
binary:split(Path, <<".">>, [global, trim_all]).
|
||||
|
||||
-spec has_additional_scopes_key(
|
||||
ResourceServer :: resource_server(), Payload :: map()) -> boolean().
|
||||
has_additional_scopes_key(ResourceServer, Payload) when is_map(Payload) ->
|
||||
case ResourceServer#resource_server.additional_scopes_key of
|
||||
undefined -> false;
|
||||
ScopeKey -> maps:is_key(ScopeKey, Payload)
|
||||
end.
|
||||
|
||||
-spec extract_scopes_from_additional_scopes_key(
|
||||
ResourceServer :: resource_server(), Payload :: map()) -> map().
|
||||
extract_scopes_from_additional_scopes_key(ResourceServer, Payload) ->
|
||||
Claim = maps:get(ResourceServer#resource_server.additional_scopes_key, Payload),
|
||||
AdditionalScopes = extract_additional_scopes(ResourceServer, Claim),
|
||||
set_scope(AdditionalScopes ++ get_scope(Payload), Payload).
|
||||
extract_scopes_from_additional_scopes_key(
|
||||
#resource_server{additional_scopes_key = Key} = ResourceServer, Payload)
|
||||
when is_list(Key) or is_binary(Key) ->
|
||||
Paths = case Key of
|
||||
B when is_binary(B) -> binary:split(B, <<" ">>, [global, trim_all]);
|
||||
L when is_list(L) -> L
|
||||
end,
|
||||
AdditionalScopes = [ extract_token_value(ResourceServer,
|
||||
Payload, Path, fun extract_scope_list_from_token_value/2) || Path <- Paths],
|
||||
set_scope(lists:flatten(AdditionalScopes) ++ get_scope(Payload), Payload);
|
||||
extract_scopes_from_additional_scopes_key(_, Payload) -> Payload.
|
||||
|
||||
extract_additional_scopes(ResourceServer, ComplexClaim) ->
|
||||
ResourceServerId = ResourceServer#resource_server.id,
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
%% This Source Code Form is subject to the terms of the Mozilla Public
|
||||
%% License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
%%
|
||||
%% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
|
||||
%%
|
||||
|
||||
-module(rabbit_oauth2_keycloak).
|
||||
|
||||
-include("oauth2.hrl").
|
||||
|
||||
-export([extract_scopes_from_keycloak_format/1, has_keycloak_scopes/1]).
|
||||
-import(uaa_jwt, [get_scope/1, set_scope/2]).
|
||||
|
||||
-define(AUTHORIZATION_CLAIM, <<"authorization">>).
|
||||
-define(PERMISSIONS_CLAIM, <<"permissions">>).
|
||||
-define(SCOPES_CLAIM, <<"scopes">>).
|
||||
|
||||
-spec has_keycloak_scopes(Payload::map()) -> boolean().
|
||||
has_keycloak_scopes(Payload) ->
|
||||
maps:is_key(?AUTHORIZATION_CLAIM, Payload).
|
||||
|
||||
-spec extract_scopes_from_keycloak_format(Payload :: map()) -> map().
|
||||
%% keycloak token format: https://github.com/rabbitmq/rabbitmq-auth-backend-oauth2/issues/36
|
||||
extract_scopes_from_keycloak_format(#{?AUTHORIZATION_CLAIM := Authorization} = Payload) ->
|
||||
AdditionalScopes = extract_scopes_from_keycloak_permissions([],
|
||||
maps:get(?PERMISSIONS_CLAIM, Authorization, [])),
|
||||
set_scope(AdditionalScopes ++ get_scope(Payload), Payload).
|
||||
|
||||
extract_scopes_from_keycloak_permissions(Acc, []) ->
|
||||
Acc;
|
||||
extract_scopes_from_keycloak_permissions(Acc, [H | T]) when is_map(H) ->
|
||||
Scopes = case maps:get(?SCOPES_CLAIM, H, []) of
|
||||
ScopesAsList when is_list(ScopesAsList) ->
|
||||
ScopesAsList;
|
||||
ScopesAsBinary when is_binary(ScopesAsBinary) ->
|
||||
[ScopesAsBinary]
|
||||
end,
|
||||
extract_scopes_from_keycloak_permissions(Acc ++ Scopes, T);
|
||||
extract_scopes_from_keycloak_permissions(Acc, [_ | T]) ->
|
||||
extract_scopes_from_keycloak_permissions(Acc, T).
|
|
@ -11,7 +11,7 @@
|
|||
-include("oauth2.hrl").
|
||||
-import(uaa_jwt, [get_scope/1, set_scope/2]).
|
||||
|
||||
-export([extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]).
|
||||
-export([extract_scopes_from_rich_auth_request/2]).
|
||||
|
||||
-define(AUTHORIZATION_DETAILS_CLAIM, <<"authorization_details">>).
|
||||
-define(RAR_ACTIONS_FIELD, <<"actions">>).
|
||||
|
@ -44,15 +44,12 @@
|
|||
<<"management">>,
|
||||
<<"policymaker">> ]).
|
||||
|
||||
-spec has_rich_auth_request_scopes(Payload::map()) -> boolean().
|
||||
has_rich_auth_request_scopes(Payload) ->
|
||||
maps:is_key(?AUTHORIZATION_DETAILS_CLAIM, Payload).
|
||||
|
||||
-spec extract_scopes_from_rich_auth_request(ResourceServer :: resource_server(),
|
||||
Payload :: map()) -> map().
|
||||
%% https://oauth.net/2/rich-authorization-requests/
|
||||
extract_scopes_from_rich_auth_request(ResourceServer,
|
||||
#{?AUTHORIZATION_DETAILS_CLAIM := Permissions} = Payload) ->
|
||||
#{?AUTHORIZATION_DETAILS_CLAIM := Permissions} = Payload)
|
||||
when is_list(Permissions) ->
|
||||
ResourceServerType = ResourceServer#resource_server.resource_server_type,
|
||||
|
||||
FilteredPermissionsByType = lists:filter(fun(P) ->
|
||||
|
@ -61,7 +58,8 @@ extract_scopes_from_rich_auth_request(ResourceServer,
|
|||
ResourceServer#resource_server.id, FilteredPermissionsByType),
|
||||
|
||||
ExistingScopes = get_scope(Payload),
|
||||
set_scope(AdditionalScopes ++ ExistingScopes, Payload).
|
||||
set_scope(AdditionalScopes ++ ExistingScopes, Payload);
|
||||
extract_scopes_from_rich_auth_request(_, Payload) -> Payload.
|
||||
|
||||
put_location_attribute(Attribute, Map) ->
|
||||
put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map).
|
||||
|
|
|
@ -96,6 +96,7 @@ parse_resource_pattern(Pattern, Permission) ->
|
|||
-spec filter_matching_scope_prefix_and_drop_it(list(), binary()|list()) -> list().
|
||||
filter_matching_scope_prefix_and_drop_it(Scopes, <<"">>) -> Scopes;
|
||||
filter_matching_scope_prefix_and_drop_it(Scopes, PrefixPattern) ->
|
||||
|
||||
PatternLength = byte_size(PrefixPattern),
|
||||
lists:filtermap(
|
||||
fun(ScopeEl) ->
|
||||
|
|
|
@ -316,5 +316,15 @@
|
|||
}
|
||||
]}
|
||||
], []
|
||||
},
|
||||
{additional_scopes_key,
|
||||
"auth_oauth2.resource_server_id = new_resource_server_id
|
||||
auth_oauth2.additional_scopes_key = roles realm.roles",
|
||||
[
|
||||
{rabbitmq_auth_backend_oauth2, [
|
||||
{resource_server_id,<<"new_resource_server_id">>},
|
||||
{extra_scopes_source, <<"roles realm.roles">> }
|
||||
]}
|
||||
], []
|
||||
}
|
||||
].
|
||||
|
|
|
@ -17,13 +17,16 @@
|
|||
user_login_authentication/2,
|
||||
user_login_authorization/2,
|
||||
normalize_token_scope/2,
|
||||
check_vhost_access/3]).
|
||||
check_vhost_access/3,
|
||||
extract_token_value/4,
|
||||
extract_scope_list_from_token_value/2]).
|
||||
-import(rabbit_oauth2_resource_server, [
|
||||
new_resource_server/1
|
||||
]).
|
||||
|
||||
all() ->
|
||||
[
|
||||
test_extract_scope_from_path_expression,
|
||||
filter_matching_scope_prefix_and_drop_it,
|
||||
normalize_token_scopes_with_scope_prefix,
|
||||
normalize_token_scope_from_space_separated_list_in_scope_claim,
|
||||
|
@ -39,6 +42,7 @@ all() ->
|
|||
test_token_expiration,
|
||||
test_invalid_signature,
|
||||
test_incorrect_kid,
|
||||
normalize_token_scope_using_multiple_scopes_key,
|
||||
normalize_token_scope_with_keycloak_scopes,
|
||||
normalize_token_scope_with_rich_auth_request,
|
||||
normalize_token_scope_with_rich_auth_request_using_regular_expression_with_cluster,
|
||||
|
@ -46,6 +50,7 @@ all() ->
|
|||
test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field,
|
||||
test_username_from,
|
||||
{group, with_rabbitmq_node}
|
||||
|
||||
].
|
||||
groups() ->
|
||||
[
|
||||
|
@ -116,6 +121,73 @@ end_per_group(_, Config) ->
|
|||
-define(RESOURCE_SERVER_TYPE, <<"rabbitmq-type">>).
|
||||
-define(DEFAULT_SCOPE_PREFIX, <<"rabbitmq.">>).
|
||||
|
||||
normalize_token_scope_using_multiple_scopes_key(_) ->
|
||||
Pairs = [
|
||||
%% common case
|
||||
{
|
||||
"keycloak format 1",
|
||||
#{<<"authorization">> =>
|
||||
#{<<"permissions">> =>
|
||||
[#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>,
|
||||
<<"rsname">> => <<"allvhost">>,
|
||||
<<"scopes">> => [<<"rabbitmq-resource.read:*/*">>]},
|
||||
#{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>,
|
||||
<<"rsname">> => <<"vhost1">>,
|
||||
<<"scopes">> => [<<"rabbitmq-resource.write:vhost1/*">>]},
|
||||
#{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>,
|
||||
<<"rsname">> => <<"Default Resource">>,
|
||||
<<"scopes">> => [<<"unknown-resource.write:vhost1/*">>]}
|
||||
]
|
||||
}
|
||||
},
|
||||
[<<"read:*/*">>, <<"write:vhost1/*">>]
|
||||
},
|
||||
{
|
||||
"keycloak format 2 using realm_access",
|
||||
#{<<"realm_access">> =>
|
||||
#{<<"roles">> => [<<"rabbitmq-resource.read:format2/*">>]}
|
||||
},
|
||||
[<<"read:format2/*">>]
|
||||
},
|
||||
{
|
||||
"keycloak format 2 using resource_access",
|
||||
#{<<"resource_access">> =>
|
||||
#{<<"account">> => #{<<"roles">> => [<<"rabbitmq-resource.read:format2bis/*">>]} }
|
||||
},
|
||||
[<<"read:format2bis/*">>]
|
||||
},
|
||||
{
|
||||
"both formats",
|
||||
#{<<"authorization">> =>
|
||||
#{<<"permissions">> =>
|
||||
[#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>,
|
||||
<<"rsname">> => <<"allvhost">>,
|
||||
<<"scopes">> => [<<"rabbitmq-resource.read:*/*">>]},
|
||||
#{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>,
|
||||
<<"rsname">> => <<"vhost1">>,
|
||||
<<"scopes">> => [<<"rabbitmq-resource.write:vhost1/*">>]},
|
||||
#{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>,
|
||||
<<"rsname">> => <<"Default Resource">>,
|
||||
<<"scopes">> => [<<"unknown-resource.write:vhost1/*">>]}
|
||||
]
|
||||
},
|
||||
<<"realm_access">> =>
|
||||
#{<<"roles">> => [<<"rabbitmq-resource.read:format2/*">>]},
|
||||
<<"resource_access">> =>
|
||||
#{<<"account">> => #{<<"roles">> => [<<"rabbitmq-resource.read:format2bis/*">>]} }
|
||||
},
|
||||
[<<"read:*/*">>, <<"write:vhost1/*">>, <<"read:format2/*">>, <<"read:format2bis/*">>]
|
||||
}
|
||||
],
|
||||
|
||||
lists:foreach(fun({Case, Token0, ExpectedScope}) ->
|
||||
ResourceServer0 = new_resource_server(<<"rabbitmq-resource">>),
|
||||
ResourceServer = ResourceServer0#resource_server{
|
||||
additional_scopes_key = <<"authorization.permissions.scopes realm_access.roles resource_access.account.roles">>
|
||||
},
|
||||
Token = normalize_token_scope(ResourceServer, Token0),
|
||||
?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token), Case)
|
||||
end, Pairs).
|
||||
|
||||
normalize_token_scope_with_keycloak_scopes(_) ->
|
||||
Pairs = [
|
||||
|
@ -169,7 +241,10 @@ normalize_token_scope_with_keycloak_scopes(_) ->
|
|||
],
|
||||
|
||||
lists:foreach(fun({Case, Authorization, ExpectedScope}) ->
|
||||
ResourceServer = new_resource_server(<<"rabbitmq-resource">>),
|
||||
ResourceServer0 = new_resource_server(<<"rabbitmq-resource">>),
|
||||
ResourceServer = ResourceServer0#resource_server{
|
||||
additional_scopes_key = <<"authorization.permissions.scopes">>
|
||||
},
|
||||
Token0 = #{<<"authorization">> => Authorization},
|
||||
Token = normalize_token_scope(ResourceServer, Token0),
|
||||
?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token), Case)
|
||||
|
@ -1286,6 +1361,77 @@ normalize_token_scope_without_scope_claim(_) ->
|
|||
Token0 = #{ },
|
||||
?assertEqual([], uaa_jwt:get_scope(normalize_token_scope(ResourceServer, Token0))).
|
||||
|
||||
|
||||
test_extract_scope_from_path_expression(_) ->
|
||||
M = fun rabbit_auth_backend_oauth2:extract_scope_list_from_token_value/2,
|
||||
R = #resource_server{id = <<"rabbitmq">>},
|
||||
|
||||
[<<"role1">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => <<"role1">> }},
|
||||
<<"auth.permission">>, M),
|
||||
[<<"role1">>,<<"role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => [<<"role1">>,<<"role2">>] }},
|
||||
<<"auth.permission">>, M),
|
||||
[<<"role1">>,<<"role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => <<"role1 role2">> }},
|
||||
<<"auth.permission">>, M),
|
||||
[<<"rabbitmq.role1">>,<<"rabbitmq.role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{
|
||||
<<"rabbitmq">> => [<<"role1">>,<<"role2">>]
|
||||
}},
|
||||
<<"auth">>, M),
|
||||
[<<"rabbitmq.role1">>,<<"rabbitmq.role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{
|
||||
<<"rabbitmq">> => <<"role1 role2">>
|
||||
}},
|
||||
<<"auth">>, M),
|
||||
%% this is the old keycloak format
|
||||
[<<"role1">>,<<"role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{
|
||||
<<"permission">> => [
|
||||
#{ <<"scopes">> => <<"role1">>},
|
||||
#{ <<"scopes">> => <<"role2">>}
|
||||
]
|
||||
}},
|
||||
<<"auth.permission.scopes">>, M),
|
||||
|
||||
[<<"role1">>,<<"role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{
|
||||
<<"permission">> => [
|
||||
#{ <<"scopes">> => [<<"role1">>]},
|
||||
#{ <<"scopes">> => [<<"role2">>]}
|
||||
]
|
||||
}},
|
||||
<<"auth.permission.scopes">>, M),
|
||||
|
||||
[<<"role1">>,<<"role2">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => [
|
||||
#{ <<"permission">> => [
|
||||
#{ <<"scopes">> => [<<"role1">>]}
|
||||
]},
|
||||
#{ <<"permission">> => [
|
||||
#{ <<"scopes">> => [<<"role2">>]}
|
||||
]}
|
||||
]},
|
||||
<<"auth.permission.scopes">>, M),
|
||||
|
||||
[<<"role1">>] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => [<<"role1">>] }},
|
||||
<<"auth.permission">>, M),
|
||||
|
||||
[] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => [<<"role1">>] }},
|
||||
<<"auth.permission2">>, M),
|
||||
|
||||
[] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => [<<"role1">>] }},
|
||||
<<"auth2.permission">>, M),
|
||||
|
||||
[] = extract_token_value(R,
|
||||
#{ <<"auth">> => #{ <<"permission">> => [<<"role1">>] }},
|
||||
<<"auth.permission2">>, M).
|
||||
|
||||
|
||||
%%
|
||||
%% Helpers
|
||||
%%
|
||||
|
|
Loading…
Reference in New Issue