Skip to content

Commit edc5d6e

Browse files
Merge pull request #4666 from rabbitmq/mergify/bp/v3.10.x/pr-4665
OAuth 2: expand all scope aliases provided (backport #4665)
2 parents f3e683f + 117dd40 commit edc5d6e

File tree

5 files changed

+214
-47
lines changed

5 files changed

+214
-47
lines changed

deps/rabbit_common/src/rabbit_data_coercion.erl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
-module(rabbit_data_coercion).
99

1010
-export([to_binary/1, to_list/1, to_atom/1, to_integer/1, to_proplist/1, to_map/1]).
11-
-export([to_atom/2, atomize_keys/1]).
11+
-export([to_atom/2, atomize_keys/1, to_list_of_binaries/1]).
1212

1313
-spec to_binary(Val :: binary() | list() | atom() | integer()) -> binary().
1414
to_binary(Val) when is_list(Val) -> list_to_binary(Val);
@@ -51,4 +51,17 @@ to_map(Val) when is_list(Val) -> maps:from_list(Val).
5151
atomize_keys(Val) when is_list(Val) ->
5252
[{to_atom(K), V} || {K, V} <- Val];
5353
atomize_keys(Val) when is_map(Val) ->
54-
maps:from_list(atomize_keys(maps:to_list(Val))).
54+
maps:from_list(atomize_keys(maps:to_list(Val))).
55+
56+
-spec to_list_of_binaries(Val :: undefined | [atom() | list() | binary() | integer()]) -> [binary()].
57+
to_list_of_binaries(Value) ->
58+
case Value of
59+
undefined ->
60+
[];
61+
List when is_list(List) ->
62+
[to_binary(LI) || LI <- List];
63+
Bin when is_binary(Bin) ->
64+
[Bin];
65+
Other ->
66+
[to_binary(Other)]
67+
end.

deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ check_token(Token) ->
164164
Settings = application:get_all_env(?APP),
165165
case uaa_jwt:decode_and_verify(Token) of
166166
{error, Reason} -> {refused, {error, Reason}};
167-
{true, Payload} -> validate_payload(post_process_payload(Payload, Settings));
167+
{true, Payload} ->
168+
ct:pal("post_process_payload(Payload, Settings): ~p", [post_process_payload(Payload, Settings)]),
169+
validate_payload(post_process_payload(Payload, Settings));
168170
{false, _} -> {refused, signature_invalid}
169171
end.
170172

@@ -246,31 +248,25 @@ post_process_payload_with_scope_alias_in_extra_scopes_source(Payload, AppEnv) ->
246248
post_process_payload_with_scope_alias_field_named(Payload, undefined, _ScopeAliasMapping) ->
247249
Payload;
248250
post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAliasMapping) ->
249-
ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []),
250-
AdditionalScopes = case FieldName of
251-
undefined -> [];
252-
[] -> [];
253-
_Value ->
254-
ScopeAlias = maps:get(FieldName, Payload, undefined),
255-
case ScopeAlias of
256-
undefined -> [];
257-
[] -> [];
258-
[Value1] ->
259-
rabbit_data_coercion:to_list(maps:get(Value1, ScopeAliasMapping, []));
260-
Value2 when is_binary(Value2) ->
261-
maps:get(Value2, ScopeAliasMapping, []);
262-
Value3 when is_list(Value3) ->
263-
maps:get(list_to_binary(Value3), ScopeAliasMapping, [])
264-
end
265-
end,
266-
267-
case AdditionalScopes of
268-
[] -> Payload;
269-
List when is_list(List) ->
270-
maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload);
271-
Bin when is_binary(Bin) ->
272-
maps:put(?SCOPE_JWT_FIELD, [Bin | ExistingScopes], Payload)
273-
end.
251+
Scopes0 = maps:get(FieldName, Payload, []),
252+
Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0),
253+
%% for all scopes, look them up in the scope alias map, and if they are
254+
%% present, add the alias to the final scope list. Note that we also preserve
255+
%% the original scopes, it should not hurt.
256+
ExpandedScopes =
257+
lists:foldl(fun(ScopeListItem, Acc) ->
258+
case maps:get(ScopeListItem, ScopeAliasMapping, undefined) of
259+
undefined ->
260+
Acc;
261+
MappedList when is_list(MappedList) ->
262+
Binaries = rabbit_data_coercion:to_list_of_binaries(MappedList),
263+
Acc ++ Binaries;
264+
Value ->
265+
Binaries = rabbit_data_coercion:to_list_of_binaries(Value),
266+
Acc ++ Binaries
267+
end
268+
end, Scopes, Scopes),
269+
maps:put(?SCOPE_JWT_FIELD, ExpandedScopes, Payload).
274270

275271

276272
-spec does_include_complex_claim_field(Payload :: map()) -> boolean().

deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,24 @@ fixture_token(ExtraScopes) ->
9898
fixture_token_with_full_permissions() ->
9999
fixture_token_with_scopes(full_permission_scopes()).
100100

