summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcial Rosales <mrosales@pivotal.io>2023-04-26 12:40:38 +0200
committerMarcial Rosales <mrosales@pivotal.io>2023-05-16 08:40:28 +0200
commitfaffd6fa98c70da90812617aeeaba4069bd4c918 (patch)
tree575c5ad7cae738bea7dbdaf456a030718b88d5a2
parentc7d0427d621493f00a3aa0d6e7720f709f3769a6 (diff)
downloadrabbitmq-server-git-faffd6fa98c70da90812617aeeaba4069bd4c918.tar.gz
Configure Oauth scope prefix
separate from resource_server_id
-rw-r--r--deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema24
-rw-r--r--deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl17
-rw-r--r--deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets2
-rw-r--r--deps/rabbitmq_auth_backend_oauth2/test/scope_SUITE.erl4
-rw-r--r--deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl42
5 files changed, 68 insertions, 21 deletions
diff --git a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema
index 6f8c048516..8ee313ba5e 100644
--- a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema
+++ b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema
@@ -5,8 +5,16 @@
%%
%% ----------------------------------------------------------------------------
-%% A prefix used for scopes to avoid scope collisions (or unintended overlap). It is an empty string by default.
+%% OAuth Resource identity. Usage:
+%% - This is the identity of a RabbitMQ server/cluster used as the
+%% recipient of JWT Tokens (see audience claim, https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3).
+%% - This is also the resource identifier used by RabbitMQ server/cluster in the authorization and access token
+%% requests (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-indicators-05#page-3)
%%
+%% Up to version 3.12, RabbitMQ's scopes followed this pattern : <resource_server_id>.<scope>.
+%% Nowadays, there is a new setting called scope_prefix and RabbitMQ's scopes follow this pattern instead:
+%% <scope_prefix><scope>. Note that there is no dot in between.
+%% The default value of this setting is `<resource_server_id>.`.
%% {resource_server_id, <<"my_rabbit_server">>},
{mapping,
@@ -19,6 +27,20 @@
fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.resource_server_id", Conf))
end}.
+%% A prefix used for scopes to avoid scope collisions (or unintended overlap). If not configured,
+%% it is defaulted to `<resource_server_id>.` to maintain backward compatibility. Empty string is a permitted value.
+%%
+%% {scope_prefix, <<"api:/rabbitmq:">>},
+
+{mapping,
+ "auth_oauth2.scope_prefix",
+ "rabbitmq_auth_backend_oauth2.scope_prefix",
+ [{datatype, string}]}.
+
+{translation,
+ "rabbitmq_auth_backend_oauth2.scope_prefix",
+ fun(Conf) -> list_to_binary(cuttlefish:conf_get("auth_oauth2.scope_prefix", Conf))
+ end}.
%% An identifier used for JWT Tokens compliant with Rich Authorization Request spec
%% RabbitMq uses this field as discriminator to filter out permissions meant for RabbitMQ
diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl
index 1d4f7fadb7..78e1242549 100644
--- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl
+++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl
@@ -33,6 +33,7 @@
-define(APP, rabbitmq_auth_backend_oauth2).
-define(RESOURCE_SERVER_ID, resource_server_id).
+-define(SCOPE_PREFIX, scope_prefix).
%% a term defined for Rich Authorization Request tokens to identify a RabbitMQ permission
-define(RESOURCE_SERVER_TYPE, resource_server_type).
%% verify server_server_id aud field is on the aud field
@@ -498,23 +499,23 @@ post_process_payload_in_rich_auth_request_format(#{<<"authorization_details">> :
validate_payload(#{?SCOPE_JWT_FIELD := _Scope } = DecodedToken) ->
ResourceServerEnv = application:get_env(?APP, ?RESOURCE_SERVER_ID, <<>>),
ResourceServerId = rabbit_data_coercion:to_binary(ResourceServerEnv),
- validate_payload(DecodedToken, ResourceServerId).
+ ScopePrefix = application:get_env(?APP, ?SCOPE_PREFIX, <<ResourceServerId/binary, ".">>),
+ validate_payload(DecodedToken, ResourceServerId, ScopePrefix).
-validate_payload(#{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ResourceServerId) ->
+validate_payload(#{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ResourceServerId, ScopePrefix) ->
case check_aud(Aud, ResourceServerId) of
- ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ResourceServerId)}};
+ ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}};
{error, Err} -> {refused, {invalid_aud, Err}}
end;
-validate_payload(#{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ResourceServerId) ->
+validate_payload(#{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ResourceServerId, ScopePrefix) ->
case application:get_env(?APP, ?VERIFY_AUD, true) of
true -> {error, {badarg, {aud_field_is_missing}}};
- false -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ResourceServerId)}}
+ false -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}
end.
filter_scopes(Scopes, <<"">>) -> Scopes;
-filter_scopes(Scopes, ResourceServerId) ->
- PrefixPattern = <<ResourceServerId/binary, ".">>,
- matching_scopes_without_prefix(Scopes, PrefixPattern).
+filter_scopes(Scopes, ScopePrefix) ->
+ matching_scopes_without_prefix(Scopes, ScopePrefix).
check_aud(_, <<>>) -> ok;
check_aud(Aud, ResourceServerId) ->
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets
index 908733238b..21aca91815 100644
--- a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets
+++ b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets
@@ -1,6 +1,7 @@
[
{oauth2_pem_config2,
"auth_oauth2.resource_server_id = new_resource_server_id
+ auth_oauth2.scope_prefix = new_resource_server_id.
auth_oauth2.resource_server_type = new_resource_server_type
auth_oauth2.additional_scopes_key = my_custom_scope_key
auth_oauth2.preferred_username_claims.1 = user_name
@@ -22,6 +23,7 @@
[
{rabbitmq_auth_backend_oauth2, [
{resource_server_id,<<"new_resource_server_id">>},
+ {scope_prefix,<<"new_resource_server_id.">>},
{resource_server_type,<<"new_resource_server_type">>},
{extra_scopes_source, <<"my_custom_scope_key">>},
{preferred_username_claims, [<<"user_name">>, <<"username">>, <<"email">>]},
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/scope_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/scope_SUITE.erl
index 84effae77c..73062ce08b 100644
--- a/deps/rabbitmq_auth_backend_oauth2/test/scope_SUITE.erl
+++ b/deps/rabbitmq_auth_backend_oauth2/test/scope_SUITE.erl
@@ -20,7 +20,7 @@ all() ->
permission_resource,
permission_topic
].
-
+
variable_expansion(_Config) ->
Scenarios = [
{ "Emtpy Scopes",
@@ -31,7 +31,7 @@ variable_expansion(_Config) ->
},
{ "No Scopes",
#{
- <<"client_id">> => <<"some_client">>
+ <<"client_id">> => <<"some_client">>
}, <<"default">>, []
},
{ "Expand token's var and vhost",
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
index c6a1b27b45..e9d775bf90 100644
--- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
+++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
@@ -17,6 +17,7 @@ all() ->
[
test_own_scope,
test_validate_payload_resource_server_id_mismatch,
+ test_validate_payload_with_scope_prefix,
test_validate_payload,
test_validate_payload_when_verify_aud_false,
test_successful_access_with_a_token,
@@ -101,6 +102,7 @@ end_per_testcase(_, Config) ->
-define(UTIL_MOD, rabbit_auth_backend_oauth2_test_util).
-define(RESOURCE_SERVER_ID, <<"rabbitmq">>).
-define(RESOURCE_SERVER_TYPE, <<"rabbitmq-type">>).
+-define(DEFAULT_SCOPE_PREFIX, <<"rabbitmq.">>).
test_post_process_token_payload(_) ->
ArgumentsExpections = [
@@ -1175,16 +1177,16 @@ test_command_pem_no_kid(Config) ->
test_own_scope(_) ->
Examples = [
- {<<"foo">>, [<<"foo">>, <<"foo.bar">>, <<"bar.foo">>,
+ {<<"foo.">>, [<<"foo">>, <<"foo.bar">>, <<"bar.foo">>,
<<"one.two">>, <<"foobar">>, <<"foo.other.third">>],
[<<"bar">>, <<"other.third">>]},
- {<<"foo">>, [], []},
- {<<"foo">>, [<<"foo">>, <<"other.foo.bar">>], []},
+ {<<"foo.">>, [], []},
+ {<<"foo.">>, [<<"foo">>, <<"other.foo.bar">>], []},
{<<"">>, [<<"foo">>, <<"bar">>], [<<"foo">>, <<"bar">>]}
],
lists:map(
- fun({ResId, Src, Dest}) ->
- Dest = rabbit_auth_backend_oauth2:filter_scopes(Src, ResId)
+ fun({ScopePrefix, Src, Dest}) ->
+ Dest = rabbit_auth_backend_oauth2:filter_scopes(Src, ScopePrefix)
end,
Examples).
@@ -1198,10 +1200,30 @@ test_validate_payload_resource_server_id_mismatch(_) ->
?assertEqual({refused, {invalid_aud, {resource_id_not_found_in_aud, ?RESOURCE_SERVER_ID,
[<<"foo">>,<<"bar">>]}}},
- rabbit_auth_backend_oauth2:validate_payload(NoKnownResourceServerId, ?RESOURCE_SERVER_ID)),
+ rabbit_auth_backend_oauth2:validate_payload(NoKnownResourceServerId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)),
?assertEqual({refused, {invalid_aud, {resource_id_not_found_in_aud, ?RESOURCE_SERVER_ID, []}}},
- rabbit_auth_backend_oauth2:validate_payload(EmptyAud, ?RESOURCE_SERVER_ID)).
+ rabbit_auth_backend_oauth2:validate_payload(EmptyAud, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)).
+
+test_validate_payload_with_scope_prefix(_) ->
+ Scenarios = [ { <<>>,
+ #{<<"aud">> => [?RESOURCE_SERVER_ID],
+ <<"scope">> => [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ]},
+ [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ]
+ },
+ { <<"some-prefix::">>,
+ #{<<"aud">> => [?RESOURCE_SERVER_ID],
+ <<"scope">> => [<<"some-prefix::foo">>, <<"foo.bar">>, <<"some-prefix::other.third">> ]},
+ [<<"foo">>, <<"other.third">>]
+ }
+
+ ],
+
+ lists:map(fun({ ScopePrefix, Token, ExpectedScopes}) ->
+ ?assertEqual({ok, #{<<"aud">> => [?RESOURCE_SERVER_ID], <<"scope">> => ExpectedScopes } },
+ rabbit_auth_backend_oauth2:validate_payload(Token, ?RESOURCE_SERVER_ID, ScopePrefix))
+ end
+ , Scenarios).
test_validate_payload(_) ->
KnownResourceServerId = #{<<"aud">> => [?RESOURCE_SERVER_ID],
@@ -1210,7 +1232,7 @@ test_validate_payload(_) ->
<<"foobar">>, <<"rabbitmq.other.third">>]},
?assertEqual({ok, #{<<"aud">> => [?RESOURCE_SERVER_ID],
<<"scope">> => [<<"bar">>, <<"other.third">>]}},
- rabbit_auth_backend_oauth2:validate_payload(KnownResourceServerId, ?RESOURCE_SERVER_ID)).
+ rabbit_auth_backend_oauth2:validate_payload(KnownResourceServerId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)).
test_validate_payload_when_verify_aud_false(_) ->
WithoutAud = #{
@@ -1219,7 +1241,7 @@ test_validate_payload_when_verify_aud_false(_) ->
<<"foobar">>, <<"rabbitmq.other.third">>]},
?assertEqual({ok, #{
<<"scope">> => [<<"bar">>, <<"other.third">>]}},
- rabbit_auth_backend_oauth2:validate_payload(WithoutAud, ?RESOURCE_SERVER_ID)),
+ rabbit_auth_backend_oauth2:validate_payload(WithoutAud, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)),
WithAudWithUnknownResourceId = #{
<<"aud">> => [<<"unknown">>],
@@ -1228,7 +1250,7 @@ test_validate_payload_when_verify_aud_false(_) ->
<<"foobar">>, <<"rabbitmq.other.third">>]},
?assertEqual({ok, #{<<"aud">> => [<<"unknown">>],
<<"scope">> => [<<"bar">>, <<"other.third">>]}},
- rabbit_auth_backend_oauth2:validate_payload(WithAudWithUnknownResourceId, ?RESOURCE_SERVER_ID)).
+ rabbit_auth_backend_oauth2:validate_payload(WithAudWithUnknownResourceId, ?RESOURCE_SERVER_ID, ?DEFAULT_SCOPE_PREFIX)).