From 0023ba2a0128f2e6ed078e5801fb3fd30b1feb1d Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 28 May 2025 16:13:43 +0200 Subject: [PATCH 1/4] Add var expansion to vhost and resource access --- .../src/rabbit_auth_backend_oauth2.erl | 9 +-- .../src/rabbit_oauth2_scope.erl | 1 + .../rabbit_auth_backend_oauth2_test_util.erl | 2 + .../test/unit_SUITE.erl | 63 ++++++++++++++----- 4 files changed, 55 insertions(+), 20 deletions(-) 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 133a566f177c..69a6a0f2f923 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 @@ -87,11 +87,8 @@ 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). @@ -99,7 +96,7 @@ 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). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl index 7e1efd24706f..75e4c1f78fbb 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl @@ -41,6 +41,7 @@ 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}, diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl index 35a8c9b3f5c2..a27dbbb07932 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_auth_backend_oauth2_test_util.erl @@ -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). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index d920db3ec05e..3cfb5c10f3d0 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -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() -> @@ -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, @@ -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", @@ -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", @@ -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}}}], @@ -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(_) -> @@ -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(), @@ -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(), @@ -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(), @@ -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(), From 1d942027a9d0763b6c3b31be5dbbd964fc46ea27 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 28 May 2025 17:02:29 +0200 Subject: [PATCH 2/4] Add system test for variable expansion --- .../src/rabbit_auth_backend_oauth2.erl | 1 + .../test/system_SUITE.erl | 30 ++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) 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 69a6a0f2f923..cf1be034f7c4 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 @@ -97,6 +97,7 @@ check_resource_access(#auth_user{impl = DecodedTokenFun}, with_decoded_token(DecodedTokenFun(), fun(Token) -> Scopes = get_expanded_scopes(Token, Resource), + rabbit_log:debug("Checking against scopes: ~p", [Scopes]), rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes) end). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl index 75a86b30b8ac..65e10bb87e38 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl @@ -35,6 +35,7 @@ groups() -> test_successful_connection_with_a_full_permission_token_and_all_defaults, test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost, test_successful_connection_with_simple_strings_for_aud_and_scope, + test_successful_connection_with_variable_expansion_on_queue_access, test_successful_token_refresh, test_successful_connection_without_verify_aud, mqtt @@ -42,6 +43,7 @@ groups() -> {basic_unhappy_path, [], [ test_failed_connection_with_expired_token, test_failed_connection_with_a_non_token, + test_failed_connection_with_a_token_with_variable_expansion, test_failed_connection_with_a_token_with_insufficient_vhost_permission, test_failed_connection_with_a_token_with_insufficient_resource_permission, more_than_one_resource_server_id_not_allowed_in_one_token, @@ -134,7 +136,8 @@ end_per_group(_Group, Config) -> %% init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost orelse - Testcase =:= test_successful_token_refresh -> + Testcase =:= test_successful_token_refresh orelse + Testcase =:= test_successful_connection_with_variable_expansion_on_queue_access -> rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost1">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; @@ -420,6 +423,19 @@ test_successful_connection_with_simple_strings_for_aud_and_scope(Config) -> amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), close_connection_and_channel(Conn, Ch). +test_successful_connection_with_variable_expansion_on_queue_access(Config) -> + {_Algo, Token} = generate_valid_token( + Config, + <<"rabbitmq.configure:*/{vhost}-{sub}-* rabbitmq.write:*/* rabbitmq.read:*/*">>, + [<<"hare">>, <<"rabbitmq">>], + <<"Bob">> + ), + Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"Bob">>, Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + #'queue.declare_ok'{} = + amqp_channel:call(Ch, #'queue.declare'{queue = <<"vhost1-Bob-1">>, exclusive = true}), + close_connection_and_channel(Conn, Ch). + test_successful_connection_without_verify_aud(Config) -> {_Algo, Token} = generate_valid_token( Config, @@ -895,6 +911,18 @@ test_failed_connection_with_a_token_with_insufficient_vhost_permission(Config) - ?assertEqual({error, not_allowed}, open_unmanaged_connection(Config, 0, <<"off-limits-vhost">>, <<"username">>, Token)). +test_failed_connection_with_a_token_with_variable_expansion(Config) -> + {_Algo, Token} = generate_valid_token( + Config, + <<"rabbitmq.configure:*/{vhost}-{sub}-* rabbitmq.write:*/* rabbitmq.read:*/*">>, + [<<"hare">>, <<"rabbitmq">>] + ), + Conn = open_unmanaged_connection(Config, 0, <<"vhost2">>, <<"username">>, Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + ?assertExit({{shutdown, {server_initiated_close, 403, _}}, _}, + amqp_channel:call(Ch, #'queue.declare'{queue = <<"vhost1-username-3">>, exclusive = true})), + close_connection(Conn). + test_failed_connection_with_a_token_with_insufficient_resource_permission(Config) -> {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost2/jwt*">>, <<"rabbitmq.write:vhost2/jwt*">>, From c73fdf79ff7e374447a5a31306787dfdb98fd252 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 28 May 2025 18:23:09 +0200 Subject: [PATCH 3/4] Remove log statement --- .../src/rabbit_auth_backend_oauth2.erl | 1 - 1 file changed, 1 deletion(-) 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 cf1be034f7c4..69a6a0f2f923 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 @@ -97,7 +97,6 @@ check_resource_access(#auth_user{impl = DecodedTokenFun}, with_decoded_token(DecodedTokenFun(), fun(Token) -> Scopes = get_expanded_scopes(Token, Resource), - rabbit_log:debug("Checking against scopes: ~p", [Scopes]), rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes) end). From efcbde4f34a6b5d28b6a1b6a0e91ccaade4050ca Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 29 May 2025 09:27:26 +0200 Subject: [PATCH 4/4] Add missing id tag --- .github/workflows/test-authnz.yaml | 4 ++-- .github/workflows/test-management-ui-for-pr.yaml | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-authnz.yaml b/.github/workflows/test-authnz.yaml index 6b1ec4f02c14..f9c329c32c0b 100644 --- a/.github/workflows/test-authnz.yaml +++ b/.github/workflows/test-authnz.yaml @@ -72,7 +72,7 @@ jobs: docker build -t mocha-test --target test . - name: Run Suites - id: run-suites + id: tests run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') CONF_DIR_PREFIX="$(mktemp -d)" RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ @@ -83,7 +83,7 @@ jobs: if: always() uses: actions/upload-artifact@v4.3.2 env: - SELENIUM_ARTIFACTS: ${{ steps.run-suites.outputs.SELENIUM_ARTIFACTS }} + SELENIUM_ARTIFACTS: ${{ steps.tests.outputs.SELENIUM_ARTIFACTS }} with: name: test-artifacts-${{ matrix.browser }}-${{ matrix.erlang_version }} path: | diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index e5fb4ecb06ae..021af8df9145 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -57,6 +57,7 @@ jobs: docker build -t mocha-test --target test . - name: Run short UI suites on a standalone rabbitmq server + id: tests run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') CONF_DIR_PREFIX="$(mktemp -d)" RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ @@ -67,7 +68,7 @@ jobs: if: ${{ failure() && steps.tests.outcome == 'failed' }} uses: actions/upload-artifact@v4 env: - SELENIUM_ARTIFACTS: ${{ steps.run-suites.outputs.SELENIUM_ARTIFACTS }} + SELENIUM_ARTIFACTS: ${{ steps.tests.outputs.SELENIUM_ARTIFACTS }} with: name: test-artifacts-${{ matrix.browser }}-${{ matrix.erlang_version }} path: |