101-
token_with_scope_alias_in_scope_field(Alias) ->
101+
token_with_scope_alias_in_scope_field(Value) ->
102102
%% expiration is a timestamp with precision in seconds
103103
#{<<"exp">> => default_expiration_moment(),
104104
<<"kid">> => <<"token-key">>,
105105
<<"iss">> => <<"unit_test">>,
106106
<<"foo">> => <<"bar">>,
107107
<<"aud">> => [<<"rabbitmq">>],
108-
<<"scope">> => Alias}.
108+
<<"scope">> => Value}.
109109

110-
token_with_scope_alias_in_claim_field(Alias, Scopes) ->
110+
token_with_scope_alias_in_claim_field(Claims, Scopes) ->
111111
%% expiration is a timestamp with precision in seconds
112112
#{<<"exp">> => default_expiration_moment(),
113113
<<"kid">> => <<"token-key">>,
114114
<<"iss">> => <<"unit_test">>,
115115
<<"foo">> => <<"bar">>,
116116
<<"aud">> => [<<"rabbitmq">>],
117117
<<"scope">> => Scopes,
118-
<<"claims">> => Alias}.
118+
<<"claims">> => Claims}.
119119

120120
seconds_in_the_future() ->
121121
seconds_in_the_future(30).

deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl

Lines changed: 78 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ groups() ->
5555
]},
5656

5757
{scope_aliases, [], [
58-
test_successful_connection_with_with_scope_alias_in_extra_scopes_source,
58+
test_successful_connection_with_with_single_scope_alias_in_extra_scopes_source,
59+
test_successful_connection_with_with_multiple_scope_aliases_in_extra_scopes_source,
5960
test_successful_connection_with_scope_alias_in_scope_field_case1,
6061
test_successful_connection_with_scope_alias_in_scope_field_case2,
62+
test_successful_connection_with_scope_alias_in_scope_field_case3,
6163
test_failed_connection_with_with_non_existent_scope_alias_in_extra_scopes_source,
6264
test_failed_connection_with_non_existent_scope_alias_in_scope_field
6365
]}
@@ -72,7 +74,9 @@ groups() ->
7274
-define(EXTRA_SCOPES_SOURCE, <<"additional_rabbitmq_scopes">>).
7375
-define(CLAIMS_FIELD, <<"claims">>).
7476

75-
-define(SCOPE_ALIAS_NAME, <<"role-1">>).
77+
-define(SCOPE_ALIAS_NAME, <<"role-1">>).
78+
-define(SCOPE_ALIAS_NAME_2, <<"role-2">>).
79+
-define(SCOPE_ALIAS_NAME_3, <<"role-3">>).
7680

7781
init_per_suite(Config) ->
7882
rabbit_ct_helpers:log_environment(),
@@ -126,7 +130,7 @@ init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection
126130
rabbit_ct_helpers:testcase_started(Config, Testcase),
127131
Config;
128132

129-
init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_scope_alias_in_extra_scopes_source ->
133+
init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_single_scope_alias_in_extra_scopes_source ->
130134
rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost1">>),
131135
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
132136
[rabbitmq_auth_backend_oauth2, extra_scopes_source, ?CLAIMS_FIELD]),
@@ -141,6 +145,25 @@ init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection
141145
rabbit_ct_helpers:testcase_started(Config, Testcase),
142146
Config;
143147

148+
init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_multiple_scope_aliases_in_extra_scopes_source ->
149+
rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost4">>),
150+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
151+
[rabbitmq_auth_backend_oauth2, extra_scopes_source, ?CLAIMS_FIELD]),
152+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
153+
[rabbitmq_auth_backend_oauth2, scope_aliases, #{
154+
?SCOPE_ALIAS_NAME => [
155+
<<"rabbitmq.configure:vhost4/*">>
156+
],
157+
?SCOPE_ALIAS_NAME_2 => [
158+
<<"rabbitmq.write:vhost4/*">>
159+
],
160+
?SCOPE_ALIAS_NAME_3 => [
161+
<<"rabbitmq.read:vhost4/*">>
162+
]
163+
}]),
164+
rabbit_ct_helpers:testcase_started(Config, Testcase),
165+
Config;
166+
144167
init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case1 orelse
145168
Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case2 ->
146169
rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost2">>),
@@ -154,6 +177,22 @@ init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection
154177
]),
155178
rabbit_ct_helpers:testcase_started(Config, Testcase),
156179
Config;
180+
init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case3 ->
181+
rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost3">>),
182+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env,
183+
[rabbitmq_auth_backend_oauth2, scope_aliases, #{
184+
?SCOPE_ALIAS_NAME => [
185+
<<"rabbitmq.configure:vhost3/*">>
186+
],
187+
?SCOPE_ALIAS_NAME_2 => [
188+
<<"rabbitmq.write:vhost3/*">>
189+
],
190+
?SCOPE_ALIAS_NAME_3 => [
191+
<<"rabbitmq.read:vhost3/*">>
192+
]
193+
}]),
194+
rabbit_ct_helpers:testcase_started(Config, Testcase),
195+
Config;
157196

158197
init_per_testcase(Testcase, Config) ->
159198
rabbit_ct_helpers:testcase_started(Config, Testcase),
@@ -179,7 +218,7 @@ end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_
179218
rabbit_ct_helpers:testcase_finished(Config, Testcase),
180219
Config;
181220

182-
end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_scope_alias_in_extra_scopes_source ->
221+
end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_single_scope_alias_in_extra_scopes_source ->
183222
rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>),
184223
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env,
185224
[rabbitmq_auth_backend_oauth2, scope_aliases]),
@@ -188,6 +227,15 @@ end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_
188227
rabbit_ct_helpers:testcase_finished(Config, Testcase),
189228
Config;
190229

230+
end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_with_multiple_scope_aliases_in_extra_scopes_source ->
231+
rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost4">>),
232+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env,
233+
[rabbitmq_auth_backend_oauth2, scope_aliases]),
234+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env,
235+
[rabbitmq_auth_backend_oauth2, extra_scopes_source]),
236+
rabbit_ct_helpers:testcase_finished(Config, Testcase),
237+
Config;
238+
191239
end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case1 orelse
192240
Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case2 ->
193241
rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost2">>),
@@ -196,6 +244,13 @@ end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_
196244
rabbit_ct_helpers:testcase_finished(Config, Testcase),
197245
Config;
198246

247+
end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_scope_alias_in_scope_field_case3 ->
248+
rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost3">>),
249+
ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env,
250+
[rabbitmq_auth_backend_oauth2, scope_aliases]),
251+
rabbit_ct_helpers:testcase_finished(Config, Testcase),
252+
Config;
253+
199254
end_per_testcase(Testcase, Config) ->
200255
rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>),
201256
rabbit_ct_helpers:testcase_finished(Config, Testcase),
@@ -447,26 +502,38 @@ test_failed_token_refresh_case2(Config) ->
447502
close_connection(Conn).
448503

449504

450-
test_successful_connection_with_with_scope_alias_in_extra_scopes_source(Config) ->
505+
test_successful_connection_with_with_single_scope_alias_in_extra_scopes_source(Config) ->
506+
test_successful_connection_with_with_scope_aliases_in_extra_scopes_source(Config, ?SCOPE_ALIAS_NAME, <<"vhost1">>).
507+
508+
test_successful_connection_with_with_multiple_scope_aliases_in_extra_scopes_source(Config) ->
509+
Claims = [?SCOPE_ALIAS_NAME, ?SCOPE_ALIAS_NAME_2, ?SCOPE_ALIAS_NAME_3],
510+
test_successful_connection_with_with_scope_aliases_in_extra_scopes_source(Config, Claims, <<"vhost4">>).
511+
512+
test_successful_connection_with_with_scope_aliases_in_extra_scopes_source(Config, Claims, VHost) ->
451513
{_Algo, Token} = generate_valid_token_with_extra_fields(
452514
Config,
453-
#{<<"claims">> => ?SCOPE_ALIAS_NAME}
515+
#{<<"claims">> => Claims}
454516
),
455-
Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token),
517+
Conn = open_unmanaged_connection(Config, 0, VHost, <<"username">>, Token),
456518
{ok, Ch} = amqp_connection:open_channel(Conn),
457519
#'queue.declare_ok'{} =
458520
amqp_channel:call(Ch, #'queue.declare'{queue = <<"one">>, exclusive = true}),
459521
close_connection_and_channel(Conn, Ch).
460522

523+
461524
test_successful_connection_with_scope_alias_in_scope_field_case1(Config) ->
462-
test_successful_connection_with_scope_alias_in_scope_field_case(Config, ?SCOPE_ALIAS_NAME).
525+
test_successful_connection_with_scope_alias_in_scope_field_case(Config, ?SCOPE_ALIAS_NAME, <<"vhost2">>).
463526

464527
test_successful_connection_with_scope_alias_in_scope_field_case2(Config) ->
465-
test_successful_connection_with_scope_alias_in_scope_field_case(Config, [?SCOPE_ALIAS_NAME]).
528+
test_successful_connection_with_scope_alias_in_scope_field_case(Config, [?SCOPE_ALIAS_NAME], <<"vhost2">>).
466529

467-
test_successful_connection_with_scope_alias_in_scope_field_case(Config, Scopes) ->
530+
test_successful_connection_with_scope_alias_in_scope_field_case3(Config) ->
531+
Scopes = [?SCOPE_ALIAS_NAME, ?SCOPE_ALIAS_NAME_2, ?SCOPE_ALIAS_NAME_3],
532+
test_successful_connection_with_scope_alias_in_scope_field_case(Config, Scopes, <<"vhost3">>).
533+
534+
test_successful_connection_with_scope_alias_in_scope_field_case(Config, Scopes, VHost) ->
468535
{_Algo, Token} = generate_valid_token(Config, Scopes),
469-
Conn = open_unmanaged_connection(Config, 0, <<"vhost2">>, <<"username">>, Token),
536+
Conn = open_unmanaged_connection(Config, 0, VHost, <<"username">>, Token),
470537
{ok, Ch} = amqp_connection:open_channel(Conn),
471538
#'queue.declare_ok'{} =
472539
amqp_channel:call(Ch, #'queue.declare'{queue = <<"one">>, exclusive = true}),

0 commit comments

Comments
 (0)