From 7aca1605eb38f0633a6f88414dede0836ba3a620 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 9 Sep 2024 12:52:42 +0100 Subject: [PATCH 01/64] Deprecate resource req parameter from authorize endpoint --- .../priv/www/js/oidc-oauth/helper.js | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js index 6ebc53a6ed01..799b41f59b7f 100644 --- a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js +++ b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js @@ -46,15 +46,9 @@ function auth_settings_apply_defaults(authSettings) { } if (!resource_server.oauth_response_type) { resource_server.oauth_response_type = authSettings.oauth_response_type - if (!resource_server.oauth_response_type) { - resource_server.oauth_response_type = "code" - } } if (!resource_server.oauth_scopes) { resource_server.oauth_scopes = authSettings.oauth_scopes - if (!resource_server.oauth_scopes) { - resource_server.oauth_scopes = "openid profile" - } } if (!resource_server.oauth_client_id) { resource_server.oauth_client_id = authSettings.oauth_client_id @@ -98,21 +92,21 @@ function get_oauth_settings() { export function oauth_initialize_if_required(state = "index") { let oauth = oauth_initialize(get_oauth_settings()) if (!oauth.enabled) return oauth; - switch (state) { - case 'login-callback': - oauth_completeLogin(); break; - case 'logout-callback': - oauth_completeLogout(); break; - default: + switch (state) { + case 'login-callback': + oauth_completeLogin(); break; + case 'logout-callback': + oauth_completeLogout(); break; + default: oauth = oauth_initiate(oauth); } - return oauth; + return oauth; } export function oauth_initiate(oauth) { if (oauth.enabled) { if (!oauth.sp_initiated) { - oauth.logged_in = has_auth_credentials(); + oauth.logged_in = has_auth_credentials(); } else { oauth_is_logged_in().then( status => { if (status.loggedIn && !has_auth_credentials()) { @@ -122,7 +116,7 @@ export function oauth_initiate(oauth) { if (!status.loggedIn) { clear_auth(); } else { - oauth.logged_in = true; + oauth.logged_in = true; oauth.expiryDate = new Date(status.user.expires_at * 1000); // it is epoch in seconds let current = new Date(); _management_logger.debug('token expires in ', (oauth.expiryDate-current)/1000, @@ -146,7 +140,7 @@ function oauth_initialize_user_manager(resource_server) { client_id: resource_server.oauth_client_id, response_type: resource_server.oauth_response_type, scope: resource_server.oauth_scopes, - resource: resource_server.id, +// resource: resource_server.id, deprecated redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", post_logout_redirect_uri: rabbit_base_uri() + "/", From 4da45996ca2a31b443bc57456c6bb7c512b4beac Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 9 Sep 2024 19:42:40 +0100 Subject: [PATCH 02/64] Minor refactor Improve logging Fix an issue running selenium tests locally WIP modify schema to configure queryParameters for oauth2 endpoints --- deps/oauth2_client/src/oauth2_client.erl | 12 ++--- .../rabbitmq_auth_backend_oauth2.schema | 27 +++++++++- .../src/rabbit_oauth2_schema.erl | 7 ++- .../src/uaa_jwt.erl | 50 ++++++++++--------- .../src/uaa_jwt_jwt.erl | 13 +++-- selenium/bin/components/rabbitmq | 1 + selenium/test/authnz-msg-protocols/env.local | 2 +- selenium/test/basic-auth/env.local | 2 +- selenium/test/basic-auth/rabbitmq.conf | 2 +- selenium/test/multi-oauth/env.local | 2 +- .../test/multi-oauth/env.local.devkeycloak | 2 +- .../test/multi-oauth/env.local.prodkeycloak | 2 +- selenium/test/oauth/env.local | 2 +- selenium/test/oauth/env.local.keycloak | 2 +- 14 files changed, 79 insertions(+), 47 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index 335bcfdfba1b..cb0ef0e9b7cb 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -254,7 +254,7 @@ get_oauth_provider(ListOfRequiredAttributes) -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> OAuthProvider = lookup_oauth_provider_from_keyconfig(), - rabbit_log:debug("Using oauth_provider ~s from keyconfig", [format_oauth_provider(OAuthProvider)]), + rabbit_log:debug("Using oauth_provider ~p from keyconfig", [format_oauth_provider(OAuthProvider)]), case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of [] -> {ok, OAuthProvider}; @@ -557,27 +557,27 @@ format_ssl_options(TlsOptions) -> [] -> 0; Certs -> length(Certs) end, - io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, crl_check: ~p, " ++ + lists:flatten(io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, crl_check: ~p, " ++ "depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [ proplists:get_value(verify, TlsOptions), proplists:get_value(fail_if_no_peer_cert, TlsOptions), proplists:get_value(crl_check, TlsOptions), proplists:get_value(depth, TlsOptions), proplists:get_value(cacertfile, TlsOptions), - CaCertsCount]). + CaCertsCount])). format_oauth_provider_id(root) -> ""; format_oauth_provider_id(Id) -> binary_to_list(Id). -spec format_oauth_provider(oauth_provider()) -> string(). format_oauth_provider(OAuthProvider) -> - io_lib:format("{id: ~p, issuer: ~p, token_endpoint: ~p, " ++ + lists:flatten(io_lib:format("{id: ~p, issuer: ~p, token_endpoint: ~p, " ++ "authorization_endpoint: ~p, end_session_endpoint: ~p, " ++ - "jwks_uri: ~p, ssl_options: ~s }", [ + "jwks_uri: ~p, ssl_options: ~p }", [ format_oauth_provider_id(OAuthProvider#oauth_provider.id), OAuthProvider#oauth_provider.issuer, OAuthProvider#oauth_provider.token_endpoint, OAuthProvider#oauth_provider.authorization_endpoint, OAuthProvider#oauth_provider.end_session_endpoint, OAuthProvider#oauth_provider.jwks_uri, - format_ssl_options(OAuthProvider#oauth_provider.ssl_options)]). + format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])). 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 399708ae2562..cabbc38d0b13 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 @@ -158,6 +158,31 @@ "rabbitmq_auth_backend_oauth2.authorization_endpoint", [{datatype, string}, {validators, ["uri", "https_uri"]}]}. +%% auth_oauth2.authorization_endpoint = https://a.com/authorize +%% auth_oauth2.authorization_endpoint.params.resource = ${resource_id} +%% auth_oauth2.authorization_endpoint.params.audience = ${resource_id} + +{mapping, + "auth_oauth2.authorization_endpoint.params.$param", + "rabbitmq_auth_backend_oauth2.authorization_endpoint.req_params", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint.req_params", + fun(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint.req_params", Conf), + rabbit_oauth2_schema:translate_endpoint_req_params(Settings) + end}. + +{mapping, + "auth_oauth2.oauth_providers.$name.algorithms.$algorithm", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.oauth_providers", + fun(Conf) -> + rabbit_oauth2_schema:translate_oauth_providers(Conf) + end}. + {mapping, "auth_oauth2.https.peer_verification", "rabbitmq_auth_backend_oauth2.key_config.peer_verification", @@ -333,5 +358,5 @@ {translation, "rabbitmq_auth_backend_oauth2.resource_servers", fun(Conf) -> - rabbit_oauth2_schema:translate_resource_servers(Conf) + rabbit_oauth2_schema:translate_resource_servers(Conf) end}. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl index d79972509ba0..e11e5816fda9 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl @@ -11,7 +11,8 @@ -export([ translate_oauth_providers/1, translate_resource_servers/1, - translate_signing_keys/1 + translate_signing_keys/1, + translate_endpoint_req_params/1 ]). extract_key_as_binary({Name,_}) -> list_to_binary(Name). @@ -63,6 +64,10 @@ translate_list_of_signing_keys(ListOfKidPath) -> end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). +-spec translate_endpoint_req_params([{list(), binary()}]) -> map(). +translate_endpoint_req_params(ListOfReqParams) -> + lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, ListOfReqParams). + validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of {ok, _} -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index eafaa2122c74..a9b3cbaea007 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -32,7 +32,7 @@ add_signing_key(KeyId, Type, Value) -> -spec update_jwks_signing_keys(oauth_provider()) -> ok | {error, term()}. update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, ssl_options = SslOptions}) -> - rabbit_log:debug("OAuth 2 JWT: downloading keys from ~tp (TLS options: ~p)", + rabbit_log:debug("Downloading signing keys from ~tp (TLS options: ~p)", [JwksUrl, SslOptions]), case uaa_jwks:get(JwksUrl, SslOptions) of {ok, {_, _, JwksBody}} -> @@ -40,13 +40,13 @@ update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, jose:decode(erlang:iolist_to_binary(JwksBody)), []), Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), - rabbit_log:debug("OAuth 2 JWT: downloaded keys ~tp", [Keys]), + rabbit_log:debug("Downloaded signing keys ~tp", [Keys]), case rabbit_oauth2_config:replace_signing_keys(Keys, Id) of {error, _} = Err -> Err; _ -> ok end; {error, _} = Err -> - rabbit_log:error("OAuth 2 JWT: failed to download keys: ~tp", [Err]), + rabbit_log:error("Failed to download signing keys: ~tp", [Err]), Err end. @@ -56,29 +56,31 @@ decode_and_verify(Token) -> {error, _} = Err -> Err; ResourceServerId -> - OAuthProviderId = - rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(ResourceServerId), - rabbit_log:debug("OAuth 2 JWT: resolved resource_server_id: ~p oauth_provider_id: ~p", - [ResourceServerId, OAuthProviderId]), - case uaa_jwt_jwt:get_key_id(rabbit_oauth2_config:get_default_key(OAuthProviderId), Token) of - {ok, KeyId} -> - rabbit_log:debug("OAuth 2 JWT: signing_key_id : '~tp'", [KeyId]), - case get_jwk(KeyId, OAuthProviderId) of - {ok, JWK} -> - case uaa_jwt_jwt:decode_and_verify( - OAuthProviderId, - JWK, - Token) of - {true, Payload} -> {true, ResourceServerId, Payload}; - {false, Payload} -> {false, ResourceServerId, Payload} - end; - {error, _} = Err -> - Err - end; - {error, _} = Err -> Err - end + decode_and_verify(Token, ResourceServerId, + rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id( + ResourceServerId)) end. +decode_and_verify(Token, ResourceServerId, OAuthProviderId) -> + rabbit_log:debug("Resolved resource_server_id: ~p -> oauth_provider_id: ~p", + [ResourceServerId, OAuthProviderId]), + case uaa_jwt_jwt:get_key_id(rabbit_oauth2_config:get_default_key(OAuthProviderId), Token) of + {ok, KeyId} -> + case get_jwk(KeyId, OAuthProviderId) of + {ok, JWK} -> + Algorithms = rabbit_oauth2_config:get_algorithms(OAuthProviderId), + rabbit_log:debug("Verifying signature using signing_key_id : '~tp' and algorithms: ~p", + [KeyId, Algorithms]), + case uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token) of + {true, Payload} -> {true, ResourceServerId, Payload}; + {false, Payload} -> {false, ResourceServerId, Payload} + end; + {error, _} = Err -> + Err + end; + {error, _} = Err -> Err + end. + resolve_resource_server_id(Token) -> case uaa_jwt_jwt:get_aud(Token) of {error, _} = Error -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl index 7d8c37457028..58da87ae639a 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl @@ -12,18 +12,17 @@ -include_lib("jose/include/jose_jws.hrl"). -decode_and_verify(OauthProviderId, Jwk, Token) -> - Verify = - case rabbit_oauth2_config:get_algorithms(OauthProviderId) of - undefined -> jose_jwt:verify(Jwk, Token); - Algs -> jose_jwt:verify_strict(Jwk, Algs, Token) - end, +-spec decode_and_verify(list() | undefined, map(), binary()) -> {boolean(), map()}. +decode_and_verify(Algs, Jwk, Token) -> + Verify = case Algs of + undefined -> jose_jwt:verify(Jwk, Token); + _ -> jose_jwt:verify_strict(Jwk, Algs, Token) + end, case Verify of {true, #jose_jwt{fields = Fields}, _} -> {true, Fields}; {false, #jose_jwt{fields = Fields}, _} -> {false, Fields} end. - get_key_id(DefaultKey, Token) -> try case jose_jwt:peek_protected(Token) of diff --git a/selenium/bin/components/rabbitmq b/selenium/bin/components/rabbitmq index 9eea9e13c2a7..3fb9cb002f85 100644 --- a/selenium/bin/components/rabbitmq +++ b/selenium/bin/components/rabbitmq @@ -52,6 +52,7 @@ start_local_rabbitmq() { init_rabbitmq RABBITMQ_SERVER_ROOT=$(realpath ../) + MOUNT_RABBITMQ_CONF="/etc/rabbitmq/rabbitmq.conf" MOUNT_ADVANCED_CONFIG="/etc/rabbitmq/advanced.config" diff --git a/selenium/test/authnz-msg-protocols/env.local b/selenium/test/authnz-msg-protocols/env.local index 69f43736edd4..3e6bec3ad0ff 100644 --- a/selenium/test/authnz-msg-protocols/env.local +++ b/selenium/test/authnz-msg-protocols/env.local @@ -1 +1 @@ -export IMPORT_DIR=test/authnz-msg-protocols/imports +export IMPORT_DIR=selenium/test/authnz-msg-protocols/imports diff --git a/selenium/test/basic-auth/env.local b/selenium/test/basic-auth/env.local index 26cc7522d3b9..bc20106b5b5d 100644 --- a/selenium/test/basic-auth/env.local +++ b/selenium/test/basic-auth/env.local @@ -1 +1 @@ -export IMPORT_DIR=deps/rabbitmq_management/selenium/test/basic-auth/imports +export IMPORT_DIR=selenium/test/basic-auth/imports diff --git a/selenium/test/basic-auth/rabbitmq.conf b/selenium/test/basic-auth/rabbitmq.conf index 7bacc14af27a..ece06fe128a1 100644 --- a/selenium/test/basic-auth/rabbitmq.conf +++ b/selenium/test/basic-auth/rabbitmq.conf @@ -1,6 +1,6 @@ auth_backends.1 = rabbit_auth_backend_internal management.login_session_timeout = 1 -load_definitions = ${IMPORT_DIR}/users.json +load_definitions = ${RABBITMQ_TEST_DIR}/imports/users.json loopback_users = none diff --git a/selenium/test/multi-oauth/env.local b/selenium/test/multi-oauth/env.local index d61f528c4e4a..c61124da53a7 100644 --- a/selenium/test/multi-oauth/env.local +++ b/selenium/test/multi-oauth/env.local @@ -1 +1 @@ -export OAUTH_SERVER_CONFIG_BASEDIR=deps/rabbitmq_management/selenium/test +export OAUTH_SERVER_CONFIG_BASEDIR=test diff --git a/selenium/test/multi-oauth/env.local.devkeycloak b/selenium/test/multi-oauth/env.local.devkeycloak index a1e2d5d596c2..8e5a2f2e9285 100644 --- a/selenium/test/multi-oauth/env.local.devkeycloak +++ b/selenium/test/multi-oauth/env.local.devkeycloak @@ -1,2 +1,2 @@ export DEVKEYCLOAK_URL=https://localhost:8442/realms/dev -export DEVKEYCLOAK_CA_CERT=deps/rabbitmq_management/selenium/test/multi-oauth/devkeycloak/ca_certificate.pem +export DEVKEYCLOAK_CA_CERT=test/multi-oauth/devkeycloak/ca_certificate.pem diff --git a/selenium/test/multi-oauth/env.local.prodkeycloak b/selenium/test/multi-oauth/env.local.prodkeycloak index e267b558cd49..c636bf8fcd55 100644 --- a/selenium/test/multi-oauth/env.local.prodkeycloak +++ b/selenium/test/multi-oauth/env.local.prodkeycloak @@ -1,2 +1,2 @@ export PRODKEYCLOAK_URL=https://localhost:8443/realms/prod -export PRODKEYCLOAK_CA_CERT=deps/rabbitmq_management/selenium/test/multi-oauth/prodkeycloak/ca_certificate.pem +export PRODKEYCLOAK_CA_CERT=test/multi-oauth/prodkeycloak/ca_certificate.pem diff --git a/selenium/test/oauth/env.local b/selenium/test/oauth/env.local index 80cfe7430e52..c61124da53a7 100644 --- a/selenium/test/oauth/env.local +++ b/selenium/test/oauth/env.local @@ -1 +1 @@ -export OAUTH_SERVER_CONFIG_BASEDIR=selenium/test +export OAUTH_SERVER_CONFIG_BASEDIR=test diff --git a/selenium/test/oauth/env.local.keycloak b/selenium/test/oauth/env.local.keycloak index 1fa28ef79232..e4fc9adbc6f0 100644 --- a/selenium/test/oauth/env.local.keycloak +++ b/selenium/test/oauth/env.local.keycloak @@ -1,3 +1,3 @@ export KEYCLOAK_URL=https://localhost:8443/realms/test export OAUTH_PROVIDER_URL=https://localhost:8443/realms/test -export OAUTH_PROVIDER_CA_CERT=deps/rabbitmq_management/selenium/test/oauth/keycloak/ca_certificate.pem +export OAUTH_PROVIDER_CA_CERT=test/oauth/keycloak/ca_certificate.pem From 3cf5b7e03eb327cad284eb22f50926de5da05083 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 10 Sep 2024 14:28:50 +0100 Subject: [PATCH 03/64] Reduce verbosity of some log statements --- deps/oauth2_client/src/oauth2_client.erl | 6 +++--- .../src/rabbit_oauth2_config.erl | 6 ++++-- deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl | 6 +++--- selenium/test/oauth/env.local.keycloak | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index cb0ef0e9b7cb..e7380d28f728 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -11,7 +11,8 @@ get_openid_configuration/2, get_openid_configuration/3, merge_openid_configuration/2, merge_oauth_provider/2, - extract_ssl_options_as_list/1 + extract_ssl_options_as_list/1, + format_ssl_options/1, format_oauth_provider/1, format_oauth_provider_id/1 ]). -include("oauth2_client.hrl"). @@ -557,8 +558,7 @@ format_ssl_options(TlsOptions) -> [] -> 0; Certs -> length(Certs) end, - lists:flatten(io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, crl_check: ~p, " ++ - "depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [ + lists:flatten(io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, crl_check: ~p, depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [ proplists:get_value(verify, TlsOptions), proplists:get_value(fail_if_no_peer_cert, TlsOptions), proplists:get_value(crl_check, TlsOptions), diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl index f6219c06ad0f..abaa677969b6 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl @@ -155,7 +155,8 @@ do_replace_signing_keys(SigningKeys, root) -> proplists:get_value(signing_keys, KeyConfig1, #{}), SigningKeys)} | KeyConfig1], application:set_env(?APP, key_config, KeyConfig2), - rabbit_log:debug("Replacing signing keys ~p", [ KeyConfig2]), + rabbit_log:debug("Replacing signing keys for key_config with ~p keys", + [maps:size(SigningKeys)]), SigningKeys; do_replace_signing_keys(SigningKeys, OauthProviderId) -> @@ -168,7 +169,8 @@ do_replace_signing_keys(SigningKeys, OauthProviderId) -> OauthProviders = maps:put(OauthProviderId, OauthProvider, OauthProviders0), application:set_env(?APP, oauth_providers, OauthProviders), - rabbit_log:debug("Replacing signing keys for ~p -> ~p", [OauthProviderId, OauthProvider]), + rabbit_log:debug("Replacing signing keys for ~p -> ~p with ~p keys", + [OauthProviderId, OauthProvider, maps:size(SigningKeys)]), SigningKeys. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index a9b3cbaea007..b9f451cf58f8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -33,14 +33,14 @@ add_signing_key(KeyId, Type, Value) -> update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, ssl_options = SslOptions}) -> rabbit_log:debug("Downloading signing keys from ~tp (TLS options: ~p)", - [JwksUrl, SslOptions]), + [JwksUrl, oauth2_client:format_ssl_options(SslOptions)]), case uaa_jwks:get(JwksUrl, SslOptions) of {ok, {_, _, JwksBody}} -> KeyList = maps:get(<<"keys">>, jose:decode(erlang:iolist_to_binary(JwksBody)), []), Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), - rabbit_log:debug("Downloaded signing keys ~tp", [Keys]), + rabbit_log:debug("Downloaded ~p signing keys", [maps:size(Keys)]), case rabbit_oauth2_config:replace_signing_keys(Keys, Id) of {error, _} = Err -> Err; _ -> ok @@ -63,7 +63,7 @@ decode_and_verify(Token) -> decode_and_verify(Token, ResourceServerId, OAuthProviderId) -> rabbit_log:debug("Resolved resource_server_id: ~p -> oauth_provider_id: ~p", - [ResourceServerId, OAuthProviderId]), + [ResourceServerId, oauth2_client:format_oauth_provider_id(OAuthProviderId)]), case uaa_jwt_jwt:get_key_id(rabbit_oauth2_config:get_default_key(OAuthProviderId), Token) of {ok, KeyId} -> case get_jwk(KeyId, OAuthProviderId) of diff --git a/selenium/test/oauth/env.local.keycloak b/selenium/test/oauth/env.local.keycloak index e4fc9adbc6f0..3ff0eb199ea0 100644 --- a/selenium/test/oauth/env.local.keycloak +++ b/selenium/test/oauth/env.local.keycloak @@ -1,3 +1,3 @@ export KEYCLOAK_URL=https://localhost:8443/realms/test export OAUTH_PROVIDER_URL=https://localhost:8443/realms/test -export OAUTH_PROVIDER_CA_CERT=test/oauth/keycloak/ca_certificate.pem +export OAUTH_PROVIDER_CA_CERT=selenium/test/oauth/keycloak/ca_certificate.pem From 6fb83af48e7ca215d19ddea0599e08dffc1d3a8c Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 10 Sep 2024 14:32:01 +0100 Subject: [PATCH 04/64] Reduce logging verbosity --- deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index b9f451cf58f8..8a3a472dcd45 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -98,7 +98,7 @@ get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> undefined -> if AllowUpdateJwks -> - rabbit_log:debug("OAuth 2 JWT: signing key '~tp' not found. Downloading it... ", [KeyId]), + rabbit_log:debug("Signing key '~tp' not found. Downloading it... ", [KeyId]), case rabbit_oauth2_config:get_oauth_provider(OAuthProviderId, [jwks_uri]) of {ok, OAuthProvider} -> case update_jwks_signing_keys(OAuthProvider) of @@ -110,15 +110,15 @@ get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> Err end; {error, _} = Error -> - rabbit_log:debug("OAuth 2 JWT: unable to download keys due to ~p", [Error]), + rabbit_log:debug("Unable to download signing keys due to ~p", [Error]), Error end; true -> - rabbit_log:debug("OAuth 2 JWT: signing key '~tp' not found. Downloading is not allowed", [KeyId]), + rabbit_log:debug("Signing key '~tp' not found. Downloading is not allowed", [KeyId]), {error, key_not_found} end; {Type, Value} -> - rabbit_log:debug("OAuth 2 JWT: signing key found: '~tp', '~tp'", [Type, Value]), + rabbit_log:debug("Signing key ~p found", [KeyId]), case Type of json -> uaa_jwt_jwk:make_jwk(Value); pem -> uaa_jwt_jwk:from_pem(Value); From 2a3dcb36798c69b7b6fa6e158f2b839dad171fcf Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 11 Sep 2024 18:50:12 +0100 Subject: [PATCH 05/64] WIP Refactor code before implementing oidc endpoints parameters --- .../rabbitmq_auth_backend_oauth2.schema | 17 +- .../src/rabbit_auth_backend_oauth2.erl | 48 ++- .../src/rabbit_oauth2_config.erl | 369 +++++++----------- .../src/rabbit_oauth2_schema.erl | 9 +- .../src/uaa_jwt.erl | 69 ++-- .../src/uaa_jwt_jwt.erl | 4 +- .../priv/schema/rabbitmq_management.schema | 1 - .../priv/www/js/oidc-oauth/helper.js | 2 +- 8 files changed, 233 insertions(+), 286 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 cabbc38d0b13..f594903d15cd 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 @@ -158,19 +158,19 @@ "rabbitmq_auth_backend_oauth2.authorization_endpoint", [{datatype, string}, {validators, ["uri", "https_uri"]}]}. -%% auth_oauth2.authorization_endpoint = https://a.com/authorize -%% auth_oauth2.authorization_endpoint.params.resource = ${resource_id} -%% auth_oauth2.authorization_endpoint.params.audience = ${resource_id} +%% auth_oauth2.authorization_endpoint_params.audience +%% auth_oauth2.resource_servers.rabbitmq.authorization_endpoint_params.audience +%% auth_oauth2.resource_servers.rabbitmq.token_endpoint_params.audience +%% auth_oauth2.resource_servers.rabbitmq.jkws_uri_params.appId = {mapping, - "auth_oauth2.authorization_endpoint.params.$param", - "rabbitmq_auth_backend_oauth2.authorization_endpoint.req_params", + "auth_oauth2.authorization_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.oauth_providers", [{datatype, string}]}. -{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint.req_params", +{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", fun(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint.req_params", Conf), - rabbit_oauth2_schema:translate_endpoint_req_params(Settings) + rabbit_oauth2_schema:translate_authorization_endpoint_params(Conf) end}. {mapping, @@ -326,6 +326,7 @@ [{datatype, string}] }. + {mapping, "auth_oauth2.resource_servers.$name.scope_prefix", "rabbitmq_auth_backend_oauth2.resource_servers", 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 a43212655b87..f37b60d21c5a 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 @@ -221,36 +221,39 @@ post_process_payload(ResourceServerId, Payload) when is_map(Payload) -> Payload4. --spec post_process_payload_with_scope_aliases(ResourceServerId :: binary(), Payload :: map()) -> map(). +-spec post_process_payload_with_scope_aliases( + ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). %% This is for those hopeless environments where the token structure is so out of %% messaging team's control that even the extra scopes field is no longer an option. %% %% This assumes that scopes can be random values that do not follow the RabbitMQ %% convention, or any other convention, in any way. They are just random client role IDs. %% See rabbitmq/rabbitmq-server#4588 for details. -post_process_payload_with_scope_aliases(ResourceServerId, Payload) -> +post_process_payload_with_scope_aliases(ResourceServer, Payload) -> %% try JWT scope field value for alias - Payload1 = post_process_payload_with_scope_alias_in_scope_field(ResourceServerId, Payload), + Payload1 = post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload), %% try the configurable 'extra_scopes_source' field value for alias - post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServerId, Payload1). + post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServer, Payload1). --spec post_process_payload_with_scope_alias_in_scope_field(ResourceServerId :: binary(), Payload :: map()) -> map(). +-spec post_process_payload_with_scope_alias_in_scope_field( + ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). %% First attempt: use the value in the 'scope' field for alias -post_process_payload_with_scope_alias_in_scope_field(ResourceServerId, Payload) -> - ScopeMappings = rabbit_oauth2_config:get_scope_aliases(ResourceServerId), +post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload) -> + ScopeMappings = ResourceServer#resource_server.scope_aliases, post_process_payload_with_scope_alias_field_named(Payload, ?SCOPE_JWT_FIELD, ScopeMappings). --spec post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServerId :: binary(), Payload :: map()) -> map(). +-spec post_process_payload_with_scope_alias_in_extra_scopes_source( + ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). %% Second attempt: use the value in the configurable 'extra scopes source' field for alias -post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServerId, Payload) -> - ExtraScopesField = rabbit_oauth2_config:get_additional_scopes_key(ResourceServerId), +post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServer, Payload) -> + ExtraScopesField = ResourceServer#resource_server.additional_scopes_key, case ExtraScopesField of %% nothing to inject {error, not_found} -> Payload; {ok, ExtraScopes} -> - ScopeMappings = rabbit_oauth2_config:get_scope_aliases(ResourceServerId), + ScopeMappings = ResourceServer#resource_server.scope_aliases, post_process_payload_with_scope_alias_field_named(Payload, ExtraScopes, ScopeMappings) end. @@ -280,16 +283,19 @@ post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAlias maps:put(?SCOPE_JWT_FIELD, ExpandedScopes, Payload). --spec does_include_complex_claim_field(ResourceServerId :: binary(), Payload :: map()) -> boolean(). -does_include_complex_claim_field(ResourceServerId, Payload) when is_map(Payload) -> - case rabbit_oauth2_config:get_additional_scopes_key(ResourceServerId) of +-spec does_include_complex_claim_field( + ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> boolean(). +does_include_complex_claim_field(ResourceServer, Payload) when is_map(Payload) -> + case ResourceServer#resource_server.additional_scopes_key of {ok, ScopeKey} -> maps:is_key(ScopeKey, Payload); {error, not_found} -> false end. --spec post_process_payload_with_complex_claim(ResourceServerId :: binary(), Payload :: map()) -> map(). -post_process_payload_with_complex_claim(ResourceServerId, Payload) -> - case rabbit_oauth2_config:get_additional_scopes_key(ResourceServerId) of +-spec post_process_payload_with_complex_claim( + ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). +post_process_payload_with_complex_claim(ResourceServer, Payload) -> + ResourceServerId = ResourceServer#resource_server.id, + case ResourceServer#resource_server.additional_scopes_key of {ok, ScopesKey} -> ComplexClaim = maps:get(ScopesKey, Payload), AdditionalScopes = @@ -479,10 +485,12 @@ is_recognized_permission(#{?ACTIONS_FIELD := _, ?LOCATIONS_FIELD:= _ , ?TYPE_FIE is_recognized_permission(_, _) -> false. --spec post_process_payload_in_rich_auth_request_format(ResourceServerId :: binary(), Payload :: map()) -> map(). +-spec post_process_payload_in_rich_auth_request_format(ResourceServer :: resource_server(), + Payload :: map()) -> map(). %% https://oauth.net/2/rich-authorization-requests/ -post_process_payload_in_rich_auth_request_format(ResourceServerId, #{<<"authorization_details">> := Permissions} = Payload) -> - ResourceServerType = rabbit_oauth2_config:get_resource_server_type(ResourceServerId), +post_process_payload_in_rich_auth_request_format(ResourceServer, + #{<<"authorization_details">> := Permissions} = Payload) -> + ResourceServerType = ResourceServer#resource_server.resource_server_type, FilteredPermissionsByType = lists:filter(fun(P) -> is_recognized_permission(P, ResourceServerType) end, Permissions), diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl index abaa677969b6..7ee647980211 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl @@ -15,73 +15,129 @@ -define(TOP_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). %% scope aliases map "role names" to a set of scopes +-record(internal_oauth_provider, { + id :: oauth_provider_id(), + default_key :: binary() | undefined, + algorithms :: list() | undefined +}). +-type internal_oauth_provider() :: #internal_oauth_provider{}. + +-record(resource_server, { + id :: resource_server_id(), + resource_server_type :: binary(), + verify_aud :: boolean(), + scope_prefix :: binary(), + additional_scopes_key :: binary(), + preferred_username_claims :: list(), + scope_aliases :: undefined | map(), + oauth_provider_id :: oauth_provider_id() + }). + +-type resource_server() :: #resource_server{}. +-type resource_server_id() :: binary() | list(). + -export([ add_signing_key/2, add_signing_key/3, replace_signing_keys/1, replace_signing_keys/2, get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2, - get_default_key/0, - get_default_resource_server_id/0, - get_resource_server_id_for_audience/1, - get_algorithms/0, get_algorithms/1, get_default_key/1, - get_oauth_provider_id_for_resource_server_id/1, + resolve_resource_server_id_from_audience/1, get_oauth_provider/2, - get_allowed_resource_server_ids/0, find_audience_in_resource_server_ids/1, - is_verify_aud/0, is_verify_aud/1, - get_additional_scopes_key/0, get_additional_scopes_key/1, - get_default_preferred_username_claims/0, get_preferred_username_claims/0, - get_preferred_username_claims/1, - get_scope_prefix/0, get_scope_prefix/1, - get_resource_server_type/0, get_resource_server_type/1, - has_scope_aliases/1, get_scope_aliases/1 + get_internal_oauth_provider/2, + get_allowed_resource_server_ids/0, find_audience_in_resource_server_ids/1 ]). +export_type([resource_server/0, internal_oauth_provider/0]). --spec get_default_preferred_username_claims() -> list(). -get_default_preferred_username_claims() -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS. - --spec get_preferred_username_claims() -> list(). -get_preferred_username_claims() -> - case application:get_env(?APP, preferred_username_claims) of - {ok, Value} -> - append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); - _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS - end. --spec get_preferred_username_claims(binary() | list()) -> list(). -get_preferred_username_claims(ResourceServerId) -> - ResourceServers = application:get_env(?APP, resource_servers, #{}), - ResourceServer = maps:get(ResourceServerId, ResourceServers, []), - case proplists:get_value(preferred_username_claims, ResourceServer, undefined) of - undefined -> - get_preferred_username_claims(); - Value -> - append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS) +-spec get_resource_server(resource_server_id()) -> resource_server() | {error, term()}. +get_resource_server(ResourceServerId) -> + case get_default_resource_server_id() of + {error, _} -> + get_resource_server(undefined, ResourceServerId); + V -> + get_resource_server(V, ResourceServerId) end. +get_resource_server(TopResourceServerId, ResourceServerId) -> + when ResourceServerId =:= TopResourceServerId -> + ScopeAlises = + application:get_env(?APP, scope_aliases, undefined), + PreferredUsernameClaims = + case application:get_env(?APP, preferred_username_claims) of + {ok, Value} -> + append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); + _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS + end, + ResourceServerType = + application:get_env(?APP, resource_server_type, <<>>), + VerifyAud = + application:get_env(?APP, verify_aud, true), + AdditionalScopesKey = + case application:get_env(?APP, extra_scopes_source, undefined) of + undefined -> {error, not_found}; + ScopeKey -> {ok, ScopeKey} + end, + DefaultScopePrefix = + case get_default_resource_server_id() of + {error, _} -> <<"">>; + V -> erlang:iolist_to_binary([V, <<".">>]) + end, + ScopePrefix = + application:get_env(?APP, scope_prefix, DefaultScopePrefix). + OAuthProviderId = + case application:get_env(?APP, default_oauth_provider) of + undefined -> root; + {ok, DefaultOauthProviderId} -> DefaultOauthProviderId + end, + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }; + +get_resource_server(TopResourceServerId, ResourceServerId) -> + when ResourceServerId =/= TopResourceServerId -> + ResourceServerProps = + maps:get(ResourceServerId, application:get_env(?APP, resource_servers, + #{}),[]), + TopResourseServer = + get_resource_server(TopResourceServerId, TopResourceServerId), + ScopeAlises = + proplists:get_value(scope_aliases, ResourceServerProps, + TopResourseServer#resource_server.scope_aliases), + PreferredUsernameClaims = + proplists:get_value(preferred_username_claims, ResourceServerProps, + TopResourseServer#resource_server.preferred_username_claims), + ResourceServerType = + proplists:get_value(resource_server_type, ResourceServerProps, + TopResourseServer#resource_server.resource_server_type), + VerifyAud = + proplists:get_value(verify_aud, ResourceServerProps, + TopResourseServer#resource_server.verify_aud), + AdditionalScopesKey = + proplists:get_value(extra_scopes_source, ResourceServerProps, + TopResourseServer#resource_server.extra_scopes_source), + ScopePrefix = + proplists:get_value(scope_prefix, ResourceServerProps, + TopResourseServer#resource_server.scope_prefix), + OAuthProviderId = + proplists:get_value(oauth_provider_id, ResourceServerProps, + TopResourseServer#resource_server.oauth_provider_id), + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }. --spec get_default_key() -> {ok, binary()} | {error, no_default_key_configured}. -get_default_key() -> - get_default_key(root). - --spec get_default_key(oauth_provider_id()) -> {ok, binary()} | {error, no_default_key_configured}. -get_default_key(root) -> - case application:get_env(?APP, key_config, undefined) of - undefined -> - {error, no_default_key_configured}; - KeyConfig -> - case proplists:get_value(default_key, KeyConfig, undefined) of - undefined -> {error, no_default_key_configured}; - V -> {ok, V} - end - end; -get_default_key(OauthProviderId) -> - OauthProviders = application:get_env(?APP, oauth_providers, #{}), - case maps:get(OauthProviderId, OauthProviders, []) of - [] -> - {error, no_default_key_configured}; - OauthProvider -> - case proplists:get_value(default_key, OauthProvider, undefined) of - undefined -> {error, no_default_key_configured}; - V -> {ok, V} - end - end. %% %% Signing Key storage: @@ -199,7 +255,13 @@ get_signing_keys(OauthProviderId) -> Jwks end. --spec get_resource_server_id_for_audience(binary() | list() | none) -> binary() | {error, term()}. +-spec resolve_resource_server_id_from_audience(binary() | list() | none) -> resource_server() | {error, term()}. +resolve_resource_server_id_from_audience(Audience) -> + case get_resource_server_id_for_audience(Audience) of + {error, _} = Error -> Error; + ResourceServerId -> get_resource_server(ResourceServerId) + end. + get_resource_server_id_for_audience(none) -> case is_verify_aud() of true -> @@ -230,36 +292,37 @@ get_resource_server_id_for_audience(Audience) -> end end. --spec get_oauth_provider_id_for_resource_server_id(binary()) -> oauth_provider_id(). - -get_oauth_provider_id_for_resource_server_id(ResourceServerId) -> - get_oauth_provider_id_for_resource_server_id(get_default_resource_server_id(), - ResourceServerId). -get_oauth_provider_id_for_resource_server_id(TopResourceServerId, - ResourceServerId) when ResourceServerId =:= TopResourceServerId -> - case application:get_env(?APP, default_oauth_provider) of - undefined -> root; - {ok, DefaultOauthProviderId} -> DefaultOauthProviderId - end; -get_oauth_provider_id_for_resource_server_id(TopResourceServerId, - ResourceServerId) when ResourceServerId =/= TopResourceServerId -> - case proplists:get_value(oauth_provider_id, get_resource_server_props(ResourceServerId)) of - undefined -> - case application:get_env(?APP, default_oauth_provider) of - undefined -> root; - {ok, DefaultOauthProviderId} -> DefaultOauthProviderId - end; - OauthProviderId -> OauthProviderId - end. -spec get_oauth_provider(oauth_provider_id(), list()) -> {ok, oauth_provider()} | {error, any()}. get_oauth_provider(OAuthProviderId, RequiredAttributeList) -> oauth2_client:get_oauth_provider(OAuthProviderId, RequiredAttributeList). --spec get_algorithms() -> list() | undefined. -get_algorithms() -> - get_algorithms(root). +-spec get_internal_oauth_provider(oauth_provider_id(), list()) -> + {ok, internal_oauth_provider()} | {error, any()}. +get_internal_oauth_provider(OAuthProviderId) -> + #internal_oauth_provider{ + id = OAuthProvider#oauth_provider.id, + default_key = get_default_key(OAuthProvider#oauth_provider.id), + algorithms :: get_algorithms(OAuthProvider#oauth_provider.id) + }. + +-spec get_default_key(oauth_provider_id()) -> binary() | undefined. +get_default_key(root) -> + case application:get_env(?APP, key_config, undefined) of + undefined -> + undefined; + KeyConfig -> + proplists:get_value(default_key, KeyConfig, undefined) + end; +get_default_key(OauthProviderId) -> + OauthProviders = application:get_env(?APP, oauth_providers, #{}), + case maps:get(OauthProviderId, OauthProviders, []) of + [] -> + undefined; + OauthProvider -> + proplists:get_value(default_key, OauthProvider, undefined) + end. -spec get_algorithms(oauth_provider_id()) -> list() | undefined. get_algorithms(root) -> @@ -272,10 +335,6 @@ get_algorithms(OAuthProviderId) -> V -> proplists:get_value(algorithms, V, undefined) end. -get_resource_server_props(ResourceServerId) -> - ResourceServers = application:get_env(?APP, resource_servers, #{}), - maps:get(ResourceServerId, ResourceServers, []). - get_signing_key(KeyId) -> maps:get(KeyId, get_signing_keys(root), undefined). get_signing_key(KeyId, OAuthProviderId) -> @@ -320,142 +379,6 @@ find_audience_in_resource_server_ids(AudList) when is_list(AudList) -> [] -> {error, no_matching_aud_found} end. --spec is_verify_aud() -> boolean(). -is_verify_aud() -> application:get_env(?APP, verify_aud, true). - --spec is_verify_aud(binary()) -> boolean(). -is_verify_aud(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - is_verify_aud(undefined, ResourceServerId); - V -> - is_verify_aud(V, ResourceServerId) - end. -is_verify_aud(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> is_verify_aud(); -is_verify_aud(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - proplists:get_value(verify_aud, maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}), []), is_verify_aud()). - --spec get_additional_scopes_key() -> {ok, binary()} | {error, not_found}. -get_additional_scopes_key() -> - case application:get_env(?APP, extra_scopes_source, undefined) of - undefined -> {error, not_found}; - ScopeKey -> {ok, ScopeKey} - end. --spec get_additional_scopes_key(binary()) -> {ok, binary()} | {error, not_found}. -get_additional_scopes_key(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_additional_scopes_key(undefined, ResourceServerId); - V -> - get_additional_scopes_key(V, ResourceServerId) - end. -get_additional_scopes_key(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> get_additional_scopes_key(); -get_additional_scopes_key(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - ResourceServer = maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}), []), - case proplists:get_value(extra_scopes_source, ResourceServer) of - undefined -> get_additional_scopes_key(); - <<>> -> get_additional_scopes_key(); - ScopeKey -> {ok, ScopeKey} - end. - --spec get_scope_prefix() -> binary(). -get_scope_prefix() -> - DefaultScopePrefix = case get_default_resource_server_id() of - {error, _} -> <<"">>; - V -> erlang:iolist_to_binary([V, <<".">>]) - end, - application:get_env(?APP, scope_prefix, DefaultScopePrefix). - --spec get_scope_prefix(binary()) -> binary(). -get_scope_prefix(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_scope_prefix(undefined, ResourceServerId); - V -> - get_scope_prefix(V, ResourceServerId) - end. -get_scope_prefix(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> get_scope_prefix(); -get_scope_prefix(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - ResourceServer = maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}), []), - case proplists:get_value(scope_prefix, ResourceServer) of - undefined -> - case application:get_env(?APP, scope_prefix) of - undefined -> <>; - {ok, Prefix} -> Prefix - end; - Prefix -> Prefix - end. - --spec get_resource_server_type() -> binary(). -get_resource_server_type() -> application:get_env(?APP, resource_server_type, <<>>). - --spec get_resource_server_type(binary()) -> binary(). -get_resource_server_type(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_resource_server_type(undefined, ResourceServerId); - V -> - get_resource_server_type(V, ResourceServerId) - end. -get_resource_server_type(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> get_resource_server_type(); -get_resource_server_type(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - ResourceServer = maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}), []), - proplists:get_value(resource_server_type, ResourceServer, - get_resource_server_type()). - --spec has_scope_aliases(binary()) -> boolean(). -has_scope_aliases(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - has_scope_aliases(undefined, ResourceServerId); - V -> - has_scope_aliases(V, ResourceServerId) - end. -has_scope_aliases(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> - case application:get_env(?APP, scope_aliases) of - undefined -> false; - _ -> true - end; -has_scope_aliases(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - ResourceServerProps = maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}),[]), - case proplists:is_defined(scope_aliases, ResourceServerProps) of - true -> true; - false -> has_scope_aliases(TopResourceServerId) - end. - --spec get_scope_aliases(binary()) -> map(). -get_scope_aliases(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_scope_aliases(undefined, ResourceServerId); - V -> - get_scope_aliases(V, ResourceServerId) - end. -get_scope_aliases(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> - application:get_env(?APP, scope_aliases, #{}); -get_scope_aliases(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> - ResourceServerProps = maps:get(ResourceServerId, - application:get_env(?APP, resource_servers, #{}),[]), - proplists:get_value(scope_aliases, ResourceServerProps, - get_scope_aliases(TopResourceServerId)). - intersection(List1, List2) -> [I || I <- List1, lists:member(I, List2)]. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl index e11e5816fda9..0e77e0fc7fb3 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl @@ -12,7 +12,7 @@ translate_oauth_providers/1, translate_resource_servers/1, translate_signing_keys/1, - translate_endpoint_req_params/1 + translate_authorization_endpoint_params/1 ]). extract_key_as_binary({Name,_}) -> list_to_binary(Name). @@ -64,9 +64,10 @@ translate_list_of_signing_keys(ListOfKidPath) -> end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). --spec translate_endpoint_req_params([{list(), binary()}]) -> map(). -translate_endpoint_req_params(ListOfReqParams) -> - lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, ListOfReqParams). +-spec translate_authorization_endpoint_params([{list(), binary()}]) -> map(). +translate_authorization_endpoint_params(Conf) -> + Params = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint_params", Conf), + lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, Params). validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index 8a3a472dcd45..65106f69049d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -50,60 +50,75 @@ update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, Err end. --spec decode_and_verify(binary()) -> {boolean(), binary(), map()} | {error, term()}. +-spec decode_and_verify(binary()) -> {boolean(), resource_server(), map()} | {error, term()}. decode_and_verify(Token) -> - case resolve_resource_server_id(Token) of + case resolve_resource_server(Token) of {error, _} = Err -> Err; - ResourceServerId -> - decode_and_verify(Token, ResourceServerId, - rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id( - ResourceServerId)) + {ResourceServer, InternalOAuthProvider} -> + decode_and_verify(Token, ResourceServer, InternalOAuthProvider) end. -decode_and_verify(Token, ResourceServerId, OAuthProviderId) -> - rabbit_log:debug("Resolved resource_server_id: ~p -> oauth_provider_id: ~p", - [ResourceServerId, oauth2_client:format_oauth_provider_id(OAuthProviderId)]), - case uaa_jwt_jwt:get_key_id(rabbit_oauth2_config:get_default_key(OAuthProviderId), Token) of +-spec decode_and_verify(binary(), resource_server(), internal_oauth_provider()) + -> {boolean(), resource_server(), map()} | {error, term()}. +decode_and_verify(Token, ResourceServer, InternalOAuthProvider) -> + OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, + rabbit_log:debug("Decoding token for resource_server: ~p using oauth_provider_id: ~p", + [ResourceServer#resource_server.id, + oauth2_client:format_oauth_provider_id(OAuthProviderId)]), + Result = case uaa_jwt_jwt:get_key_id(Token) of + undefined -> + InternalOAuthProvider#internal_oauth_provider.default_key; {ok, KeyId} -> - case get_jwk(KeyId, OAuthProviderId) of + KeyId; + {error, _} = Err -> + Err + end, + case Result of + {error, _} = Err -> + Err; + KeyId -> + case get_jwk(KeyId, OAuthProvider) of {ok, JWK} -> - Algorithms = rabbit_oauth2_config:get_algorithms(OAuthProviderId), + Algorithms = OAuthProvider#internal_oauth_provider.algorithms, rabbit_log:debug("Verifying signature using signing_key_id : '~tp' and algorithms: ~p", [KeyId, Algorithms]), case uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token) of - {true, Payload} -> {true, ResourceServerId, Payload}; - {false, Payload} -> {false, ResourceServerId, Payload} + {true, Payload} -> {true, ResourceServer, Payload}; + {false, Payload} -> {false, ResourceServer, Payload} end; {error, _} = Err -> Err - end; - {error, _} = Err -> Err - end. + end + end. + -resolve_resource_server_id(Token) -> +resolve_resource_server(Token) -> case uaa_jwt_jwt:get_aud(Token) of {error, _} = Error -> Error; {ok, Audience} -> - rabbit_oauth2_config:get_resource_server_id_for_audience(Audience) + ResourceServer = rabbit_oauth2_config:resolve_resource_server_from_audience(Audience) + {ResourceServer, + rabbit_oauth2_config:get_internal_oauth_provider(ResourceServer#resource_server.id)} end. --spec get_jwk(binary(), oauth_provider_id()) -> {ok, map()} | {error, term()}. -get_jwk(KeyId, OAuthProviderId) -> - get_jwk(KeyId, OAuthProviderId, true). +-spec get_jwk(binary(), internal_oauth_provider()) -> {ok, map()} | {error, term()}. +get_jwk(KeyId, OAuthProvider) -> + get_jwk(KeyId, OAuthProvider, true). -get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> +get_jwk(KeyId, InternalOAuthProvider, AllowUpdateJwks) -> + OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, case rabbit_oauth2_config:get_signing_key(KeyId, OAuthProviderId) of undefined -> - if - AllowUpdateJwks -> + case AllowUpdateJwks of + true -> rabbit_log:debug("Signing key '~tp' not found. Downloading it... ", [KeyId]), case rabbit_oauth2_config:get_oauth_provider(OAuthProviderId, [jwks_uri]) of {ok, OAuthProvider} -> case update_jwks_signing_keys(OAuthProvider) of ok -> - get_jwk(KeyId, OAuthProviderId, false); + get_jwk(KeyId, InternalOAuthProvider, false); {error, no_jwks_url} -> {error, key_not_found}; {error, _} = Err -> @@ -113,7 +128,7 @@ get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> rabbit_log:debug("Unable to download signing keys due to ~p", [Error]), Error end; - true -> + false -> rabbit_log:debug("Signing key '~tp' not found. Downloading is not allowed", [KeyId]), {error, key_not_found} end; diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl index 58da87ae639a..5389f5f845fb 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl @@ -23,11 +23,11 @@ decode_and_verify(Algs, Jwk, Token) -> {false, #jose_jwt{fields = Fields}, _} -> {false, Fields} end. -get_key_id(DefaultKey, Token) -> +get_key_id(Token) -> try case jose_jwt:peek_protected(Token) of #jose_jws{fields = #{<<"kid">> := Kid}} -> {ok, Kid}; - #jose_jws{} -> DefaultKey + #jose_jws{} -> undefined end catch Type:Err:Stacktrace -> {error, {invalid_token, Type, Err, Stacktrace}} diff --git a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema index 83c32b3022ac..396e6b537321 100644 --- a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema +++ b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema @@ -488,7 +488,6 @@ end}. {mapping, "management.oauth_initiated_logon_type", "rabbitmq_management.oauth_initiated_logon_type", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. - {mapping, "management.oauth_resource_servers.$name.id", "rabbitmq_management.oauth_resource_servers", diff --git a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js index 799b41f59b7f..0df8b3d056d5 100644 --- a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js +++ b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js @@ -140,7 +140,7 @@ function oauth_initialize_user_manager(resource_server) { client_id: resource_server.oauth_client_id, response_type: resource_server.oauth_response_type, scope: resource_server.oauth_scopes, -// resource: resource_server.id, deprecated +// resource: resource_server.id, redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", post_logout_redirect_uri: rabbit_base_uri() + "/", From 7064969ca501f583689ba1d6a0f0702c73c77c51 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 12 Sep 2024 18:20:31 +0100 Subject: [PATCH 06/64] WIP More refactoring split rabbit_oauth2_config into - rabbit_oauth2_resource_server - rabbit_oauth2_oauth_provider and their respective test modules Signing keys is an oauth provider concern hence it stays with the oauth_provider module. --- deps/rabbitmq_auth_backend_oauth2/BUILD.bazel | 6 +- deps/rabbitmq_auth_backend_oauth2/app.bzl | 25 +- .../include/oauth2.hrl | 35 + .../src/rabbit_oauth2_config.erl | 399 ------- .../src/rabbit_oauth2_oauth_provider.erl | 193 ++++ .../src/rabbit_oauth2_resource_server.erl | 190 +++ .../src/uaa_jwt.erl | 1 + .../test/rabbit_oauth2_config_SUITE.erl | 1016 ----------------- .../rabbit_oauth2_oauth_provider_SUITE.erl | 660 +++++++++++ .../rabbit_oauth2_resource_server_SUITE.erl | 525 +++++++++ 10 files changed, 1628 insertions(+), 1422 deletions(-) create mode 100644 deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl delete mode 100644 deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl create mode 100644 deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl create mode 100644 deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl delete mode 100644 deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_config_SUITE.erl create mode 100644 deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl create mode 100644 deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 71c3d2e46289..2509e27f20ab 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -113,7 +113,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "rabbit_oauth2_config_SUITE", + name = "rabbit_oauth2_oauth_provider_SUITE", additional_beam = [ "test/oauth2_http_mock.beam", ], @@ -122,6 +122,10 @@ rabbitmq_integration_suite( ], ) +rabbitmq_integration_suite( + name = "rabbit_oauth2_resource_server_SUITE" +) + rabbitmq_integration_suite( name = "jwks_SUITE", additional_beam = [ diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index 003818ac74be..5e42e061dcab 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -13,7 +13,8 @@ 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_config.erl", + "src/rabbit_oauth2_oauth_provider.erl", + "src/rabbit_oauth2_resource_server.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -48,7 +49,8 @@ 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_config.erl", + "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth2_oauth_provider.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -85,6 +87,7 @@ def all_srcs(name = "all_srcs"): ) filegroup( name = "public_hdrs", + srcs = ["include/oauth2.hrl"], ) filegroup( @@ -94,7 +97,8 @@ 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_config.erl", + "src/rabbit_oauth2_oauth_provider.erl", + "src/rabbit_oauth2_resource_server.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -236,10 +240,19 @@ def test_suite_beam_files(name = "test_suite_beam_files"): erlc_opts = "//:test_erlc_opts", ) erlang_bytecode( - name = "rabbit_oauth2_config_SUITE_beam_files", + name = "rabbit_oauth2_oauth_provider_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_oauth2_config_SUITE.erl"], - outs = ["test/rabbit_oauth2_config_SUITE.beam"], + srcs = ["test/rabbit_oauth2_oauth_provider_SUITE.erl"], + outs = ["test/rabbit_oauth2_oauth_provider_SUITE.beam"], + app_name = "rabbitmq_auth_backend_oauth2", + erlc_opts = "//:test_erlc_opts", + deps = ["//deps/oauth2_client:erlang_app"], + ) + erlang_bytecode( + name = "rabbit_oauth2_resource_server_SUITE_beam_files", + testonly = True, + srcs = ["test/rabbit_oauth2_resource_server_SUITE.erl"], + outs = ["test/rabbit_oauth2_resource_server_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl new file mode 100644 index 000000000000..01fbf1134b8d --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -0,0 +1,35 @@ +%% 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) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. +%% + + +-include_lib("oauth2_client/include/oauth2_client.hrl"). + +-define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). + +-define(TOP_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). +%% scope aliases map "role names" to a set of scopes + +-record(internal_oauth_provider, { + id :: oauth_provider_id(), + default_key :: binary() | undefined, + algorithms :: list() | undefined +}). +-type internal_oauth_provider() :: #internal_oauth_provider{}. + +-record(resource_server, { + id :: resource_server_id(), + resource_server_type :: binary(), + verify_aud :: boolean(), + scope_prefix :: binary(), + additional_scopes_key :: binary(), + preferred_username_claims :: list(), + scope_aliases :: undefined | map(), + oauth_provider_id :: oauth_provider_id() + }). + +-type resource_server() :: #resource_server{}. +-type resource_server_id() :: binary() | list(). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl deleted file mode 100644 index 7ee647980211..000000000000 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_config.erl +++ /dev/null @@ -1,399 +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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. -%% - --module(rabbit_oauth2_config). - --include_lib("oauth2_client/include/oauth2_client.hrl"). - --define(APP, rabbitmq_auth_backend_oauth2). --define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). - --define(TOP_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). -%% scope aliases map "role names" to a set of scopes - --record(internal_oauth_provider, { - id :: oauth_provider_id(), - default_key :: binary() | undefined, - algorithms :: list() | undefined -}). --type internal_oauth_provider() :: #internal_oauth_provider{}. - --record(resource_server, { - id :: resource_server_id(), - resource_server_type :: binary(), - verify_aud :: boolean(), - scope_prefix :: binary(), - additional_scopes_key :: binary(), - preferred_username_claims :: list(), - scope_aliases :: undefined | map(), - oauth_provider_id :: oauth_provider_id() - }). - --type resource_server() :: #resource_server{}. --type resource_server_id() :: binary() | list(). - --export([ - add_signing_key/2, add_signing_key/3, replace_signing_keys/1, - replace_signing_keys/2, - get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2, - resolve_resource_server_id_from_audience/1, - get_oauth_provider/2, - get_internal_oauth_provider/2, - get_allowed_resource_server_ids/0, find_audience_in_resource_server_ids/1 - ]). -export_type([resource_server/0, internal_oauth_provider/0]). - --spec get_resource_server(resource_server_id()) -> resource_server() | {error, term()}. -get_resource_server(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_resource_server(undefined, ResourceServerId); - V -> - get_resource_server(V, ResourceServerId) - end. -get_resource_server(TopResourceServerId, ResourceServerId) -> - when ResourceServerId =:= TopResourceServerId -> - ScopeAlises = - application:get_env(?APP, scope_aliases, undefined), - PreferredUsernameClaims = - case application:get_env(?APP, preferred_username_claims) of - {ok, Value} -> - append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); - _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS - end, - ResourceServerType = - application:get_env(?APP, resource_server_type, <<>>), - VerifyAud = - application:get_env(?APP, verify_aud, true), - AdditionalScopesKey = - case application:get_env(?APP, extra_scopes_source, undefined) of - undefined -> {error, not_found}; - ScopeKey -> {ok, ScopeKey} - end, - DefaultScopePrefix = - case get_default_resource_server_id() of - {error, _} -> <<"">>; - V -> erlang:iolist_to_binary([V, <<".">>]) - end, - ScopePrefix = - application:get_env(?APP, scope_prefix, DefaultScopePrefix). - OAuthProviderId = - case application:get_env(?APP, default_oauth_provider) of - undefined -> root; - {ok, DefaultOauthProviderId} -> DefaultOauthProviderId - end, - - #resource_server{ - id = ResourceServerId, - resource_server_type = ResourceServerType, - verify_aud = VerifyAud, - scope_prefix = ScopePrefix, - additional_scopes_key = AdditionalScopesKey, - preferred_username_claims = PreferredUsernameClaims, - scope_aliases = ScopeAliases, - oauth_provider_id = OAuthProviderId - }; - -get_resource_server(TopResourceServerId, ResourceServerId) -> - when ResourceServerId =/= TopResourceServerId -> - ResourceServerProps = - maps:get(ResourceServerId, application:get_env(?APP, resource_servers, - #{}),[]), - TopResourseServer = - get_resource_server(TopResourceServerId, TopResourceServerId), - ScopeAlises = - proplists:get_value(scope_aliases, ResourceServerProps, - TopResourseServer#resource_server.scope_aliases), - PreferredUsernameClaims = - proplists:get_value(preferred_username_claims, ResourceServerProps, - TopResourseServer#resource_server.preferred_username_claims), - ResourceServerType = - proplists:get_value(resource_server_type, ResourceServerProps, - TopResourseServer#resource_server.resource_server_type), - VerifyAud = - proplists:get_value(verify_aud, ResourceServerProps, - TopResourseServer#resource_server.verify_aud), - AdditionalScopesKey = - proplists:get_value(extra_scopes_source, ResourceServerProps, - TopResourseServer#resource_server.extra_scopes_source), - ScopePrefix = - proplists:get_value(scope_prefix, ResourceServerProps, - TopResourseServer#resource_server.scope_prefix), - OAuthProviderId = - proplists:get_value(oauth_provider_id, ResourceServerProps, - TopResourseServer#resource_server.oauth_provider_id), - - #resource_server{ - id = ResourceServerId, - resource_server_type = ResourceServerType, - verify_aud = VerifyAud, - scope_prefix = ScopePrefix, - additional_scopes_key = AdditionalScopesKey, - preferred_username_claims = PreferredUsernameClaims, - scope_aliases = ScopeAliases, - oauth_provider_id = OAuthProviderId - }. - - -%% -%% Signing Key storage: -%% -%% * Static signing keys configured via config file are stored under signing_keys attribute -%% in their respective location (under key_config for the root oauth provider and -%% directly under each oauth provider) -%% * Dynamic signing keys loaded via rabbitmqctl or via JWKS endpoint are stored under -%% jwks attribute in their respective location. However, this attribute stores the -%% combination of static signing keys and dynamic signing keys. If the same kid is -%% found in both sets, the dynamic kid overrides the static kid. -%% - --type key_type() :: json | pem | map. --spec add_signing_key(binary(), {key_type(), binary()} ) -> map() | {error, term()}. -add_signing_key(KeyId, Key) -> - LockId = lock(), - try do_add_signing_key(KeyId, Key, root) of - V -> V - after - unlock(LockId) - end. - --spec add_signing_key(binary(), {key_type(), binary()}, oauth_provider_id()) -> - map() | {error, term()}. -add_signing_key(KeyId, Key, OAuthProviderId) -> - case lock() of - {error, _} = Error -> - Error; - LockId -> - try do_add_signing_key(KeyId, Key, OAuthProviderId) of - V -> V - after - unlock(LockId) - end - end. - -do_add_signing_key(KeyId, Key, OAuthProviderId) -> - do_replace_signing_keys(maps:put(KeyId, Key, - get_signing_keys_from_jwks(OAuthProviderId)), OAuthProviderId). - -get_signing_keys_from_jwks(root) -> - KeyConfig = application:get_env(?APP, key_config, []), - proplists:get_value(jwks, KeyConfig, #{}); -get_signing_keys_from_jwks(OAuthProviderId) -> - OAuthProviders0 = application:get_env(?APP, oauth_providers, #{}), - OAuthProvider0 = maps:get(OAuthProviderId, OAuthProviders0, []), - proplists:get_value(jwks, OAuthProvider0, #{}). - --spec replace_signing_keys(map()) -> map() | {error, term()}. -replace_signing_keys(SigningKeys) -> - replace_signing_keys(SigningKeys, root). - --spec replace_signing_keys(map(), oauth_provider_id()) -> map() | {error, term()}. -replace_signing_keys(SigningKeys, OAuthProviderId) -> - case lock() of - {error,_} = Error -> - Error; - LockId -> - try do_replace_signing_keys(SigningKeys, OAuthProviderId) of - V -> V - after - unlock(LockId) - end - end. - -do_replace_signing_keys(SigningKeys, root) -> - KeyConfig = application:get_env(?APP, key_config, []), - KeyConfig1 = proplists:delete(jwks, KeyConfig), - KeyConfig2 = [{jwks, maps:merge( - proplists:get_value(signing_keys, KeyConfig1, #{}), - SigningKeys)} | KeyConfig1], - application:set_env(?APP, key_config, KeyConfig2), - rabbit_log:debug("Replacing signing keys for key_config with ~p keys", - [maps:size(SigningKeys)]), - SigningKeys; - -do_replace_signing_keys(SigningKeys, OauthProviderId) -> - OauthProviders0 = application:get_env(?APP, oauth_providers, #{}), - OauthProvider0 = maps:get(OauthProviderId, OauthProviders0, []), - OauthProvider1 = proplists:delete(jwks, OauthProvider0), - OauthProvider = [{jwks, maps:merge( - proplists:get_value(signing_keys, OauthProvider1, #{}), - SigningKeys)} | OauthProvider1], - - OauthProviders = maps:put(OauthProviderId, OauthProvider, OauthProviders0), - application:set_env(?APP, oauth_providers, OauthProviders), - rabbit_log:debug("Replacing signing keys for ~p -> ~p with ~p keys", - [OauthProviderId, OauthProvider, maps:size(SigningKeys)]), - SigningKeys. - - --spec get_signing_keys() -> map(). -get_signing_keys() -> - get_signing_keys(root). - --spec get_signing_keys(oauth_provider_id()) -> map(). -get_signing_keys(root) -> - case application:get_env(?APP, key_config, undefined) of - undefined -> - #{}; - KeyConfig -> - case proplists:get_value(jwks, KeyConfig, undefined) of - undefined -> proplists:get_value(signing_keys, KeyConfig, #{}); - Jwks -> Jwks - end - end; -get_signing_keys(OauthProviderId) -> - OauthProviders = application:get_env(?APP, oauth_providers, #{}), - OauthProvider = maps:get(OauthProviderId, OauthProviders, []), - case proplists:get_value(jwks, OauthProvider, undefined) of - undefined -> - proplists:get_value(signing_keys, OauthProvider, #{}); - Jwks -> - Jwks - end. - --spec resolve_resource_server_id_from_audience(binary() | list() | none) -> resource_server() | {error, term()}. -resolve_resource_server_id_from_audience(Audience) -> - case get_resource_server_id_for_audience(Audience) of - {error, _} = Error -> Error; - ResourceServerId -> get_resource_server(ResourceServerId) - end. - -get_resource_server_id_for_audience(none) -> - case is_verify_aud() of - true -> - {error, no_matching_aud_found}; - false -> - case get_default_resource_server_id() of - {error, missing_resource_server_id_in_config} -> - {error, mising_audience_in_token_and_resource_server_in_config}; - V -> V - end - end; -get_resource_server_id_for_audience(Audience) -> - case find_audience_in_resource_server_ids(Audience) of - {ok, ResourceServerId} -> - ResourceServerId; - {error, only_one_resource_server_as_audience_found_many} = Error -> - Error; - {error, no_matching_aud_found} -> - case is_verify_aud() of - true -> - {error, no_matching_aud_found}; - false -> - case get_default_resource_server_id() of - {error, missing_resource_server_id_in_config} -> - {error, mising_audience_in_token_and_resource_server_in_config}; - V -> V - end - end - end. - - --spec get_oauth_provider(oauth_provider_id(), list()) -> - {ok, oauth_provider()} | {error, any()}. -get_oauth_provider(OAuthProviderId, RequiredAttributeList) -> - oauth2_client:get_oauth_provider(OAuthProviderId, RequiredAttributeList). - --spec get_internal_oauth_provider(oauth_provider_id(), list()) -> - {ok, internal_oauth_provider()} | {error, any()}. -get_internal_oauth_provider(OAuthProviderId) -> - #internal_oauth_provider{ - id = OAuthProvider#oauth_provider.id, - default_key = get_default_key(OAuthProvider#oauth_provider.id), - algorithms :: get_algorithms(OAuthProvider#oauth_provider.id) - }. - --spec get_default_key(oauth_provider_id()) -> binary() | undefined. -get_default_key(root) -> - case application:get_env(?APP, key_config, undefined) of - undefined -> - undefined; - KeyConfig -> - proplists:get_value(default_key, KeyConfig, undefined) - end; -get_default_key(OauthProviderId) -> - OauthProviders = application:get_env(?APP, oauth_providers, #{}), - case maps:get(OauthProviderId, OauthProviders, []) of - [] -> - undefined; - OauthProvider -> - proplists:get_value(default_key, OauthProvider, undefined) - end. - --spec get_algorithms(oauth_provider_id()) -> list() | undefined. -get_algorithms(root) -> - proplists:get_value(algorithms, application:get_env(?APP, key_config, []), - undefined); -get_algorithms(OAuthProviderId) -> - OAuthProviders = application:get_env(?APP, oauth_providers, #{}), - case maps:get(OAuthProviderId, OAuthProviders, undefined) of - undefined -> undefined; - V -> proplists:get_value(algorithms, V, undefined) - end. - -get_signing_key(KeyId) -> - maps:get(KeyId, get_signing_keys(root), undefined). -get_signing_key(KeyId, OAuthProviderId) -> - maps:get(KeyId, get_signing_keys(OAuthProviderId), undefined). - - -append_or_return_default(ListOrBinary, Default) -> - case ListOrBinary of - VarList when is_list(VarList) -> VarList ++ Default; - VarBinary when is_binary(VarBinary) -> [VarBinary] ++ Default; - _ -> Default - end. - --spec get_default_resource_server_id() -> binary() | {error, term()}. -get_default_resource_server_id() -> - case ?TOP_RESOURCE_SERVER_ID of - undefined -> {error, missing_resource_server_id_in_config }; - {ok, ResourceServerId} -> ResourceServerId - end. - --spec get_allowed_resource_server_ids() -> list(). -get_allowed_resource_server_ids() -> - ResourceServers = application:get_env(?APP, resource_servers, #{}), - rabbit_log:debug("ResourceServers: ~p", [ResourceServers]), - ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ - [proplists:get_value(id, V, K)] end, [], ResourceServers), - rabbit_log:debug("ResourceServersIds: ~p", [ResourceServerIds]), - ResourceServerIds ++ case get_default_resource_server_id() of - {error, _} -> []; - ResourceServerId -> [ ResourceServerId ] - end. - --spec find_audience_in_resource_server_ids(binary() | list()) -> - {ok, binary()} | {error, term()}. -find_audience_in_resource_server_ids(Audience) when is_binary(Audience) -> - find_audience_in_resource_server_ids(binary:split(Audience, <<" ">>, [global, trim_all])); -find_audience_in_resource_server_ids(AudList) when is_list(AudList) -> - AllowedAudList = get_allowed_resource_server_ids(), - case intersection(AudList, AllowedAudList) of - [One] -> {ok, One}; - [_One|_Tail] -> {error, only_one_resource_server_as_audience_found_many}; - [] -> {error, no_matching_aud_found} - end. - - -intersection(List1, List2) -> - [I || I <- List1, lists:member(I, List2)]. - -lock() -> - Nodes = rabbit_nodes:list_running(), - Retries = rabbit_nodes:lock_retries(), - LockId = case global:set_lock({oauth2_config_lock, - rabbitmq_auth_backend_oauth2}, Nodes, Retries) of - true -> rabbitmq_auth_backend_oauth2; - false -> {error, unable_to_claim_lock} - end, - LockId. - -unlock(LockId) -> - Nodes = rabbit_nodes:list_running(), - global:del_lock({oauth2_config_lock, LockId}, Nodes), - ok. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl new file mode 100644 index 000000000000..ef580dd29c26 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl @@ -0,0 +1,193 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_config). + +-include("oauth2.hrl"). + +%-include_lib("oauth2_client/include/oauth2_client.hrl"). + +-export([ + get_internal_oauth_provider/0, get_internal_oauth_provider/1, + add_signing_key/2, add_signing_key/3, replace_signing_keys/1, + replace_signing_keys/2, + get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2 +]). + +-spec get_internal_oauth_provider() -> internal_oauth_provider(). + get_internal_oauth_provider(root). + +-spec get_internal_oauth_provider(oauth_provider_id()) -> internal_oauth_provider(). +get_internal_oauth_provider(OAuthProviderId) -> + #internal_oauth_provider{ + id = OAuthProvider#oauth_provider.id, + default_key = get_default_key(OAuthProvider#oauth_provider.id), + algorithms = get_algorithms(OAuthProvider#oauth_provider.id) + }. + + +%% +%% Signing Key storage: +%% +%% * Static signing keys configured via config file are stored under signing_keys attribute +%% in their respective location (under key_config for the root oauth provider and +%% directly under each oauth provider) +%% * Dynamic signing keys loaded via rabbitmqctl or via JWKS endpoint are stored under +%% jwks attribute in their respective location. However, this attribute stores the +%% combination of static signing keys and dynamic signing keys. If the same kid is +%% found in both sets, the dynamic kid overrides the static kid. +%% + +-type key_type() :: json | pem | map. +-spec add_signing_key(binary(), {key_type(), binary()} ) -> map() | {error, term()}. +add_signing_key(KeyId, Key) -> + LockId = lock(), + try do_add_signing_key(KeyId, Key, root) of + V -> V + after + unlock(LockId) + end. + +-spec add_signing_key(binary(), {key_type(), binary()}, oauth_provider_id()) -> + map() | {error, term()}. +add_signing_key(KeyId, Key, OAuthProviderId) -> + case lock() of + {error, _} = Error -> + Error; + LockId -> + try do_add_signing_key(KeyId, Key, OAuthProviderId) of + V -> V + after + unlock(LockId) + end + end. + +do_add_signing_key(KeyId, Key, OAuthProviderId) -> + do_replace_signing_keys(maps:put(KeyId, Key, + get_signing_keys_from_jwks(OAuthProviderId)), OAuthProviderId). + +get_signing_keys_from_jwks(root) -> + KeyConfig = application:get_env(?APP, key_config, []), + proplists:get_value(jwks, KeyConfig, #{}); +get_signing_keys_from_jwks(OAuthProviderId) -> + OAuthProviders0 = application:get_env(?APP, oauth_providers, #{}), + OAuthProvider0 = maps:get(OAuthProviderId, OAuthProviders0, []), + proplists:get_value(jwks, OAuthProvider0, #{}). + +-spec replace_signing_keys(map()) -> map() | {error, term()}. +replace_signing_keys(SigningKeys) -> + replace_signing_keys(SigningKeys, root). + +-spec replace_signing_keys(map(), oauth_provider_id()) -> map() | {error, term()}. +replace_signing_keys(SigningKeys, OAuthProviderId) -> + case lock() of + {error,_} = Error -> + Error; + LockId -> + try do_replace_signing_keys(SigningKeys, OAuthProviderId) of + V -> V + after + unlock(LockId) + end + end. + +do_replace_signing_keys(SigningKeys, root) -> + KeyConfig = application:get_env(?APP, key_config, []), + KeyConfig1 = proplists:delete(jwks, KeyConfig), + KeyConfig2 = [{jwks, maps:merge( + proplists:get_value(signing_keys, KeyConfig1, #{}), + SigningKeys)} | KeyConfig1], + application:set_env(?APP, key_config, KeyConfig2), + rabbit_log:debug("Replacing signing keys for key_config with ~p keys", + [maps:size(SigningKeys)]), + SigningKeys; + +do_replace_signing_keys(SigningKeys, OauthProviderId) -> + OauthProviders0 = application:get_env(?APP, oauth_providers, #{}), + OauthProvider0 = maps:get(OauthProviderId, OauthProviders0, []), + OauthProvider1 = proplists:delete(jwks, OauthProvider0), + OauthProvider = [{jwks, maps:merge( + proplists:get_value(signing_keys, OauthProvider1, #{}), + SigningKeys)} | OauthProvider1], + + OauthProviders = maps:put(OauthProviderId, OauthProvider, OauthProviders0), + application:set_env(?APP, oauth_providers, OauthProviders), + rabbit_log:debug("Replacing signing keys for ~p -> ~p with ~p keys", + [OauthProviderId, OauthProvider, maps:size(SigningKeys)]), + SigningKeys. + + +-spec get_signing_keys() -> map(). +get_signing_keys() -> + get_signing_keys(root). + +-spec get_signing_keys(oauth_provider_id()) -> map(). +get_signing_keys(root) -> + case application:get_env(?APP, key_config, undefined) of + undefined -> + #{}; + KeyConfig -> + case proplists:get_value(jwks, KeyConfig, undefined) of + undefined -> proplists:get_value(signing_keys, KeyConfig, #{}); + Jwks -> Jwks + end + end; +get_signing_keys(OauthProviderId) -> + OauthProviders = application:get_env(?APP, oauth_providers, #{}), + OauthProvider = maps:get(OauthProviderId, OauthProviders, []), + case proplists:get_value(jwks, OauthProvider, undefined) of + undefined -> + proplists:get_value(signing_keys, OauthProvider, #{}); + Jwks -> + Jwks + end. + +get_signing_key(KeyId) -> + maps:get(KeyId, get_signing_keys(root), undefined). +get_signing_key(KeyId, OAuthProviderId) -> + maps:get(KeyId, get_signing_keys(OAuthProviderId), undefined). + +-spec get_default_key(oauth_provider_id()) -> binary() | undefined. +get_default_key(root) -> + case application:get_env(?APP, key_config, undefined) of + undefined -> undefined; + KeyConfig -> proplists:get_value(default_key, KeyConfig, undefined) + end; +get_default_key(OauthProviderId) -> + OauthProviders = application:get_env(?APP, oauth_providers, #{}), + case maps:get(OauthProviderId, OauthProviders, []) of + [] -> + undefined; + OauthProvider -> + proplists:get_value(default_key, OauthProvider, undefined) + end. + +-spec get_algorithms(oauth_provider_id()) -> list() | undefined. +get_algorithms(root) -> + proplists:get_value(algorithms, application:get_env(?APP, key_config, []), + undefined); +get_algorithms(OAuthProviderId) -> + OAuthProviders = application:get_env(?APP, oauth_providers, #{}), + case maps:get(OAuthProviderId, OAuthProviders, undefined) of + undefined -> undefined; + V -> proplists:get_value(algorithms, V, undefined) + end. + +lock() -> + Nodes = rabbit_nodes:list_running(), + Retries = rabbit_nodes:lock_retries(), + LockId = case global:set_lock({oauth2_config_lock, + rabbitmq_auth_backend_oauth2}, Nodes, Retries) of + true -> rabbitmq_auth_backend_oauth2; + false -> {error, unable_to_claim_lock} + end, + LockId. + +unlock(LockId) -> + Nodes = rabbit_nodes:list_running(), + global:del_lock({oauth2_config_lock, LockId}, Nodes), + ok. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl new file mode 100644 index 000000000000..8a4eea731941 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl @@ -0,0 +1,190 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_resource_server). + +-include("oauth2.hrl"). + +%-include_lib("oauth2_client/include/oauth2_client.hrl"). + + +-export([ + resolve_resource_server_id_from_audience/1, + get_resource_server/1 +]). + +-spec get_resource_server(resource_server_id()) -> resource_server() | {error, term()}. +get_resource_server(ResourceServerId) -> + case get_default_resource_server_id() of + {error, _} -> + get_resource_server(undefined, ResourceServerId); + V -> + get_resource_server(V, ResourceServerId) + end. +get_resource_server(TopResourceServerId, ResourceServerId) -> + when ResourceServerId =:= TopResourceServerId -> + ScopeAlises = + application:get_env(?APP, scope_aliases, undefined), + PreferredUsernameClaims = + case application:get_env(?APP, preferred_username_claims) of + {ok, Value} -> + append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); + _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS + end, + ResourceServerType = + application:get_env(?APP, resource_server_type, <<>>), + VerifyAud = + application:get_env(?APP, verify_aud, true), + AdditionalScopesKey = + case application:get_env(?APP, extra_scopes_source, undefined) of + undefined -> {error, not_found}; + ScopeKey -> {ok, ScopeKey} + end, + DefaultScopePrefix = + case get_default_resource_server_id() of + {error, _} -> <<"">>; + V -> erlang:iolist_to_binary([V, <<".">>]) + end, + ScopePrefix = + application:get_env(?APP, scope_prefix, DefaultScopePrefix). + OAuthProviderId = + case application:get_env(?APP, default_oauth_provider) of + undefined -> root; + {ok, DefaultOauthProviderId} -> DefaultOauthProviderId + end, + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }; + +get_resource_server(TopResourceServerId, ResourceServerId) -> + when ResourceServerId =/= TopResourceServerId -> + ResourceServerProps = + maps:get(ResourceServerId, application:get_env(?APP, resource_servers, + #{}),[]), + TopResourseServer = + get_resource_server(TopResourceServerId, TopResourceServerId), + ScopeAlises = + proplists:get_value(scope_aliases, ResourceServerProps, + TopResourseServer#resource_server.scope_aliases), + PreferredUsernameClaims = + proplists:get_value(preferred_username_claims, ResourceServerProps, + TopResourseServer#resource_server.preferred_username_claims), + ResourceServerType = + proplists:get_value(resource_server_type, ResourceServerProps, + TopResourseServer#resource_server.resource_server_type), + VerifyAud = + proplists:get_value(verify_aud, ResourceServerProps, + TopResourseServer#resource_server.verify_aud), + AdditionalScopesKey = + proplists:get_value(extra_scopes_source, ResourceServerProps, + TopResourseServer#resource_server.extra_scopes_source), + ScopePrefix = + proplists:get_value(scope_prefix, ResourceServerProps, + TopResourseServer#resource_server.scope_prefix), + OAuthProviderId = + proplists:get_value(oauth_provider_id, ResourceServerProps, + TopResourseServer#resource_server.oauth_provider_id), + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }. + + +-spec resolve_resource_server_id_from_audience(binary() | list() | none) -> + resource_server() | {error, term()}. +resolve_resource_server_id_from_audience(Audience) -> + case get_resource_server_id_for_audience(Audience) of + {error, _} = Error -> Error; + ResourceServerId -> get_resource_server(ResourceServerId) + end. + +get_resource_server_id_for_audience(none) -> + case is_verify_aud() of + true -> + {error, missing_audience_in_token}; + false -> + case get_default_resource_server_id() of + {error, missing_resource_server_id_in_config} -> + {error, mising_audience_in_token_and_resource_server_in_config}; + V -> V + end + end; +get_resource_server_id_for_audience(Audience) -> + case find_audience_in_resource_server_ids(Audience) of + {ok, ResourceServerId} -> + ResourceServerId; + {error, only_one_resource_server_as_audience_found_many} = Error -> + Error; + {error, no_matching_aud_found} -> + case is_verify_aud() of + true -> + {error, no_matching_aud_found}; + false -> + case get_default_resource_server_id() of + {error, missing_resource_server_id_in_config} -> + {error, mising_audience_in_token_and_resource_server_in_config}; + V -> V + end + end + end. + +-spec get_default_resource_server_id() -> binary() | {error, term()}. +get_default_resource_server_id() -> + case ?TOP_RESOURCE_SERVER_ID of + undefined -> {error, missing_resource_server_id_in_config }; + {ok, ResourceServerId} -> ResourceServerId + end. + +-spec get_allowed_resource_server_ids() -> list(). +get_allowed_resource_server_ids() -> + ResourceServers = application:get_env(?APP, resource_servers, #{}), + rabbit_log:debug("ResourceServers: ~p", [ResourceServers]), + ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ + [proplists:get_value(id, V, K)] end, [], ResourceServers), + rabbit_log:debug("ResourceServersIds: ~p", [ResourceServerIds]), + ResourceServerIds ++ case get_default_resource_server_id() of + {error, _} -> []; + ResourceServerId -> [ ResourceServerId ] + end. + +-spec find_audience_in_resource_server_ids(binary() | list()) -> + {ok, binary()} | {error, term()}. +find_audience_in_resource_server_ids(Audience) when is_binary(Audience) -> + find_audience_in_resource_server_ids(binary:split(Audience, <<" ">>, [global, trim_all])); +find_audience_in_resource_server_ids(AudList) when is_list(AudList) -> + AllowedAudList = get_allowed_resource_server_ids(), + case intersection(AudList, AllowedAudList) of + [One] -> {ok, One}; + [_One|_Tail] -> {error, only_one_resource_server_as_audience_found_many}; + [] -> {error, no_matching_aud_found} + end. + + +append_or_return_default(ListOrBinary, Default) -> + case ListOrBinary of + VarList when is_list(VarList) -> VarList ++ Default; + VarBinary when is_binary(VarBinary) -> [VarBinary] ++ Default; + _ -> Default + end. + +intersection(List1, List2) -> + [I || I <- List1, lists:member(I, List2)]. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index 65106f69049d..2fb6f3784aea 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -13,6 +13,7 @@ -export([client_id/1, sub/1, client_id/2, sub/2]). +-include("oauth2.hrl"). -include_lib("jose/include/jose_jwk.hrl"). -include_lib("oauth2_client/include/oauth2_client.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_config_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_config_SUITE.erl deleted file mode 100644 index 1d3736bd414a..000000000000 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_config_SUITE.erl +++ /dev/null @@ -1,1016 +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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. -%% - --module(rabbit_oauth2_config_SUITE). - --compile(export_all). --include_lib("common_test/include/ct.hrl"). --include_lib("eunit/include/eunit.hrl"). --include_lib("oauth2_client/include/oauth2_client.hrl"). - --define(RABBITMQ,<<"rabbitmq">>). --define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). --define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). --define(AUTH_PORT, 8000). - - -all() -> [ - {group, with_rabbitmq_node}, - {group, with_resource_server_id}, - {group, without_resource_server_id}, - {group, with_resource_servers}, - {group, with_resource_servers_and_resource_server_id}, - {group, inheritance_group} -]. -groups() -> [ - {with_rabbitmq_node, [], [ - add_signing_keys_for_specific_oauth_provider, - add_signing_keys_for_root_oauth_provider, - - replace_signing_keys_for_root_oauth_provider, - replace_signing_keys_for_specific_oauth_provider, - {with_root_static_signing_keys, [], [ - replace_merge_root_static_keys_with_newly_added_keys, - replace_override_root_static_keys_with_newly_added_keys - ]}, - {with_static_signing_keys_for_specific_oauth_provider, [], [ - replace_merge_static_keys_with_newly_added_keys, - replace_override_static_keys_with_newly_added_keys - ]} - ]}, - {with_resource_server_id, [], [ - get_default_resource_server_id, - get_allowed_resource_server_ids_returns_resource_server_id, - get_resource_server_id_for_rabbit_audience_returns_rabbit, - get_resource_server_id_for_none_audience_should_fail, - get_resource_server_id_for_unknown_audience_should_fail, - {with_verify_aud_false, [], [ - get_resource_server_id_for_rabbit_audience_returns_rabbit, - get_resource_server_id_for_none_audience_returns_rabbit, - get_resource_server_id_for_unknown_audience_returns_rabbit - ]}, - find_audience_in_resource_server_ids_found_resource_server_id, - get_oauth_provider_root_with_jwks_uri_should_fail, - get_default_key_should_fail, - {with_default_key, [], [ - get_default_key - ]}, - {with_static_signing_keys, [], [ - get_signing_keys - ]}, - {with_static_signing_keys_for_oauth_provider_A, [], [ - get_signing_keys_for_oauth_provider_A - ]}, - get_algorithms_should_return_undefined, - {with_algorithms, [], [ - get_algorithms - ]}, - {with_jwks_url, [], [ - get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri, - {with_oauth_providers_A_with_jwks_uri, [], [ - get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri, - {with_default_oauth_provider_A, [], [ - get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri - ]} - ]}, - {with_oauth_providers_A_B_with_jwks_uri, [], [ - get_default_key_for_provider_A_should_fail, - {with_default_key, [], [ - get_default_key_for_provider_A_should_fail - ]}, - {with_default_key_for_provider_A, [], [ - get_default_key_for_provider_A - ]}, - get_algorithms_for_provider_A_should_return_undefined, - {with_algorithms_for_provider_A, [], [ - get_algorithms_for_provider_A - ]}, - get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri, - {with_default_oauth_provider_B, [], [ - get_oauth_provider_should_return_oauth_provider_B_with_jwks_uri - ]} - ]} - ]}, - {with_oauth_providers_A_with_jwks_uri, [], [ - get_oauth_provider_root_with_jwks_uri_should_fail, - {with_default_oauth_provider_A, [], [ - get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri - ]} - ]}, - {with_issuer, [], [ - get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints, - {with_oauth_providers_A_with_issuer, [], [ - get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints, - {with_default_oauth_provider_A, [], [ - get_oauth_provider_should_return_oauth_provider_A_with_all_discovered_endpoints - ]} - ]}, - {with_oauth_providers_A_B_with_issuer, [], [ - get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints, - {with_default_oauth_provider_B, [], [ - get_oauth_provider_should_return_oauth_provider_B_with_all_discovered_endpoints - ]} - ]} - ]} - ]}, - {without_resource_server_id, [], [ - get_default_resource_server_id_returns_error, - get_allowed_resource_server_ids_returns_empty_list - ]}, - {with_resource_servers, [], [ - get_allowed_resource_server_ids_returns_resource_servers_ids, - find_audience_in_resource_server_ids_found_one_resource_servers, - index_resource_servers_by_id_else_by_key, - is_verify_aud_for_resource_two_returns_true, - {with_verify_aud_false_for_resource_two, [], [ - is_verify_aud_for_resource_one_returns_true, - is_verify_aud_for_resource_two_returns_false - ]}, - get_scope_prefix_for_resource_one_returns_default_scope_prefix, - {with_root_scope_prefix, [], [ - get_scope_prefix_for_resource_one_returns_root_scope_prefix, - {with_empty_scope_prefix_for_resource_one, [], [ - get_scope_prefix_for_resource_one_returns_empty_scope_prefix, - get_scope_prefix_for_resource_two_returns_root_scope_prefix - ]} - ]}, - {with_jwks_url, [], [ - get_oauth_provider_for_both_resources_should_return_root_oauth_provider, - {with_oauth_providers_A_with_jwks_uri, [], [ - {with_default_oauth_provider_A, [], [ - get_oauth_provider_for_both_resources_should_return_oauth_provider_A - ]} - ]}, - {with_different_oauth_provider_for_each_resource, [], [ - {with_oauth_providers_A_B_with_jwks_uri, [], [ - get_oauth_provider_for_resource_one_should_return_oauth_provider_A, - get_oauth_provider_for_resource_two_should_return_oauth_provider_B - ]} - ]} - ]} - ]}, - {with_resource_servers_and_resource_server_id, [], [ - get_allowed_resource_server_ids_returns_all_resource_servers_ids, - find_audience_in_resource_server_ids_found_resource_server_id, - find_audience_in_resource_server_ids_found_one_resource_servers, - find_audience_in_resource_server_ids_using_binary_audience - ]}, - - {inheritance_group, [], [ - get_additional_scopes_key, - get_additional_scopes_key_when_not_defined, - is_verify_aud, - is_verify_aud_when_is_false, - get_default_preferred_username_claims, - get_preferred_username_claims, - get_scope_prefix, - get_empty_scope_prefix, - get_scope_prefix_when_not_defined, - get_resource_server_type, - get_resource_server_type_when_not_defined, - has_scope_aliases, - has_scope_aliases_when_not_defined, - get_scope_aliases - ]} -]. - -init_per_suite(Config) -> - rabbit_ct_helpers:log_environment(), - rabbit_ct_helpers:run_setup_steps(Config). - -end_per_suite(Config) -> - rabbit_ct_helpers:run_teardown_steps(Config). - -init_per_group(with_rabbitmq_node, Config) -> - Config1 = rabbit_ct_helpers:set_config(Config, [ - {rmq_nodename_suffix, with_rabbitmq_node}, - {rmq_nodes_count, 1} - ]), - rabbit_ct_helpers:run_steps(Config1, rabbit_ct_broker_helpers:setup_steps()); -init_per_group(with_default_key, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, - proplists:delete(default_key, KeyConfig) ++ [{default_key,<<"default-key">>}]), - Config; -init_per_group(with_root_static_signing_keys, Config) -> - KeyConfig = call_get_env(Config, key_config, []), - SigningKeys = #{ - <<"mykey-root-1">> => <<"some key root-1">>, - <<"mykey-root-2">> => <<"some key root-2">> - }, - call_set_env(Config, key_config, - proplists:delete(default_key, KeyConfig) ++ [{signing_keys,SigningKeys}]), - Config; -init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> - OAuthProviders = call_get_env(Config, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - SigningKeys = #{ - <<"mykey-root-1">> => <<"some key root-1">>, - <<"mykey-root-2">> => <<"some key root-2">> - }, - OAuthProvider1 = proplists:delete(signing_keys, OAuthProvider) ++ [{signing_keys, SigningKeys}], - - call_set_env(Config, oauth_providers, maps:put(<<"A">>, OAuthProvider1, OAuthProviders)), - Config; - -init_per_group(with_default_key_for_provider_A, Config) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, maps:put(<<"A">>, - proplists:delete(default_key, OAuthProvider) ++ [{default_key,<<"A-default-key">>}], - OAuthProviders)), - Config; -init_per_group(with_static_signing_keys, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - SigningKeys = #{<<"mykey-1-1">> => <<"some key 1-1">>, - <<"mykey-1-2">> => <<"some key 1-2">>}, - application:set_env(rabbitmq_auth_backend_oauth2, key_config, - proplists:delete(signing_keys, KeyConfig) ++ [{signing_keys, SigningKeys}]), - Config; -init_per_group(with_static_signing_keys_for_oauth_provider_A, Config) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - SigningKeys = #{<<"A-mykey-1-1">> => <<"A-some key 1-1">>, - <<"A-mykey-1-2">> => <<"A-some key 1-2">>}, - - OAuthProvider0 = proplists:delete(signing_keys, OAuthProvider) ++ - [{signing_keys, SigningKeys}], - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - maps:put(<<"A">>, OAuthProvider0, OAuthProviders)), - Config; - -init_per_group(with_jwks_url, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig - ++ [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), - [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; -init_per_group(with_issuer, Config) -> - {ok, _} = application:ensure_all_started(inets), - {ok, _} = application:ensure_all_started(ssl), - application:ensure_all_started(cowboy), - CertsDir = ?config(rmq_certsdir, Config), - CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), - SslOptions = ssl_options(verify_peer, false, CaCertFile), - - HttpOauthServerExpectations = get_openid_configuration_expectations(), - ListOfExpectations = maps:values(proplists:to_map(HttpOauthServerExpectations)), - - start_https_oauth_server(?AUTH_PORT, CertsDir, ListOfExpectations), - application:set_env(rabbitmq_auth_backend_oauth2, use_global_locks, false), - application:set_env(rabbitmq_auth_backend_oauth2, issuer, build_url_to_oauth_provider(<<"/">>)), - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig ++ SslOptions), - - [{key_config_before_group_with_issuer, KeyConfig}, {ssl_options, SslOptions} | Config]; - -init_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - #{<<"A">> => [ - {issuer,build_url_to_oauth_provider(<<"/A">>) }, - {jwks_uri,build_url_to_oauth_provider(<<"/A/keys">>) } - ] } ), - Config; -init_per_group(with_oauth_providers_A_with_issuer, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - #{<<"A">> => [ - {issuer,build_url_to_oauth_provider(<<"/A">>) }, - {https, ?config(ssl_options, Config)} - ] } ), - Config; -init_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - #{ <<"A">> => [ - {issuer,build_url_to_oauth_provider(<<"/A">>) }, - {jwks_uri,build_url_to_oauth_provider(<<"/A/keys">>)} - ], - <<"B">> => [ - {issuer,build_url_to_oauth_provider(<<"/B">>) }, - {jwks_uri,build_url_to_oauth_provider(<<"/B/keys">>)} - ] }), - Config; -init_per_group(with_oauth_providers_A_B_with_issuer, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - #{ <<"A">> => [ - {issuer,build_url_to_oauth_provider(<<"/A">>) }, - {https, ?config(ssl_options, Config)} - ], - <<"B">> => [ - {issuer,build_url_to_oauth_provider(<<"/B">>) }, - {https, ?config(ssl_options, Config)} - ] }), - Config; - -init_per_group(with_default_oauth_provider_A, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, <<"A">>), - Config; - -init_per_group(with_default_oauth_provider_B, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, <<"B">>), - Config; - - - -init_per_group(with_resource_server_id, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?RABBITMQ), - Config; - -init_per_group(with_root_scope_prefix, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, scope_prefix, <<"some-prefix:">>), - Config; -init_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - maps:put(?RABBITMQ_RESOURCE_ONE, [{scope_prefix, <<"">>} | proplists:delete(scope_prefix, Proplist)], ResourceServers)), - Config; - -init_per_group(with_verify_aud_false, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, verify_aud, false), - Config; -init_per_group(with_verify_aud_false_for_resource_two, Config) -> - ResourceServers = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - maps:put(?RABBITMQ_RESOURCE_TWO, [{verify_aud, false} | proplists:delete(verify_aud, Proplist)], ResourceServers)), - Config; -init_per_group(with_algorithms, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig ++ - [{algorithms, [<<"HS256">>, <<"RS256">>]}]), - [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; -init_per_group(with_algorithms_for_provider_A, Config) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - maps:put(<<"A">>, [{algorithms, [<<"HS256">>, <<"RS256">>]} | OAuthProvider], OAuthProviders)), - [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; - -init_per_group(with_resource_servers_and_resource_server_id, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?RABBITMQ), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, [{jwks_url,<<"https://oauth-for-rabbitmq">> }]), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq1">> } - ]} - - ], - ?RABBITMQ_RESOURCE_TWO => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq2">> } - ]} - ] - }), - Config; -init_per_group(with_different_oauth_provider_for_each_resource, Config) -> - {ok, ResourceServers} = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers), - Rabbit1 = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers) ++ [ {oauth_provider_id, <<"A">>} ], - Rabbit2 = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers) ++ [ {oauth_provider_id, <<"B">>} ], - ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, ResourceServers1)), - Config; - -init_per_group(with_resource_servers, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq1">> } - ]} - ], - ?RABBITMQ_RESOURCE_TWO => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq2">> } - ]} - ], - <<"0">> => [ {id, <<"rabbitmq-0">> } ], - <<"1">> => [ {id, <<"rabbitmq-1">> } ] - - }), - Config; - -init_per_group(inheritance_group, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?RABBITMQ), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_type, <<"rabbitmq-type">>), - application:set_env(rabbitmq_auth_backend_oauth2, scope_prefix, <<"some-prefix-">>), - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"roles">>), - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{}), - - application:set_env(rabbitmq_auth_backend_oauth2, key_config, [ {jwks_url,<<"https://oauth-for-rabbitmq">> } ]), - - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { extra_scopes_source, <<"extra-scope-1">>}, - { verify_aud, false}, - { preferred_username_claims, [<<"email-address">>] }, - { scope_prefix, <<"my-prefix:">> }, - { resource_server_type, <<"my-type">> }, - { scope_aliases, #{} } - ], - ?RABBITMQ_RESOURCE_TWO => [ {id, ?RABBITMQ_RESOURCE_TWO } ] - } - ), - Config; - -init_per_group(_any, Config) -> - Config. - -end_per_group(with_rabbitmq_node, Config) -> - rabbit_ct_helpers:run_steps(Config, rabbit_ct_broker_helpers:teardown_steps()); -end_per_group(with_root_static_signing_keys, Config) -> - KeyConfig = call_get_env(Config, key_config, []), - call_set_env(Config, key_config, KeyConfig), - Config; -end_per_group(get_empty_scope_prefix, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix), - Config; -end_per_group(with_resource_server_id, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), - Config; -end_per_group(with_verify_aud_false, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, verify_aud), - Config; -end_per_group(with_verify_aud_false_for_resource_two, Config) -> - ResourceServers = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - maps:put(?RABBITMQ_RESOURCE_TWO, proplists:delete(verify_aud, Proplist), ResourceServers)), - Config; -end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, - maps:put(?RABBITMQ_RESOURCE_ONE, proplists:delete(scope_prefix, Proplist), ResourceServers)), - Config; - -end_per_group(with_default_key, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, - proplists:delete(default_key, KeyConfig)), - Config; -end_per_group(with_algorithms, Config) -> - KeyConfig = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, - proplists:delete(algorithms, KeyConfig)), - Config; -end_per_group(with_algorithms_for_provider_A, Config) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - maps:put(<<"A">>, proplists:delete(algorithms, OAuthProvider),OAuthProviders)), - Config; -end_per_group(with_static_signing_keys_for_oauth_provider_A, Config) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders), - OAuthProvider0 = proplists:delete(signing_keys, OAuthProvider), - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, - maps:put(<<"A">>, OAuthProvider0, OAuthProviders)), - Config; -end_per_group(with_jwks_url, Config) -> - KeyConfig = ?config(key_config_before_group_with_jwks_url, Config), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig), - Config; -end_per_group(with_issuer, Config) -> - KeyConfig = ?config(key_config_before_group_with_issuer, Config), - application:unset_env(rabbitmq_auth_backend_oauth2, issuer), - application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig), - stop_http_auth_server(), - Config; -end_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; -end_per_group(with_oauth_providers_A_with_issuer, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; -end_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; -end_per_group(with_oauth_providers_A_B_with_issuer, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; - -end_per_group(with_oauth_providers_A, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; -end_per_group(with_oauth_providers_A_B, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), - Config; -end_per_group(with_default_oauth_provider_B, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), - Config; -end_per_group(with_default_oauth_provider_A, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), - Config; - -end_per_group(get_oauth_provider_for_resource_server_id, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), - Config; - -end_per_group(with_resource_servers_and_resource_server_id, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), - Config; - -end_per_group(with_resource_servers, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_servers), - Config; - -end_per_group(with_different_oauth_provider_for_each_resource, Config) -> - {ok, ResourceServers} = application:get_env(rabbitmq_auth_backend_oauth2, resource_servers), - Rabbit1 = proplists:delete(oauth_provider_id, maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers)), - Rabbit2 = proplists:delete(oauth_provider_id, maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers)), - ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), - application:set_env(rabbitmq_auth_backend_oauth2, resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, ResourceServers1)), - Config; - -end_per_group(inheritance_group, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), - application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix), - application:unset_env(rabbitmq_auth_backend_oauth2, extra_scopes_source), - - application:unset_env(rabbitmq_auth_backend_oauth2, key_config), - - application:unset_env(rabbitmq_auth_backend_oauth2, resource_servers), - Config; - -end_per_group(with_root_scope_prefix, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix), - Config; - -end_per_group(_any, Config) -> - Config. - -init_per_testcase(get_preferred_username_claims, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, preferred_username_claims, [<<"username">>]), - Config; - -init_per_testcase(get_additional_scopes_key_when_not_defined, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, extra_scopes_source), - Config; -init_per_testcase(is_verify_aud_when_is_false, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, verify_aud, false), - Config; -init_per_testcase(get_empty_scope_prefix, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, scope_prefix, <<"">>), - Config; -init_per_testcase(get_scope_prefix_when_not_defined, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, scope_prefix), - Config; -init_per_testcase(get_resource_server_type_when_not_defined, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_type), - Config; -init_per_testcase(has_scope_aliases_when_not_defined, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, scope_aliases), - Config; - -init_per_testcase(_TestCase, Config) -> - Config. - -end_per_testcase(get_preferred_username_claims, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, preferred_username_claims), - Config; - - -end_per_testcase(_Testcase, Config) -> - Config. - -%% ----- - -call_set_env(Config, Par, Value) -> - rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, Par, Value]). - -call_get_env(Config, Par, Def) -> - rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, Par, Def]). - -call_add_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_key, Args). - -call_get_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_keys, Args). -call_get_signing_keys(Config) -> - call_get_signing_keys(Config, []). - -call_get_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_key, Args). - -call_add_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_keys, Args). - -call_replace_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, replace_signing_keys, Args). - - -add_signing_keys_for_root_oauth_provider(Config) -> - #{<<"mykey-1">> := <<"some key 1">>} = - call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), - #{<<"mykey-1">> := <<"some key 1">>} = - call_get_signing_keys(Config), - - #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = - call_add_signing_key(Config, [<<"mykey-2">>, <<"some key 2">>]), - #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = - call_get_signing_keys(Config), - - ?assertEqual(<<"some key 1">>, - call_get_signing_key(Config, [<<"mykey-1">>])). - -add_signing_keys_for_specific_oauth_provider(Config) -> - #{<<"mykey-3-1">> := <<"some key 3-1">>} = - call_add_signing_key(Config, - [<<"mykey-3-1">>, <<"some key 3-1">>, <<"my-oauth-provider-3">>]), - #{<<"mykey-4-1">> := <<"some key 4-1">>} = - call_add_signing_key(Config, - [<<"mykey-4-1">>, <<"some key 4-1">>, <<"my-oauth-provider-4">>]), - #{<<"mykey-3-1">> := <<"some key 3-1">>} = - call_get_signing_keys(Config, [<<"my-oauth-provider-3">>]), - #{<<"mykey-4-1">> := <<"some key 4-1">>} = - call_get_signing_keys(Config, [<<"my-oauth-provider-4">>]), - - #{<<"mykey-3-1">> := <<"some key 3-1">>, - <<"mykey-3-2">> := <<"some key 3-2">>} = - call_add_signing_key(Config, [ - <<"mykey-3-2">>, <<"some key 3-2">>, <<"my-oauth-provider-3">>]), - - #{<<"mykey-1">> := <<"some key 1">>} = - call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), - #{<<"mykey-1">> := <<"some key 1">>} = - call_get_signing_keys(Config, []), - - ?assertEqual(<<"some key 3-1">>, - call_get_signing_key(Config, [<<"mykey-3-1">> , <<"my-oauth-provider-3">>])). - -replace_merge_root_static_keys_with_newly_added_keys(Config) -> - NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys]), - #{ <<"mykey-root-1">> := <<"some key root-1">>, - <<"mykey-root-2">> := <<"some key root-2">>, - <<"key-2">> := <<"some key 2">>, - <<"key-3">> := <<"some key 3">> - } = call_get_signing_keys(Config). -replace_merge_static_keys_with_newly_added_keys(Config) -> - NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys, <<"A">>]), - #{ <<"mykey-root-1">> := <<"some key root-1">>, - <<"mykey-root-2">> := <<"some key root-2">>, - <<"key-2">> := <<"some key 2">>, - <<"key-3">> := <<"some key 3">> - } = call_get_signing_keys(Config, [<<"A">>]). -replace_override_root_static_keys_with_newly_added_keys(Config) -> - NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys]), - #{ <<"mykey-root-1">> := <<"new key root-1">>, - <<"mykey-root-2">> := <<"some key root-2">>, - <<"key-3">> := <<"some key 3">> - } = call_get_signing_keys(Config). -replace_override_static_keys_with_newly_added_keys(Config) -> - NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys, <<"A">>]), - #{ <<"mykey-root-1">> := <<"new key root-1">>, - <<"mykey-root-2">> := <<"some key root-2">>, - <<"key-3">> := <<"some key 3">> - } = call_get_signing_keys(Config, [<<"A">>]). - -replace_signing_keys_for_root_oauth_provider(Config) -> - call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), - NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys]), - #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = - call_get_signing_keys(Config). - -replace_signing_keys_for_specific_oauth_provider(Config) -> - OAuthProviderId = <<"my-oauth-provider-3">>, - #{<<"mykey-3-1">> := <<"some key 3-1">>} = - call_add_signing_key(Config, - [<<"mykey-3-1">>, <<"some key 3-1">>, OAuthProviderId]), - NewKeys = #{<<"key-2">> => <<"some key 2">>, - <<"key-3">> => <<"some key 3">>}, - call_replace_signing_keys(Config, [NewKeys, OAuthProviderId]), - #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = - call_get_signing_keys(Config, [OAuthProviderId]). - - -get_default_resource_server_id_returns_error(_Config) -> - {error, _} = rabbit_oauth2_config:get_default_resource_server_id(). - -get_resource_server_id_for_rabbit_audience_returns_rabbit(_Config) -> - ?assertEqual(?RABBITMQ, rabbit_oauth2_config:get_resource_server_id_for_audience(?RABBITMQ)). -get_resource_server_id_for_none_audience_returns_rabbit(_Config) -> - ?assertEqual(?RABBITMQ, rabbit_oauth2_config:get_resource_server_id_for_audience(none)). -get_resource_server_id_for_unknown_audience_returns_rabbit(_Config) -> - ?assertEqual(?RABBITMQ, rabbit_oauth2_config:get_resource_server_id_for_audience(<<"unknown">>)). - -get_resource_server_id_for_none_audience_should_fail(_Config) -> - ?assertEqual({error, no_matching_aud_found}, rabbit_oauth2_config:get_resource_server_id_for_audience(none)). -get_resource_server_id_for_unknown_audience_should_fail(_Config) -> - ?assertEqual({error, no_matching_aud_found}, rabbit_oauth2_config:get_resource_server_id_for_audience(<<"unknown">>)). - -get_default_resource_server_id(_Config) -> - ?assertEqual(?RABBITMQ, rabbit_oauth2_config:get_default_resource_server_id()). - -get_allowed_resource_server_ids_returns_empty_list(_Config) -> - [] = rabbit_oauth2_config:get_allowed_resource_server_ids(). - -get_allowed_resource_server_ids_returns_resource_server_id(_Config) -> - [?RABBITMQ] = rabbit_oauth2_config:get_allowed_resource_server_ids(). - -get_allowed_resource_server_ids_returns_all_resource_servers_ids(_Config) -> - [ <<"rabbitmq1">>, <<"rabbitmq2">>, ?RABBITMQ] = rabbit_oauth2_config:get_allowed_resource_server_ids(). - -get_allowed_resource_server_ids_returns_resource_servers_ids(_Config) -> - [<<"rabbitmq-0">>, <<"rabbitmq-1">>, <<"rabbitmq1">>, <<"rabbitmq2">> ] = - lists:sort(rabbit_oauth2_config:get_allowed_resource_server_ids()). - -index_resource_servers_by_id_else_by_key(_Config) -> - {error, no_matching_aud_found} = rabbit_oauth2_config:find_audience_in_resource_server_ids(<<"0">>), - {ok, <<"rabbitmq-0">>} = rabbit_oauth2_config:find_audience_in_resource_server_ids([<<"rabbitmq-0">>]), - {ok, <<"rabbitmq-0">>} = rabbit_oauth2_config:find_audience_in_resource_server_ids(<<"rabbitmq-0">>). - -find_audience_in_resource_server_ids_returns_key_not_found(_Config) -> - {error, no_matching_aud_found} = rabbit_oauth2_config:find_audience_in_resource_server_ids(?RABBITMQ). - -find_audience_in_resource_server_ids_returns_found_too_many(_Config) -> - {error, only_one_resource_server_as_audience_found_many} = rabbit_oauth2_config:find_audience_in_resource_server_ids([?RABBITMQ, <<"rabbitmq1">>]). - -find_audience_in_resource_server_ids_found_one_resource_servers(_Config) -> - {ok, <<"rabbitmq1">>} = rabbit_oauth2_config:find_audience_in_resource_server_ids(<<"rabbitmq1">>), - {ok, <<"rabbitmq1">>} = rabbit_oauth2_config:find_audience_in_resource_server_ids([<<"rabbitmq1">>, <<"other">>]). - -find_audience_in_resource_server_ids_found_resource_server_id(_Config) -> - {ok, ?RABBITMQ} = rabbit_oauth2_config:find_audience_in_resource_server_ids(?RABBITMQ), - {ok, ?RABBITMQ} = rabbit_oauth2_config:find_audience_in_resource_server_ids([?RABBITMQ, <<"other">>]). - -find_audience_in_resource_server_ids_using_binary_audience(_Config) -> - {ok, ?RABBITMQ} = rabbit_oauth2_config:find_audience_in_resource_server_ids(<<"rabbitmq other">>). - -get_additional_scopes_key(_Config) -> - ?assertEqual({ok, <<"roles">>}, rabbit_oauth2_config:get_additional_scopes_key()), - ?assertEqual({ok, <<"extra-scope-1">>}, rabbit_oauth2_config:get_additional_scopes_key(<<"rabbitmq1">> )), - ?assertEqual(rabbit_oauth2_config:get_additional_scopes_key(), rabbit_oauth2_config:get_additional_scopes_key(<<"rabbitmq2">>)), - ?assertEqual({ok, <<"roles">>}, rabbit_oauth2_config:get_additional_scopes_key(?RABBITMQ)). - -get_additional_scopes_key_when_not_defined(_Config) -> - ?assertEqual({error, not_found}, rabbit_oauth2_config:get_additional_scopes_key()), - ?assertEqual(rabbit_oauth2_config:get_additional_scopes_key(), rabbit_oauth2_config:get_additional_scopes_key(<<"rabbitmq2">>)). - -is_verify_aud(_Config) -> - ?assertEqual(true, rabbit_oauth2_config:is_verify_aud()), - ?assertEqual(rabbit_oauth2_config:is_verify_aud(?RABBITMQ), rabbit_oauth2_config:is_verify_aud()), - ?assertEqual(false, rabbit_oauth2_config:is_verify_aud(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:is_verify_aud(), rabbit_oauth2_config:is_verify_aud(<<"rabbitmq2">>)). -is_verify_aud_for_resource_one_returns_false(_Config) -> - ?assertEqual(false, rabbit_oauth2_config:is_verify_aud(?RABBITMQ_RESOURCE_ONE)). - -is_verify_aud_for_resource_two_returns_true(_Config) -> - ?assertEqual(true, rabbit_oauth2_config:is_verify_aud(?RABBITMQ_RESOURCE_TWO)). - -is_verify_aud_when_is_false(_Config) -> - ?assertEqual(false, rabbit_oauth2_config:is_verify_aud()), - ?assertEqual(rabbit_oauth2_config:is_verify_aud(), rabbit_oauth2_config:is_verify_aud(<<"rabbitmq2">>)). - -is_verify_aud_for_resource_one_returns_true(_Config) -> - ?assertEqual(true, rabbit_oauth2_config:is_verify_aud(?RABBITMQ_RESOURCE_ONE)). -is_verify_aud_for_resource_two_returns_false(_Config) -> - ?assertEqual(false, rabbit_oauth2_config:is_verify_aud(?RABBITMQ_RESOURCE_TWO)). - -get_default_preferred_username_claims(_Config) -> - ?assertEqual(rabbit_oauth2_config:get_default_preferred_username_claims(), rabbit_oauth2_config:get_preferred_username_claims()). - -get_preferred_username_claims(_Config) -> - ?assertEqual([<<"username">>] ++ rabbit_oauth2_config:get_default_preferred_username_claims(), - rabbit_oauth2_config:get_preferred_username_claims()), - ?assertEqual([<<"email-address">>] ++ rabbit_oauth2_config:get_default_preferred_username_claims(), - rabbit_oauth2_config:get_preferred_username_claims(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:get_preferred_username_claims(), - rabbit_oauth2_config:get_preferred_username_claims(<<"rabbitmq2">>)). - -get_scope_prefix_when_not_defined(_Config) -> - ?assertEqual(<<"rabbitmq.">>, rabbit_oauth2_config:get_scope_prefix()), - ?assertEqual(<<"rabbitmq2.">>, rabbit_oauth2_config:get_scope_prefix(<<"rabbitmq2">>)). - -get_empty_scope_prefix(_Config) -> - ?assertEqual(<<"">>, rabbit_oauth2_config:get_scope_prefix()), - ?assertEqual(<<"">>, rabbit_oauth2_config:get_scope_prefix(<<"rabbitmq2">>)). - -get_scope_prefix(_Config) -> - ?assertEqual(<<"some-prefix-">>, rabbit_oauth2_config:get_scope_prefix()), - ?assertEqual(<<"my-prefix:">>, rabbit_oauth2_config:get_scope_prefix(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:get_scope_prefix(), rabbit_oauth2_config:get_scope_prefix(<<"rabbitmq2">>)). - -get_scope_prefix_for_resource_one_returns_default_scope_prefix(_Config) -> - ?assertEqual(undefined, application:get_env(rabbitmq_auth_backend_oauth2, scope_prefix)), - ?assertEqual(append_paths(?RABBITMQ_RESOURCE_ONE, <<".">>), - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_ONE)). -get_scope_prefix_for_resource_one_returns_root_scope_prefix(_Config) -> - {ok, Prefix} = application:get_env(rabbitmq_auth_backend_oauth2, scope_prefix), - ?assertEqual(rabbit_oauth2_config:get_scope_prefix(), - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_ONE)), - ?assertEqual(Prefix, - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_ONE)). -get_scope_prefix_for_resource_one_returns_empty_scope_prefix(_Config) -> - ?assertEqual(<<"">>, - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_ONE)). -get_scope_prefix_for_resource_two_returns_root_scope_prefix(_Config) -> - {ok, Prefix} = application:get_env(rabbitmq_auth_backend_oauth2, scope_prefix), - ?assertEqual(rabbit_oauth2_config:get_scope_prefix(), - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_TWO)), - ?assertEqual(Prefix, - rabbit_oauth2_config:get_scope_prefix(?RABBITMQ_RESOURCE_TWO)). - -get_resource_server_type_when_not_defined(_Config) -> - ?assertEqual(<<>>, rabbit_oauth2_config:get_resource_server_type()), - ?assertEqual(<<>>, rabbit_oauth2_config:get_resource_server_type(<<"rabbitmq2">>)). - -get_resource_server_type(_Config) -> - ?assertEqual(<<"rabbitmq-type">>, rabbit_oauth2_config:get_resource_server_type()), - ?assertEqual(<<"my-type">>, rabbit_oauth2_config:get_resource_server_type(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:get_resource_server_type(), rabbit_oauth2_config:get_resource_server_type(<<"rabbitmq2">>)). - -has_scope_aliases_when_not_defined(_Config) -> - ?assertEqual(false, rabbit_oauth2_config:has_scope_aliases(?RABBITMQ)), - ?assertEqual(true, rabbit_oauth2_config:has_scope_aliases(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:has_scope_aliases(?RABBITMQ), rabbit_oauth2_config:has_scope_aliases(<<"rabbitmq2">>)). - -has_scope_aliases(_Config) -> - ?assertEqual(true, rabbit_oauth2_config:has_scope_aliases(?RABBITMQ)), - ?assertEqual(true, rabbit_oauth2_config:has_scope_aliases(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:has_scope_aliases(?RABBITMQ), rabbit_oauth2_config:has_scope_aliases(<<"rabbitmq2">>)). - -get_scope_aliases(_Config) -> - ?assertEqual(#{}, rabbit_oauth2_config:get_scope_aliases(?RABBITMQ)), - ?assertEqual(#{}, rabbit_oauth2_config:get_scope_aliases(<<"rabbitmq1">>)), - ?assertEqual(rabbit_oauth2_config:get_scope_aliases(?RABBITMQ), rabbit_oauth2_config:get_scope_aliases(<<"rabbitmq2">>)). - -get_default_key_should_fail(_Config) -> - {error, no_default_key_configured} = rabbit_oauth2_config:get_default_key(). -get_default_key(_Config) -> - {ok, <<"default-key">>} = rabbit_oauth2_config:get_default_key(). -get_default_key_for_provider_A_should_fail(_Config) -> - {error, no_default_key_configured} = rabbit_oauth2_config:get_default_key(<<"A">>). -get_default_key_for_provider_A(_Config) -> - {ok, <<"A-default-key">>} = rabbit_oauth2_config:get_default_key(<<"A">>). - -get_signing_keys(_Config) -> - #{<<"mykey-1-1">> := <<"some key 1-1">>, - <<"mykey-1-2">> := <<"some key 1-2">>} = rabbit_oauth2_config:get_signing_keys(), - <<"some key 1-1">> = rabbit_oauth2_config:get_signing_key(<<"mykey-1-1">>), - undefined = rabbit_oauth2_config:get_signing_key(<<"unknown">>). -get_signing_keys_for_oauth_provider_A(_Config) -> - #{<<"A-mykey-1-1">> := <<"A-some key 1-1">>, - <<"A-mykey-1-2">> := <<"A-some key 1-2">>} = rabbit_oauth2_config:get_signing_keys(<<"A">>), - <<"A-some key 1-1">> = rabbit_oauth2_config:get_signing_key(<<"A-mykey-1-1">>, <<"A">>), - undefined = rabbit_oauth2_config:get_signing_key(<<"unknown">>, <<"A">>). - -get_algorithms_should_return_undefined(_Config) -> - undefined = rabbit_oauth2_config:get_algorithms(). -get_algorithms(Config) -> - ?assertEqual(?config(algorithms, Config), rabbit_oauth2_config:get_algorithms()). -get_algorithms_for_provider_A_should_return_undefined(_Config) -> - undefined = rabbit_oauth2_config:get_algorithms(<<"A">>). -get_algorithms_for_provider_A(Config) -> - ?assertEqual(?config(algorithms, Config), rabbit_oauth2_config:get_algorithms(<<"A">>)). - -get_oauth_provider_root_with_jwks_uri_should_fail(_Config) -> - root = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {error, _Message} = rabbit_oauth2_config:get_oauth_provider(root, [jwks_uri]). -get_oauth_provider_A_with_jwks_uri_should_fail(_Config) -> - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {error, _Message} = rabbit_oauth2_config:get_oauth_provider(<<"A">>, [jwks_uri]). -get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri(_Config) -> - root = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri). -get_oauth_provider_for_both_resources_should_return_root_oauth_provider(_Config) -> - root = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_ONE), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri), - root = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_TWO). -get_oauth_provider_for_resource_one_should_return_oauth_provider_A(_Config) -> - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_ONE), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). -get_oauth_provider_for_both_resources_should_return_oauth_provider_A(_Config) -> - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_ONE), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri), - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_TWO). -get_oauth_provider_for_resource_two_should_return_oauth_provider_B(_Config) -> - <<"B">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ_RESOURCE_TWO), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). - -get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints(_Config) -> - root = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/">>), OAuthProvider#oauth_provider.issuer). -append_paths(Path1, Path2) -> - erlang:iolist_to_binary([Path1, Path2]). - -get_oauth_provider_should_return_oauth_provider_B_with_jwks_uri(_Config) -> - <<"B">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). - -get_oauth_provider_should_return_oauth_provider_B_with_all_discovered_endpoints(_Config) -> - <<"B">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/B">>), OAuthProvider#oauth_provider.issuer). - -get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri(_Config) -> - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). - -get_oauth_provider_should_return_oauth_provider_A_with_all_discovered_endpoints(_Config) -> - <<"A">> = rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(?RABBITMQ), - {ok, OAuthProvider} = rabbit_oauth2_config:get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/A">>), OAuthProvider#oauth_provider.issuer). - -get_openid_configuration_expectations() -> - [ {get_root_openid_configuration, - - #{request => #{ - method => <<"GET">>, - path => <<"/.well-known/openid-configuration">> - }, - response => [ - {code, 200}, - {content_type, ?CONTENT_JSON}, - {payload, [ - {issuer, build_url_to_oauth_provider(<<"/">>) }, - {jwks_uri, build_url_to_oauth_provider(<<"/keys">>)} - ]} - ] - } - }, - {get_A_openid_configuration, - - #{request => #{ - method => <<"GET">>, - path => <<"/A/.well-known/openid-configuration">> - }, - response => [ - {code, 200}, - {content_type, ?CONTENT_JSON}, - {payload, [ - {issuer, build_url_to_oauth_provider(<<"/A">>) }, - {jwks_uri, build_url_to_oauth_provider(<<"/A/keys">>)} - ]} - ] - } - }, - {get_B_openid_configuration, - - #{request => #{ - method => <<"GET">>, - path => <<"/B/.well-known/openid-configuration">> - }, - response => [ - {code, 200}, - {content_type, ?CONTENT_JSON}, - {payload, [ - {issuer, build_url_to_oauth_provider(<<"/B">>) }, - {jwks_uri, build_url_to_oauth_provider(<<"/B/keys">>)} - ]} - ] - } - } - ]. - -start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations) -> - Dispatch = cowboy_router:compile([ - {'_', [{Path, oauth2_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} - ]), - ct:log("start_https_oauth_server (port:~p) with expectation list : ~p -> dispatch: ~p", [Port, Expectations, Dispatch]), - {ok, Pid} = cowboy:start_tls( - mock_http_auth_listener, - [{port, Port}, - {certfile, filename:join([CertsDir, "server", "cert.pem"])}, - {keyfile, filename:join([CertsDir, "server", "key.pem"])} - ], - #{env => #{dispatch => Dispatch}}), - ct:log("Started on Port ~p and pid ~p", [ranch:get_port(mock_http_auth_listener), Pid]). - -build_url_to_oauth_provider(Path) -> - uri_string:recompose(#{scheme => "https", - host => "localhost", - port => rabbit_data_coercion:to_integer(?AUTH_PORT), - path => Path}). - -stop_http_auth_server() -> - cowboy:stop_listener(mock_http_auth_listener). - --spec ssl_options(ssl:verify_type(), boolean(), file:filename()) -> list(). -ssl_options(PeerVerification, FailIfNoPeerCert, CaCertFile) -> - [{verify, PeerVerification}, - {depth, 10}, - {fail_if_no_peer_cert, FailIfNoPeerCert}, - {crl_check, false}, - {crl_cache, {ssl_crl_cache, {internal, [{http, 10000}]}}}, - {cacertfile, CaCertFile}]. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl new file mode 100644 index 000000000000..6305a553832b --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl @@ -0,0 +1,660 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_oauth_provider_SUITE). + +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("oauth2_client/include/oauth2_client.hrl"). + +-define(RABBITMQ,<<"rabbitmq">>). +-define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). +-define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). +-define(AUTH_PORT, 8000). + +-import(rabbit_oauth2_oauth_provider, [ + get_internal_oauth_provider/2, + add_signing_key/2, add_signing_key/3, replace_signing_keys/1, + replace_signing_keys/2, + get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2 +]). +-import(oauth2_client, [get_oauth_provider/2]). + +all() -> [ + {group, with_rabbitmq_node}, + {group, with_resource_server_id}, + {group, with_resource_servers} +]. +groups() -> [ + {with_rabbitmq_node, [], [ + add_signing_keys_for_specific_oauth_provider, + add_signing_keys_for_root_oauth_provider, + + replace_signing_keys_for_root_oauth_provider, + replace_signing_keys_for_specific_oauth_provider, + {with_root_static_signing_keys, [], [ + replace_merge_root_static_keys_with_newly_added_keys, + replace_override_root_static_keys_with_newly_added_keys + ]}, + {with_static_signing_keys_for_specific_oauth_provider, [], [ + replace_merge_static_keys_with_newly_added_keys, + replace_override_static_keys_with_newly_added_keys + ]} + ]}, + {verify_oauth_provider_A, [], [ + internal_oauth_provider_A_has_no_default_key, + {oauth_provider_A_with_default_key, [], [ + internal_oauth_provider_A_has_default_key + ]}, + internal_oauth_provider_A_has_no_algorithms, + {oauth_provider_A_with_algorithms, [], [ + internal_oauth_provider_A_has_algorithms + ]}, + oauth_provider_A_with_jwks_uri_returns_error, + {oauth_provider_A_with_jwks_uri, [], [ + oauth_provider_A_has_jwks_uri + ]}, + {oauth_provider_A_with_issuer, [], [ + {oauth_provider_A_with_jwks_uri, [], [ + oauth_provider_A_has_jwks_uri + ]}, + oauth_provider_A_has_to_discover_jwks_uri_endpoint + ]} + ]}, + {verify_oauth_provider_root, [], [ + internal_oauth_provider_root_has_no_default_key, + {with_default_key, [], [ + internal_oauth_provider_root_has_default_key + ]}, + internal_oauth_provider_root_has_no_algorithms, + {with_algorithms, [], [ + internal_oauth_provider_root_has_algorithms + ]}, + oauth_provider_root_with_jwks_uri_returns_error, + {with_jwks_uri, [], [ + oauth_provider_root_has_jwks_uri + ]}, + {with_issuer, [], [ + {with_jwks_uri, [], [ + oauth_provider_root_has_jwks_uri + ]}, + oauth_provider_root_has_to_discover_jwks_uri_endpoint + ]} + ]} +]. + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config). + +end_per_suite(Config) -> + rabbit_ct_helpers:run_teardown_steps(Config). + +init_per_group(with_rabbitmq_node, Config) -> + Config1 = rabbit_ct_helpers:set_config(Config, [ + {rmq_nodename_suffix, with_rabbitmq_node}, + {rmq_nodes_count, 1} + ]), + rabbit_ct_helpers:run_steps(Config1, rabbit_ct_broker_helpers:setup_steps()); +init_per_group(with_default_key, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, proplists:delete(default_key, KeyConfig) ++ + [{default_key,<<"default-key">>}]), + Config; +init_per_group(with_root_static_signing_keys, Config) -> + KeyConfig = call_get_env(Config, key_config, []), + SigningKeys = #{ + <<"mykey-root-1">> => <<"some key root-1">>, + <<"mykey-root-2">> => <<"some key root-2">> + }, + call_set_env(Config, key_config, + proplists:delete(default_key, KeyConfig) ++ [{signing_keys,SigningKeys}]), + Config; +init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> + OAuthProviders = call_get_env(Config, oauth_providers, #{}), + OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), + SigningKeys = #{ + <<"mykey-root-1">> => <<"some key root-1">>, + <<"mykey-root-2">> => <<"some key root-2">> + }, + OAuthProvider1 = proplists:delete(signing_keys, OAuthProvider) ++ + [{signing_keys, SigningKeys}], + + call_set_env(Config, oauth_providers, maps:put(<<"A">>, OAuthProvider1, + OAuthProviders)), + Config; + + +init_per_group(with_jwks_url, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, KeyConfig ++ [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), + [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; + +init_per_group(with_issuer, Config) -> + {ok, _} = application:ensure_all_started(inets), + {ok, _} = application:ensure_all_started(ssl), + application:ensure_all_started(cowboy), + CertsDir = ?config(rmq_certsdir, Config), + CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), + SslOptions = ssl_options(verify_peer, false, CaCertFile), + + HttpOauthServerExpectations = get_openid_configuration_expectations(), + ListOfExpectations = maps:values(proplists:to_map(HttpOauthServerExpectations)), + + start_https_oauth_server(?AUTH_PORT, CertsDir, ListOfExpectations), + set_env(use_global_locks, false), + set_env(issuer, + build_url_to_oauth_provider(<<"/">>)), + KeyConfig = get_env(key_config, []), + set_env(key_config, + KeyConfig ++ SslOptions), + + [{key_config_before_group_with_issuer, KeyConfig}, + {ssl_options, SslOptions} | Config]; + +init_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> + set_env(oauth_providers, + #{<<"A">> => [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {jwks_uri,build_url_to_oauth_provider(<<"/A/keys">>) } + ] } ), + Config; + +init_per_group(with_oauth_providers_A_with_issuer, Config) -> + set_env(oauth_providers, + #{<<"A">> => [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {https, ?config(ssl_options, Config)} + ] } ), + Config; + +init_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> + set_env(oauth_providers, + #{ <<"A">> => [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/A/keys">>)} + ], + <<"B">> => [ + {issuer, build_url_to_oauth_provider(<<"/B">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/B/keys">>)} + ] }), + Config; + +init_per_group(with_oauth_providers_A_B_with_issuer, Config) -> + set_env(oauth_providers, + #{ <<"A">> => [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {https, ?config(ssl_options, Config)} + ], + <<"B">> => [ + {issuer, build_url_to_oauth_provider(<<"/B">>) }, + {https, ?config(ssl_options, Config)} + ] }), + Config; + +init_per_group(with_default_oauth_provider_A, Config) -> + set_env(default_oauth_provider, <<"A">>), + Config; + +init_per_group(with_default_oauth_provider_B, Config) -> + set_env(default_oauth_provider, <<"B">>), + Config; + +init_per_group(with_resource_server_id, Config) -> + set_env(resource_server_id, ?RABBITMQ), + Config; + +init_per_group(with_algorithms, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, KeyConfig ++ [{algorithms, [<<"HS256">>, <<"RS256">>]}]), + [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; + +init_per_group(with_algorithms_for_provider_A, Config) -> + OAuthProviders = get_env(oauth_providers, #{}), + OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), + set_env(oauth_providers, maps:put(<<"A">>, + [{algorithms, [<<"HS256">>, <<"RS256">>]} | OAuthProvider], OAuthProviders)), + [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; + +init_per_group(with_different_oauth_provider_for_each_resource, Config) -> + {ok, ResourceServers} = get_env(resource_servers), + Rabbit1 = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers) ++ + [ {oauth_provider_id, <<"A">>} ], + Rabbit2 = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers) ++ + [ {oauth_provider_id, <<"B">>} ], + ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), + set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, + ResourceServers1)), + Config; + +init_per_group(with_resource_servers, Config) -> + set_env(resource_servers, + #{?RABBITMQ_RESOURCE_ONE => [ + { key_config, [ + {jwks_url,<<"https://oauth-for-rabbitmq1">> } + ]} + ], + ?RABBITMQ_RESOURCE_TWO => [ + { key_config, [ + {jwks_url,<<"https://oauth-for-rabbitmq2">> } + ]} + ], + <<"0">> => [ {id, <<"rabbitmq-0">> } ], + <<"1">> => [ {id, <<"rabbitmq-1">> } ] + + }), + Config; + +init_per_group(verify_oauth_provider_A, Config) -> + set_env(oauth_providers, + #{ <<"A">> => [ + {id, <<"A">>} + ] + ] }), + Config; + +init_per_group(_any, Config) -> + Config. + +end_per_group(with_rabbitmq_node, Config) -> + rabbit_ct_helpers:run_steps(Config, rabbit_ct_broker_helpers:teardown_steps()); + +end_per_group(with_root_static_signing_keys, Config) -> + KeyConfig = call_get_env(Config, key_config, []), + call_set_env(Config, key_config, KeyConfig), + Config; + +end_per_group(with_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(with_verify_aud_false, Config) -> + unset_env(verify_aud), + Config; + +end_per_group(with_verify_aud_false_for_resource_two, Config) -> + ResourceServers = get_env(resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_TWO, + proplists:delete(verify_aud, Proplist), ResourceServers)), + Config; + +end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> + ResourceServers = get_env(resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, + proplists:delete(scope_prefix, Proplist), ResourceServers)), + Config; + +end_per_group(with_default_key, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, proplists:delete(default_key, KeyConfig)), + Config; + +end_per_group(with_algorithms, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, proplists:delete(algorithms, KeyConfig)), + Config; + +end_per_group(with_algorithms_for_provider_A, Config) -> + OAuthProviders = get_env(oauth_providers, #{}), + OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), + set_env(oauth_providers, maps:put(<<"A">>, + proplists:delete(algorithms, OAuthProvider), OAuthProviders)), + Config; + + +end_per_group(with_jwks_url, Config) -> + KeyConfig = ?config(key_config_before_group_with_jwks_url, Config), + set_env(key_config, KeyConfig), + Config; + +end_per_group(with_issuer, Config) -> + KeyConfig = ?config(key_config_before_group_with_issuer, Config), + unset_env(issuer), + set_env(key_config, KeyConfig), + stop_http_auth_server(), + Config; + +end_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_oauth_providers_A_with_issuer, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_oauth_providers_A_B_with_issuer, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_oauth_providers_A, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_oauth_providers_A_B, Config) -> + unset_env(oauth_providers), + Config; + +end_per_group(with_default_oauth_provider_B, Config) -> + unset_env(default_oauth_provider), + Config; + +end_per_group(with_default_oauth_provider_A, Config) -> + unset_env(default_oauth_provider), + Config; + +end_per_group(get_oauth_provider_for_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(with_resource_servers_and_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(with_resource_servers, Config) -> + unset_env(resource_servers), + Config; + +end_per_group(with_root_scope_prefix, Config) -> + unset_env(scope_prefix), + Config; + +end_per_group(_any, Config) -> + Config. + +%% ----- Utility functions + +call_set_env(Config, Par, Value) -> + rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, + [rabbitmq_auth_backend_oauth2, Par, Value]). + +call_get_env(Config, Par, Def) -> + rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par, Def]). + +call_add_signing_key(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_key, Args). + +call_get_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_keys, Args). + +call_get_signing_keys(Config) -> + call_get_signing_keys(Config, []). + +call_get_signing_key(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_key, Args). + +call_add_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_keys, Args). + +call_replace_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, replace_signing_keys, Args). + +%% ----- Test cases + +add_signing_keys_for_root_oauth_provider(Config) -> + #{<<"mykey-1">> := <<"some key 1">>} = + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + #{<<"mykey-1">> := <<"some key 1">>} = + call_get_signing_keys(Config), + + #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = + call_add_signing_key(Config, [<<"mykey-2">>, <<"some key 2">>]), + #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = + call_get_signing_keys(Config), + + ?assertEqual(<<"some key 1">>, + call_get_signing_key(Config, [<<"mykey-1">>])). + +add_signing_keys_for_specific_oauth_provider(Config) -> + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_add_signing_key(Config, + [<<"mykey-3-1">>, <<"some key 3-1">>, <<"my-oauth-provider-3">>]), + #{<<"mykey-4-1">> := <<"some key 4-1">>} = + call_add_signing_key(Config, + [<<"mykey-4-1">>, <<"some key 4-1">>, <<"my-oauth-provider-4">>]), + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_get_signing_keys(Config, [<<"my-oauth-provider-3">>]), + #{<<"mykey-4-1">> := <<"some key 4-1">>} = + call_get_signing_keys(Config, [<<"my-oauth-provider-4">>]), + + #{<<"mykey-3-1">> := <<"some key 3-1">>, + <<"mykey-3-2">> := <<"some key 3-2">>} = + call_add_signing_key(Config, [ + <<"mykey-3-2">>, <<"some key 3-2">>, <<"my-oauth-provider-3">>]), + + #{<<"mykey-1">> := <<"some key 1">>} = + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + #{<<"mykey-1">> := <<"some key 1">>} = + call_get_signing_keys(Config, []), + + ?assertEqual(<<"some key 3-1">>, + call_get_signing_key(Config, [<<"mykey-3-1">> , <<"my-oauth-provider-3">>])). + +replace_merge_root_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{ <<"mykey-root-1">> := <<"some key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-2">> := <<"some key 2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config). + +replace_merge_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, <<"A">>]), + #{ <<"mykey-root-1">> := <<"some key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-2">> := <<"some key 2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config, [<<"A">>]). + +replace_override_root_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{ <<"mykey-root-1">> := <<"new key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config). +replace_override_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, <<"A">>]), + #{ <<"mykey-root-1">> := <<"new key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config, [<<"A">>]). + +replace_signing_keys_for_root_oauth_provider(Config) -> + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = + call_get_signing_keys(Config). + +replace_signing_keys_for_specific_oauth_provider(Config) -> + OAuthProviderId = <<"my-oauth-provider-3">>, + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_add_signing_key(Config, + [<<"mykey-3-1">>, <<"some key 3-1">>, OAuthProviderId]), + NewKeys = #{<<"key-2">> => <<"some key 2">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, OAuthProviderId]), + #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = + call_get_signing_keys(Config, [OAuthProviderId]). + + +get_algorithms_should_return_undefined(_Config) -> + OAuthProvider = get_internal_oauth_provider(), + undefined = OAuthProvider#internal_oauth_provider.algorithms. + +get_algorithms(Config) -> + OAuthProvider = get_internal_oauth_provider(), + Algorithms = OAuthProvider#internal_oauth_provider.algorithms, + ?assertEqual(?config(algorithms, Config), Algorithms). + +get_algorithms_for_provider_A_should_return_undefined(_Config) -> + OAuthProvider = get_internal_oauth_provider(<<"A">>), + undefined = OAuthProvider#internal_oauth_provider.algorithms. + +get_algorithms_for_provider_A(Config) -> + OAuthProvider = get_internal_oauth_provider(<<"A">>), + Algorithms = OAuthProvider#internal_oauth_provider.algorithms, + ?assertEqual(?config(algorithms, Config), Algorithms). + +get_oauth_provider_root_with_jwks_uri_should_fail(_Config) -> + {error, _Message} = get_oauth_provider(root, [jwks_uri]). + +get_oauth_provider_A_with_jwks_uri_should_fail(_Config) -> + {error, _Message} = get_oauth_provider(<<"A">>, [jwks_uri]). + +get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_for_both_resources_should_return_root_oauth_provider(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_for_resource_one_should_return_oauth_provider_A(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_for_both_resources_should_return_oauth_provider_A(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_for_resource_two_should_return_oauth_provider_B(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/">>), OAuthProvider#oauth_provider.issuer). + +append_paths(Path1, Path2) -> + erlang:iolist_to_binary([Path1, Path2]). + +get_oauth_provider_should_return_oauth_provider_B_with_jwks_uri(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_should_return_oauth_provider_B_with_all_discovered_endpoints(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/B">>), OAuthProvider#oauth_provider.issuer). + +get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + +get_oauth_provider_should_return_oauth_provider_A_with_all_discovered_endpoints(_Config) -> + {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/A">>), OAuthProvider#oauth_provider.issuer). + +%% ---- Utility functions + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). +unset_env(Par) -> + application:unset_env(rabbitmq_auth_backend_oauth2, Par). + +get_openid_configuration_expectations() -> + [ {get_root_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/keys">>)} + ]} + ] + } + }, + {get_A_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/A/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/A/keys">>)} + ]} + ] + } + }, + {get_B_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/B/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/B">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/B/keys">>)} + ]} + ] + } + } + ]. + +start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations) -> + Dispatch = cowboy_router:compile([ + {'_', [{Path, oauth2_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} + ]), + ct:log("start_https_oauth_server (port:~p) with expectation list : ~p -> dispatch: ~p", [Port, Expectations, Dispatch]), + {ok, Pid} = cowboy:start_tls( + mock_http_auth_listener, + [{port, Port}, + {certfile, filename:join([CertsDir, "server", "cert.pem"])}, + {keyfile, filename:join([CertsDir, "server", "key.pem"])} + ], + #{env => #{dispatch => Dispatch}}), + ct:log("Started on Port ~p and pid ~p", [ranch:get_port(mock_http_auth_listener), Pid]). + +build_url_to_oauth_provider(Path) -> + uri_string:recompose(#{scheme => "https", + host => "localhost", + port => rabbit_data_coercion:to_integer(?AUTH_PORT), + path => Path}). + +stop_http_auth_server() -> + cowboy:stop_listener(mock_http_auth_listener). + +-spec ssl_options(ssl:verify_type(), boolean(), file:filename()) -> list(). +ssl_options(PeerVerification, FailIfNoPeerCert, CaCertFile) -> + [{verify, PeerVerification}, + {depth, 10}, + {fail_if_no_peer_cert, FailIfNoPeerCert}, + {crl_check, false}, + {crl_cache, {ssl_crl_cache, {internal, [{http, 10000}]}}}, + {cacertfile, CaCertFile}]. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl new file mode 100644 index 000000000000..20b1c0bc8d4f --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl @@ -0,0 +1,525 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_resource_server_SUITE). + +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("oauth2_client/include/oauth2_client.hrl"). + +-define(RABBITMQ,<<"rabbitmq">>). +-define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). +-define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). +-define(OAUTH_PROVIDER_A,<<"A">>). +-define(OAUTH_PROVIDER_B,<<"B">>). + +-import(oauth2_client, [get_oauth_provider/2]). +-import(rabbit_oauth2_resource_server, [ + resolve_resource_server_id_from_audience/1, + get_resource_server/1 +]). + + +all() -> [ + {group, without_resource_server_id}, + {group, with_rabbitmq_as_resource_server_id}, + {group, with_two_resource_servers}, + {group, with_two_resource_servers_and_rabbitmq_as_resource_server_id} +]. +groups() -> [ + + {verify_get_rabbitmq_server_configuration, [], [ + rabbitmq_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq_verify_aud_is_false + ]}, + rabbitmq_has_no_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq_has_scope_prefix + ]}, + {with_empty_scope_prefix, [], [ + rabbitmq_has_empty_scope_prefix + ]}, + rabbitmq_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq_oauth_provider_id_is_A + ]}, + rabbitmq_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq_has_additional_scopes_key + ]}, + rabbitmq_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq_has_preferred_username_claims_plus_default + ]}, + rabbitmq_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq_has_scope_aliases + ]} + ]}, + {with_rabbitmq_as_resource_server_id, [], [ + resolve_resource_server_for_rabbitmq_audience, + resolve_resource_server_for_rabbitmq_plus_unknown_audience, + resolve_resource_server_for_none_audience_returns_error, + resolve_resource_server_for_unknown_audience_returns_error, + {with_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_rabbitmq, + resolve_resource_server_for_unknown_audience_returns_rabbitmq + ]}, + {group, verify_get_rabbitmq_server_configuration} + ]}, + {without_resource_server_id, [], [ + resolve_resource_server_id_for_any_audience_returns_error + ]}, + {verify_configuration_inheritance_with_rabbitmq2, [], [ + rabbitmq2_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq2_verify_aud_is_false + ]}, + rabbitmq2_has_no_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq2_has_scope_prefix + ]}, + rabbitmq2_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq2_oauth_provider_id_is_A + ]}, + rabbitmq2_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq2_has_additional_scopes_key + ]}, + rabbitmq2_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq2_has_preferred_username_claims_plus_default + ]}, + rabbitmq2_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq2_has_scope_aliases + ]} + ]}, + {with_two_resource_servers, [], [ + resolve_resource_server_id_for_rabbitmq1, + resolve_resource_server_id_for_rabbitmq2, + resolve_resource_server_id_for_both_resources_returns_error, + resolve_resource_server_for_none_audience_returns_error, + resolve_resource_server_for_unknown_audience_returns_error, + {with_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_rabbitmq1, + resolve_resource_server_for_unknown_audience_returns_rabbitmq1, + {with_rabbitmq1_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_error + ]} + ]}, + {group, verify_rabbitmq1_server_configuration}, + {group, verify_configuration_inheritance_with_rabbitmq2}, + {with_rabbitmq_as_resource_server_id, [], [ + resolve_resource_server_for_rabbitmq_audience, + resolve_resource_server_id_for_rabbitmq1, + resolve_resource_server_id_for_rabbitmq2 + ]} + ]} +]. + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config). + +end_per_suite(Config) -> + rabbit_ct_helpers:run_teardown_steps(Config). + +init_per_group(with_jwks_url, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, KeyConfig ++ + [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), + [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; + +init_per_group(with_default_oauth_provider_A, Config) -> + set_env(default_oauth_provider, ?OAUTH_PROVIDER_A), + Config; + +init_per_group(with_default_oauth_provider_B, Config) -> + set_env(default_oauth_provider, ?OAUTH_PROVIDER_B), + Config; + +init_per_group(with_rabbitmq_as_resource_server_id, Config) -> + set_env(resource_server_id, ?RABBITMQ), + Config; + +init_per_group(with_scope_prefix, Config) -> + Prefix = <<"some-prefix:">>, + set_env(scope_prefix, Prefix), + [{scope_prefix, Prefix} | Config]; + +init_per_group(with_empty_scope_prefix, Config) -> + Prefix = <<"">>, + set_env(scope_prefix, Prefix), + Config; + +init_per_group(with_additional_scopes_key, Config) -> + Key = <<"roles">>, + set_env(additional_scopes_key, Key), + [{additional_scopes_key, Prefix} | Config; + +init_per_group(with_preferred_username_claims, Config) -> + Claims = [<<"new-user">>, <<"new-email">>], + set_env(preferred_username_claims, Key), + [{preferred_username_claims, Claims} | Config; + + +init_per_group(with_scope_aliases, Config) -> + Aliases = #{ + <<"admin">> -> [<<"rabbitmq.tag:administrator">>] + }, + set_env(scope_aliases, Aliases), + [{scope_aliases, Aliases} | Config; + +init_per_group(with_empty_scope_prefix_for_resource_one, Config) -> + ResourceServers = get_env(resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, + [{scope_prefix, <<"">>} | proplists:delete(scope_prefix, Proplist)], + ResourceServers)), + Config; + +init_per_group(with_verify_aud_false, Config) -> + set_env(verify_aud, false), + Config; + +init_per_group(with_rabbitmq2_verify_aud_false, Config) -> + ResourceServers = get_env(resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_TWO, + [{verify_aud, false} | proplists:delete(verify_aud, Proplist)], + ResourceServers)), + Config; + +init_per_group(with_two_resource_servers_and_rabbitmq_as_resource_server_id, Config) -> + set_env(resource_server_id, ?RABBITMQ), + set_env(key_config, [{jwks_url,<<"https://oauth-for-rabbitmq">> }]), + set_env(resource_servers, + #{?RABBITMQ_RESOURCE_ONE => [ + { key_config, [ + {jwks_url,<<"https://oauth-for-rabbitmq1">> } + ]} + + ], + ?RABBITMQ_RESOURCE_TWO => [ + { key_config, [ + {jwks_url,<<"https://oauth-for-rabbitmq2">> } + ]} + ] + }), + Config; + +init_per_group(with_different_oauth_provider_for_each_resource, Config) -> + {ok, ResourceServers} = get_env(resource_servers), + Rabbit1 = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers) ++ + [ {oauth_provider_id, <<"A">>} ], + Rabbit2 = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers) ++ + [ {oauth_provider_id, <<"B">>} ], + ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), + set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, + ResourceServers1)), + Config; + +init_per_group(with_two_resource_servers, Config) -> + RabbitMQ1 = [ + {id, ?RABBITMQ_RESOURCE_ONE}, + {resource_server_type, <<"some-type">>}, + {verify_aud, false}, + {scope_prefix, <<"some-prefix">>}, + {additional_scopes_key, <<"roles">>}, + {preferred_username_claims, [<<"x-username">>, <<"x-email">>]}, + {scope_aliases, #{ <<"admin">> -> [<<"rabbitmq.tag:administrator"]}, + {oauth_provider_id, ?OAUTH_PROVIDER_A} + ], + RabbitMQ2 = [ + {id, ?RABBITMQ_RESOURCE_ONE} + ], + set_env(resource_servers, #{ + ?RABBITMQ_RESOURCE_ONE => RabbitMQ1, + ?RABBITMQ_RESOURCE_TWO => RabbitMQ2 + }), + [{?RABBITMQ_RESOURCE_ONE, RabbitMQ1} | {?RABBITMQ_RESOURCE_TWO, RabbitMQ2} + | Config; + +init_per_group(inheritance_group, Config) -> + set_env(resource_server_id, ?RABBITMQ), + set_env(resource_server_type, <<"rabbitmq-type">>), + set_env(scope_prefix, <<"some-prefix-">>), + set_env(extra_scopes_source, <<"roles">>), + set_env(scope_aliases, #{}), + + set_env(key_config, [ {jwks_url,<<"https://oauth-for-rabbitmq">> } ]), + + set_env(resource_servers, + #{?RABBITMQ_RESOURCE_ONE => [ + { extra_scopes_source, <<"extra-scope-1">>}, + { verify_aud, false}, + { preferred_username_claims, [<<"email-address">>] }, + { scope_prefix, <<"my-prefix:">> }, + { resource_server_type, <<"my-type">> }, + { scope_aliases, #{} } + ], + ?RABBITMQ_RESOURCE_TWO => [ {id, ?RABBITMQ_RESOURCE_TWO } ] + } + ), + Config; + +init_per_group(_any, Config) -> + Config. + +end_per_group(with_empty_scope_prefix, Config) -> + unset_env(scope_prefix), + Config; + +end_per_group(with_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(with_verify_aud_false, Config) -> + unset_env(verify_aud), + Config; + +end_per_group(with_verify_aud_false_for_resource_two, Config) -> + ResourceServers = get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), + set_env(resource_servers, + maps:put(?RABBITMQ_RESOURCE_TWO, proplists:delete(verify_aud, Proplist), ResourceServers)), + Config; + +end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> + ResourceServers = get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), + Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), + set_env(resource_servers, + maps:put(?RABBITMQ_RESOURCE_ONE, proplists:delete(scope_prefix, Proplist), ResourceServers)), + Config; + +end_per_group(with_two_resource_servers, Config) -> + unset_env(resource_servers), + Config; + +end_per_group(with_different_oauth_provider_for_each_resource, Config) -> + {ok, ResourceServers} = get_env(resource_servers), + Rabbit1 = proplists:delete(oauth_provider_id, + maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers)), + Rabbit2 = proplists:delete(oauth_provider_id, + maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers)), + ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, + ResourceServers), + set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, + ResourceServers1)), + Config; + +end_per_group(inheritance_group, Config) -> + unset_env(resource_server_id), + unset_env(scope_prefix), + unset_env(extra_scopes_source), + unset_env(key_config), + unset_env(resource_servers), + Config; + +end_per_group(with_scope_prefix, Config) -> + unset_env(scope_prefix), + Config; + +end_per_group(_any, Config) -> + Config. + + +%% --- Test cases + +resolve_resource_server_for_rabbitmq_audience(_ -> + ?RABBITMQ = resolve_resource_server_id_for_audience(?RABBITMQ). + +resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> + ?RABBITMQ = resolve_resource_server_id_for_audience([?RABBITMQ, + <<"unknown">>]). + +resolve_resource_server_for_none_audience_returns_error(_) -> + {error, missing_audience_in_token} = + resolve_resource_server_id_for_audience(none). + +resolve_resource_server_for_unknown_audience_returns_error(_) -> + {error, no_matching_aud_found} = + resolve_resource_server_id_for_audience(<<"unknown">>). + +resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> + ?RABBITMQ = resolve_resource_server_id_for_audience(none). + +resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> + ?RABBITMQ = resolve_resource_server_id_for_audience(<<"unknown">>). + +resolve_resource_server_id_for_any_audience_returns_error(_) -> + {error, no_matching_aud_found} = + resolve_resource_server_id_for_audience(?RABBITMQ), + {error, no_matching_aud_found} = + resolve_resource_server_id_for_audience(<<"unknown">>), + +resolve_resource_server_id_for_rabbitmq1(_) -> + ?RABBITMQ_RESOURCE_ONE = resolve_resource_server_id_for_audience( + ?RABBITMQ_RESOURCE_ONE). + +resolve_resource_server_id_for_rabbitmq2(_) -> + ?RABBITMQ_RESOURCE_TWO = resolve_resource_server_id_for_audience( + ?RABBITMQ_RESOURCE_TWO). + +resolve_resource_server_id_for_both_resources_returns_error(_) -> + {error, only_one_resource_server_as_audience_found_many} = + resolve_resource_server_id_for_audience([?RABBITMQ_RESOURCE_TWO, + ?RABBITMQ_RESOURCE_ONE]). + +rabbitmq_verify_aud_is_true(_) -> + #resource_server{verify_aud = true} = + resolve_resource_server_id_for_audience(?RABBITMQ). + +rabbitmq_verify_aud_is_false(_) -> + #resource_server{verify_aud = false} = + resolve_resource_server_id_for_audience(?RABBITMQ). + +rabbitmq2_verify_aud_is_true(_) -> + #resource_server{verify_aud = true} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +both_resources_oauth_provider_id_is_root(_) -> + #resource_server{oauth_provider_id = root} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_ONE), + #resource_server{oauth_provider_id = root} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_verify_aud_is_false(_) -> + #resource_server{verify_aud = false} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_no_scope_prefix(_) -> + #resource_server{scope_prefix = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_scope_prefix(Config) -> + #resource_server{scope_prefix = ScopePrefix} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + ?assertEqual(?config(scope_prefix, Config), ScopePrefix). + +rabbitmq2_oauth_provider_id_is_root(_) -> + #resource_server{oauth_provider_id = root} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_oauth_provider_id_is_A(_) -> + #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_no_additional_scopes_key(_) -> + #resource_server{additional_scopes_key = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_additional_scopes_key(Config) -> + #resource_server{additional_scopes_key = ScopesKey} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + ?assertEqual(?config(additional_scopes_key, Config), ScopesKey). + +rabbitmq2_has_no_preferred_username_claims_but_gets_default(_) -> + #resource_server{preferred_username_claims = Claims} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + ?assertEqual(?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). + +rabbitmq2_has_preferred_username_claims_plus_default(Config) -> + #resource_server{preferred_username_claims = Claims} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + ?assertEqual(?config(preferred_username_claims, Config) + ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). + +rabbitmq2_has_no_scope_aliases(_) -> + #resource_server{scope_aliases = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_scope_aliases(_) -> + #resource_server{scope_aliases = Aliases} = + resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + ?assertEqual(?config(scope_aliases, Config), Aliases). + +rabbitmq_oauth_provider_id_is_root(_) -> + #resource_server{oauth_provider_id = root} = + resolve_resource_server_id_for_audience(?RABBITMQ). + +rabbitmq_oauth_provider_id_is_A(_) -> + #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A} = + resolve_resource_server_id_for_audience(?RABBITMQ). + +rabbitmq_has_no_scope_prefix(_) -> + #resource_server{scope_prefix = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ), + +rabbitmq_has_scope_prefix(Config) -> + #resource_server{scope_prefix = ScopePrefix} = + resolve_resource_server_id_for_audience (?RABBITMQ), + ?assertEqual(?config(scope_prefix, Config), ScopePrefix). + +rabbitmq_has_empty_scope_prefix() -> + #resource_server{scope_prefix = <<"">>} = + resolve_resource_server_id_for_audience (?RABBITMQ). + +rabbitmq_has_no_additional_scopes_key(_) -> + #resource_server{additional_scopes_key = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ), + +rabbitmq_has_additional_scopes_key(Config) -> + #resource_server{additional_scopes_key = AdditionalScopesKey} = + resolve_resource_server_id_for_audience (?RABBITMQ), + ?assertEqual(?config(additional_scopes_key, Config), AdditionalScopesKey). + +rabbitmq_has_no_preferred_username_claims_but_gets_default(_) -> + #resource_server{preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS} = + resolve_resource_server_id_for_audience(?RABBITMQ). + +rabbitmq_has_preferred_username_claims_plus_default(Config) -> + #resource_server{additional_scopes_key = AdditionalScopesKey} = + resolve_resource_server_id_for_audience (?RABBITMQ), + ?assertEqual(?config(preferred_username_claims, Config) ++ + ?DEFAULT_PREFERRED_USERNAME_CLAIMS, AdditionalScopesKey). + +rabbitmq_has_no_scope_aliases(_) -> + #resource_server{scope_aliases = undefined} = + resolve_resource_server_id_for_audience(?RABBITMQ), + +rabbitmq_has_scope_aliases(Config) -> + #resource_server{scope_aliases = Aliases} = + resolve_resource_server_id_for_audience (?RABBITMQ), + ?assertEqual(?config(scope_aliases, Config), Aliases). + + +verify_rabbitmq1_server_configuration(Config) -> + ConfigRabbitMQ = ?config(?RABBITMQ_RESOURCE_ONE, Config), + ActualRabbitMQ = get_resource_server(?RABBITMQ_RESOURCE_ONE), + ?assertEqual(ConfigRabbitMQ#resource_server.id, + ActualRabbitMQ#resource_server.id), + ?assertEqual(ConfigRabbitMQ#resource_server.resource_server_type, + ActualRabbitMQ#resource_server.resource_server_type), + ?assertEqual(ConfigRabbitMQ#resource_server.verify_aud, + ActualRabbitMQ#resource_server.verify_aud), + ?assertEqual(ConfigRabbitMQ#resource_server.scope_prefix, + ActualRabbitMQ#resource_server.scope_prefix), + ?assertEqual(ConfigRabbitMQ#resource_server.additional_scopes_key, + ActualRabbitMQ#resource_server.additional_scopes_key), + ?assertEqual(ConfigRabbitMQ#resource_server.preferred_username_claims ++ + ?DEFAULT_PREFERRED_USERNAME_CLAIMS, + ActualRabbitMQ#resource_server.preferred_username_claims), + ?assertEqual(ConfigRabbitMQ#resource_server.scope_aliases, + ActualRabbitMQ#resource_server.scope_aliases), + ?assertEqual(ConfigRabbitMQ#resource_server.oauth_provider_id, + ActualRabbitMQ#resource_server.oauth_provider_id). + +%% ----- + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). +unset_env(Par) -> + unset_env(rabbitmq_auth_backend_oauth2, Par). From 9984eef2d141b21fcf231391cb3653f33d98959f Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 10:07:20 +0100 Subject: [PATCH 07/64] WIP Fix compilation errors --- deps/rabbitmq_auth_backend_oauth2/BUILD.bazel | 4 ++-- deps/rabbitmq_auth_backend_oauth2/app.bzl | 24 +++++++++---------- .../include/oauth2.hrl | 2 +- .../src/rabbit_auth_backend_oauth2.erl | 6 +++-- ...provider.erl => rabbit_oauth_provider.erl} | 11 ++++----- ..._server.erl => rabbit_resource_server.erl} | 14 +++++------ ...TE.erl => rabbit_oauth_provider_SUITE.erl} | 4 ++-- ...E.erl => rabbit_resource_server_SUITE.erl} | 4 ++-- 8 files changed, 35 insertions(+), 34 deletions(-) rename deps/rabbitmq_auth_backend_oauth2/src/{rabbit_oauth2_oauth_provider.erl => rabbit_oauth_provider.erl} (96%) rename deps/rabbitmq_auth_backend_oauth2/src/{rabbit_oauth2_resource_server.erl => rabbit_resource_server.erl} (96%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_oauth_provider_SUITE.erl => rabbit_oauth_provider_SUITE.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_resource_server_SUITE.erl => rabbit_resource_server_SUITE.erl} (99%) diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 2509e27f20ab..3713706decb3 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -113,7 +113,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "rabbit_oauth2_oauth_provider_SUITE", + name = "rabbit_oauth_provider_SUITE", additional_beam = [ "test/oauth2_http_mock.beam", ], @@ -123,7 +123,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "rabbit_oauth2_resource_server_SUITE" + name = "rabbit_resource_server_SUITE" ) rabbitmq_integration_suite( diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index 5e42e061dcab..d26064d1a213 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -13,8 +13,8 @@ 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_oauth_provider.erl", - "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth_provider.erl", + "src/rabbit_resource_server.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -49,8 +49,8 @@ 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_resource_server.erl", - "src/rabbit_oauth2_oauth_provider.erl", + "src/rabbit_resource_server.erl", + "src/rabbit_oauth_provider.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -97,8 +97,8 @@ 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_oauth_provider.erl", - "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth_provider.erl", + "src/rabbit_resource_server.erl", "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -240,19 +240,19 @@ def test_suite_beam_files(name = "test_suite_beam_files"): erlc_opts = "//:test_erlc_opts", ) erlang_bytecode( - name = "rabbit_oauth2_oauth_provider_SUITE_beam_files", + name = "rabbit_oauth_provider_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_oauth2_oauth_provider_SUITE.erl"], - outs = ["test/rabbit_oauth2_oauth_provider_SUITE.beam"], + srcs = ["test/rabbit_oauth_provider_SUITE.erl"], + outs = ["test/rabbit_oauth_provider_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], ) erlang_bytecode( - name = "rabbit_oauth2_resource_server_SUITE_beam_files", + name = "rabbit_resource_server_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_oauth2_resource_server_SUITE.erl"], - outs = ["test/rabbit_oauth2_resource_server_SUITE.beam"], + srcs = ["test/rabbit_resource_server_SUITE.erl"], + outs = ["test/rabbit_resource_server_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl index 01fbf1134b8d..74813e68520e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -8,8 +8,8 @@ -include_lib("oauth2_client/include/oauth2_client.hrl"). +-define(APP, rabbitmq_auth_backend_oauth2). -define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). - -define(TOP_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). %% scope aliases map "role names" to a set of scopes 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 f37b60d21c5a..eac24aab5a6d 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 @@ -8,6 +8,7 @@ -module(rabbit_auth_backend_oauth2). -include_lib("rabbit_common/include/rabbit.hrl"). +-include("oauth2.hrl"). -behaviour(rabbit_authn_backend). -behaviour(rabbit_authz_backend). @@ -18,7 +19,7 @@ check_topic_access/4, check_token/1, update_state/2, expiry_timestamp/1]). -% for testing +%% for testing -export([post_process_payload/2, get_expanded_scopes/2]). -import(rabbit_data_coercion, [to_map/1]). @@ -494,7 +495,8 @@ post_process_payload_in_rich_auth_request_format(ResourceServer, FilteredPermissionsByType = lists:filter(fun(P) -> is_recognized_permission(P, ResourceServerType) end, Permissions), - AdditionalScopes = map_rich_auth_permissions_to_scopes(ResourceServerId, FilteredPermissionsByType), + AdditionalScopes = map_rich_auth_permissions_to_scopes( + ResourceServer#resource_server.id, FilteredPermissionsByType), ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl similarity index 96% rename from deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl index ef580dd29c26..c10ef05871fc 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_oauth_provider.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl @@ -5,12 +5,10 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_config). +-module(rabbit_oauth_provider). -include("oauth2.hrl"). -%-include_lib("oauth2_client/include/oauth2_client.hrl"). - -export([ get_internal_oauth_provider/0, get_internal_oauth_provider/1, add_signing_key/2, add_signing_key/3, replace_signing_keys/1, @@ -19,14 +17,15 @@ ]). -spec get_internal_oauth_provider() -> internal_oauth_provider(). +get_internal_oauth_provider() -> get_internal_oauth_provider(root). -spec get_internal_oauth_provider(oauth_provider_id()) -> internal_oauth_provider(). get_internal_oauth_provider(OAuthProviderId) -> #internal_oauth_provider{ - id = OAuthProvider#oauth_provider.id, - default_key = get_default_key(OAuthProvider#oauth_provider.id), - algorithms = get_algorithms(OAuthProvider#oauth_provider.id) + id = OAuthProviderId, + default_key = get_default_key(OAuthProviderId), + algorithms = get_algorithms(OAuthProviderId) }. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl similarity index 96% rename from deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index 8a4eea731941..a48d8f609979 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_resource_server). +-module(rabbit_resource_server). -include("oauth2.hrl"). @@ -25,9 +25,9 @@ get_resource_server(ResourceServerId) -> V -> get_resource_server(V, ResourceServerId) end. -get_resource_server(TopResourceServerId, ResourceServerId) -> +get_resource_server(TopResourceServerId, ResourceServerId) when ResourceServerId =:= TopResourceServerId -> - ScopeAlises = + ScopeAliases = application:get_env(?APP, scope_aliases, undefined), PreferredUsernameClaims = case application:get_env(?APP, preferred_username_claims) of @@ -50,7 +50,7 @@ get_resource_server(TopResourceServerId, ResourceServerId) -> V -> erlang:iolist_to_binary([V, <<".">>]) end, ScopePrefix = - application:get_env(?APP, scope_prefix, DefaultScopePrefix). + application:get_env(?APP, scope_prefix, DefaultScopePrefix), OAuthProviderId = case application:get_env(?APP, default_oauth_provider) of undefined -> root; @@ -68,14 +68,14 @@ get_resource_server(TopResourceServerId, ResourceServerId) -> oauth_provider_id = OAuthProviderId }; -get_resource_server(TopResourceServerId, ResourceServerId) -> +get_resource_server(TopResourceServerId, ResourceServerId) when ResourceServerId =/= TopResourceServerId -> ResourceServerProps = maps:get(ResourceServerId, application:get_env(?APP, resource_servers, #{}),[]), TopResourseServer = get_resource_server(TopResourceServerId, TopResourceServerId), - ScopeAlises = + ScopeAliases = proplists:get_value(scope_aliases, ResourceServerProps, TopResourseServer#resource_server.scope_aliases), PreferredUsernameClaims = @@ -89,7 +89,7 @@ get_resource_server(TopResourceServerId, ResourceServerId) -> TopResourseServer#resource_server.verify_aud), AdditionalScopesKey = proplists:get_value(extra_scopes_source, ResourceServerProps, - TopResourseServer#resource_server.extra_scopes_source), + TopResourseServer#resource_server.additional_scopes_key), ScopePrefix = proplists:get_value(scope_prefix, ResourceServerProps, TopResourseServer#resource_server.scope_prefix), diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl index 6305a553832b..19642ac964d0 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_oauth_provider_SUITE). +-module(rabbit_oauth_provider_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -17,7 +17,7 @@ -define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). -define(AUTH_PORT, 8000). --import(rabbit_oauth2_oauth_provider, [ +-import(rabbit_oauth_provider, [ get_internal_oauth_provider/2, add_signing_key/2, add_signing_key/3, replace_signing_keys/1, replace_signing_keys/2, diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index 20b1c0bc8d4f..389ea1d749f7 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_resource_server_SUITE). +-module(rabbit_resource_server_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -19,7 +19,7 @@ -define(OAUTH_PROVIDER_B,<<"B">>). -import(oauth2_client, [get_oauth_provider/2]). --import(rabbit_oauth2_resource_server, [ +-import(rabbit_resource_server, [ resolve_resource_server_id_from_audience/1, get_resource_server/1 ]). From 91e46668b0c79fc9c3a8ff1db8e5db72342cf4d4 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 14:06:27 +0100 Subject: [PATCH 08/64] WIP Continue refactoring + clean up --- deps/oauth2_client/app.bzl | 6 +- deps/oauth2_client/include/oauth2_client.hrl | 64 +----- deps/oauth2_client/include/types.hrl | 69 ++++++ .../include/oauth2.hrl | 5 +- .../src/rabbit_oauth_provider.erl | 35 +-- .../src/rabbit_resource_server.erl | 205 +++++++++--------- .../src/uaa_jwt.erl | 62 +++--- .../src/uaa_jwt_jwt.erl | 2 +- 8 files changed, 231 insertions(+), 217 deletions(-) create mode 100644 deps/oauth2_client/include/types.hrl diff --git a/deps/oauth2_client/app.bzl b/deps/oauth2_client/app.bzl index 6b4b31789a16..3ddba5d9a082 100644 --- a/deps/oauth2_client/app.bzl +++ b/deps/oauth2_client/app.bzl @@ -64,7 +64,7 @@ def all_srcs(name = "all_srcs"): ) filegroup( name = "public_hdrs", - srcs = ["include/oauth2_client.hrl"], + srcs = ["include/oauth2_client.hrl", "include/types.hrl"], ) filegroup( name = "license_files", @@ -88,7 +88,7 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/system_SUITE.erl"], outs = ["test/system_SUITE.beam"], - hdrs = ["include/oauth2_client.hrl"], + hdrs = ["include/oauth2_client.hrl", "include/types.hrl"], app_name = "oauth2_client", erlc_opts = "//:test_erlc_opts", ) @@ -97,7 +97,7 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/unit_SUITE.erl"], outs = ["test/unit_SUITE.beam"], - hdrs = ["include/oauth2_client.hrl"], + hdrs = ["include/oauth2_client.hrl", "include/types.hrl"], app_name = "oauth2_client", erlc_opts = "//:test_erlc_opts", ) diff --git a/deps/oauth2_client/include/oauth2_client.hrl b/deps/oauth2_client/include/oauth2_client.hrl index b7f93104f167..24534dc136f4 100644 --- a/deps/oauth2_client/include/oauth2_client.hrl +++ b/deps/oauth2_client/include/oauth2_client.hrl @@ -5,6 +5,7 @@ %% Copyright (c) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. %% +-include("types.hrl"). % define access token request common constants @@ -44,66 +45,3 @@ -define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>). -define(RESPONSE_JWKS_URI, <<"jwks_uri">>). -define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>). - -%% The closest we have to a type import in Erlang --type option(T) :: rabbit_types:option(T). - --type oauth_provider_id() :: root | binary(). - --record(openid_configuration, { - issuer :: option(uri_string:uri_string()), - token_endpoint :: option(uri_string:uri_string()), - authorization_endpoint :: option(uri_string:uri_string()), - end_session_endpoint :: option(uri_string:uri_string()), - jwks_uri :: option(uri_string:uri_string()) - }). --type openid_configuration() :: #openid_configuration{}. - --record(oauth_provider, { - id :: oauth_provider_id(), - issuer :: option(uri_string:uri_string()), - token_endpoint :: option(uri_string:uri_string()), - authorization_endpoint :: option(uri_string:uri_string()), - end_session_endpoint :: option(uri_string:uri_string()), - jwks_uri :: option(uri_string:uri_string()), - ssl_options :: option(list()) - }). - --type oauth_provider() :: #oauth_provider{}. - --record(access_token_request, { - client_id :: string() | binary(), - client_secret :: string() | binary(), - scope :: string() | binary() | undefined, - timeout :: option(integer()) - }). - --type access_token_request() :: #access_token_request{}. - --record(successful_access_token_response, { - access_token :: binary(), - token_type :: binary(), - refresh_token :: option(binary()), % A refresh token SHOULD NOT be included - % .. for client-credentials flow. - % https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 - expires_in :: option(integer()) -}). - --type successful_access_token_response() :: #successful_access_token_response{}. - --record(unsuccessful_access_token_response, { - error :: integer(), - error_description :: binary() | string() | undefined -}). - --type unsuccessful_access_token_response() :: #unsuccessful_access_token_response{}. - --record(refresh_token_request, { - client_id :: string() | binary(), - client_secret :: string() | binary(), - scope :: string() | binary() | undefined, - refresh_token :: binary(), - timeout :: option(integer()) - }). - --type refresh_token_request() :: #refresh_token_request{}. diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl new file mode 100644 index 000000000000..13c61cfd2c96 --- /dev/null +++ b/deps/oauth2_client/include/types.hrl @@ -0,0 +1,69 @@ +%% 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) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. +%% + +%% The closest we have to a type import in Erlang +-type option(T) :: rabbit_types:option(T). + +-type oauth_provider_id() :: root | binary(). + +-record(openid_configuration, { + issuer :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()) + }). +-type openid_configuration() :: #openid_configuration{}. + +-record(oauth_provider, { + id :: oauth_provider_id(), + issuer :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()), + ssl_options :: option(list()) + }). + +-type oauth_provider() :: #oauth_provider{}. + +-record(access_token_request, { + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: string() | binary() | undefined, + timeout :: option(integer()) + }). + +-type access_token_request() :: #access_token_request{}. + +-record(successful_access_token_response, { + access_token :: binary(), + token_type :: binary(), + refresh_token :: option(binary()), % A refresh token SHOULD NOT be included + % .. for client-credentials flow. + % https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 + expires_in :: option(integer()) +}). + +-type successful_access_token_response() :: #successful_access_token_response{}. + +-record(unsuccessful_access_token_response, { + error :: integer(), + error_description :: binary() | string() | undefined +}). + +-type unsuccessful_access_token_response() :: #unsuccessful_access_token_response{}. + +-record(refresh_token_request, { + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: string() | binary() | undefined, + refresh_token :: binary(), + timeout :: option(integer()) + }). + +-type refresh_token_request() :: #refresh_token_request{}. diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl index 74813e68520e..7febcebf1d4a 100644 --- a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -6,11 +6,10 @@ %% --include_lib("oauth2_client/include/oauth2_client.hrl"). +-include_lib("oauth2_client/include/types.hrl"). -define(APP, rabbitmq_auth_backend_oauth2). -define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). --define(TOP_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). %% scope aliases map "role names" to a set of scopes -record(internal_oauth_provider, { @@ -22,7 +21,7 @@ -record(resource_server, { id :: resource_server_id(), - resource_server_type :: binary(), + resource_server_type :: binary() | undefined, verify_aud :: boolean(), scope_prefix :: binary(), additional_scopes_key :: binary(), diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl index c10ef05871fc..d60cfc482126 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl @@ -70,10 +70,10 @@ do_add_signing_key(KeyId, Key, OAuthProviderId) -> get_signing_keys_from_jwks(OAuthProviderId)), OAuthProviderId). get_signing_keys_from_jwks(root) -> - KeyConfig = application:get_env(?APP, key_config, []), + KeyConfig = get_env(key_config, []), proplists:get_value(jwks, KeyConfig, #{}); get_signing_keys_from_jwks(OAuthProviderId) -> - OAuthProviders0 = application:get_env(?APP, oauth_providers, #{}), + OAuthProviders0 = get_env(oauth_providers, #{}), OAuthProvider0 = maps:get(OAuthProviderId, OAuthProviders0, []), proplists:get_value(jwks, OAuthProvider0, #{}). @@ -95,18 +95,18 @@ replace_signing_keys(SigningKeys, OAuthProviderId) -> end. do_replace_signing_keys(SigningKeys, root) -> - KeyConfig = application:get_env(?APP, key_config, []), + KeyConfig = get_env(key_config, []), KeyConfig1 = proplists:delete(jwks, KeyConfig), KeyConfig2 = [{jwks, maps:merge( proplists:get_value(signing_keys, KeyConfig1, #{}), SigningKeys)} | KeyConfig1], - application:set_env(?APP, key_config, KeyConfig2), + set_env(key_config, KeyConfig2), rabbit_log:debug("Replacing signing keys for key_config with ~p keys", [maps:size(SigningKeys)]), SigningKeys; do_replace_signing_keys(SigningKeys, OauthProviderId) -> - OauthProviders0 = application:get_env(?APP, oauth_providers, #{}), + OauthProviders0 = get_env(oauth_providers, #{}), OauthProvider0 = maps:get(OauthProviderId, OauthProviders0, []), OauthProvider1 = proplists:delete(jwks, OauthProvider0), OauthProvider = [{jwks, maps:merge( @@ -114,7 +114,7 @@ do_replace_signing_keys(SigningKeys, OauthProviderId) -> SigningKeys)} | OauthProvider1], OauthProviders = maps:put(OauthProviderId, OauthProvider, OauthProviders0), - application:set_env(?APP, oauth_providers, OauthProviders), + set_env(oauth_providers, OauthProviders), rabbit_log:debug("Replacing signing keys for ~p -> ~p with ~p keys", [OauthProviderId, OauthProvider, maps:size(SigningKeys)]), SigningKeys. @@ -126,7 +126,7 @@ get_signing_keys() -> -spec get_signing_keys(oauth_provider_id()) -> map(). get_signing_keys(root) -> - case application:get_env(?APP, key_config, undefined) of + case get_env(key_config) of undefined -> #{}; KeyConfig -> @@ -136,7 +136,7 @@ get_signing_keys(root) -> end end; get_signing_keys(OauthProviderId) -> - OauthProviders = application:get_env(?APP, oauth_providers, #{}), + OauthProviders = get_env(oauth_providers, #{}), OauthProvider = maps:get(OauthProviderId, OauthProviders, []), case proplists:get_value(jwks, OauthProvider, undefined) of undefined -> @@ -159,23 +159,28 @@ get_default_key(root) -> get_default_key(OauthProviderId) -> OauthProviders = application:get_env(?APP, oauth_providers, #{}), case maps:get(OauthProviderId, OauthProviders, []) of - [] -> - undefined; - OauthProvider -> - proplists:get_value(default_key, OauthProvider, undefined) + [] -> undefined; + OauthProvider -> proplists:get_value(default_key, OauthProvider, undefined) end. -spec get_algorithms(oauth_provider_id()) -> list() | undefined. get_algorithms(root) -> - proplists:get_value(algorithms, application:get_env(?APP, key_config, []), - undefined); + proplists:get_value(algorithms, get_env(key_config, []), undefined); get_algorithms(OAuthProviderId) -> - OAuthProviders = application:get_env(?APP, oauth_providers, #{}), + OAuthProviders = get_env(oauth_providers, #{}), case maps:get(OAuthProviderId, OAuthProviders, undefined) of undefined -> undefined; V -> proplists:get_value(algorithms, V, undefined) end. +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Value) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Value). + + lock() -> Nodes = rabbit_nodes:list_running(), Retries = rabbit_nodes:lock_retries(), diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index a48d8f609979..067ef2876b8f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -8,51 +8,73 @@ -module(rabbit_resource_server). -include("oauth2.hrl"). - -%-include_lib("oauth2_client/include/oauth2_client.hrl"). - +-define(ROOT_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). -export([ - resolve_resource_server_id_from_audience/1, - get_resource_server/1 + resolve_resource_server_from_audience/1 ]). --spec get_resource_server(resource_server_id()) -> resource_server() | {error, term()}. -get_resource_server(ResourceServerId) -> - case get_default_resource_server_id() of - {error, _} -> - get_resource_server(undefined, ResourceServerId); - V -> - get_resource_server(V, ResourceServerId) +-spec resolve_resource_server_from_audience(binary() | list() | none) -> + {ok, resource_server()} | + {error, only_one_resource_server_as_audience_found_many} | + {error, no_matching_aud_found} | + {error, zero_declared_resource_servers} | + {error, cannot_default_resource_server_found_many}. +resolve_resource_server_from_audience(none) -> + find_unique_resource_server_without_verify_aud(get_root_resource_server()); + +resolve_resource_server_from_audience(Audience) -> + Root = get_root_resource_server(), + ResourceServers = get_env(resource_servers, #{}), + ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ + [proplists:get_value(id, V, K)] end, [], ResourceServers), + AllowedResourceServerIds = ResourceServerIds ++ + case Root#resource_server.id of + undefined -> []; + ID -> [ID] + end, + RootResourseServerId = Root#resource_server.id, + case find_audience(Audience, AllowedResourceServerIds) of + {error, only_one_resource_server_as_audience_found_many} = Error -> + Error; + {error, no_matching_aud_found} -> + find_unique_resource_server_without_verify_aud(Root); + {ok, RootResourseServerId} -> + {ok, Root}; + {ok, ResourceServerId} -> + {ok, get_resource_server(ResourceServerId, Root)} end. -get_resource_server(TopResourceServerId, ResourceServerId) - when ResourceServerId =:= TopResourceServerId -> + +-spec get_root_resource_server() -> resource_server(). +get_root_resource_server() -> + ResourceServerId = + case ?ROOT_RESOURCE_SERVER_ID of + undefined -> undefined; + {ok, V} -> V + end, ScopeAliases = - application:get_env(?APP, scope_aliases, undefined), + get_env(scope_aliases), PreferredUsernameClaims = - case application:get_env(?APP, preferred_username_claims) of + case get_env(preferred_username_claims) of {ok, Value} -> append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS end, ResourceServerType = - application:get_env(?APP, resource_server_type, <<>>), + get_env(resource_server_type), VerifyAud = - application:get_env(?APP, verify_aud, true), + get_boolean_env(verify_aud, true), AdditionalScopesKey = - case application:get_env(?APP, extra_scopes_source, undefined) of - undefined -> {error, not_found}; - ScopeKey -> {ok, ScopeKey} - end, + get_env(extra_scopes_source), DefaultScopePrefix = - case get_default_resource_server_id() of - {error, _} -> <<"">>; - V -> erlang:iolist_to_binary([V, <<".">>]) + case ResourceServerId of + undefined -> undefined; + _ -> erlang:iolist_to_binary([ResourceServerId, <<".">>]) end, ScopePrefix = - application:get_env(?APP, scope_prefix, DefaultScopePrefix), + get_env(scope_prefix, DefaultScopePrefix), OAuthProviderId = - case application:get_env(?APP, default_oauth_provider) of + case get_env(default_oauth_provider) of undefined -> root; {ok, DefaultOauthProviderId} -> DefaultOauthProviderId end, @@ -66,36 +88,34 @@ get_resource_server(TopResourceServerId, ResourceServerId) preferred_username_claims = PreferredUsernameClaims, scope_aliases = ScopeAliases, oauth_provider_id = OAuthProviderId - }; + }. -get_resource_server(TopResourceServerId, ResourceServerId) - when ResourceServerId =/= TopResourceServerId -> +-spec get_resource_server(resource_server_id(), resource_server()) -> + resource_server(). +get_resource_server(ResourceServerId, RootResourseServer) -> ResourceServerProps = - maps:get(ResourceServerId, application:get_env(?APP, resource_servers, - #{}),[]), - TopResourseServer = - get_resource_server(TopResourceServerId, TopResourceServerId), + maps:get(ResourceServerId, get_env(resource_servers, #{}), []), ScopeAliases = proplists:get_value(scope_aliases, ResourceServerProps, - TopResourseServer#resource_server.scope_aliases), + RootResourseServer#resource_server.scope_aliases), PreferredUsernameClaims = proplists:get_value(preferred_username_claims, ResourceServerProps, - TopResourseServer#resource_server.preferred_username_claims), + RootResourseServer#resource_server.preferred_username_claims), ResourceServerType = proplists:get_value(resource_server_type, ResourceServerProps, - TopResourseServer#resource_server.resource_server_type), + RootResourseServer#resource_server.resource_server_type), VerifyAud = proplists:get_value(verify_aud, ResourceServerProps, - TopResourseServer#resource_server.verify_aud), + RootResourseServer#resource_server.verify_aud), AdditionalScopesKey = proplists:get_value(extra_scopes_source, ResourceServerProps, - TopResourseServer#resource_server.additional_scopes_key), + RootResourseServer#resource_server.additional_scopes_key), ScopePrefix = proplists:get_value(scope_prefix, ResourceServerProps, - TopResourseServer#resource_server.scope_prefix), + erlang:iolist_to_binary([ResourceServerId, <<".">>])), OAuthProviderId = proplists:get_value(oauth_provider_id, ResourceServerProps, - TopResourseServer#resource_server.oauth_provider_id), + RootResourseServer#resource_server.oauth_provider_id), #resource_server{ id = ResourceServerId, @@ -108,76 +128,31 @@ get_resource_server(TopResourceServerId, ResourceServerId) oauth_provider_id = OAuthProviderId }. - --spec resolve_resource_server_id_from_audience(binary() | list() | none) -> - resource_server() | {error, term()}. -resolve_resource_server_id_from_audience(Audience) -> - case get_resource_server_id_for_audience(Audience) of - {error, _} = Error -> Error; - ResourceServerId -> get_resource_server(ResourceServerId) - end. - -get_resource_server_id_for_audience(none) -> - case is_verify_aud() of - true -> - {error, missing_audience_in_token}; - false -> - case get_default_resource_server_id() of - {error, missing_resource_server_id_in_config} -> - {error, mising_audience_in_token_and_resource_server_in_config}; - V -> V - end - end; -get_resource_server_id_for_audience(Audience) -> - case find_audience_in_resource_server_ids(Audience) of - {ok, ResourceServerId} -> - ResourceServerId; - {error, only_one_resource_server_as_audience_found_many} = Error -> - Error; - {error, no_matching_aud_found} -> - case is_verify_aud() of - true -> - {error, no_matching_aud_found}; - false -> - case get_default_resource_server_id() of - {error, missing_resource_server_id_in_config} -> - {error, mising_audience_in_token_and_resource_server_in_config}; - V -> V - end - end - end. - --spec get_default_resource_server_id() -> binary() | {error, term()}. -get_default_resource_server_id() -> - case ?TOP_RESOURCE_SERVER_ID of - undefined -> {error, missing_resource_server_id_in_config }; - {ok, ResourceServerId} -> ResourceServerId - end. - --spec get_allowed_resource_server_ids() -> list(). -get_allowed_resource_server_ids() -> - ResourceServers = application:get_env(?APP, resource_servers, #{}), - rabbit_log:debug("ResourceServers: ~p", [ResourceServers]), - ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ - [proplists:get_value(id, V, K)] end, [], ResourceServers), - rabbit_log:debug("ResourceServersIds: ~p", [ResourceServerIds]), - ResourceServerIds ++ case get_default_resource_server_id() of - {error, _} -> []; - ResourceServerId -> [ ResourceServerId ] - end. - --spec find_audience_in_resource_server_ids(binary() | list()) -> - {ok, binary()} | {error, term()}. -find_audience_in_resource_server_ids(Audience) when is_binary(Audience) -> - find_audience_in_resource_server_ids(binary:split(Audience, <<" ">>, [global, trim_all])); -find_audience_in_resource_server_ids(AudList) when is_list(AudList) -> - AllowedAudList = get_allowed_resource_server_ids(), - case intersection(AudList, AllowedAudList) of +-spec find_audience(binary() | list(), list()) -> + {ok, resource_server_id()} | + {error, only_one_resource_server_as_audience_found_many} | + {error, no_matching_aud_found}. +find_audience(Audience, ResourceIdList) when is_binary(Audience) -> + AudList = binary:split(Audience, <<" ">>, [global, trim_all]), + find_audience(AudList, ResourceIdList); +find_audience(AudList, ResourceIdList) when is_list(AudList) -> + case intersection(AudList, ResourceIdList) of [One] -> {ok, One}; [_One|_Tail] -> {error, only_one_resource_server_as_audience_found_many}; [] -> {error, no_matching_aud_found} end. +-spec find_unique_resource_server_without_verify_aud(resource_server()) -> + {ok, resource_server()} | {error, not_found} | {error, too_many}. +find_unique_resource_server_without_verify_aud(Root) -> + Map = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, + Root#resource_server.verify_aud) end, get_env(resource_servers, #{})), + case {maps:size(Map), Root} of + {0, undefined} -> {error, zero_declared_resource_servers}; + {0, _} -> {ok, Root}; + {1, undefined} -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; + {_, _} -> {error, cannot_default_resource_server_found_many} + end. append_or_return_default(ListOrBinary, Default) -> case ListOrBinary of @@ -186,5 +161,23 @@ append_or_return_default(ListOrBinary, Default) -> _ -> Default end. +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +-spec get_boolean_env(atom(), boolean()) -> boolean(). +get_boolean_env(Par, Def) -> + case get_env(Par, Def) of + true -> true; + false -> false; + _ -> true + end. +-spec get_boolean_value(term(), list(), boolean()) -> boolean(). +get_boolean_value(Key, Proplist, Def) -> + case proplists:get_value(Key, Proplist, Def) of + true -> true; + false -> false; + _ -> true + end. intersection(List1, List2) -> [I || I <- List1, lists:member(I, List2)]. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index 2fb6f3784aea..d7050998d00a 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -15,9 +15,19 @@ -include("oauth2.hrl"). -include_lib("jose/include/jose_jwk.hrl"). --include_lib("oauth2_client/include/oauth2_client.hrl"). --define(APP, rabbitmq_auth_backend_oauth2). +-import(rabbit_data_coercion, [ + to_map/1]). +-import(oauth2_client, [ + format_ssl_options/1, + format_oauth_provider_id/1, + get_oauth_provider/2]). +-import(rabbit_resource_server, [ + resolve_resource_server_from_audience/1]). +-import(rabbit_oauth_provider, [ + add_signing_key/2, get_signing_key/2, + get_internal_oauth_provider/1, + replace_signing_keys/2]). -type key_type() :: json | pem | map. @@ -25,7 +35,7 @@ add_signing_key(KeyId, Type, Value) -> case verify_signing_key(Type, Value) of ok -> - {ok, rabbit_oauth2_config:add_signing_key(KeyId, {Type, Value})}; + {ok, add_signing_key(KeyId, {Type, Value})}; {error, _} = Err -> Err end. @@ -34,7 +44,7 @@ add_signing_key(KeyId, Type, Value) -> update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, ssl_options = SslOptions}) -> rabbit_log:debug("Downloading signing keys from ~tp (TLS options: ~p)", - [JwksUrl, oauth2_client:format_ssl_options(SslOptions)]), + [JwksUrl, format_ssl_options(SslOptions)]), case uaa_jwks:get(JwksUrl, SslOptions) of {ok, {_, _, JwksBody}} -> KeyList = maps:get(<<"keys">>, @@ -42,7 +52,7 @@ update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), rabbit_log:debug("Downloaded ~p signing keys", [maps:size(Keys)]), - case rabbit_oauth2_config:replace_signing_keys(Keys, Id) of + case replace_signing_keys(Keys, Id) of {error, _} = Err -> Err; _ -> ok end; @@ -66,56 +76,56 @@ decode_and_verify(Token, ResourceServer, InternalOAuthProvider) -> OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, rabbit_log:debug("Decoding token for resource_server: ~p using oauth_provider_id: ~p", [ResourceServer#resource_server.id, - oauth2_client:format_oauth_provider_id(OAuthProviderId)]), + format_oauth_provider_id(OAuthProviderId)]), Result = case uaa_jwt_jwt:get_key_id(Token) of - undefined -> - InternalOAuthProvider#internal_oauth_provider.default_key; - {ok, KeyId} -> - KeyId; - {error, _} = Err -> - Err + undefined -> InternalOAuthProvider#internal_oauth_provider.default_key; + {ok, KeyId0} -> KeyId0; + {error, _} = Err -> Err end, case Result of - {error, _} = Err -> - Err; + {error, _} = Err2 -> + Err2; KeyId -> - case get_jwk(KeyId, OAuthProvider) of + case get_jwk(KeyId, InternalOAuthProvider) of {ok, JWK} -> - Algorithms = OAuthProvider#internal_oauth_provider.algorithms, + Algorithms = InternalOAuthProvider#internal_oauth_provider.algorithms, rabbit_log:debug("Verifying signature using signing_key_id : '~tp' and algorithms: ~p", [KeyId, Algorithms]), case uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token) of {true, Payload} -> {true, ResourceServer, Payload}; {false, Payload} -> {false, ResourceServer, Payload} end; - {error, _} = Err -> - Err + {error, _} = Err3 -> + Err3 end end. - resolve_resource_server(Token) -> case uaa_jwt_jwt:get_aud(Token) of {error, _} = Error -> Error; {ok, Audience} -> - ResourceServer = rabbit_oauth2_config:resolve_resource_server_from_audience(Audience) - {ResourceServer, - rabbit_oauth2_config:get_internal_oauth_provider(ResourceServer#resource_server.id)} + case resolve_resource_server_from_audience(Audience) of + {error, _} = Error -> + Error; + {ok, ResourceServer} -> + {ResourceServer, get_internal_oauth_provider( + ResourceServer#resource_server.id)} + end end. -spec get_jwk(binary(), internal_oauth_provider()) -> {ok, map()} | {error, term()}. -get_jwk(KeyId, OAuthProvider) -> - get_jwk(KeyId, OAuthProvider, true). +get_jwk(KeyId, InternalOAuthProvider) -> + get_jwk(KeyId, InternalOAuthProvider, true). get_jwk(KeyId, InternalOAuthProvider, AllowUpdateJwks) -> OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, - case rabbit_oauth2_config:get_signing_key(KeyId, OAuthProviderId) of + case get_signing_key(KeyId, OAuthProviderId) of undefined -> case AllowUpdateJwks of true -> rabbit_log:debug("Signing key '~tp' not found. Downloading it... ", [KeyId]), - case rabbit_oauth2_config:get_oauth_provider(OAuthProviderId, [jwks_uri]) of + case get_oauth_provider(OAuthProviderId, [jwks_uri]) of {ok, OAuthProvider} -> case update_jwks_signing_keys(OAuthProvider) of ok -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl index 5389f5f845fb..bd2cd557d0cf 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl @@ -6,7 +6,7 @@ %% -module(uaa_jwt_jwt). --export([decode_and_verify/3, get_key_id/2, get_aud/1]). +-export([decode_and_verify/3, get_key_id/1, get_aud/1]). -include_lib("jose/include/jose_jwt.hrl"). -include_lib("jose/include/jose_jws.hrl"). From af4ce0b1e811003f0eaa55535aa80b0ae00e14fd Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 14:41:11 +0100 Subject: [PATCH 09/64] WIP Fix compilation errors Fixing test cases --- .../include/oauth2.hrl | 2 +- .../test/rabbit_oauth_provider_SUITE.erl | 56 ++++-- .../test/rabbit_resource_server_SUITE.erl | 179 +++++++++--------- 3 files changed, 124 insertions(+), 113 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl index 7febcebf1d4a..bfc570082d4d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -6,7 +6,7 @@ %% --include_lib("oauth2_client/include/types.hrl"). +-include_lib("oauth2_client/include/oauth2_client.hrl"). -define(APP, rabbitmq_auth_backend_oauth2). -define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl index 19642ac964d0..dcf56515222e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl @@ -10,7 +10,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). --include_lib("oauth2_client/include/oauth2_client.hrl"). +-include("oauth2.hrl"). -define(RABBITMQ,<<"rabbitmq">>). -define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). @@ -18,7 +18,7 @@ -define(AUTH_PORT, 8000). -import(rabbit_oauth_provider, [ - get_internal_oauth_provider/2, + get_internal_oauth_provider/0,get_internal_oauth_provider/1, add_signing_key/2, add_signing_key/3, replace_signing_keys/1, replace_signing_keys/2, get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2 @@ -101,11 +101,13 @@ init_per_group(with_rabbitmq_node, Config) -> {rmq_nodes_count, 1} ]), rabbit_ct_helpers:run_steps(Config1, rabbit_ct_broker_helpers:setup_steps()); + init_per_group(with_default_key, Config) -> KeyConfig = get_env(key_config, []), set_env(key_config, proplists:delete(default_key, KeyConfig) ++ [{default_key,<<"default-key">>}]), Config; + init_per_group(with_root_static_signing_keys, Config) -> KeyConfig = call_get_env(Config, key_config, []), SigningKeys = #{ @@ -115,6 +117,7 @@ init_per_group(with_root_static_signing_keys, Config) -> call_set_env(Config, key_config, proplists:delete(default_key, KeyConfig) ++ [{signing_keys,SigningKeys}]), Config; + init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> OAuthProviders = call_get_env(Config, oauth_providers, #{}), OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), @@ -129,10 +132,10 @@ init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> OAuthProviders)), Config; - init_per_group(with_jwks_url, Config) -> KeyConfig = get_env(key_config, []), - set_env(key_config, KeyConfig ++ [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), + set_env(key_config, KeyConfig ++ + [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; init_per_group(with_issuer, Config) -> @@ -255,7 +258,7 @@ init_per_group(verify_oauth_provider_A, Config) -> #{ <<"A">> => [ {id, <<"A">>} ] - ] }), + }), Config; init_per_group(_any, Config) -> @@ -308,7 +311,6 @@ end_per_group(with_algorithms_for_provider_A, Config) -> proplists:delete(algorithms, OAuthProvider), OAuthProviders)), Config; - end_per_group(with_jwks_url, Config) -> KeyConfig = ?config(key_config_before_group_with_jwks_url, Config), set_env(key_config, KeyConfig), @@ -521,49 +523,62 @@ get_oauth_provider_A_with_jwks_uri_should_fail(_Config) -> get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri(_Config) -> {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_for_both_resources_should_return_root_oauth_provider(_Config) -> {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_for_resource_one_should_return_oauth_provider_A(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_for_both_resources_should_return_oauth_provider_A(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_for_resource_two_should_return_oauth_provider_B(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints(_Config) -> {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/">>), OAuthProvider#oauth_provider.issuer). + ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), + OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/">>), + OAuthProvider#oauth_provider.issuer). append_paths(Path1, Path2) -> erlang:iolist_to_binary([Path1, Path2]). get_oauth_provider_should_return_oauth_provider_B_with_jwks_uri(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_should_return_oauth_provider_B_with_all_discovered_endpoints(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/B">>), OAuthProvider#oauth_provider.issuer). + ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), + OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/B">>), + OAuthProvider#oauth_provider.issuer). get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri). + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), + OAuthProvider#oauth_provider.jwks_uri). get_oauth_provider_should_return_oauth_provider_A_with_all_discovered_endpoints(_Config) -> {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/A">>), OAuthProvider#oauth_provider.issuer). + ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), + OAuthProvider#oauth_provider.jwks_uri), + ?assertEqual(build_url_to_oauth_provider(<<"/A">>), + OAuthProvider#oauth_provider.issuer). %% ---- Utility functions @@ -629,7 +644,8 @@ get_openid_configuration_expectations() -> start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations) -> Dispatch = cowboy_router:compile([ - {'_', [{Path, oauth2_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} + {'_', [{Path, oauth2_http_mock, Expected} || + #{request := #{path := Path}} = Expected <- Expectations ]} ]), ct:log("start_https_oauth_server (port:~p) with expectation list : ~p -> dispatch: ~p", [Port, Expectations, Dispatch]), {ok, Pid} = cowboy:start_tls( diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index 389ea1d749f7..cd61754b8bd2 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -10,7 +10,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). --include_lib("oauth2_client/include/oauth2_client.hrl"). +-include("oauth2.hrl"). -define(RABBITMQ,<<"rabbitmq">>). -define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). @@ -20,8 +20,7 @@ -import(oauth2_client, [get_oauth_provider/2]). -import(rabbit_resource_server, [ - resolve_resource_server_id_from_audience/1, - get_resource_server/1 + resolve_resource_server_from_audience/1 ]). @@ -132,12 +131,6 @@ init_per_suite(Config) -> end_per_suite(Config) -> rabbit_ct_helpers:run_teardown_steps(Config). -init_per_group(with_jwks_url, Config) -> - KeyConfig = get_env(key_config, []), - set_env(key_config, KeyConfig ++ - [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), - [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; - init_per_group(with_default_oauth_provider_A, Config) -> set_env(default_oauth_provider, ?OAUTH_PROVIDER_A), Config; @@ -163,20 +156,19 @@ init_per_group(with_empty_scope_prefix, Config) -> init_per_group(with_additional_scopes_key, Config) -> Key = <<"roles">>, set_env(additional_scopes_key, Key), - [{additional_scopes_key, Prefix} | Config; + [{additional_scopes_key, Key} | Config]; init_per_group(with_preferred_username_claims, Config) -> Claims = [<<"new-user">>, <<"new-email">>], - set_env(preferred_username_claims, Key), - [{preferred_username_claims, Claims} | Config; - + set_env(preferred_username_claims, Claims), + [{preferred_username_claims, Claims} | Config]; init_per_group(with_scope_aliases, Config) -> Aliases = #{ - <<"admin">> -> [<<"rabbitmq.tag:administrator">>] + <<"admin">> => [<<"rabbitmq.tag:administrator">>] }, set_env(scope_aliases, Aliases), - [{scope_aliases, Aliases} | Config; + [{scope_aliases, Aliases} | Config]; init_per_group(with_empty_scope_prefix_for_resource_one, Config) -> ResourceServers = get_env(resource_servers, #{}), @@ -235,7 +227,7 @@ init_per_group(with_two_resource_servers, Config) -> {scope_prefix, <<"some-prefix">>}, {additional_scopes_key, <<"roles">>}, {preferred_username_claims, [<<"x-username">>, <<"x-email">>]}, - {scope_aliases, #{ <<"admin">> -> [<<"rabbitmq.tag:administrator"]}, + {scope_aliases, #{ <<"admin">> => [<<"rabbitmq.tag:administrator">>]}}, {oauth_provider_id, ?OAUTH_PROVIDER_A} ], RabbitMQ2 = [ @@ -245,8 +237,8 @@ init_per_group(with_two_resource_servers, Config) -> ?RABBITMQ_RESOURCE_ONE => RabbitMQ1, ?RABBITMQ_RESOURCE_TWO => RabbitMQ2 }), - [{?RABBITMQ_RESOURCE_ONE, RabbitMQ1} | {?RABBITMQ_RESOURCE_TWO, RabbitMQ2} - | Config; + [{?RABBITMQ_RESOURCE_ONE, RabbitMQ1}, {?RABBITMQ_RESOURCE_TWO, RabbitMQ2}] + ++ Config; init_per_group(inheritance_group, Config) -> set_env(resource_server_id, ?RABBITMQ), @@ -287,14 +279,14 @@ end_per_group(with_verify_aud_false, Config) -> Config; end_per_group(with_verify_aud_false_for_resource_two, Config) -> - ResourceServers = get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), + ResourceServers = get_env(resource_servers, #{}), Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_TWO, proplists:delete(verify_aud, Proplist), ResourceServers)), Config; end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = get_env(rabbitmq_auth_backend_oauth2, resource_servers, #{}), + ResourceServers = get_env(resource_servers, #{}), Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, proplists:delete(scope_prefix, Proplist), ResourceServers)), @@ -334,167 +326,170 @@ end_per_group(_any, Config) -> %% --- Test cases -resolve_resource_server_for_rabbitmq_audience(_ -> - ?RABBITMQ = resolve_resource_server_id_for_audience(?RABBITMQ). +resolve_resource_server_for_rabbitmq_audience(_) -> + {ok, #resource_server{id = ?RABBITMQ}} = + resolve_resource_server_from_audience(?RABBITMQ). resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> - ?RABBITMQ = resolve_resource_server_id_for_audience([?RABBITMQ, - <<"unknown">>]). + {ok, #resource_server{id = ?RABBITMQ}} = + resolve_resource_server_from_audience([?RABBITMQ, <<"unknown">>]). resolve_resource_server_for_none_audience_returns_error(_) -> {error, missing_audience_in_token} = - resolve_resource_server_id_for_audience(none). + resolve_resource_server_from_audience(none). resolve_resource_server_for_unknown_audience_returns_error(_) -> {error, no_matching_aud_found} = - resolve_resource_server_id_for_audience(<<"unknown">>). + resolve_resource_server_from_audience(<<"unknown">>). resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> - ?RABBITMQ = resolve_resource_server_id_for_audience(none). + {ok, #resource_server{id = ?RABBITMQ}} = + resolve_resource_server_from_audience(none). resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> - ?RABBITMQ = resolve_resource_server_id_for_audience(<<"unknown">>). + {ok, #resource_server{id = ?RABBITMQ}} = + resolve_resource_server_from_audience(<<"unknown">>). resolve_resource_server_id_for_any_audience_returns_error(_) -> {error, no_matching_aud_found} = - resolve_resource_server_id_for_audience(?RABBITMQ), + resolve_resource_server_from_audience(?RABBITMQ), {error, no_matching_aud_found} = - resolve_resource_server_id_for_audience(<<"unknown">>), + resolve_resource_server_from_audience(<<"unknown">>). resolve_resource_server_id_for_rabbitmq1(_) -> - ?RABBITMQ_RESOURCE_ONE = resolve_resource_server_id_for_audience( - ?RABBITMQ_RESOURCE_ONE). + {ok, #resource_server{id = ?RABBITMQ_RESOURCE_ONE}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE). resolve_resource_server_id_for_rabbitmq2(_) -> - ?RABBITMQ_RESOURCE_TWO = resolve_resource_server_id_for_audience( - ?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{id = ?RABBITMQ_RESOURCE_TWO}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). resolve_resource_server_id_for_both_resources_returns_error(_) -> {error, only_one_resource_server_as_audience_found_many} = - resolve_resource_server_id_for_audience([?RABBITMQ_RESOURCE_TWO, + resolve_resource_server_from_audience([?RABBITMQ_RESOURCE_TWO, ?RABBITMQ_RESOURCE_ONE]). rabbitmq_verify_aud_is_true(_) -> - #resource_server{verify_aud = true} = - resolve_resource_server_id_for_audience(?RABBITMQ). + {ok, #resource_server{verify_aud = true}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_verify_aud_is_false(_) -> - #resource_server{verify_aud = false} = - resolve_resource_server_id_for_audience(?RABBITMQ). + {ok, #resource_server{verify_aud = false}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq2_verify_aud_is_true(_) -> - #resource_server{verify_aud = true} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{verify_aud = true}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). both_resources_oauth_provider_id_is_root(_) -> - #resource_server{oauth_provider_id = root} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_ONE), - #resource_server{oauth_provider_id = root} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{oauth_provider_id = root}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), + {ok, #resource_server{oauth_provider_id = root}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_verify_aud_is_false(_) -> - #resource_server{verify_aud = false} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{verify_aud = false}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_scope_prefix(_) -> - #resource_server{scope_prefix = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{scope_prefix = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_scope_prefix(Config) -> - #resource_server{scope_prefix = ScopePrefix} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + {ok, #resource_server{scope_prefix = ScopePrefix}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), ?assertEqual(?config(scope_prefix, Config), ScopePrefix). rabbitmq2_oauth_provider_id_is_root(_) -> - #resource_server{oauth_provider_id = root} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{oauth_provider_id = root}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_oauth_provider_id_is_A(_) -> - #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_additional_scopes_key(_) -> - #resource_server{additional_scopes_key = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{additional_scopes_key = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_additional_scopes_key(Config) -> - #resource_server{additional_scopes_key = ScopesKey} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + {ok, #resource_server{additional_scopes_key = ScopesKey}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), ?assertEqual(?config(additional_scopes_key, Config), ScopesKey). rabbitmq2_has_no_preferred_username_claims_but_gets_default(_) -> - #resource_server{preferred_username_claims = Claims} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + {ok, #resource_server{preferred_username_claims = Claims}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), ?assertEqual(?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). rabbitmq2_has_preferred_username_claims_plus_default(Config) -> - #resource_server{preferred_username_claims = Claims} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), + {ok, #resource_server{preferred_username_claims = Claims}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), ?assertEqual(?config(preferred_username_claims, Config) ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). rabbitmq2_has_no_scope_aliases(_) -> - #resource_server{scope_aliases = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO). + {ok, #resource_server{scope_aliases = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). -rabbitmq2_has_scope_aliases(_) -> - #resource_server{scope_aliases = Aliases} = - resolve_resource_server_id_for_audience(?RABBITMQ_RESOURCE_TWO), +rabbitmq2_has_scope_aliases(Config) -> + {ok, #resource_server{scope_aliases = Aliases}} = + resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), ?assertEqual(?config(scope_aliases, Config), Aliases). rabbitmq_oauth_provider_id_is_root(_) -> - #resource_server{oauth_provider_id = root} = - resolve_resource_server_id_for_audience(?RABBITMQ). + {ok, #resource_server{oauth_provider_id = root}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_oauth_provider_id_is_A(_) -> - #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A} = - resolve_resource_server_id_for_audience(?RABBITMQ). + {ok, #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_has_no_scope_prefix(_) -> - #resource_server{scope_prefix = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ), + {ok, #resource_server{scope_prefix = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_has_scope_prefix(Config) -> - #resource_server{scope_prefix = ScopePrefix} = - resolve_resource_server_id_for_audience (?RABBITMQ), + {ok, #resource_server{scope_prefix = ScopePrefix}} = + resolve_resource_server_from_audience (?RABBITMQ), ?assertEqual(?config(scope_prefix, Config), ScopePrefix). rabbitmq_has_empty_scope_prefix() -> - #resource_server{scope_prefix = <<"">>} = - resolve_resource_server_id_for_audience (?RABBITMQ). + {ok, #resource_server{scope_prefix = <<"">>}} = + resolve_resource_server_from_audience (?RABBITMQ). rabbitmq_has_no_additional_scopes_key(_) -> - #resource_server{additional_scopes_key = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ), + {ok, #resource_server{additional_scopes_key = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_has_additional_scopes_key(Config) -> - #resource_server{additional_scopes_key = AdditionalScopesKey} = - resolve_resource_server_id_for_audience (?RABBITMQ), + {ok, #resource_server{additional_scopes_key = AdditionalScopesKey}} = + resolve_resource_server_from_audience (?RABBITMQ), ?assertEqual(?config(additional_scopes_key, Config), AdditionalScopesKey). rabbitmq_has_no_preferred_username_claims_but_gets_default(_) -> - #resource_server{preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS} = - resolve_resource_server_id_for_audience(?RABBITMQ). + {ok, #resource_server{preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_has_preferred_username_claims_plus_default(Config) -> - #resource_server{additional_scopes_key = AdditionalScopesKey} = - resolve_resource_server_id_for_audience (?RABBITMQ), + {ok, #resource_server{additional_scopes_key = AdditionalScopesKey}} = + resolve_resource_server_from_audience (?RABBITMQ), ?assertEqual(?config(preferred_username_claims, Config) ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, AdditionalScopesKey). rabbitmq_has_no_scope_aliases(_) -> - #resource_server{scope_aliases = undefined} = - resolve_resource_server_id_for_audience(?RABBITMQ), + {ok, #resource_server{scope_aliases = undefined}} = + resolve_resource_server_from_audience(?RABBITMQ). rabbitmq_has_scope_aliases(Config) -> - #resource_server{scope_aliases = Aliases} = - resolve_resource_server_id_for_audience (?RABBITMQ), + {ok, #resource_server{scope_aliases = Aliases}} = + resolve_resource_server_from_audience (?RABBITMQ), ?assertEqual(?config(scope_aliases, Config), Aliases). verify_rabbitmq1_server_configuration(Config) -> ConfigRabbitMQ = ?config(?RABBITMQ_RESOURCE_ONE, Config), - ActualRabbitMQ = get_resource_server(?RABBITMQ_RESOURCE_ONE), + ActualRabbitMQ = resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), ?assertEqual(ConfigRabbitMQ#resource_server.id, ActualRabbitMQ#resource_server.id), ?assertEqual(ConfigRabbitMQ#resource_server.resource_server_type, @@ -522,4 +517,4 @@ get_env(Par, Def) -> set_env(Par, Val) -> application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). unset_env(Par) -> - unset_env(rabbitmq_auth_backend_oauth2, Par). + application:unset_env(rabbitmq_auth_backend_oauth2, Par). From 4576aaa32ee55618f2a330b4caeed0ff3c51a945 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 16:35:22 +0100 Subject: [PATCH 10/64] Refactor assertion function --- .../src/rabbit_resource_server.erl | 34 +- .../test/rabbit_resource_server_SUITE.erl | 393 +++++++----------- 2 files changed, 172 insertions(+), 255 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index 067ef2876b8f..60c4bca65551 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -16,12 +16,13 @@ -spec resolve_resource_server_from_audience(binary() | list() | none) -> {ok, resource_server()} | - {error, only_one_resource_server_as_audience_found_many} | + {error, aud_matched_many_resource_servers_only_one_allowed} | {error, no_matching_aud_found} | - {error, zero_declared_resource_servers} | - {error, cannot_default_resource_server_found_many}. + {error, no_aud_found} | + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}. resolve_resource_server_from_audience(none) -> - find_unique_resource_server_without_verify_aud(get_root_resource_server()); + translate_error_if_any(find_unique_resource_server_without_verify_aud( + get_root_resource_server())); resolve_resource_server_from_audience(Audience) -> Root = get_root_resource_server(), @@ -38,7 +39,8 @@ resolve_resource_server_from_audience(Audience) -> {error, only_one_resource_server_as_audience_found_many} = Error -> Error; {error, no_matching_aud_found} -> - find_unique_resource_server_without_verify_aud(Root); + translate_error_if_any( + find_unique_resource_server_without_verify_aud(Root)); {ok, RootResourseServerId} -> {ok, Root}; {ok, ResourceServerId} -> @@ -142,16 +144,32 @@ find_audience(AudList, ResourceIdList) when is_list(AudList) -> [] -> {error, no_matching_aud_found} end. +-spec translate_error_if_any({ok, resource_server()} | + {error, not_found} | {error, found_too_many}) -> + {ok, resource_server()} | + {error, no_aud_found} | + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}. +translate_error_if_any(ResourceServerOrError) -> + case ResourceServerOrError of + {ok, _} = Ok -> + Ok; + {error, not_found} -> + {error, no_aud_found}; + {error, found_too_many} -> + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} + end. -spec find_unique_resource_server_without_verify_aud(resource_server()) -> - {ok, resource_server()} | {error, not_found} | {error, too_many}. + {ok, resource_server()} | + {error, not_found} | + {error, found_too_many}. find_unique_resource_server_without_verify_aud(Root) -> Map = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, Root#resource_server.verify_aud) end, get_env(resource_servers, #{})), case {maps:size(Map), Root} of - {0, undefined} -> {error, zero_declared_resource_servers}; + {0, undefined} -> {error, not_found}; {0, _} -> {ok, Root}; {1, undefined} -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; - {_, _} -> {error, cannot_default_resource_server_found_many} + {_, _} -> {error, found_too_many} end. append_or_return_default(ListOrBinary, Default) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index cd61754b8bd2..e204263a6678 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -19,94 +19,38 @@ -define(OAUTH_PROVIDER_B,<<"B">>). -import(oauth2_client, [get_oauth_provider/2]). --import(rabbit_resource_server, [ - resolve_resource_server_from_audience/1 -]). +-import(rabbit_resource_server, [resolve_resource_server_from_audience/1]). all() -> [ {group, without_resource_server_id}, {group, with_rabbitmq_as_resource_server_id}, - {group, with_two_resource_servers}, - {group, with_two_resource_servers_and_rabbitmq_as_resource_server_id} + {group, with_two_resource_servers} + %{group, with_two_resource_servers_and_rabbitmq_as_resource_server_id} ]. groups() -> [ - - {verify_get_rabbitmq_server_configuration, [], [ - rabbitmq_verify_aud_is_true, - {with_verify_aud_false, [], [ - rabbitmq_verify_aud_is_false - ]}, - rabbitmq_has_no_scope_prefix, - {with_scope_prefix, [], [ - rabbitmq_has_scope_prefix - ]}, - {with_empty_scope_prefix, [], [ - rabbitmq_has_empty_scope_prefix - ]}, - rabbitmq_oauth_provider_id_is_root, - {with_default_oauth_provider_A, [], [ - rabbitmq_oauth_provider_id_is_A - ]}, - rabbitmq_has_no_additional_scopes_key, - {with_additional_scopes_key, [], [ - rabbitmq_has_additional_scopes_key - ]}, - rabbitmq_has_no_preferred_username_claims_but_gets_default, - {with_preferred_username_claims, [], [ - rabbitmq_has_preferred_username_claims_plus_default - ]}, - rabbitmq_has_no_scope_aliases, - {with_scope_aliases, [], [ - rabbitmq_has_scope_aliases - ]} - ]}, {with_rabbitmq_as_resource_server_id, [], [ resolve_resource_server_for_rabbitmq_audience, resolve_resource_server_for_rabbitmq_plus_unknown_audience, - resolve_resource_server_for_none_audience_returns_error, - resolve_resource_server_for_unknown_audience_returns_error, + resolve_resource_server_for_none_audience_returns_no_aud_found, + resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, {with_verify_aud_false, [], [ resolve_resource_server_for_none_audience_returns_rabbitmq, resolve_resource_server_for_unknown_audience_returns_rabbitmq ]}, - {group, verify_get_rabbitmq_server_configuration} + {verify_get_rabbitmq_server_configuration, [], + verify_get_rabbitmq_server_configuration()} ]}, {without_resource_server_id, [], [ resolve_resource_server_id_for_any_audience_returns_error ]}, - {verify_configuration_inheritance_with_rabbitmq2, [], [ - rabbitmq2_verify_aud_is_true, - {with_verify_aud_false, [], [ - rabbitmq2_verify_aud_is_false - ]}, - rabbitmq2_has_no_scope_prefix, - {with_scope_prefix, [], [ - rabbitmq2_has_scope_prefix - ]}, - rabbitmq2_oauth_provider_id_is_root, - {with_default_oauth_provider_A, [], [ - rabbitmq2_oauth_provider_id_is_A - ]}, - rabbitmq2_has_no_additional_scopes_key, - {with_additional_scopes_key, [], [ - rabbitmq2_has_additional_scopes_key - ]}, - rabbitmq2_has_no_preferred_username_claims_but_gets_default, - {with_preferred_username_claims, [], [ - rabbitmq2_has_preferred_username_claims_plus_default - ]}, - rabbitmq2_has_no_scope_aliases, - {with_scope_aliases, [], [ - rabbitmq2_has_scope_aliases - ]} - ]}, + {with_two_resource_servers, [], [ resolve_resource_server_id_for_rabbitmq1, resolve_resource_server_id_for_rabbitmq2, resolve_resource_server_id_for_both_resources_returns_error, - resolve_resource_server_for_none_audience_returns_error, - resolve_resource_server_for_unknown_audience_returns_error, + resolve_resource_server_for_none_audience_returns_no_aud_found, + resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, {with_verify_aud_false, [], [ resolve_resource_server_for_none_audience_returns_rabbitmq1, resolve_resource_server_for_unknown_audience_returns_rabbitmq1, @@ -114,8 +58,9 @@ groups() -> [ resolve_resource_server_for_none_audience_returns_error ]} ]}, - {group, verify_rabbitmq1_server_configuration}, - {group, verify_configuration_inheritance_with_rabbitmq2}, + verify_rabbitmq1_server_configuration, + {verify_configuration_inheritance_with_rabbitmq2, [], + verify_configuration_inheritance_with_rabbitmq2()}, {with_rabbitmq_as_resource_server_id, [], [ resolve_resource_server_for_rabbitmq_audience, resolve_resource_server_id_for_rabbitmq1, @@ -124,6 +69,63 @@ groups() -> [ ]} ]. +verify_get_rabbitmq_server_configuration() -> [ + rabbitmq_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq_verify_aud_is_false + ]}, + rabbitmq_has_no_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq_has_scope_prefix + ]}, + {with_empty_scope_prefix, [], [ + rabbitmq_has_empty_scope_prefix + ]}, + rabbitmq_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq_oauth_provider_id_is_A + ]}, + rabbitmq_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq_has_additional_scopes_key + ]}, + rabbitmq_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq_has_preferred_username_claims_plus_default + ]}, + rabbitmq_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq_has_scope_aliases + ]} +]. + +verify_configuration_inheritance_with_rabbitmq2() -> [ + rabbitmq2_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq2_verify_aud_is_false + ]}, + rabbitmq2_has_no_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq2_has_scope_prefix + ]}, + rabbitmq2_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq2_oauth_provider_id_is_A + ]}, + rabbitmq2_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq2_has_additional_scopes_key + ]}, + rabbitmq2_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq2_has_preferred_username_claims_plus_default + ]}, + rabbitmq2_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq2_has_scope_aliases + ]} +]. + init_per_suite(Config) -> rabbit_ct_helpers:log_environment(), rabbit_ct_helpers:run_setup_steps(Config). @@ -170,26 +172,10 @@ init_per_group(with_scope_aliases, Config) -> set_env(scope_aliases, Aliases), [{scope_aliases, Aliases} | Config]; -init_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), - set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, - [{scope_prefix, <<"">>} | proplists:delete(scope_prefix, Proplist)], - ResourceServers)), - Config; - init_per_group(with_verify_aud_false, Config) -> set_env(verify_aud, false), Config; -init_per_group(with_rabbitmq2_verify_aud_false, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), - set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_TWO, - [{verify_aud, false} | proplists:delete(verify_aud, Proplist)], - ResourceServers)), - Config; - init_per_group(with_two_resource_servers_and_rabbitmq_as_resource_server_id, Config) -> set_env(resource_server_id, ?RABBITMQ), set_env(key_config, [{jwks_url,<<"https://oauth-for-rabbitmq">> }]), @@ -208,17 +194,6 @@ init_per_group(with_two_resource_servers_and_rabbitmq_as_resource_server_id, Con }), Config; -init_per_group(with_different_oauth_provider_for_each_resource, Config) -> - {ok, ResourceServers} = get_env(resource_servers), - Rabbit1 = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers) ++ - [ {oauth_provider_id, <<"A">>} ], - Rabbit2 = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers) ++ - [ {oauth_provider_id, <<"B">>} ], - ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), - set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, - ResourceServers1)), - Config; - init_per_group(with_two_resource_servers, Config) -> RabbitMQ1 = [ {id, ?RABBITMQ_RESOURCE_ONE}, @@ -240,29 +215,6 @@ init_per_group(with_two_resource_servers, Config) -> [{?RABBITMQ_RESOURCE_ONE, RabbitMQ1}, {?RABBITMQ_RESOURCE_TWO, RabbitMQ2}] ++ Config; -init_per_group(inheritance_group, Config) -> - set_env(resource_server_id, ?RABBITMQ), - set_env(resource_server_type, <<"rabbitmq-type">>), - set_env(scope_prefix, <<"some-prefix-">>), - set_env(extra_scopes_source, <<"roles">>), - set_env(scope_aliases, #{}), - - set_env(key_config, [ {jwks_url,<<"https://oauth-for-rabbitmq">> } ]), - - set_env(resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { extra_scopes_source, <<"extra-scope-1">>}, - { verify_aud, false}, - { preferred_username_claims, [<<"email-address">>] }, - { scope_prefix, <<"my-prefix:">> }, - { resource_server_type, <<"my-type">> }, - { scope_aliases, #{} } - ], - ?RABBITMQ_RESOURCE_TWO => [ {id, ?RABBITMQ_RESOURCE_TWO } ] - } - ), - Config; - init_per_group(_any, Config) -> Config. @@ -270,52 +222,14 @@ end_per_group(with_empty_scope_prefix, Config) -> unset_env(scope_prefix), Config; -end_per_group(with_resource_server_id, Config) -> - unset_env(resource_server_id), - Config; - end_per_group(with_verify_aud_false, Config) -> unset_env(verify_aud), Config; -end_per_group(with_verify_aud_false_for_resource_two, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), - set_env(resource_servers, - maps:put(?RABBITMQ_RESOURCE_TWO, proplists:delete(verify_aud, Proplist), ResourceServers)), - Config; - -end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), - set_env(resource_servers, - maps:put(?RABBITMQ_RESOURCE_ONE, proplists:delete(scope_prefix, Proplist), ResourceServers)), - Config; - end_per_group(with_two_resource_servers, Config) -> unset_env(resource_servers), Config; -end_per_group(with_different_oauth_provider_for_each_resource, Config) -> - {ok, ResourceServers} = get_env(resource_servers), - Rabbit1 = proplists:delete(oauth_provider_id, - maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers)), - Rabbit2 = proplists:delete(oauth_provider_id, - maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers)), - ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, - ResourceServers), - set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, - ResourceServers1)), - Config; - -end_per_group(inheritance_group, Config) -> - unset_env(resource_server_id), - unset_env(scope_prefix), - unset_env(extra_scopes_source), - unset_env(key_config), - unset_env(resource_servers), - Config; - end_per_group(with_scope_prefix, Config) -> unset_env(scope_prefix), Config; @@ -327,165 +241,120 @@ end_per_group(_any, Config) -> %% --- Test cases resolve_resource_server_for_rabbitmq_audience(_) -> - {ok, #resource_server{id = ?RABBITMQ}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_resource_server_id(?RABBITMQ, ?RABBITMQ). resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> - {ok, #resource_server{id = ?RABBITMQ}} = - resolve_resource_server_from_audience([?RABBITMQ, <<"unknown">>]). + assert_resource_server_id(?RABBITMQ, [?RABBITMQ, <<"unknown">>]). -resolve_resource_server_for_none_audience_returns_error(_) -> - {error, missing_audience_in_token} = - resolve_resource_server_from_audience(none). +resolve_resource_server_for_none_audience_returns_no_aud_found(_) -> + assert_resource_server_id({error, no_aud_found}, none). -resolve_resource_server_for_unknown_audience_returns_error(_) -> - {error, no_matching_aud_found} = - resolve_resource_server_from_audience(<<"unknown">>). +resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found(_) -> + assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> - {ok, #resource_server{id = ?RABBITMQ}} = - resolve_resource_server_from_audience(none). + assert_resource_server_id(?RABBITMQ, none). resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> - {ok, #resource_server{id = ?RABBITMQ}} = - resolve_resource_server_from_audience(<<"unknown">>). + assert_resource_server_id(?RABBITMQ, <<"unknown">>). resolve_resource_server_id_for_any_audience_returns_error(_) -> - {error, no_matching_aud_found} = - resolve_resource_server_from_audience(?RABBITMQ), - {error, no_matching_aud_found} = - resolve_resource_server_from_audience(<<"unknown">>). + assert_resource_server_id({error, no_matching_aud_found}, ?RABBITMQ), + assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). resolve_resource_server_id_for_rabbitmq1(_) -> - {ok, #resource_server{id = ?RABBITMQ_RESOURCE_ONE}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE). + assert_resource_server_id(?RABBITMQ_RESOURCE_ONE, ?RABBITMQ_RESOURCE_ONE). resolve_resource_server_id_for_rabbitmq2(_) -> - {ok, #resource_server{id = ?RABBITMQ_RESOURCE_TWO}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, ?RABBITMQ_RESOURCE_TWO). resolve_resource_server_id_for_both_resources_returns_error(_) -> - {error, only_one_resource_server_as_audience_found_many} = - resolve_resource_server_from_audience([?RABBITMQ_RESOURCE_TWO, - ?RABBITMQ_RESOURCE_ONE]). + assert_resource_server_id({error, aud_matched_many_resource_servers_only_one_allowed}, + [?RABBITMQ_RESOURCE_TWO, ?RABBITMQ_RESOURCE_ONE]). rabbitmq_verify_aud_is_true(_) -> - {ok, #resource_server{verify_aud = true}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_verify_aud(true, ?RABBITMQ). rabbitmq_verify_aud_is_false(_) -> - {ok, #resource_server{verify_aud = false}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_verify_aud(false, ?RABBITMQ). rabbitmq2_verify_aud_is_true(_) -> - {ok, #resource_server{verify_aud = true}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_verify_aud(true, ?RABBITMQ_RESOURCE_TWO). both_resources_oauth_provider_id_is_root(_) -> - {ok, #resource_server{oauth_provider_id = root}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), - {ok, #resource_server{oauth_provider_id = root}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_ONE), + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_verify_aud_is_false(_) -> - {ok, #resource_server{verify_aud = false}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_verify_aud(false, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_scope_prefix(_) -> - {ok, #resource_server{scope_prefix = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_scope_prefix(undefined, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_scope_prefix(Config) -> - {ok, #resource_server{scope_prefix = ScopePrefix}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), - ?assertEqual(?config(scope_prefix, Config), ScopePrefix). + assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ_RESOURCE_TWO). rabbitmq2_oauth_provider_id_is_root(_) -> - {ok, #resource_server{oauth_provider_id = root}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_oauth_provider_id_is_A(_) -> - {ok, #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_additional_scopes_key(_) -> - {ok, #resource_server{additional_scopes_key = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_additional_scopes_key(undefined, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_additional_scopes_key(Config) -> - {ok, #resource_server{additional_scopes_key = ScopesKey}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), - ?assertEqual(?config(additional_scopes_key, Config), ScopesKey). + assert_additional_scopes_key(?config(additional_scopes_key, Config), + ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_preferred_username_claims_but_gets_default(_) -> - {ok, #resource_server{preferred_username_claims = Claims}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), - ?assertEqual(?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). + assert_preferred_username_claims(?DEFAULT_PREFERRED_USERNAME_CLAIMS, + ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_preferred_username_claims_plus_default(Config) -> - {ok, #resource_server{preferred_username_claims = Claims}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), - ?assertEqual(?config(preferred_username_claims, Config) - ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, Claims). + assert_preferred_username_claims(?config(preferred_username_claims, Config) + ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_scope_aliases(_) -> - {ok, #resource_server{scope_aliases = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO). + assert_scope_aliases(undefined, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_scope_aliases(Config) -> - {ok, #resource_server{scope_aliases = Aliases}} = - resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_TWO), - ?assertEqual(?config(scope_aliases, Config), Aliases). + assert_scope_aliases(?config(scope_aliases, Config), ?RABBITMQ_RESOURCE_TWO). rabbitmq_oauth_provider_id_is_root(_) -> - {ok, #resource_server{oauth_provider_id = root}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_oauth_provider_id(root, ?RABBITMQ). rabbitmq_oauth_provider_id_is_A(_) -> - {ok, #resource_server{oauth_provider_id = ?OAUTH_PROVIDER_A}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ). rabbitmq_has_no_scope_prefix(_) -> - {ok, #resource_server{scope_prefix = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_scope_prefix(undefined, ?RABBITMQ). rabbitmq_has_scope_prefix(Config) -> - {ok, #resource_server{scope_prefix = ScopePrefix}} = - resolve_resource_server_from_audience (?RABBITMQ), - ?assertEqual(?config(scope_prefix, Config), ScopePrefix). + assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ). rabbitmq_has_empty_scope_prefix() -> - {ok, #resource_server{scope_prefix = <<"">>}} = - resolve_resource_server_from_audience (?RABBITMQ). + assert_scope_prefix(<<"">>, ?RABBITMQ). rabbitmq_has_no_additional_scopes_key(_) -> - {ok, #resource_server{additional_scopes_key = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_additional_scopes_key(undefined, ?RABBITMQ). rabbitmq_has_additional_scopes_key(Config) -> - {ok, #resource_server{additional_scopes_key = AdditionalScopesKey}} = - resolve_resource_server_from_audience (?RABBITMQ), - ?assertEqual(?config(additional_scopes_key, Config), AdditionalScopesKey). + assert_additional_scopes_key(?config(additional_scopes_key, Config), + ?RABBITMQ). rabbitmq_has_no_preferred_username_claims_but_gets_default(_) -> - {ok, #resource_server{preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_preferred_username_claims(?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ). rabbitmq_has_preferred_username_claims_plus_default(Config) -> - {ok, #resource_server{additional_scopes_key = AdditionalScopesKey}} = - resolve_resource_server_from_audience (?RABBITMQ), - ?assertEqual(?config(preferred_username_claims, Config) ++ - ?DEFAULT_PREFERRED_USERNAME_CLAIMS, AdditionalScopesKey). + assert_preferred_username_claims(?config(preferred_username_claims, Config) ++ + ?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ). rabbitmq_has_no_scope_aliases(_) -> - {ok, #resource_server{scope_aliases = undefined}} = - resolve_resource_server_from_audience(?RABBITMQ). + assert_scope_aliases(undefined, ?RABBITMQ). rabbitmq_has_scope_aliases(Config) -> - {ok, #resource_server{scope_aliases = Aliases}} = - resolve_resource_server_from_audience (?RABBITMQ), - ?assertEqual(?config(scope_aliases, Config), Aliases). - + assert_scope_aliases(?config(scope_aliases, Config), ?RABBITMQ). verify_rabbitmq1_server_configuration(Config) -> ConfigRabbitMQ = ?config(?RABBITMQ_RESOURCE_ONE, Config), @@ -510,6 +379,36 @@ verify_rabbitmq1_server_configuration(Config) -> %% ----- +assert_resource_server_id(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.id); +assert_resource_server_id({error, ExpectedError}, Audience) -> + {error, ExpectedError} = resolve_resource_server_from_audience(Audience). + +assert_verify_aud(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.verify_aud). + +assert_oauth_provider_id(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.oauth_provider_id). + +assert_scope_prefix(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.scope_prefix). + +assert_additional_scopes_key(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.additional_scopes_key). + +assert_preferred_username_claims(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.preferred_username_claims). + +assert_scope_aliases(Expected, Audience) -> + Actual = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.scope_aliases). + get_env(Par) -> application:get_env(rabbitmq_auth_backend_oauth2, Par). get_env(Par, Def) -> From aecb86d56d6be7558d60a69fbb7863209b20e5df Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 18:08:52 +0100 Subject: [PATCH 11/64] WIP Fix test cases --- .../src/rabbit_resource_server.erl | 112 +++++++++++------- .../test/rabbit_resource_server_SUITE.erl | 24 ++-- 2 files changed, 81 insertions(+), 55 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index 60c4bca65551..c47dabfaaa25 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -8,7 +8,6 @@ -module(rabbit_resource_server). -include("oauth2.hrl"). --define(ROOT_RESOURCE_SERVER_ID, application:get_env(?APP, resource_server_id)). -export([ resolve_resource_server_from_audience/1 @@ -19,41 +18,38 @@ {error, aud_matched_many_resource_servers_only_one_allowed} | {error, no_matching_aud_found} | {error, no_aud_found} | - {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}. + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} | + {error, too_many_resources_with_verify_aud_false}. resolve_resource_server_from_audience(none) -> - translate_error_if_any(find_unique_resource_server_without_verify_aud( - get_root_resource_server())); + translate_error_if_any( + find_unique_resource_server_without_verify_aud(), false); resolve_resource_server_from_audience(Audience) -> - Root = get_root_resource_server(), + RootResourseServerId = get_root_resource_server_id(), ResourceServers = get_env(resource_servers, #{}), ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ [proplists:get_value(id, V, K)] end, [], ResourceServers), - AllowedResourceServerIds = ResourceServerIds ++ - case Root#resource_server.id of - undefined -> []; - ID -> [ID] - end, - RootResourseServerId = Root#resource_server.id, + AllowedResourceServerIds = append(ResourceServerIds, RootResourseServerId), + case find_audience(Audience, AllowedResourceServerIds) of - {error, only_one_resource_server_as_audience_found_many} = Error -> + {error, aud_matched_many_resource_servers_only_one_allowed} = Error -> Error; {error, no_matching_aud_found} -> translate_error_if_any( - find_unique_resource_server_without_verify_aud(Root)); - {ok, RootResourseServerId} -> - {ok, Root}; + find_unique_resource_server_without_verify_aud(), + true); {ok, ResourceServerId} -> - {ok, get_resource_server(ResourceServerId, Root)} + {ok, get_resource_server(ResourceServerId)} end. +-spec get_root_resource_server_id() -> resource_server_id(). +get_root_resource_server_id() -> + get_env(resource_server_id). + -spec get_root_resource_server() -> resource_server(). get_root_resource_server() -> ResourceServerId = - case ?ROOT_RESOURCE_SERVER_ID of - undefined -> undefined; - {ok, V} -> V - end, + get_root_resource_server_id(), ScopeAliases = get_env(scope_aliases), PreferredUsernameClaims = @@ -92,9 +88,22 @@ get_root_resource_server() -> oauth_provider_id = OAuthProviderId }. --spec get_resource_server(resource_server_id(), resource_server()) -> - resource_server(). -get_resource_server(ResourceServerId, RootResourseServer) -> +-spec get_resource_server(resource_server_id()) -> resource_server(). +get_resource_server(ResourceServerId) -> + RootResourseServer = get_root_resource_server(), + RootResourseServerId = RootResourseServer#resource_server.id, + case ResourceServerId of + undefined -> undefined; + RootResourseServerId -> RootResourseServer; + _ -> get_resource_server(ResourceServerId, RootResourseServer) + end. + +-spec get_resource_server(resource_server_id(), resource_server()) -> resource_server(). +get_resource_server(ResourceServerId, RootResourseServer) when + ResourceServerId == RootResourseServer#resource_server.id -> + RootResourseServer; +get_resource_server(ResourceServerId, RootResourseServer) when + ResourceServerId =/= RootResourseServer#resource_server.id -> ResourceServerProps = maps:get(ResourceServerId, get_env(resource_servers, #{}), []), ScopeAliases = @@ -132,7 +141,7 @@ get_resource_server(ResourceServerId, RootResourseServer) -> -spec find_audience(binary() | list(), list()) -> {ok, resource_server_id()} | - {error, only_one_resource_server_as_audience_found_many} | + {error, aud_matched_many_resource_servers_only_one_allowed} | {error, no_matching_aud_found}. find_audience(Audience, ResourceIdList) when is_binary(Audience) -> AudList = binary:split(Audience, <<" ">>, [global, trim_all]), @@ -140,36 +149,49 @@ find_audience(Audience, ResourceIdList) when is_binary(Audience) -> find_audience(AudList, ResourceIdList) when is_list(AudList) -> case intersection(AudList, ResourceIdList) of [One] -> {ok, One}; - [_One|_Tail] -> {error, only_one_resource_server_as_audience_found_many}; + [_One|_Tail] -> {error, aud_matched_many_resource_servers_only_one_allowed}; [] -> {error, no_matching_aud_found} end. --spec translate_error_if_any({ok, resource_server()} | - {error, not_found} | {error, found_too_many}) -> +-spec translate_error_if_any( + {ok, resource_server()} | + {error, not_found} | + {error, found_too_many}, boolean()) -> {ok, resource_server()} | {error, no_aud_found} | - {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}. -translate_error_if_any(ResourceServerOrError) -> - case ResourceServerOrError of - {ok, _} = Ok -> + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} | + {error, no_matching_aud_found} | + {error, too_many_resources_with_verify_aud_false}. +translate_error_if_any(ResourceServerOrError, HasAudience) -> + case {ResourceServerOrError, HasAudience} of + {{ok, _}, _} = Ok -> Ok; - {error, not_found} -> + {{error, not_found}, false} -> {error, no_aud_found}; - {error, found_too_many} -> - {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} + {{error, not_found}, _} -> + {error, no_matching_aud_found}; + {{error, found_too_many}, false} -> + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}; + {{error, found_too_many}, _} -> + {error, too_many_resources_with_verify_aud_false} end. --spec find_unique_resource_server_without_verify_aud(resource_server()) -> +-spec find_unique_resource_server_without_verify_aud() -> {ok, resource_server()} | {error, not_found} | {error, found_too_many}. -find_unique_resource_server_without_verify_aud(Root) -> - Map = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, +find_unique_resource_server_without_verify_aud() -> + Root = get_root_resource_server(), + Map0 = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, Root#resource_server.verify_aud) end, get_env(resource_servers, #{})), - case {maps:size(Map), Root} of - {0, undefined} -> {error, not_found}; - {0, _} -> {ok, Root}; - {1, undefined} -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; - {_, _} -> {error, found_too_many} + Map = case {Root#resource_server.id, Root#resource_server.verify_aud} of + {undefined, _} -> Map0; + {_, true} -> Map0; + {Id, false} -> maps:put(Id, Root, Map0) + end, + case maps:size(Map) of + 0 -> {error, not_found}; + 1 -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; + _ -> {error, found_too_many} end. append_or_return_default(ListOrBinary, Default) -> @@ -178,7 +200,11 @@ append_or_return_default(ListOrBinary, Default) -> VarBinary when is_binary(VarBinary) -> [VarBinary] ++ Default; _ -> Default end. - +append(List, Value) -> + case Value of + undefined -> List; + _ -> List ++ [Value] + end. get_env(Par) -> application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). get_env(Par, Def) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index e204263a6678..95a56ca2728a 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -42,7 +42,7 @@ groups() -> [ verify_get_rabbitmq_server_configuration()} ]}, {without_resource_server_id, [], [ - resolve_resource_server_id_for_any_audience_returns_error + resolve_resource_server_id_for_any_audience_returns_no_matching_aud_found ]}, {with_two_resource_servers, [], [ @@ -258,7 +258,7 @@ resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> assert_resource_server_id(?RABBITMQ, <<"unknown">>). -resolve_resource_server_id_for_any_audience_returns_error(_) -> +resolve_resource_server_id_for_any_audience_returns_no_matching_aud_found(_) -> assert_resource_server_id({error, no_matching_aud_found}, ?RABBITMQ), assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). @@ -379,34 +379,34 @@ verify_rabbitmq1_server_configuration(Config) -> %% ----- -assert_resource_server_id(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), - ?assertEqual(Expected, Actual#resource_server.id); assert_resource_server_id({error, ExpectedError}, Audience) -> - {error, ExpectedError} = resolve_resource_server_from_audience(Audience). + {error, ExpectedError} = resolve_resource_server_from_audience(Audience); +assert_resource_server_id(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.id). assert_verify_aud(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.verify_aud). assert_oauth_provider_id(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.oauth_provider_id). assert_scope_prefix(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.scope_prefix). assert_additional_scopes_key(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.additional_scopes_key). assert_preferred_username_claims(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.preferred_username_claims). assert_scope_aliases(Expected, Audience) -> - Actual = resolve_resource_server_from_audience(Audience), + {ok, Actual} = resolve_resource_server_from_audience(Audience), ?assertEqual(Expected, Actual#resource_server.scope_aliases). get_env(Par) -> From 158fa3b6b1f0536cd7d9bd2878da36886975e8f1 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 18:31:30 +0100 Subject: [PATCH 12/64] WIP fix some test cases Pending to add more scenarios whch combine +2 resources with and without verify_aud and with and without audience in token --- .../src/rabbit_resource_server.erl | 10 +++++----- .../test/rabbit_resource_server_SUITE.erl | 16 ++++++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index c47dabfaaa25..c9e9179e0068 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -54,9 +54,9 @@ get_root_resource_server() -> get_env(scope_aliases), PreferredUsernameClaims = case get_env(preferred_username_claims) of - {ok, Value} -> - append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS); - _ -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS + undefined -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS; + Value -> + append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS) end, ResourceServerType = get_env(resource_server_type), @@ -74,7 +74,7 @@ get_root_resource_server() -> OAuthProviderId = case get_env(default_oauth_provider) of undefined -> root; - {ok, DefaultOauthProviderId} -> DefaultOauthProviderId + DefaultOauthProviderId -> DefaultOauthProviderId end, #resource_server{ @@ -164,7 +164,7 @@ find_audience(AudList, ResourceIdList) when is_list(AudList) -> {error, too_many_resources_with_verify_aud_false}. translate_error_if_any(ResourceServerOrError, HasAudience) -> case {ResourceServerOrError, HasAudience} of - {{ok, _}, _} = Ok -> + {{ok, _} = Ok, _} -> Ok; {{error, not_found}, false} -> {error, no_aud_found}; diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index 95a56ca2728a..a9d2adf27ab8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -49,7 +49,7 @@ groups() -> [ resolve_resource_server_id_for_rabbitmq1, resolve_resource_server_id_for_rabbitmq2, resolve_resource_server_id_for_both_resources_returns_error, - resolve_resource_server_for_none_audience_returns_no_aud_found, + resolve_resource_server_for_none_audience_returns_rabbitmq1, resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, {with_verify_aud_false, [], [ resolve_resource_server_for_none_audience_returns_rabbitmq1, @@ -157,7 +157,7 @@ init_per_group(with_empty_scope_prefix, Config) -> init_per_group(with_additional_scopes_key, Config) -> Key = <<"roles">>, - set_env(additional_scopes_key, Key), + set_env(extra_scopes_source, Key), [{additional_scopes_key, Key} | Config]; init_per_group(with_preferred_username_claims, Config) -> @@ -206,7 +206,7 @@ init_per_group(with_two_resource_servers, Config) -> {oauth_provider_id, ?OAUTH_PROVIDER_A} ], RabbitMQ2 = [ - {id, ?RABBITMQ_RESOURCE_ONE} + {id, ?RABBITMQ_RESOURCE_TWO} ], set_env(resource_servers, #{ ?RABBITMQ_RESOURCE_ONE => RabbitMQ1, @@ -249,6 +249,9 @@ resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> resolve_resource_server_for_none_audience_returns_no_aud_found(_) -> assert_resource_server_id({error, no_aud_found}, none). +resolve_resource_server_for_none_audience_returns_rabbitmq1(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_ONE, none). + resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found(_) -> assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). @@ -289,7 +292,8 @@ rabbitmq2_verify_aud_is_false(_) -> assert_verify_aud(false, ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_scope_prefix(_) -> - assert_scope_prefix(undefined, ?RABBITMQ_RESOURCE_TWO). + assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ_RESOURCE_TWO, <<".">>]), + ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_scope_prefix(Config) -> assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ_RESOURCE_TWO). @@ -328,12 +332,12 @@ rabbitmq_oauth_provider_id_is_A(_) -> assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ). rabbitmq_has_no_scope_prefix(_) -> - assert_scope_prefix(undefined, ?RABBITMQ). + assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ, <<".">>]), ?RABBITMQ). rabbitmq_has_scope_prefix(Config) -> assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ). -rabbitmq_has_empty_scope_prefix() -> +rabbitmq_has_empty_scope_prefix(_) -> assert_scope_prefix(<<"">>, ?RABBITMQ). rabbitmq_has_no_additional_scopes_key(_) -> From 34f5d107d2a51b40c091dea17acfc96ebd4999fd Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 13 Sep 2024 19:08:08 +0100 Subject: [PATCH 13/64] WIP fix more test cases --- .../src/rabbit_resource_server.erl | 10 +-- .../test/rabbit_resource_server_SUITE.erl | 62 +++++++++++-------- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl index c9e9179e0068..7623b8b09175 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl @@ -156,7 +156,7 @@ find_audience(AudList, ResourceIdList) when is_list(AudList) -> -spec translate_error_if_any( {ok, resource_server()} | {error, not_found} | - {error, found_too_many}, boolean()) -> + {error, found_many}, boolean()) -> {ok, resource_server()} | {error, no_aud_found} | {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} | @@ -170,15 +170,15 @@ translate_error_if_any(ResourceServerOrError, HasAudience) -> {error, no_aud_found}; {{error, not_found}, _} -> {error, no_matching_aud_found}; - {{error, found_too_many}, false} -> + {{error, found_many}, false} -> {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}; - {{error, found_too_many}, _} -> + {{error, found_many}, _} -> {error, too_many_resources_with_verify_aud_false} end. -spec find_unique_resource_server_without_verify_aud() -> {ok, resource_server()} | {error, not_found} | - {error, found_too_many}. + {error, found_many}. find_unique_resource_server_without_verify_aud() -> Root = get_root_resource_server(), Map0 = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, @@ -191,7 +191,7 @@ find_unique_resource_server_without_verify_aud() -> case maps:size(Map) of 0 -> {error, not_found}; 1 -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; - _ -> {error, found_too_many} + _ -> {error, found_many} end. append_or_return_default(ListOrBinary, Default) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl index a9d2adf27ab8..1aa7aee730c2 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl @@ -49,11 +49,11 @@ groups() -> [ resolve_resource_server_id_for_rabbitmq1, resolve_resource_server_id_for_rabbitmq2, resolve_resource_server_id_for_both_resources_returns_error, - resolve_resource_server_for_none_audience_returns_rabbitmq1, + resolve_resource_server_for_none_audience_returns_no_aud_found, resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, {with_verify_aud_false, [], [ - resolve_resource_server_for_none_audience_returns_rabbitmq1, - resolve_resource_server_for_unknown_audience_returns_rabbitmq1, + resolve_resource_server_for_none_audience_returns_rabbitmq2, + resolve_resource_server_for_unknown_audience_returns_rabbitmq2, {with_rabbitmq1_verify_aud_false, [], [ resolve_resource_server_for_none_audience_returns_error ]} @@ -74,7 +74,7 @@ verify_get_rabbitmq_server_configuration() -> [ {with_verify_aud_false, [], [ rabbitmq_verify_aud_is_false ]}, - rabbitmq_has_no_scope_prefix, + rabbitmq_has_default_scope_prefix, {with_scope_prefix, [], [ rabbitmq_has_scope_prefix ]}, @@ -104,7 +104,7 @@ verify_configuration_inheritance_with_rabbitmq2() -> [ {with_verify_aud_false, [], [ rabbitmq2_verify_aud_is_false ]}, - rabbitmq2_has_no_scope_prefix, + rabbitmq2_has_default_scope_prefix, {with_scope_prefix, [], [ rabbitmq2_has_scope_prefix ]}, @@ -176,29 +176,19 @@ init_per_group(with_verify_aud_false, Config) -> set_env(verify_aud, false), Config; -init_per_group(with_two_resource_servers_and_rabbitmq_as_resource_server_id, Config) -> - set_env(resource_server_id, ?RABBITMQ), - set_env(key_config, [{jwks_url,<<"https://oauth-for-rabbitmq">> }]), - set_env(resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq1">> } - ]} - - ], - ?RABBITMQ_RESOURCE_TWO => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq2">> } - ]} - ] - }), +init_per_group(with_rabbitmq1_verify_aud_false, Config) -> + RabbitMQServers = get_env(resource_servers, #{}), + Resource0 = maps:get(?RABBITMQ_RESOURCE_ONE, RabbitMQServers, []), + Resource = [{verify_aud, false} | Resource0], + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, Resource, + RabbitMQServers)), Config; init_per_group(with_two_resource_servers, Config) -> RabbitMQ1 = [ {id, ?RABBITMQ_RESOURCE_ONE}, {resource_server_type, <<"some-type">>}, - {verify_aud, false}, + {verify_aud, true}, {scope_prefix, <<"some-prefix">>}, {additional_scopes_key, <<"roles">>}, {preferred_username_claims, [<<"x-username">>, <<"x-email">>]}, @@ -218,6 +208,11 @@ init_per_group(with_two_resource_servers, Config) -> init_per_group(_any, Config) -> Config. + +end_per_group(with_rabbitmq_as_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + end_per_group(with_empty_scope_prefix, Config) -> unset_env(scope_prefix), Config; @@ -234,6 +229,14 @@ end_per_group(with_scope_prefix, Config) -> unset_env(scope_prefix), Config; +end_per_group(with_rabbitmq1_verify_aud_false, Config) -> + RabbitMQServers = get_env(resource_servers, #{}), + Resource = maps:get(?RABBITMQ_RESOURCE_ONE, RabbitMQServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, + proplists:delete(verify_aud, Resource), + RabbitMQServers)), + Config; + end_per_group(_any, Config) -> Config. @@ -249,8 +252,8 @@ resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> resolve_resource_server_for_none_audience_returns_no_aud_found(_) -> assert_resource_server_id({error, no_aud_found}, none). -resolve_resource_server_for_none_audience_returns_rabbitmq1(_) -> - assert_resource_server_id(?RABBITMQ_RESOURCE_ONE, none). +resolve_resource_server_for_none_audience_returns_rabbitmq2(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, none). resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found(_) -> assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). @@ -261,6 +264,13 @@ resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> assert_resource_server_id(?RABBITMQ, <<"unknown">>). +resolve_resource_server_for_unknown_audience_returns_rabbitmq2(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, <<"unknown">>). + +resolve_resource_server_for_none_audience_returns_error(_) -> + assert_resource_server_id( + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}, + none). resolve_resource_server_id_for_any_audience_returns_no_matching_aud_found(_) -> assert_resource_server_id({error, no_matching_aud_found}, ?RABBITMQ), assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). @@ -291,7 +301,7 @@ both_resources_oauth_provider_id_is_root(_) -> rabbitmq2_verify_aud_is_false(_) -> assert_verify_aud(false, ?RABBITMQ_RESOURCE_TWO). -rabbitmq2_has_no_scope_prefix(_) -> +rabbitmq2_has_default_scope_prefix(_) -> assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ_RESOURCE_TWO, <<".">>]), ?RABBITMQ_RESOURCE_TWO). @@ -331,7 +341,7 @@ rabbitmq_oauth_provider_id_is_root(_) -> rabbitmq_oauth_provider_id_is_A(_) -> assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ). -rabbitmq_has_no_scope_prefix(_) -> +rabbitmq_has_default_scope_prefix(_) -> assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ, <<".">>]), ?RABBITMQ). rabbitmq_has_scope_prefix(Config) -> From 66d932314893423ec5b9e25a4d49dab21796d98b Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 09:42:04 +0200 Subject: [PATCH 14/64] Simplify module names --- deps/rabbitmq_auth_backend_oauth2/BUILD.bazel | 6 +-- deps/rabbitmq_auth_backend_oauth2/app.bzl | 38 ++++++++++--------- ...it_oauth2_schema.erl => oauth2_schema.erl} | 2 +- ..._oauth_provider.erl => oauth_provider.erl} | 2 +- ...esource_server.erl => resource_server.erl} | 2 +- ...hema_SUITE.erl => oauth2_schema_SUITE.erl} | 2 +- ...der_SUITE.erl => oauth_provider_SUITE.erl} | 10 ++--- ...er_SUITE.erl => resource_server_SUITE.erl} | 4 +- 8 files changed, 34 insertions(+), 32 deletions(-) rename deps/rabbitmq_auth_backend_oauth2/src/{rabbit_oauth2_schema.erl => oauth2_schema.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/src/{rabbit_oauth_provider.erl => oauth_provider.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/src/{rabbit_resource_server.erl => resource_server.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_schema_SUITE.erl => oauth2_schema_SUITE.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth_provider_SUITE.erl => oauth_provider_SUITE.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_resource_server_SUITE.erl => resource_server_SUITE.erl} (99%) diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 3713706decb3..71529eca5e3b 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -113,7 +113,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "rabbit_oauth_provider_SUITE", + name = "oauth_provider_SUITE", additional_beam = [ "test/oauth2_http_mock.beam", ], @@ -123,7 +123,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "rabbit_resource_server_SUITE" + name = "resource_server_SUITE" ) rabbitmq_integration_suite( @@ -149,7 +149,7 @@ rabbitmq_suite( ) rabbitmq_suite( - name = "rabbit_oauth2_schema_SUITE", + name = "oauth2_schema_SUITE", size = "medium", deps = [ "//deps/rabbit_common:erlang_app", diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index d26064d1a213..cb9b4c7a2b8b 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -13,9 +13,9 @@ 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_oauth_provider.erl", - "src/rabbit_resource_server.erl", - "src/rabbit_oauth2_schema.erl", + "src/oauth_provider.erl", + "src/resource_server.erl", + "src/oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -49,9 +49,9 @@ 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_resource_server.erl", - "src/rabbit_oauth_provider.erl", - "src/rabbit_oauth2_schema.erl", + "src/resource_server.erl", + "src/oauth_provider.erl", + "src/oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -97,9 +97,9 @@ 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_oauth_provider.erl", - "src/rabbit_resource_server.erl", - "src/rabbit_oauth2_schema.erl", + "src/oauth_provider.erl", + "src/resource_server.erl", + "src/oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -163,10 +163,10 @@ def test_suite_beam_files(name = "test_suite_beam_files"): deps = ["//deps/rabbit_common:erlang_app"], ) erlang_bytecode( - name = "rabbit_oauth2_schema_SUITE_beam_files", + name = "oauth2_schema_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_oauth2_schema_SUITE.erl"], - outs = ["test/rabbit_oauth2_schema_SUITE.beam"], + srcs = ["test/oauth2_schema_SUITE.erl"], + outs = ["test/oauth2_schema_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/rabbit_common:erlang_app"], @@ -240,19 +240,21 @@ def test_suite_beam_files(name = "test_suite_beam_files"): erlc_opts = "//:test_erlc_opts", ) erlang_bytecode( - name = "rabbit_oauth_provider_SUITE_beam_files", + name = "oauth_provider_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_oauth_provider_SUITE.erl"], - outs = ["test/rabbit_oauth_provider_SUITE.beam"], + srcs = ["test/oauth_provider_SUITE.erl"], + outs = ["test/oauth_provider_SUITE.beam"], + hdrs = ["include/oauth2.hrl"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], ) erlang_bytecode( - name = "rabbit_resource_server_SUITE_beam_files", + name = "resource_server_SUITE_beam_files", testonly = True, - srcs = ["test/rabbit_resource_server_SUITE.erl"], - outs = ["test/rabbit_resource_server_SUITE.beam"], + srcs = ["test/resource_server_SUITE.erl"], + outs = ["test/resource_server_SUITE.beam"], + hdrs = ["include/oauth2.hrl"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl rename to deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index 0e77e0fc7fb3..93ff3669b18c 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_schema). +-module(oauth2_schema). -export([ diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl rename to deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl index d60cfc482126..7eaa20aa8268 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth_provider.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth_provider). +-module(oauth_provider). -include("oauth2.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl rename to deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index 7623b8b09175..96c023052724 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_resource_server). +-module(resource_server). -include("oauth2.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 58e69c334d83..c941a21fb56f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -4,7 +4,7 @@ %% %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth2_schema_SUITE). +-module(oauth2_schema_SUITE). -compile(export_all). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl index dcf56515222e..3fe791135ed8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_oauth_provider_SUITE). +-module(oauth_provider_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -17,7 +17,7 @@ -define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). -define(AUTH_PORT, 8000). --import(rabbit_oauth_provider, [ +-import(oauth_provider, [ get_internal_oauth_provider/0,get_internal_oauth_provider/1, add_signing_key/2, add_signing_key/3, replace_signing_keys/1, replace_signing_keys/2, @@ -27,8 +27,8 @@ all() -> [ {group, with_rabbitmq_node}, - {group, with_resource_server_id}, - {group, with_resource_servers} + {group, verify_oauth_provider_A}, + {group, verify_oauth_provider_root} ]. groups() -> [ {with_rabbitmq_node, [], [ @@ -385,7 +385,7 @@ call_get_env(Config, Par, Def) -> [rabbitmq_auth_backend_oauth2, Par, Def]). call_add_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_key, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, add_signing_key, Args). call_get_signing_keys(Config, Args) -> rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_keys, Args). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl index 1aa7aee730c2..f55251b5f5c3 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(rabbit_resource_server_SUITE). +-module(resource_server_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -19,7 +19,7 @@ -define(OAUTH_PROVIDER_B,<<"B">>). -import(oauth2_client, [get_oauth_provider/2]). --import(rabbit_resource_server, [resolve_resource_server_from_audience/1]). +-import(resource_server, [resolve_resource_server_from_audience/1]). all() -> [ From b5230f7afd7bd7a983004ac3fb978f79856f69a2 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 14:51:09 +0200 Subject: [PATCH 15/64] Fix some test cases --- .../rabbitmq_auth_backend_oauth2.schema | 10 +- .../test/oauth_provider_SUITE.erl | 368 +++++------------- 2 files changed, 101 insertions(+), 277 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 f594903d15cd..7c1b116eca5b 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 @@ -130,7 +130,7 @@ {translation, "rabbitmq_auth_backend_oauth2.key_config.signing_keys", fun(Conf) -> - rabbit_oauth2_schema:translate_signing_keys(Conf) + oauth2_schema:translate_signing_keys(Conf) end}. {mapping, @@ -170,7 +170,7 @@ {translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", fun(Conf) -> - rabbit_oauth2_schema:translate_authorization_endpoint_params(Conf) + oauth2_schema:translate_authorization_endpoint_params(Conf) end}. {mapping, @@ -180,7 +180,7 @@ {translation, "rabbitmq_auth_backend_oauth2.oauth_providers", fun(Conf) -> - rabbit_oauth2_schema:translate_oauth_providers(Conf) + oauth2_schema:translate_oauth_providers(Conf) end}. {mapping, @@ -317,7 +317,7 @@ {translation, "rabbitmq_auth_backend_oauth2.oauth_providers", fun(Conf) -> - rabbit_oauth2_schema:translate_oauth_providers(Conf) + oauth2_schema:translate_oauth_providers(Conf) end}. {mapping, @@ -359,5 +359,5 @@ {translation, "rabbitmq_auth_backend_oauth2.resource_servers", fun(Conf) -> - rabbit_oauth2_schema:translate_resource_servers(Conf) + oauth2_schema:translate_resource_servers(Conf) end}. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl index 3fe791135ed8..9a9fd50ea6cd 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl @@ -46,45 +46,25 @@ groups() -> [ replace_override_static_keys_with_newly_added_keys ]} ]}, - {verify_oauth_provider_A, [], [ - internal_oauth_provider_A_has_no_default_key, - {oauth_provider_A_with_default_key, [], [ - internal_oauth_provider_A_has_default_key - ]}, - internal_oauth_provider_A_has_no_algorithms, - {oauth_provider_A_with_algorithms, [], [ - internal_oauth_provider_A_has_algorithms - ]}, - oauth_provider_A_with_jwks_uri_returns_error, - {oauth_provider_A_with_jwks_uri, [], [ - oauth_provider_A_has_jwks_uri - ]}, - {oauth_provider_A_with_issuer, [], [ - {oauth_provider_A_with_jwks_uri, [], [ - oauth_provider_A_has_jwks_uri - ]}, - oauth_provider_A_has_to_discover_jwks_uri_endpoint - ]} + {verify_oauth_provider_A, [], verify_provider()}, + {verify_oauth_provider_root, [], verify_provider()} +]. + +verify_provider() -> [ + internal_oauth_provider_has_no_default_key, + {oauth_provider_with_default_key, [], [ + internal_oauth_provider_has_default_key ]}, - {verify_oauth_provider_root, [], [ - internal_oauth_provider_root_has_no_default_key, - {with_default_key, [], [ - internal_oauth_provider_root_has_default_key - ]}, - internal_oauth_provider_root_has_no_algorithms, - {with_algorithms, [], [ - internal_oauth_provider_root_has_algorithms - ]}, - oauth_provider_root_with_jwks_uri_returns_error, - {with_jwks_uri, [], [ - oauth_provider_root_has_jwks_uri - ]}, - {with_issuer, [], [ - {with_jwks_uri, [], [ - oauth_provider_root_has_jwks_uri - ]}, - oauth_provider_root_has_to_discover_jwks_uri_endpoint - ]} + internal_oauth_provider_has_no_algorithms, + {oauth_provider_with_algorithms, [], [ + internal_oauth_provider_has_algorithms + ]}, + get_oauth_provider_with_jwks_uri_returns_error, + {oauth_provider_with_jwks_uri, [], [ + get_oauth_provider_has_jwks_uri + ]}, + {oauth_provider_with_issuer, [], [ + get_oauth_provider_has_jwks_uri ]} ]. @@ -102,12 +82,6 @@ init_per_group(with_rabbitmq_node, Config) -> ]), rabbit_ct_helpers:run_steps(Config1, rabbit_ct_broker_helpers:setup_steps()); -init_per_group(with_default_key, Config) -> - KeyConfig = get_env(key_config, []), - set_env(key_config, proplists:delete(default_key, KeyConfig) ++ - [{default_key,<<"default-key">>}]), - Config; - init_per_group(with_root_static_signing_keys, Config) -> KeyConfig = call_get_env(Config, key_config, []), SigningKeys = #{ @@ -132,13 +106,15 @@ init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> OAuthProviders)), Config; -init_per_group(with_jwks_url, Config) -> - KeyConfig = get_env(key_config, []), - set_env(key_config, KeyConfig ++ - [{jwks_url,build_url_to_oauth_provider(<<"/keys">>)}]), - [{key_config_before_group_with_jwks_url, KeyConfig} | Config]; +init_per_group(oauth_provider_with_jwks_uri, Config) -> + URL = build_url_to_oauth_provider(<<"/keys">>), + case ?config(oauth_provider_id) of + root -> set_env(jkws_url, URL); + Id -> set_oauth_provider_properties(Id, [{jwks_uri, URL}]) + end, + [{jwks_uri, URL} | Config]; -init_per_group(with_issuer, Config) -> +init_per_group(oauth_provider_with_issuer, Config) -> {ok, _} = application:ensure_all_started(inets), {ok, _} = application:ensure_all_started(ssl), application:ensure_all_started(cowboy), @@ -151,61 +127,12 @@ init_per_group(with_issuer, Config) -> start_https_oauth_server(?AUTH_PORT, CertsDir, ListOfExpectations), set_env(use_global_locks, false), - set_env(issuer, - build_url_to_oauth_provider(<<"/">>)), - KeyConfig = get_env(key_config, []), - set_env(key_config, - KeyConfig ++ SslOptions), - - [{key_config_before_group_with_issuer, KeyConfig}, - {ssl_options, SslOptions} | Config]; - -init_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> - set_env(oauth_providers, - #{<<"A">> => [ - {issuer, build_url_to_oauth_provider(<<"/A">>) }, - {jwks_uri,build_url_to_oauth_provider(<<"/A/keys">>) } - ] } ), - Config; - -init_per_group(with_oauth_providers_A_with_issuer, Config) -> - set_env(oauth_providers, - #{<<"A">> => [ - {issuer, build_url_to_oauth_provider(<<"/A">>) }, - {https, ?config(ssl_options, Config)} - ] } ), - Config; - -init_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> - set_env(oauth_providers, - #{ <<"A">> => [ - {issuer, build_url_to_oauth_provider(<<"/A">>) }, - {jwks_uri, build_url_to_oauth_provider(<<"/A/keys">>)} - ], - <<"B">> => [ - {issuer, build_url_to_oauth_provider(<<"/B">>) }, - {jwks_uri, build_url_to_oauth_provider(<<"/B/keys">>)} - ] }), - Config; - -init_per_group(with_oauth_providers_A_B_with_issuer, Config) -> - set_env(oauth_providers, - #{ <<"A">> => [ - {issuer, build_url_to_oauth_provider(<<"/A">>) }, - {https, ?config(ssl_options, Config)} - ], - <<"B">> => [ - {issuer, build_url_to_oauth_provider(<<"/B">>) }, - {https, ?config(ssl_options, Config)} - ] }), - Config; - -init_per_group(with_default_oauth_provider_A, Config) -> - set_env(default_oauth_provider, <<"A">>), - Config; - -init_per_group(with_default_oauth_provider_B, Config) -> - set_env(default_oauth_provider, <<"B">>), + IssuerUrl = build_url_to_oauth_provider(<<"/">>), + case ?config(oauth_provider_id, Config) of + root -> set_env(issuer, IssuerUrl); + Id -> set_oauth_provider_properties(Id, + [{issuer, IssuerUrl}, {ssl_options, SslOptions}]) + end, Config; init_per_group(with_resource_server_id, Config) -> @@ -235,23 +162,6 @@ init_per_group(with_different_oauth_provider_for_each_resource, Config) -> ResourceServers1)), Config; -init_per_group(with_resource_servers, Config) -> - set_env(resource_servers, - #{?RABBITMQ_RESOURCE_ONE => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq1">> } - ]} - ], - ?RABBITMQ_RESOURCE_TWO => [ - { key_config, [ - {jwks_url,<<"https://oauth-for-rabbitmq2">> } - ]} - ], - <<"0">> => [ {id, <<"rabbitmq-0">> } ], - <<"1">> => [ {id, <<"rabbitmq-1">> } ] - - }), - Config; init_per_group(verify_oauth_provider_A, Config) -> set_env(oauth_providers, @@ -259,7 +169,10 @@ init_per_group(verify_oauth_provider_A, Config) -> {id, <<"A">>} ] }), - Config; + [{oauth_provider_id, <<"A">>} |Config]; + +init_per_group(verify_oauth_provider_root, Config) -> + [{oauth_provider_id, root} |Config]; init_per_group(_any, Config) -> Config. @@ -276,99 +189,20 @@ end_per_group(with_resource_server_id, Config) -> unset_env(resource_server_id), Config; -end_per_group(with_verify_aud_false, Config) -> - unset_env(verify_aud), - Config; - -end_per_group(with_verify_aud_false_for_resource_two, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers, []), - set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_TWO, - proplists:delete(verify_aud, Proplist), ResourceServers)), - Config; - -end_per_group(with_empty_scope_prefix_for_resource_one, Config) -> - ResourceServers = get_env(resource_servers, #{}), - Proplist = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers, []), - set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, - proplists:delete(scope_prefix, Proplist), ResourceServers)), - Config; - -end_per_group(with_default_key, Config) -> - KeyConfig = get_env(key_config, []), - set_env(key_config, proplists:delete(default_key, KeyConfig)), - Config; - -end_per_group(with_algorithms, Config) -> - KeyConfig = get_env(key_config, []), - set_env(key_config, proplists:delete(algorithms, KeyConfig)), - Config; - -end_per_group(with_algorithms_for_provider_A, Config) -> - OAuthProviders = get_env(oauth_providers, #{}), - OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), - set_env(oauth_providers, maps:put(<<"A">>, - proplists:delete(algorithms, OAuthProvider), OAuthProviders)), - Config; - -end_per_group(with_jwks_url, Config) -> - KeyConfig = ?config(key_config_before_group_with_jwks_url, Config), - set_env(key_config, KeyConfig), - Config; - -end_per_group(with_issuer, Config) -> - KeyConfig = ?config(key_config_before_group_with_issuer, Config), - unset_env(issuer), - set_env(key_config, KeyConfig), +end_per_group(oauth_provider_with_issuer, Config) -> + case ?config(oauth_provider_id, Config) of + root -> unset_env(issuer); + Id -> unset_oauth_provider_properties(Id, [issuer]) + end, stop_http_auth_server(), Config; -end_per_group(with_oauth_providers_A_with_jwks_uri, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_oauth_providers_A_with_issuer, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_oauth_providers_A_B_with_jwks_uri, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_oauth_providers_A_B_with_issuer, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_oauth_providers_A, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_oauth_providers_A_B, Config) -> - unset_env(oauth_providers), - Config; - -end_per_group(with_default_oauth_provider_B, Config) -> - unset_env(default_oauth_provider), - Config; - -end_per_group(with_default_oauth_provider_A, Config) -> - unset_env(default_oauth_provider), - Config; - -end_per_group(get_oauth_provider_for_resource_server_id, Config) -> - unset_env(resource_server_id), - Config; - -end_per_group(with_resource_servers_and_resource_server_id, Config) -> - unset_env(resource_server_id), - Config; - -end_per_group(with_resource_servers, Config) -> - unset_env(resource_servers), - Config; - -end_per_group(with_root_scope_prefix, Config) -> - unset_env(scope_prefix), +end_per_group(oauth_provider_with_default_key, Config) -> + DefaultKey = <<"default-key">>, + case ?config(oauth_provider_id, Config) of + root -> unset_env(default_key); + Id -> unset_oauth_provider_properties(Id, [default_key]) + end, Config; end_per_group(_any, Config) -> @@ -388,19 +222,19 @@ call_add_signing_key(Config, Args) -> rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, add_signing_key, Args). call_get_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, get_signing_keys, Args). call_get_signing_keys(Config) -> call_get_signing_keys(Config, []). call_get_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, get_signing_key, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, get_signing_key, Args). call_add_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, add_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, add_signing_keys, Args). call_replace_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_config, replace_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, replace_signing_keys, Args). %% ----- Test cases @@ -515,70 +349,44 @@ get_algorithms_for_provider_A(Config) -> Algorithms = OAuthProvider#internal_oauth_provider.algorithms, ?assertEqual(?config(algorithms, Config), Algorithms). -get_oauth_provider_root_with_jwks_uri_should_fail(_Config) -> - {error, _Message} = get_oauth_provider(root, [jwks_uri]). +append_paths(Path1, Path2) -> + erlang:iolist_to_binary([Path1, Path2]). -get_oauth_provider_A_with_jwks_uri_should_fail(_Config) -> - {error, _Message} = get_oauth_provider(<<"A">>, [jwks_uri]). -get_oauth_provider_should_return_root_oauth_provider_with_jwks_uri(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), - OAuthProvider#oauth_provider.jwks_uri). -get_oauth_provider_for_both_resources_should_return_root_oauth_provider(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), - OAuthProvider#oauth_provider.jwks_uri). +internal_oauth_provider_has_no_default_key(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(undefined, + InternalOAuthProvider#internal_oauth_provider.default_key). -get_oauth_provider_for_resource_one_should_return_oauth_provider_A(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), - OAuthProvider#oauth_provider.jwks_uri). +internal_oauth_provider_has_default_key(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(?config(default_key, Config), + InternalOAuthProvider#internal_oauth_provider.default_key). -get_oauth_provider_for_both_resources_should_return_oauth_provider_A(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), - OAuthProvider#oauth_provider.jwks_uri). +internal_oauth_provider_has_no_algorithms(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(undefined, + InternalOAuthProvider#internal_oauth_provider.algorithms). -get_oauth_provider_for_resource_two_should_return_oauth_provider_B(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), - OAuthProvider#oauth_provider.jwks_uri). +internal_oauth_provider_has_algorithms(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(?config(algorithms, Config), + InternalOAuthProvider#internal_oauth_provider.algorithms). -get_oauth_provider_should_return_root_oauth_provider_with_all_discovered_endpoints(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(root, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/keys">>), - OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/">>), - OAuthProvider#oauth_provider.issuer). +get_oauth_provider_with_jwks_uri_returns_error(Config) -> + {error, _} = get_oauth_provider( + ?config(oauth_provider_id, Config), [jwks_uri]). -append_paths(Path1, Path2) -> - erlang:iolist_to_binary([Path1, Path2]). +get_oauth_provider_has_jwks_uri(Config) -> + OAuthProvider = get_oauth_provider( + ?config(oauth_provider_id, Config), [jwks_uri]), + ?assertEqual(?config(jwks_uri, Config), OAuthProvider#oauth_provider.jwks_uri). -get_oauth_provider_should_return_oauth_provider_B_with_jwks_uri(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), - OAuthProvider#oauth_provider.jwks_uri). - -get_oauth_provider_should_return_oauth_provider_B_with_all_discovered_endpoints(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"B">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/B/keys">>), - OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/B">>), - OAuthProvider#oauth_provider.issuer). - -get_oauth_provider_should_return_oauth_provider_A_with_jwks_uri(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), - OAuthProvider#oauth_provider.jwks_uri). - -get_oauth_provider_should_return_oauth_provider_A_with_all_discovered_endpoints(_Config) -> - {ok, OAuthProvider} = get_oauth_provider(<<"A">>, [jwks_uri]), - ?assertEqual(build_url_to_oauth_provider(<<"/A/keys">>), - OAuthProvider#oauth_provider.jwks_uri), - ?assertEqual(build_url_to_oauth_provider(<<"/A">>), - OAuthProvider#oauth_provider.issuer). %% ---- Utility functions @@ -666,6 +474,22 @@ build_url_to_oauth_provider(Path) -> stop_http_auth_server() -> cowboy:stop_listener(mock_http_auth_listener). +set_oauth_provider_properties(OAuthProviderId, Proplist) -> + OAuthProviders = get_env(oauth_providers, #{}), + CurProplist = maps:get(OAuthProviderId, OAuthProviders), + CurMap = proplists:to_map(CurProplist), + Map = proplists:to_map(Proplist), + set_env(oauth_providers, maps:put(OAuthProviderId, maps:to_list(maps:merge(CurMap, Map)), + OAuthProviders)). + +unset_oauth_provider_properties(OAuthProviderId, PropertyNameList) -> + OAuthProviders = get_env(oauth_providers, #{}), + CurProplist = maps:get(OAuthProviderId, OAuthProviders), + CurMap = proplists:to_map(CurProplist), + set_env(oauth_provider, maps:put(OAuthProviderId, + maps:filter(fun(K,V) -> not proplists:is_defined(K, PropertyNameList) end, CurMap), + OAuthProviders)). + -spec ssl_options(ssl:verify_type(), boolean(), file:filename()) -> list(). ssl_options(PeerVerification, FailIfNoPeerCert, CaCertFile) -> [{verify, PeerVerification}, From 2f0faec58c49080e02c4308ebd0a12eae7e8e348 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 15:40:57 +0200 Subject: [PATCH 16/64] Fix test cases --- .../test/oauth_provider_SUITE.erl | 55 +++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl index 9a9fd50ea6cd..1e5a6929b5e6 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl @@ -107,10 +107,15 @@ init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> Config; init_per_group(oauth_provider_with_jwks_uri, Config) -> - URL = build_url_to_oauth_provider(<<"/keys">>), - case ?config(oauth_provider_id) of - root -> set_env(jkws_url, URL); - Id -> set_oauth_provider_properties(Id, [{jwks_uri, URL}]) + URL = case ?config(oauth_provider_id, Config) of + root -> + RootUrl = build_url_to_oauth_provider(<<"/keys">>), + set_env(key_config, [{jwks_url, RootUrl}]), + RootUrl; + <<"A">> -> + AUrl = build_url_to_oauth_provider(<<"/A/keys">>), + set_oauth_provider_properties(<<"A">>, [{jwks_uri, AUrl}]), + AUrl end, [{jwks_uri, URL} | Config]; @@ -127,13 +132,18 @@ init_per_group(oauth_provider_with_issuer, Config) -> start_https_oauth_server(?AUTH_PORT, CertsDir, ListOfExpectations), set_env(use_global_locks, false), - IssuerUrl = build_url_to_oauth_provider(<<"/">>), - case ?config(oauth_provider_id, Config) of - root -> set_env(issuer, IssuerUrl); - Id -> set_oauth_provider_properties(Id, - [{issuer, IssuerUrl}, {ssl_options, SslOptions}]) + {Issuer, JwksUri} = case ?config(oauth_provider_id, Config) of + root -> + Url = build_url_to_oauth_provider(<<"/">>), + set_env(issuer, Url), + set_env(key_config, SslOptions), + {Url, build_url_to_oauth_provider(<<"/keys">>)}; + <<"A">> -> + Url = build_url_to_oauth_provider(<<"/A">>), + set_oauth_provider_properties(<<"A">>, [{issuer, Url}, {https, SslOptions}]), + {Url, build_url_to_oauth_provider(<<"/A/keys">>)} end, - Config; + [{issuer, Issuer}, {jwks_uri, JwksUri}] ++ Config; init_per_group(with_resource_server_id, Config) -> set_env(resource_server_id, ?RABBITMQ), @@ -191,11 +201,20 @@ end_per_group(with_resource_server_id, Config) -> end_per_group(oauth_provider_with_issuer, Config) -> case ?config(oauth_provider_id, Config) of - root -> unset_env(issuer); - Id -> unset_oauth_provider_properties(Id, [issuer]) + root -> + unset_env(issuer), + unset_env(https); + Id -> + unset_oauth_provider_properties(Id, [issuer, https]) end, stop_http_auth_server(), Config; +end_per_group(oauth_provider_with_jwks_uri, Config) -> + case ?config(oauth_provider_id, Config) of + root -> unset_env(jwks_url); + Id -> unset_oauth_provider_properties(Id, [jwks_uri]) + end, + Config; end_per_group(oauth_provider_with_default_key, Config) -> DefaultKey = <<"default-key">>, @@ -383,8 +402,9 @@ get_oauth_provider_with_jwks_uri_returns_error(Config) -> ?config(oauth_provider_id, Config), [jwks_uri]). get_oauth_provider_has_jwks_uri(Config) -> - OAuthProvider = get_oauth_provider( + {ok, OAuthProvider} = get_oauth_provider( ?config(oauth_provider_id, Config), [jwks_uri]), + ct:log("OAuthProvider: ~p", [OAuthProvider]), ?assertEqual(?config(jwks_uri, Config), OAuthProvider#oauth_provider.jwks_uri). @@ -479,15 +499,16 @@ set_oauth_provider_properties(OAuthProviderId, Proplist) -> CurProplist = maps:get(OAuthProviderId, OAuthProviders), CurMap = proplists:to_map(CurProplist), Map = proplists:to_map(Proplist), - set_env(oauth_providers, maps:put(OAuthProviderId, maps:to_list(maps:merge(CurMap, Map)), - OAuthProviders)). + set_env(oauth_providers, maps:put(OAuthProviderId, + maps:to_list(maps:merge(CurMap, Map)), OAuthProviders)). unset_oauth_provider_properties(OAuthProviderId, PropertyNameList) -> OAuthProviders = get_env(oauth_providers, #{}), CurProplist = maps:get(OAuthProviderId, OAuthProviders), CurMap = proplists:to_map(CurProplist), - set_env(oauth_provider, maps:put(OAuthProviderId, - maps:filter(fun(K,V) -> not proplists:is_defined(K, PropertyNameList) end, CurMap), + set_env(oauth_providers, maps:put(OAuthProviderId, + maps:to_list(maps:filter(fun(K,V) -> + not proplists:is_defined(K, PropertyNameList) end, CurMap)), OAuthProviders)). -spec ssl_options(ssl:verify_type(), boolean(), file:filename()) -> list(). From 5c2b90bece6579e4f647c47398fd68b6966d3cf0 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 16:28:53 +0200 Subject: [PATCH 17/64] fix some test cases --- .../src/resource_server.erl | 6 ++++- .../test/resource_server_SUITE.erl | 27 ++++++++++++------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index 96c023052724..710a51c0b188 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -121,9 +121,13 @@ get_resource_server(ResourceServerId, RootResourseServer) when AdditionalScopesKey = proplists:get_value(extra_scopes_source, ResourceServerProps, RootResourseServer#resource_server.additional_scopes_key), + RootScopePrefix = get_env(scope_prefix, undefined), ScopePrefix = proplists:get_value(scope_prefix, ResourceServerProps, - erlang:iolist_to_binary([ResourceServerId, <<".">>])), + case RootScopePrefix of + undefined -> erlang:iolist_to_binary([ResourceServerId, <<".">>]); + Prefix -> Prefix + end), OAuthProviderId = proplists:get_value(oauth_provider_id, ResourceServerProps, RootResourseServer#resource_server.oauth_provider_id), diff --git a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl index f55251b5f5c3..0375b0a27acc 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl @@ -208,6 +208,13 @@ init_per_group(with_two_resource_servers, Config) -> init_per_group(_any, Config) -> Config. +end_per_group(with_default_oauth_provider_A, Config) -> + unset_env(default_oauth_provider), + Config; + +end_per_group(with_default_oauth_provider_B, Config) -> + unset_env(default_oauth_provider), + Config; end_per_group(with_rabbitmq_as_resource_server_id, Config) -> unset_env(resource_server_id), @@ -372,23 +379,22 @@ rabbitmq_has_scope_aliases(Config) -> verify_rabbitmq1_server_configuration(Config) -> ConfigRabbitMQ = ?config(?RABBITMQ_RESOURCE_ONE, Config), - ActualRabbitMQ = resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), - ?assertEqual(ConfigRabbitMQ#resource_server.id, + {ok, ActualRabbitMQ} = resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), + ?assertEqual(proplists:get_value(id, ConfigRabbitMQ), ActualRabbitMQ#resource_server.id), - ?assertEqual(ConfigRabbitMQ#resource_server.resource_server_type, + ?assertEqual(proplists:get_value(resource_server_type, ConfigRabbitMQ), ActualRabbitMQ#resource_server.resource_server_type), - ?assertEqual(ConfigRabbitMQ#resource_server.verify_aud, + ?assertEqual(proplists:get_value(verify_aud, ConfigRabbitMQ), ActualRabbitMQ#resource_server.verify_aud), - ?assertEqual(ConfigRabbitMQ#resource_server.scope_prefix, + ?assertEqual(proplists:get_value(scope_prefix, ConfigRabbitMQ), ActualRabbitMQ#resource_server.scope_prefix), - ?assertEqual(ConfigRabbitMQ#resource_server.additional_scopes_key, + ?assertEqual(proplists:get_value(additional_scopes_key, ConfigRabbitMQ), ActualRabbitMQ#resource_server.additional_scopes_key), - ?assertEqual(ConfigRabbitMQ#resource_server.preferred_username_claims ++ - ?DEFAULT_PREFERRED_USERNAME_CLAIMS, + ?assertEqual(proplists:get_value(preferred_username_claims, ConfigRabbitMQ), ActualRabbitMQ#resource_server.preferred_username_claims), - ?assertEqual(ConfigRabbitMQ#resource_server.scope_aliases, + ?assertEqual(proplists:get_value(scope_aliases, ConfigRabbitMQ), ActualRabbitMQ#resource_server.scope_aliases), - ?assertEqual(ConfigRabbitMQ#resource_server.oauth_provider_id, + ?assertEqual(proplists:get_value(oauth_provider_id, ConfigRabbitMQ), ActualRabbitMQ#resource_server.oauth_provider_id). %% ----- @@ -405,6 +411,7 @@ assert_verify_aud(Expected, Audience) -> assert_oauth_provider_id(Expected, Audience) -> {ok, Actual} = resolve_resource_server_from_audience(Audience), + ct:log("Actual:~p", [Actual]), ?assertEqual(Expected, Actual#resource_server.oauth_provider_id). assert_scope_prefix(Expected, Audience) -> From 42a1a47b7d933d91470fe9a0ab7520d557910aae Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 16:39:55 +0200 Subject: [PATCH 18/64] Fix test cases --- .../src/resource_server.erl | 2 +- .../test/resource_server_SUITE.erl | 24 ++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index 710a51c0b188..e0475d0623bf 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -56,7 +56,7 @@ get_root_resource_server() -> case get_env(preferred_username_claims) of undefined -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS; Value -> - append_or_return_default(Value, ?DEFAULT_PREFERRED_USERNAME_CLAIMS) + Value end, ResourceServerType = get_env(resource_server_type), diff --git a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl index 0375b0a27acc..dba11c6b4c98 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl @@ -91,7 +91,7 @@ verify_get_rabbitmq_server_configuration() -> [ ]}, rabbitmq_has_no_preferred_username_claims_but_gets_default, {with_preferred_username_claims, [], [ - rabbitmq_has_preferred_username_claims_plus_default + rabbitmq_has_preferred_username_claims ]}, rabbitmq_has_no_scope_aliases, {with_scope_aliases, [], [ @@ -244,6 +244,18 @@ end_per_group(with_rabbitmq1_verify_aud_false, Config) -> RabbitMQServers)), Config; +end_per_group(with_additional_scopes_key, Config) -> + unset_env(extra_scopes_source), + Config; + +end_per_group(with_preferred_username_claims, Config) -> + unset_env(preferred_username_claims), + Config; + +end_per_group(with_scope_aliases, Config) -> + unset_env(scope_aliases), + Config; + end_per_group(_any, Config) -> Config. @@ -334,7 +346,7 @@ rabbitmq2_has_no_preferred_username_claims_but_gets_default(_) -> rabbitmq2_has_preferred_username_claims_plus_default(Config) -> assert_preferred_username_claims(?config(preferred_username_claims, Config) - ++ ?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ_RESOURCE_TWO). + , ?RABBITMQ_RESOURCE_TWO). rabbitmq2_has_no_scope_aliases(_) -> assert_scope_aliases(undefined, ?RABBITMQ_RESOURCE_TWO). @@ -367,9 +379,9 @@ rabbitmq_has_additional_scopes_key(Config) -> rabbitmq_has_no_preferred_username_claims_but_gets_default(_) -> assert_preferred_username_claims(?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ). -rabbitmq_has_preferred_username_claims_plus_default(Config) -> - assert_preferred_username_claims(?config(preferred_username_claims, Config) ++ - ?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ). +rabbitmq_has_preferred_username_claims(Config) -> + assert_preferred_username_claims(?config(preferred_username_claims, Config), + ?RABBITMQ). rabbitmq_has_no_scope_aliases(_) -> assert_scope_aliases(undefined, ?RABBITMQ). @@ -388,7 +400,7 @@ verify_rabbitmq1_server_configuration(Config) -> ActualRabbitMQ#resource_server.verify_aud), ?assertEqual(proplists:get_value(scope_prefix, ConfigRabbitMQ), ActualRabbitMQ#resource_server.scope_prefix), - ?assertEqual(proplists:get_value(additional_scopes_key, ConfigRabbitMQ), + ?assertEqual(proplists:get_value(extract_scopes_source, ConfigRabbitMQ), ActualRabbitMQ#resource_server.additional_scopes_key), ?assertEqual(proplists:get_value(preferred_username_claims, ConfigRabbitMQ), ActualRabbitMQ#resource_server.preferred_username_claims), From 8339015f80e0822c11643308c56a7537996fe410 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 16 Sep 2024 18:20:55 +0200 Subject: [PATCH 19/64] WIP Use resource_server() type check_token still needs some work --- .../src/rabbit_auth_backend_oauth2.erl | 110 +++++++++--------- .../src/uaa_jwt.erl | 4 +- 2 files changed, 60 insertions(+), 54 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 eac24aab5a6d..992a51454c69 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 @@ -168,7 +168,7 @@ validate_token_expiry(#{<<"exp">> := Exp}) when is_integer(Exp) -> validate_token_expiry(#{}) -> ok. -spec check_token(binary() | map()) -> - {'ok', map()} | + {'ok', map(), resource_server()} | {'error', term() }| {'refused', 'signature_invalid' | @@ -181,14 +181,17 @@ check_token(DecodedToken) when is_map(DecodedToken) -> check_token(Token) -> case uaa_jwt:decode_and_verify(Token) of {error, Reason} -> - {refused, {error, Reason}}; - {true, TargetResourceServerId, Payload} -> - Payload0 = post_process_payload(TargetResourceServerId, Payload), - validate_payload(TargetResourceServerId, Payload0); + {refused, {error, Reason}}; + {true, ResourceServer, Payload} -> + Payload0 = post_process_payload(ResourceServer, Payload), + case validate_payload(ResourceServer, Payload0) of + {ok, DecodedToken} -> {ok, DecodedToken, ResourceServer}; + {error, _} = Error -> Error + end; {false, _, _} -> {refused, signature_invalid} end. -post_process_payload(ResourceServerId, Payload) when is_map(Payload) -> +post_process_payload(ResourceServer, Payload) when is_map(Payload) -> Payload0 = maps:map(fun(K, V) -> case K of ?AUD_JWT_FIELD when is_binary(V) -> binary:split(V, <<" ">>, [global, trim_all]); @@ -199,8 +202,8 @@ post_process_payload(ResourceServerId, Payload) when is_map(Payload) -> Payload ), - Payload1 = case does_include_complex_claim_field(ResourceServerId, Payload0) of - true -> post_process_payload_with_complex_claim(ResourceServerId, Payload0); + Payload1 = case does_include_complex_claim_field(ResourceServer, Payload0) of + true -> post_process_payload_with_complex_claim(ResourceServer, Payload0); false -> Payload0 end, @@ -209,13 +212,13 @@ post_process_payload(ResourceServerId, Payload) when is_map(Payload) -> false -> Payload1 end, - Payload3 = case rabbit_oauth2_config:has_scope_aliases(ResourceServerId) of - true -> post_process_payload_with_scope_aliases(ResourceServerId, Payload2); + Payload3 = case rabbit_oauth2_config:has_scope_aliases(ResourceServer) of + true -> post_process_payload_with_scope_aliases(ResourceServer, Payload2); false -> Payload2 end, Payload4 = case maps:is_key(<<"authorization_details">>, Payload3) of - true -> post_process_payload_in_rich_auth_request_format(ResourceServerId, Payload3); + true -> post_process_payload_in_rich_auth_request_format(ResourceServer, Payload3); false -> Payload3 end, @@ -223,7 +226,7 @@ post_process_payload(ResourceServerId, Payload) when is_map(Payload) -> -spec post_process_payload_with_scope_aliases( - ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). + ResourceServer :: resource_server(), Payload :: map()) -> map(). %% This is for those hopeless environments where the token structure is so out of %% messaging team's control that even the extra scopes field is no longer an option. %% @@ -285,7 +288,7 @@ post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAlias -spec does_include_complex_claim_field( - ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> boolean(). + ResourceServer :: resource_server(), Payload :: map()) -> boolean(). does_include_complex_claim_field(ResourceServer, Payload) when is_map(Payload) -> case ResourceServer#resource_server.additional_scopes_key of {ok, ScopeKey} -> maps:is_key(ScopeKey, Payload); @@ -293,37 +296,37 @@ does_include_complex_claim_field(ResourceServer, Payload) when is_map(Payload) - end. -spec post_process_payload_with_complex_claim( - ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). + ResourceServer :: resource_server(), Payload :: map()) -> map(). post_process_payload_with_complex_claim(ResourceServer, Payload) -> ResourceServerId = ResourceServer#resource_server.id, case ResourceServer#resource_server.additional_scopes_key of - {ok, ScopesKey} -> - ComplexClaim = maps:get(ScopesKey, Payload), - AdditionalScopes = - case ComplexClaim of - L when is_list(L) -> L; - M when is_map(M) -> - case maps:get(ResourceServerId, M, 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; - Bin when is_binary(Bin) -> - binary:split(Bin, <<" ">>, [global, trim_all]); + undefined -> Payload; + ScopesKey -> + AdditionalScopes = extract_additional_scopes(ResourceServerId, + maps:get(ScopesKey, Payload)) + case AdditionalScopes of + [] -> Payload; + _ -> + ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), + maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload) + end + end. +extract_additional_scopes(ResourceServerId, ComplexClaim) -> + case ComplexClaim of + L when is_list(L) -> L; + M when is_map(M) -> + case maps:get(ResourceServerId, M, 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, - - case AdditionalScopes of - [] -> Payload; - _ -> - ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), - maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload) - end; - {error, not_found} -> Payload + end; + Bin when is_binary(Bin) -> + binary:split(Bin, <<" ">>, [global, trim_all]); + _ -> [] end. -spec post_process_payload_in_keycloak_format(Payload :: map()) -> map(). @@ -501,22 +504,25 @@ post_process_payload_in_rich_auth_request_format(ResourceServer, ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload). -validate_payload(ResourceServerId, DecodedToken) -> - ScopePrefix = rabbit_oauth2_config:get_scope_prefix(ResourceServerId), - validate_payload(ResourceServerId, DecodedToken, ScopePrefix). +validate_payload(ResourceServer, DecodedToken) -> + ScopePrefix = ResourceServerId#resource_server.scope_prefix, + validate_payload(ResourceServer, DecodedToken, ScopePrefix). -validate_payload(ResourceServerId, #{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> - case check_aud(Aud, ResourceServerId) of +validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> + ResourceServerId = ResourceServer#resource_server.id, + VerifyAud = ResourceServer#resource_server.verify_aud, + case check_aud(Aud, ResourceServer, VerifyAud) of ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}; {error, Err} -> {refused, {invalid_aud, Err}} end; -validate_payload(ResourceServerId, #{?AUD_JWT_FIELD := Aud} = DecodedToken, _ScopePrefix) -> - case check_aud(Aud, ResourceServerId) of +validate_payload(ResourceServer, #{?AUD_JWT_FIELD := Aud} = DecodedToken, _ScopePrefix) -> + case check_aud(Aud, ResourceServer, VerifyAud) of ok -> {ok, DecodedToken}; {error, Err} -> {refused, {invalid_aud, Err}} end; -validate_payload(ResourceServerId, #{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ScopePrefix) -> - case rabbit_oauth2_config:is_verify_aud(ResourceServerId) of +validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ScopePrefix) -> + VerifyAud = ResourceServer#resource_server.verify_aud, + case VerifyAud of true -> {error, {badarg, {aud_field_is_missing}}}; false -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}} end. @@ -525,9 +531,9 @@ filter_scopes(Scopes, <<"">>) -> Scopes; filter_scopes(Scopes, ScopePrefix) -> matching_scopes_without_prefix(Scopes, ScopePrefix). -check_aud(_, <<>>) -> ok; -check_aud(Aud, ResourceServerId) -> - case rabbit_oauth2_config:is_verify_aud(ResourceServerId) of +check_aud(_, <<>>, _) -> ok; +check_aud(Aud, ResourceServerId, Verify) -> + case Verify of true -> case Aud of List when is_list(List) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index d7050998d00a..7a55b701f5b4 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -22,9 +22,9 @@ format_ssl_options/1, format_oauth_provider_id/1, get_oauth_provider/2]). --import(rabbit_resource_server, [ +-import(resource_server, [ resolve_resource_server_from_audience/1]). --import(rabbit_oauth_provider, [ +-import(oauth_provider, [ add_signing_key/2, get_signing_key/2, get_internal_oauth_provider/1, replace_signing_keys/2]). From c4e852116b2bfdaa80aadf074e745525026458c3 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 17 Sep 2024 10:50:29 +0200 Subject: [PATCH 20/64] Fix test cases --- .../src/rabbit_auth_backend_oauth2.erl | 41 ++++++++++--------- .../src/resource_server.erl | 6 --- .../test/oauth2_schema_SUITE.erl | 28 ++++++------- .../certs/cacert.pem | 0 .../certs/cert.pem | 0 .../certs/key.pem | 0 6 files changed, 36 insertions(+), 39 deletions(-) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_schema_SUITE_data => oauth2_schema_SUITE_data}/certs/cacert.pem (100%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_schema_SUITE_data => oauth2_schema_SUITE_data}/certs/cert.pem (100%) rename deps/rabbitmq_auth_backend_oauth2/test/{rabbit_oauth2_schema_SUITE_data => oauth2_schema_SUITE_data}/certs/key.pem (100%) 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 992a51454c69..417551b8c4a9 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 @@ -303,7 +303,7 @@ post_process_payload_with_complex_claim(ResourceServer, Payload) -> undefined -> Payload; ScopesKey -> AdditionalScopes = extract_additional_scopes(ResourceServerId, - maps:get(ScopesKey, Payload)) + maps:get(ScopesKey, Payload)), case AdditionalScopes of [] -> Payload; _ -> @@ -505,18 +505,18 @@ post_process_payload_in_rich_auth_request_format(ResourceServer, maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload). validate_payload(ResourceServer, DecodedToken) -> - ScopePrefix = ResourceServerId#resource_server.scope_prefix, + ScopePrefix = ResourceServer#resource_server.scope_prefix, validate_payload(ResourceServer, DecodedToken, ScopePrefix). validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> ResourceServerId = ResourceServer#resource_server.id, VerifyAud = ResourceServer#resource_server.verify_aud, - case check_aud(Aud, ResourceServer, VerifyAud) of + case check_aud(Aud, ResourceServer) of ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}; {error, Err} -> {refused, {invalid_aud, Err}} end; validate_payload(ResourceServer, #{?AUD_JWT_FIELD := Aud} = DecodedToken, _ScopePrefix) -> - case check_aud(Aud, ResourceServer, VerifyAud) of + case check_aud(Aud, ResourceServer) of ok -> {ok, DecodedToken}; {error, Err} -> {refused, {invalid_aud, Err}} end; @@ -531,21 +531,24 @@ filter_scopes(Scopes, <<"">>) -> Scopes; filter_scopes(Scopes, ScopePrefix) -> matching_scopes_without_prefix(Scopes, ScopePrefix). -check_aud(_, <<>>, _) -> ok; -check_aud(Aud, ResourceServerId, Verify) -> - case Verify of - true -> - case Aud of - List when is_list(List) -> - case lists:member(ResourceServerId, Aud) of - true -> ok; - false -> {error, {resource_id_not_found_in_aud, ResourceServerId, Aud}} - end; - _ -> {error, {badarg, {aud_is_not_a_list, Aud}}} - end; - false -> ok - end. - +check_aud(Aud, ResourceServer) -> + case ResourceServer#resource_server.id of + <<>> -> ok; + ResourceServerId -> + case ResourceServer#resource_server.verify_aud of + true -> + case Aud of + List when is_list(List) -> + case lists:member(ResourceServerId, Aud) of + true -> ok; + false -> {error, {resource_id_not_found_in_aud, + ResourceServerId, Aud}} + end; + _ -> {error, {badarg, {aud_is_not_a_list, Aud}}} + end; + false -> ok + end + end. %%-------------------------------------------------------------------- get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index e0475d0623bf..56ed0f3866ff 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -198,12 +198,6 @@ find_unique_resource_server_without_verify_aud() -> _ -> {error, found_many} end. -append_or_return_default(ListOrBinary, Default) -> - case ListOrBinary of - VarList when is_list(VarList) -> VarList ++ Default; - VarBinary when is_binary(VarBinary) -> [VarBinary] ++ Default; - _ -> Default - end. append(List, Value) -> case Value of undefined -> List; diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index c941a21fb56f..7830ce623172 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -33,22 +33,22 @@ all() -> test_without_oauth_providers(_) -> - #{} = rabbit_oauth2_schema:translate_oauth_providers([]). + #{} = oauth2_schema:translate_oauth_providers([]). test_without_resource_servers(_) -> - #{} = rabbit_oauth2_schema:translate_resource_servers([]). + #{} = oauth2_schema:translate_resource_servers([]). test_with_one_oauth_provider(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} ], #{<<"keycloak">> := [{issuer, <<"https://rabbit">>}] - } = rabbit_oauth2_schema:translate_oauth_providers(Conf). + } = oauth2_schema:translate_oauth_providers(Conf). test_with_one_resource_server(_) -> Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"} ], #{<<"rabbitmq1">> := [{id, <<"rabbitmq1">>}] - } = rabbit_oauth2_schema:translate_resource_servers(Conf). + } = oauth2_schema:translate_resource_servers(Conf). test_with_many_oauth_providers(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, @@ -58,7 +58,7 @@ test_with_many_oauth_providers(_) -> ], <<"uaa">> := [{issuer, <<"https://uaa">>} ] - } = rabbit_oauth2_schema:translate_oauth_providers(Conf). + } = oauth2_schema:translate_oauth_providers(Conf). test_with_many_resource_servers(_) -> @@ -69,7 +69,7 @@ test_with_many_resource_servers(_) -> ], <<"rabbitmq2">> := [{id, <<"rabbitmq2">>} ] - } = rabbit_oauth2_schema:translate_resource_servers(Conf). + } = oauth2_schema:translate_resource_servers(Conf). test_oauth_providers_attributes(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, @@ -78,7 +78,7 @@ test_oauth_providers_attributes(_) -> #{<<"keycloak">> := [{default_key, <<"token-key">>}, {issuer, <<"https://keycloak">>} ] - } = sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)). + } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). test_resource_servers_attributes(_) -> Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1xxx"}, @@ -92,7 +92,7 @@ test_resource_servers_attributes(_) -> {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, {scope_prefix, <<"somescope.">>} ] - } = sort_settings(rabbit_oauth2_schema:translate_resource_servers(Conf)), + } = sort_settings(oauth2_schema:translate_resource_servers(Conf)), Conf2 = [ {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"],"somescope."}, @@ -105,13 +105,13 @@ test_resource_servers_attributes(_) -> {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, {scope_prefix, <<"somescope.">>} ] - } = sort_settings(rabbit_oauth2_schema:translate_resource_servers(Conf2)). + } = sort_settings(oauth2_schema:translate_resource_servers(Conf2)). test_oauth_providers_attributes_with_invalid_uri(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"http://keycloak"}, {["auth_oauth2","oauth_providers","keycloak","default_key"],"token-key"} ], - try sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)) of + try sort_settings(oauth2_schema:translate_oauth_providers(Conf)) of _ -> {throw, should_have_failed} catch _ -> ok @@ -125,7 +125,7 @@ test_oauth_providers_algorithms(_) -> #{<<"keycloak">> := [{algorithms, [<<"RS256">>, <<"HS256">>]}, {issuer, <<"https://keycloak">>} ] - } = sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)). + } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). test_oauth_providers_https(Conf) -> @@ -148,14 +148,14 @@ test_oauth_providers_https(Conf) -> ]}, {issuer, <<"https://keycloak">>} ] - } = sort_settings(rabbit_oauth2_schema:translate_oauth_providers(CuttlefishConf)). + } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)). test_oauth_providers_https_with_missing_cacertfile(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"],"/non-existent.pem"} ], - try sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)) of + try sort_settings(oauth2_schema:translate_oauth_providers(Conf)) of _ -> {throw, should_have_failed} catch _ -> ok @@ -169,7 +169,7 @@ test_oauth_providers_signing_keys(Conf) -> #{<<"keycloak">> := [{issuer, <<"https://keycloak">>}, {signing_keys, SigningKeys} ] - } = sort_settings(rabbit_oauth2_schema:translate_oauth_providers(CuttlefishConf)), + } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)), ct:log("SigningKey: ~p", [SigningKeys]), #{<<"1">> := {pem, <<"I'm not a certificate">>}, <<"2">> := {pem, <<"I'm not a certificate">>} diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cacert.pem b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cacert.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cacert.pem rename to deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cacert.pem diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cert.pem b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cert.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cert.pem rename to deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cert.pem diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/key.pem b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/key.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/key.pem rename to deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/key.pem From 9ecca5ae7a5f0b98547e90abf9d7dedc38fff6d5 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 17 Sep 2024 12:33:18 +0200 Subject: [PATCH 21/64] Fix test system test cases And move constants to oauth2.hrl --- .../include/oauth2.hrl | 31 +++ .../src/oauth2_client.hrl | 47 +++++ .../src/rabbit_auth_backend_oauth2.erl | 195 ++++++++---------- .../src/uaa_jwt.erl | 47 ++--- .../test/oauth_provider_SUITE.erl | 5 +- 5 files changed, 186 insertions(+), 139 deletions(-) create mode 100644 deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl index bfc570082d4d..f826bae7f80e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -12,6 +12,37 @@ -define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). %% scope aliases map "role names" to a set of scopes +%% +%% Key JWT fields +%% + +-define(AUD_JWT_FIELD, <<"aud">>). +-define(SCOPE_JWT_FIELD, <<"scope">>). + +%% End of Key JWT fields + +%% +%% Rich Authorization Request fields +%% +-define(RAR_ACTIONS_FIELD, <<"actions">>). +-define(RAR_LOCATIONS_FIELD, <<"locations">>). +-define(RAR_TYPE_FIELD, <<"type">>). + +-define(RAR_CLUSTER_LOCATION_ATTRIBUTE, <<"cluster">>). +-define(RAR_VHOST_LOCATION_ATTRIBUTE, <<"vhost">>). +-define(RAR_QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). +-define(RAR_EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). +-define(RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). +-define(RAR_LOCATION_ATTRIBUTES, [?RAR_CLUSTER_LOCATION_ATTRIBUTE, ?RAR_VHOST_LOCATION_ATTRIBUTE, + ?RAR_QUEUE_LOCATION_ATTRIBUTE, ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). + +-define(RAR_ALLOWED_TAG_VALUES, [<<"monitoring">>, <<"administrator">>, <<"management">>, <<"policymaker">> ]). +-define(RAR_ALLOWED_ACTION_VALUES, [<<"read">>, <<"write">>, <<"configure">>, <<"monitoring">>, + <<"administrator">>, <<"management">>, <<"policymaker">> ]). + +%% end of Rich Authorization Request fields + + -record(internal_oauth_provider, { id :: oauth_provider_id(), default_key :: binary() | undefined, diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl new file mode 100644 index 000000000000..24534dc136f4 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl @@ -0,0 +1,47 @@ +%% 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) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. +%% + +-include("types.hrl"). + +% define access token request common constants + +-define(DEFAULT_HTTP_TIMEOUT, 60000). + +% Refresh tome this number of seconds before expires_in token's attribute +-define(REFRESH_IN_BEFORE_EXPIRES_IN, 5). + +-define(DEFAULT_OPENID_CONFIGURATION_PATH, "/.well-known/openid-configuration"). + +% define access token request constants +-define(CONTENT_URLENCODED, "application/x-www-form-urlencoded"). +-define(CONTENT_JSON, "application/json"). +-define(REQUEST_GRANT_TYPE, "grant_type"). +-define(CLIENT_CREDENTIALS_GRANT_TYPE, "client_credentials"). +-define(REFRESH_TOKEN_GRANT_TYPE, "refresh_token"). + +-define(REQUEST_CLIENT_ID, "client_id"). +-define(REQUEST_CLIENT_SECRET, "client_secret"). +-define(REQUEST_SCOPE, "scope"). +-define(REQUEST_REFRESH_TOKEN, "refresh_token"). + +% define access token response constants +-define(BEARER_TOKEN_TYPE, <<"Bearer">>). + +-define(RESPONSE_ACCESS_TOKEN, <<"access_token">>). +-define(RESPONSE_TOKEN_TYPE, <<"token_type">>). +-define(RESPONSE_EXPIRES_IN, <<"expires_in">>). +-define(RESPONSE_REFRESH_TOKEN, <<"refresh_token">>). + +-define(RESPONSE_ERROR, <<"error">>). +-define(RESPONSE_ERROR_DESCRIPTION, <<"error_description">>). + +-define(RESPONSE_ISSUER, <<"issuer">>). +-define(RESPONSE_TOKEN_ENDPOINT, <<"token_endpoint">>). +-define(RESPONSE_AUTHORIZATION_ENDPOINT, <<"authorization_endpoint">>). +-define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>). +-define(RESPONSE_JWKS_URI, <<"jwks_uri">>). +-define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>). 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 417551b8c4a9..f3c243581a8c 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 @@ -16,7 +16,7 @@ -export([description/0]). -export([user_login_authentication/2, user_login_authorization/2, check_vhost_access/3, check_resource_access/4, - check_topic_access/4, check_token/1, update_state/2, + check_topic_access/4, update_state/2, expiry_timestamp/1]). %% for testing @@ -33,19 +33,12 @@ %% --define(RESOURCE_SERVER_ID, resource_server_id). %% a term defined for Rich Authorization Request tokens to identify a RabbitMQ permission %% verify server_server_id aud field is on the aud field %% a term used by the IdentityServer community %% scope aliases map "role names" to a set of scopes -%% -%% Key JWT fields -%% - --define(AUD_JWT_FIELD, <<"aud">>). --define(SCOPE_JWT_FIELD, <<"scope">>). %% %% API %% @@ -76,7 +69,7 @@ check_vhost_access(#auth_user{impl = DecodedTokenFun}, with_decoded_token(DecodedTokenFun(), fun(_Token) -> DecodedToken = DecodedTokenFun(), - Scopes = get_scopes(DecodedToken), + Scopes = uaa_jwt:get_scopes(DecodedToken), ScopeString = rabbit_oauth2_scope:concat_scopes(Scopes, ","), rabbit_log:debug("Matching virtual host '~ts' against the following scopes: ~ts", [VHost, ScopeString]), rabbit_oauth2_scope:vhost_access(VHost, Scopes) @@ -86,7 +79,7 @@ check_resource_access(#auth_user{impl = DecodedTokenFun}, Resource, Permission, _AuthzContext) -> with_decoded_token(DecodedTokenFun(), fun(Token) -> - Scopes = get_scopes(Token), + Scopes = uaa_jwt:get_scopes(Token), rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes) end). @@ -99,19 +92,22 @@ check_topic_access(#auth_user{impl = DecodedTokenFun}, end). update_state(AuthUser, NewToken) -> - case check_token(NewToken) of - %% avoid logging the token - {error, _} = E -> E; - {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> - {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid"}; - {refused, Err} -> - {refused, rabbit_misc:format("Authentication using an OAuth 2/JWT token failed: ~tp", [Err])}; - {ok, DecodedToken} -> - Tags = tags_from(DecodedToken), - - {ok, AuthUser#auth_user{tags = Tags, - impl = fun() -> DecodedToken end}} - end. + case uaa_jwt:resolve_resource_server(NewToken) of + {error, _} = Err0 -> Err0; + Tuple -> + case check_token(NewToken, Tuple) of + %% avoid logging the token + {error, _} = E -> E; + {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> + {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid"}; + {refused, Err} -> + {refused, rabbit_misc:format("Authentication using an OAuth 2/JWT token failed: ~tp", [Err])}; + {ok, DecodedToken} -> + Tags = tags_from(DecodedToken), + {ok, AuthUser#auth_user{tags = Tags, + impl = fun() -> DecodedToken end}} + end + end. expiry_timestamp(#auth_user{impl = DecodedTokenFun}) -> case DecodedTokenFun() of @@ -126,31 +122,34 @@ expiry_timestamp(#auth_user{impl = DecodedTokenFun}) -> authenticate(_, AuthProps0) -> AuthProps = to_map(AuthProps0), Token = token_from_context(AuthProps), - - case check_token(Token) of - %% avoid logging the token - {error, _} = E -> E; - {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> - {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid", []}; - {refused, Err} -> - {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [Err]}; - {ok, DecodedToken} -> - Func = fun(Token0) -> - Username = username_from(rabbit_oauth2_config:get_preferred_username_claims(), Token0), - Tags = tags_from(Token0), - - {ok, #auth_user{username = Username, - tags = Tags, - impl = fun() -> Token0 end}} - end, - case with_decoded_token(DecodedToken, Func) of - {error, Err} -> + case uaa_jwt:resolve_resource_server(Token) of + {error, _} = Err0 -> + Err0; + {ResourceServer, _InternalOAuthProvider} = Tuple -> + case check_token(Token, Tuple) of + {error, _} = E -> E; + {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> + {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid", []}; + {refused, Err} -> {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [Err]}; - Else -> - Else + {ok, DecodedToken} -> + Func = fun(Token0) -> + Username = username_from( + ResourceServer#resource_server.preferred_username_claims, + Token0), + Tags = tags_from(Token0), + {ok, #auth_user{username = Username, + tags = Tags, + impl = fun() -> Token0 end}} + end, + case with_decoded_token(DecodedToken, Func) of + {error, Err} -> + {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [Err]}; + Else -> + Else + end end end. - with_decoded_token(DecodedToken, Fun) -> case validate_token_expiry(DecodedToken) of ok -> Fun(DecodedToken); @@ -167,27 +166,21 @@ validate_token_expiry(#{<<"exp">> := Exp}) when is_integer(Exp) -> end; validate_token_expiry(#{}) -> ok. --spec check_token(binary() | map()) -> - {'ok', map(), resource_server()} | +-spec check_token(binary() | map(), {resource_server(), internal_oauth_provider()}) -> + {'ok', map()} | {'error', term() }| - {'refused', - 'signature_invalid' | + {'refused', 'signature_invalid' | {'error', term()} | {'invalid_aud', term()}}. -check_token(DecodedToken) when is_map(DecodedToken) -> +check_token(DecodedToken, _) when is_map(DecodedToken) -> {ok, DecodedToken}; -check_token(Token) -> - case uaa_jwt:decode_and_verify(Token) of - {error, Reason} -> - {refused, {error, Reason}}; - {true, ResourceServer, Payload} -> - Payload0 = post_process_payload(ResourceServer, Payload), - case validate_payload(ResourceServer, Payload0) of - {ok, DecodedToken} -> {ok, DecodedToken, ResourceServer}; - {error, _} = Error -> Error - end; +check_token(Token, {ResourceServer, InternalOAuthProvider}) -> + case uaa_jwt:decode_and_verify(Token, ResourceServer, InternalOAuthProvider) of + {error, Reason} -> {refused, {error, Reason}}; + {true, Payload} -> validate_payload(ResourceServer, + post_process_payload(ResourceServer, Payload)); {false, _, _} -> {refused, signature_invalid} end. @@ -212,9 +205,9 @@ post_process_payload(ResourceServer, Payload) when is_map(Payload) -> false -> Payload1 end, - Payload3 = case rabbit_oauth2_config:has_scope_aliases(ResourceServer) of - true -> post_process_payload_with_scope_aliases(ResourceServer, Payload2); - false -> Payload2 + Payload3 = case ResourceServer#resource_server.scope_aliases of + undefined -> Payload2; + _ -> post_process_payload_with_scope_aliases(ResourceServer, Payload2) end, Payload4 = case maps:is_key(<<"authorization_details">>, Payload3) of @@ -241,7 +234,7 @@ post_process_payload_with_scope_aliases(ResourceServer, Payload) -> -spec post_process_payload_with_scope_alias_in_scope_field( - ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). + ResourceServer :: resource_server(), Payload :: map()) -> map(). %% First attempt: use the value in the 'scope' field for alias post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload) -> ScopeMappings = ResourceServer#resource_server.scope_aliases, @@ -249,14 +242,13 @@ post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload) -> -spec post_process_payload_with_scope_alias_in_extra_scopes_source( - ResourceServer :: rabbit_oauth2_config:resource_server(), Payload :: map()) -> map(). + ResourceServer :: resource_server(), Payload :: map()) -> map(). %% Second attempt: use the value in the configurable 'extra scopes source' field for alias post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServer, Payload) -> ExtraScopesField = ResourceServer#resource_server.additional_scopes_key, case ExtraScopesField of - %% nothing to inject - {error, not_found} -> Payload; - {ok, ExtraScopes} -> + undefined -> Payload; + ExtraScopes -> ScopeMappings = ResourceServer#resource_server.scope_aliases, post_process_payload_with_scope_alias_field_named(Payload, ExtraScopes, ScopeMappings) end. @@ -290,19 +282,18 @@ post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAlias -spec does_include_complex_claim_field( ResourceServer :: resource_server(), Payload :: map()) -> boolean(). does_include_complex_claim_field(ResourceServer, Payload) when is_map(Payload) -> - case ResourceServer#resource_server.additional_scopes_key of - {ok, ScopeKey} -> maps:is_key(ScopeKey, Payload); - {error, not_found} -> false - end. + case ResourceServer#resource_server.additional_scopes_key of + undefined -> false; + ScopeKey -> maps:is_key(ScopeKey, Payload) + end. -spec post_process_payload_with_complex_claim( ResourceServer :: resource_server(), Payload :: map()) -> map(). post_process_payload_with_complex_claim(ResourceServer, Payload) -> - ResourceServerId = ResourceServer#resource_server.id, case ResourceServer#resource_server.additional_scopes_key of undefined -> Payload; ScopesKey -> - AdditionalScopes = extract_additional_scopes(ResourceServerId, + AdditionalScopes = extract_additional_scopes(ResourceServer, maps:get(ScopesKey, Payload)), case AdditionalScopes of [] -> Payload; @@ -311,7 +302,8 @@ post_process_payload_with_complex_claim(ResourceServer, Payload) -> maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload) end end. -extract_additional_scopes(ResourceServerId, ComplexClaim) -> +extract_additional_scopes(ResourceServer, ComplexClaim) -> + ResourceServerId = ResourceServer#resource_server.id, case ComplexClaim of L when is_list(L) -> L; M when is_map(M) -> @@ -353,28 +345,12 @@ extract_scopes_from_keycloak_permissions(Acc, [_ | T]) -> extract_scopes_from_keycloak_permissions(Acc, T). --define(ACTIONS_FIELD, <<"actions">>). --define(LOCATIONS_FIELD, <<"locations">>). --define(TYPE_FIELD, <<"type">>). - --define(CLUSTER_LOCATION_ATTRIBUTE, <<"cluster">>). --define(VHOST_LOCATION_ATTRIBUTE, <<"vhost">>). --define(QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). --define(EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). --define(ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). --define(LOCATION_ATTRIBUTES, [?CLUSTER_LOCATION_ATTRIBUTE, ?VHOST_LOCATION_ATTRIBUTE, - ?QUEUE_LOCATION_ATTRIBUTE, ?EXCHANGE_LOCATION_ATTRIBUTE, ?ROUTING_KEY_LOCATION_ATTRIBUTE]). - --define(ALLOWED_TAG_VALUES, [<<"monitoring">>, <<"administrator">>, <<"management">>, <<"policymaker">> ]). --define(ALLOWED_ACTION_VALUES, [<<"read">>, <<"write">>, <<"configure">>, <<"monitoring">>, - <<"administrator">>, <<"management">>, <<"policymaker">> ]). - put_location_attribute(Attribute, Map) -> put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). put_attribute([Key, Value | _], Map) -> - case lists:member(Key, ?LOCATION_ATTRIBUTES) of + case lists:member(Key, ?RAR_LOCATION_ATTRIBUTES) of true -> maps:put(Key, Value, Map); false -> Map end; @@ -390,10 +366,10 @@ convert_attribute_list_to_attribute_map([H|L],Map) when is_binary(H) -> convert_attribute_list_to_attribute_map([], Map) -> Map. build_permission_resource_path(Map) -> - Vhost = maps:get(?VHOST_LOCATION_ATTRIBUTE, Map, <<"*">>), - Resource = maps:get(?QUEUE_LOCATION_ATTRIBUTE, Map, - maps:get(?EXCHANGE_LOCATION_ATTRIBUTE, Map, <<"*">>)), - RoutingKey = maps:get(?ROUTING_KEY_LOCATION_ATTRIBUTE, Map, <<"*">>), + Vhost = maps:get(?RAR_VHOST_LOCATION_ATTRIBUTE, Map, <<"*">>), + Resource = maps:get(?RAR_QUEUE_LOCATION_ATTRIBUTE, Map, + maps:get(?RAR_EXCHANGE_LOCATION_ATTRIBUTE, Map, <<"*">>)), + RoutingKey = maps:get(?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, Map, <<"*">>), <>. @@ -417,15 +393,15 @@ map_locations_to_permission_resource_paths(ResourceServerId, L) -> FilteredLocations. -cluster_matches_resource_server_id(#{?CLUSTER_LOCATION_ATTRIBUTE := Cluster}, +cluster_matches_resource_server_id(#{?RAR_CLUSTER_LOCATION_ATTRIBUTE := Cluster}, ResourceServerId) -> wildcard:match(ResourceServerId, Cluster); cluster_matches_resource_server_id(_,_) -> false. -legal_queue_and_exchange_values(#{?QUEUE_LOCATION_ATTRIBUTE := Queue, - ?EXCHANGE_LOCATION_ATTRIBUTE := Exchange}) -> +legal_queue_and_exchange_values(#{?RAR_QUEUE_LOCATION_ATTRIBUTE := Queue, + ?RAR_EXCHANGE_LOCATION_ATTRIBUTE := Exchange}) -> case Queue of <<>> -> case Exchange of @@ -444,7 +420,7 @@ map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions) -> map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions, []). map_rich_auth_permissions_to_scopes(_, [], Acc) -> Acc; map_rich_auth_permissions_to_scopes(ResourceServerId, - [ #{?ACTIONS_FIELD := Actions, ?LOCATIONS_FIELD := Locations } | T ], Acc) -> + [ #{?RAR_ACTIONS_FIELD := Actions, ?RAR_LOCATIONS_FIELD := Locations } | T ], Acc) -> ResourcePaths = map_locations_to_permission_resource_paths(ResourceServerId, Locations), case ResourcePaths of [] -> map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc); @@ -462,10 +438,10 @@ map_rich_auth_permissions_to_scopes(ResourceServerId, end. skip_unknown_actions(Actions) -> - lists:filter(fun(A) -> lists:member(A, ?ALLOWED_ACTION_VALUES) end, Actions). + lists:filter(fun(A) -> lists:member(A, ?RAR_ALLOWED_ACTION_VALUES) end, Actions). produce_list_of_user_tag_or_action_on_resources(ResourceServerId, ActionOrUserTag, Locations) -> - case lists:member(ActionOrUserTag, ?ALLOWED_TAG_VALUES) of + case lists:member(ActionOrUserTag, ?RAR_ALLOWED_TAG_VALUES) of true -> [<< ResourceServerId/binary, ".tag:", ActionOrUserTag/binary >>]; _ -> build_scopes_for_action(ResourceServerId, ActionOrUserTag, Locations, []) end. @@ -480,7 +456,8 @@ build_scopes(ResourceServerId, Actions, Locations) -> produce_list_of_user_tag_or_action_on_resources(ResourceServerId, Action, Locations) end, Actions). -is_recognized_permission(#{?ACTIONS_FIELD := _, ?LOCATIONS_FIELD:= _ , ?TYPE_FIELD := Type }, ResourceServerType) -> +is_recognized_permission(#{?RAR_ACTIONS_FIELD := _, ?RAR_LOCATIONS_FIELD:= _ , + ?RAR_TYPE_FIELD := Type }, ResourceServerType) -> case ResourceServerType of <<>> -> false; V when V == Type -> true; @@ -508,9 +485,8 @@ validate_payload(ResourceServer, DecodedToken) -> ScopePrefix = ResourceServer#resource_server.scope_prefix, validate_payload(ResourceServer, DecodedToken, ScopePrefix). -validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope, ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> - ResourceServerId = ResourceServer#resource_server.id, - VerifyAud = ResourceServer#resource_server.verify_aud, +validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope, + ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> case check_aud(Aud, ResourceServer) of ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}; {error, Err} -> {refused, {invalid_aud, Err}} @@ -521,8 +497,7 @@ validate_payload(ResourceServer, #{?AUD_JWT_FIELD := Aud} = DecodedToken, _Scope {error, Err} -> {refused, {invalid_aud, Err}} end; validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ScopePrefix) -> - VerifyAud = ResourceServer#resource_server.verify_aud, - case VerifyAud of + case ResourceServer#resource_server.verify_aud of true -> {error, {badarg, {aud_field_is_missing}}}; false -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}} end. @@ -551,13 +526,11 @@ check_aud(Aud, ResourceServer) -> end. %%-------------------------------------------------------------------- -get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; -get_scopes(#{}) -> []. -spec get_expanded_scopes(map(), #resource{}) -> [binary()]. get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> Context = #{ token => Token , vhost => VHost}, - case maps:get(?SCOPE_JWT_FIELD, Token, []) of + case uaa_jwt:get_scopes(Token) of [] -> []; Scopes -> lists:map(fun(Scope) -> list_to_binary(parse_scope(Scope, Context)) end, Scopes) end. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index 7a55b701f5b4..dec06c301f93 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -7,11 +7,12 @@ -module(uaa_jwt). -export([add_signing_key/3, - decode_and_verify/1, + decode_and_verify/3, get_jwk/2, - verify_signing_key/2]). + verify_signing_key/2, + resolve_resource_server/1]). --export([client_id/1, sub/1, client_id/2, sub/2]). +-export([client_id/1, sub/1, client_id/2, sub/2, get_scopes/1]). -include("oauth2.hrl"). -include_lib("jose/include/jose_jwk.hrl"). @@ -61,17 +62,8 @@ update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, Err end. --spec decode_and_verify(binary()) -> {boolean(), resource_server(), map()} | {error, term()}. -decode_and_verify(Token) -> - case resolve_resource_server(Token) of - {error, _} = Err -> - Err; - {ResourceServer, InternalOAuthProvider} -> - decode_and_verify(Token, ResourceServer, InternalOAuthProvider) - end. - -spec decode_and_verify(binary(), resource_server(), internal_oauth_provider()) - -> {boolean(), resource_server(), map()} | {error, term()}. + -> {boolean(), map()} | {error, term()}. decode_and_verify(Token, ResourceServer, InternalOAuthProvider) -> OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, rabbit_log:debug("Decoding token for resource_server: ~p using oauth_provider_id: ~p", @@ -91,27 +83,29 @@ decode_and_verify(Token, ResourceServer, InternalOAuthProvider) -> Algorithms = InternalOAuthProvider#internal_oauth_provider.algorithms, rabbit_log:debug("Verifying signature using signing_key_id : '~tp' and algorithms: ~p", [KeyId, Algorithms]), - case uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token) of - {true, Payload} -> {true, ResourceServer, Payload}; - {false, Payload} -> {false, ResourceServer, Payload} - end; + uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token); {error, _} = Err3 -> Err3 end end. +-spec resolve_resource_server(binary()|map()) -> {error, term()} | + {resource_server(), internal_oauth_provider()}. +resolve_resource_server(DecodedToken) when is_map(DecodedToken) -> + Aud = maps:get(?AUD_JWT_FIELD, DecodedToken, none), + resolve_resource_server_given_audience(Aud); resolve_resource_server(Token) -> case uaa_jwt_jwt:get_aud(Token) of + {error, _} = Error -> Error; + {ok, Audience} -> resolve_resource_server_given_audience(Audience) + end. +resolve_resource_server_given_audience(Audience) -> + case resolve_resource_server_from_audience(Audience) of {error, _} = Error -> Error; - {ok, Audience} -> - case resolve_resource_server_from_audience(Audience) of - {error, _} = Error -> - Error; - {ok, ResourceServer} -> - {ResourceServer, get_internal_oauth_provider( - ResourceServer#resource_server.id)} - end + {ok, ResourceServer} -> + {ResourceServer, get_internal_oauth_provider( + ResourceServer#resource_server.oauth_provider_id)} end. -spec get_jwk(binary(), internal_oauth_provider()) -> {ok, map()} | {error, term()}. @@ -171,6 +165,9 @@ verify_signing_key(Type, Value) -> Err -> Err end. +-spec get_scopes(map()) -> binary() | list(). +get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; +get_scopes(#{}) -> []. -spec client_id(map()) -> binary() | undefined. client_id(DecodedToken) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl index 1e5a6929b5e6..12fb06d054ec 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl @@ -217,8 +217,7 @@ end_per_group(oauth_provider_with_jwks_uri, Config) -> Config; end_per_group(oauth_provider_with_default_key, Config) -> - DefaultKey = <<"default-key">>, - case ?config(oauth_provider_id, Config) of +case ?config(oauth_provider_id, Config) of root -> unset_env(default_key); Id -> unset_oauth_provider_properties(Id, [default_key]) end, @@ -507,7 +506,7 @@ unset_oauth_provider_properties(OAuthProviderId, PropertyNameList) -> CurProplist = maps:get(OAuthProviderId, OAuthProviders), CurMap = proplists:to_map(CurProplist), set_env(oauth_providers, maps:put(OAuthProviderId, - maps:to_list(maps:filter(fun(K,V) -> + maps:to_list(maps:filter(fun(K,_V) -> not proplists:is_defined(K, PropertyNameList) end, CurMap)), OAuthProviders)). From 0f5f76677f600e119b03faa390fff430f1eb139d Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 17 Sep 2024 15:47:44 +0200 Subject: [PATCH 22/64] More test fixes + clean up + refactor --- .../src/rabbit_auth_backend_oauth2.erl | 192 +++++------------- .../src/rabbit_oauth2_scope.erl | 24 ++- .../src/uaa_jwt.erl | 12 +- .../test/unit_SUITE.erl | 11 +- 4 files changed, 91 insertions(+), 148 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 f3c243581a8c..65c21ed9a07e 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 @@ -20,9 +20,15 @@ expiry_timestamp/1]). %% for testing --export([post_process_payload/2, get_expanded_scopes/2]). +-export([normalize_token_scope/2, get_expanded_scopes/2]). -import(rabbit_data_coercion, [to_map/1]). +-import(uaa_jwt, [ + decode_and_verify/3, + get_scope/1, set_scope/2, + resolve_resource_server/1]). + +-import(rabbit_oauth2_scope, [filter_matching_scope_prefix_and_drop_it/2]). -ifdef(TEST). -compile(export_all). @@ -69,7 +75,7 @@ check_vhost_access(#auth_user{impl = DecodedTokenFun}, with_decoded_token(DecodedTokenFun(), fun(_Token) -> DecodedToken = DecodedTokenFun(), - Scopes = uaa_jwt:get_scopes(DecodedToken), + 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]), rabbit_oauth2_scope:vhost_access(VHost, Scopes) @@ -79,7 +85,7 @@ check_resource_access(#auth_user{impl = DecodedTokenFun}, Resource, Permission, _AuthzContext) -> with_decoded_token(DecodedTokenFun(), fun(Token) -> - Scopes = uaa_jwt:get_scopes(Token), + Scopes = get_scope(Token), rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes) end). @@ -92,9 +98,9 @@ check_topic_access(#auth_user{impl = DecodedTokenFun}, end). update_state(AuthUser, NewToken) -> - case uaa_jwt:resolve_resource_server(NewToken) of + case resolve_resource_server(NewToken) of {error, _} = Err0 -> Err0; - Tuple -> + {_, _} = Tuple -> case check_token(NewToken, Tuple) of %% avoid logging the token {error, _} = E -> E; @@ -122,10 +128,10 @@ expiry_timestamp(#auth_user{impl = DecodedTokenFun}) -> authenticate(_, AuthProps0) -> AuthProps = to_map(AuthProps0), Token = token_from_context(AuthProps), - case uaa_jwt:resolve_resource_server(Token) of + case resolve_resource_server(Token) of {error, _} = Err0 -> Err0; - {ResourceServer, _InternalOAuthProvider} = Tuple -> + {ResourceServer, _} = Tuple -> case check_token(Token, Tuple) of {error, _} = E -> E; {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> @@ -177,88 +183,52 @@ check_token(DecodedToken, _) when is_map(DecodedToken) -> {ok, DecodedToken}; check_token(Token, {ResourceServer, InternalOAuthProvider}) -> - case uaa_jwt:decode_and_verify(Token, ResourceServer, InternalOAuthProvider) of + case decode_and_verify(Token, ResourceServer, InternalOAuthProvider) of {error, Reason} -> {refused, {error, Reason}}; - {true, Payload} -> validate_payload(ResourceServer, - post_process_payload(ResourceServer, Payload)); + {true, Payload} -> {ok, normalize_token_scope(ResourceServer, Payload)}; {false, _, _} -> {refused, signature_invalid} end. -post_process_payload(ResourceServer, Payload) when is_map(Payload) -> +-spec normalize_token_scope( + ResourceServer :: resource_server(), DecodedToken :: map()) -> map(). +normalize_token_scope(ResourceServer, Payload) -> Payload0 = maps:map(fun(K, V) -> - case K of - ?AUD_JWT_FIELD when is_binary(V) -> binary:split(V, <<" ">>, [global, trim_all]); - ?SCOPE_JWT_FIELD when is_binary(V) -> binary:split(V, <<" ">>, [global, trim_all]); - _ -> V - end - end, - Payload - ), - - Payload1 = case does_include_complex_claim_field(ResourceServer, Payload0) of - true -> post_process_payload_with_complex_claim(ResourceServer, Payload0); + 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 maps:is_key(<<"authorization">>, Payload1) of - true -> post_process_payload_in_keycloak_format(Payload1); + true -> extract_scopes_from_keycloak_format(Payload1); false -> Payload1 end, Payload3 = case ResourceServer#resource_server.scope_aliases of undefined -> Payload2; - _ -> post_process_payload_with_scope_aliases(ResourceServer, Payload2) + ScopeAliases -> extract_scopes_using_scope_aliases(ScopeAliases, Payload2) end, Payload4 = case maps:is_key(<<"authorization_details">>, Payload3) of - true -> post_process_payload_in_rich_auth_request_format(ResourceServer, Payload3); + true -> extract_scopes_from_rich_auth_request(ResourceServer, Payload3); false -> Payload3 end, - Payload4. - - --spec post_process_payload_with_scope_aliases( - ResourceServer :: resource_server(), Payload :: map()) -> map(). -%% This is for those hopeless environments where the token structure is so out of -%% messaging team's control that even the extra scopes field is no longer an option. -%% -%% This assumes that scopes can be random values that do not follow the RabbitMQ -%% convention, or any other convention, in any way. They are just random client role IDs. -%% See rabbitmq/rabbitmq-server#4588 for details. -post_process_payload_with_scope_aliases(ResourceServer, Payload) -> - %% try JWT scope field value for alias - Payload1 = post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload), - %% try the configurable 'extra_scopes_source' field value for alias - post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServer, Payload1). - - --spec post_process_payload_with_scope_alias_in_scope_field( - ResourceServer :: resource_server(), Payload :: map()) -> map(). -%% First attempt: use the value in the 'scope' field for alias -post_process_payload_with_scope_alias_in_scope_field(ResourceServer, Payload) -> - ScopeMappings = ResourceServer#resource_server.scope_aliases, - post_process_payload_with_scope_alias_field_named(Payload, ?SCOPE_JWT_FIELD, ScopeMappings). - - --spec post_process_payload_with_scope_alias_in_extra_scopes_source( - ResourceServer :: resource_server(), Payload :: map()) -> map(). -%% Second attempt: use the value in the configurable 'extra scopes source' field for alias -post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServer, Payload) -> - ExtraScopesField = ResourceServer#resource_server.additional_scopes_key, - case ExtraScopesField of - undefined -> Payload; - ExtraScopes -> - ScopeMappings = ResourceServer#resource_server.scope_aliases, - post_process_payload_with_scope_alias_field_named(Payload, ExtraScopes, ScopeMappings) - end. + FilteredScopes = filter_matching_scope_prefix_and_drop_it( + get_scope(Payload4), ResourceServer#resource_server.scope_prefix), + set_scope(FilteredScopes, Payload4). --spec post_process_payload_with_scope_alias_field_named(Payload :: map(), - Field :: binary(), - ScopeAliasMapping :: map()) -> map(). -post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAliasMapping) -> - Scopes0 = maps:get(FieldName, Payload, []), +-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 @@ -276,20 +246,19 @@ post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAlias Acc ++ Binaries end end, Scopes, Scopes), - maps:put(?SCOPE_JWT_FIELD, ExpandedScopes, Payload). + set_scope(ExpandedScopes, Payload). - --spec does_include_complex_claim_field( +-spec has_additional_scopes_key( ResourceServer :: resource_server(), Payload :: map()) -> boolean(). -does_include_complex_claim_field(ResourceServer, Payload) when is_map(Payload) -> +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 post_process_payload_with_complex_claim( +-spec extract_scopes_from_additional_scopes_key( ResourceServer :: resource_server(), Payload :: map()) -> map(). -post_process_payload_with_complex_claim(ResourceServer, Payload) -> +extract_scopes_from_additional_scopes_key(ResourceServer, Payload) -> case ResourceServer#resource_server.additional_scopes_key of undefined -> Payload; ScopesKey -> @@ -321,9 +290,9 @@ extract_additional_scopes(ResourceServer, ComplexClaim) -> _ -> [] end. --spec post_process_payload_in_keycloak_format(Payload :: map()) -> map(). +-spec extract_scopes_from_keycloak_format(Payload :: map()) -> map(). %% keycloak token format: https://github.com/rabbitmq/rabbitmq-auth-backend-oauth2/issues/36 -post_process_payload_in_keycloak_format(#{<<"authorization">> := Authorization} = Payload) -> +extract_scopes_from_keycloak_format(#{<<"authorization">> := Authorization} = Payload) -> AdditionalScopes = case maps:get(<<"permissions">>, Authorization, undefined) of undefined -> []; Permissions -> extract_scopes_from_keycloak_permissions([], Permissions) @@ -466,10 +435,10 @@ is_recognized_permission(#{?RAR_ACTIONS_FIELD := _, ?RAR_LOCATIONS_FIELD:= _ , is_recognized_permission(_, _) -> false. --spec post_process_payload_in_rich_auth_request_format(ResourceServer :: resource_server(), +-spec extract_scopes_from_rich_auth_request(ResourceServer :: resource_server(), Payload :: map()) -> map(). %% https://oauth.net/2/rich-authorization-requests/ -post_process_payload_in_rich_auth_request_format(ResourceServer, +extract_scopes_from_rich_auth_request(ResourceServer, #{<<"authorization_details">> := Permissions} = Payload) -> ResourceServerType = ResourceServer#resource_server.resource_server_type, @@ -478,59 +447,13 @@ post_process_payload_in_rich_auth_request_format(ResourceServer, AdditionalScopes = map_rich_auth_permissions_to_scopes( ResourceServer#resource_server.id, FilteredPermissionsByType), - ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), - maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload). - -validate_payload(ResourceServer, DecodedToken) -> - ScopePrefix = ResourceServer#resource_server.scope_prefix, - validate_payload(ResourceServer, DecodedToken, ScopePrefix). - -validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope, - ?AUD_JWT_FIELD := Aud} = DecodedToken, ScopePrefix) -> - case check_aud(Aud, ResourceServer) of - ok -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}}; - {error, Err} -> {refused, {invalid_aud, Err}} - end; -validate_payload(ResourceServer, #{?AUD_JWT_FIELD := Aud} = DecodedToken, _ScopePrefix) -> - case check_aud(Aud, ResourceServer) of - ok -> {ok, DecodedToken}; - {error, Err} -> {refused, {invalid_aud, Err}} - end; -validate_payload(ResourceServer, #{?SCOPE_JWT_FIELD := Scope} = DecodedToken, ScopePrefix) -> - case ResourceServer#resource_server.verify_aud of - true -> {error, {badarg, {aud_field_is_missing}}}; - false -> {ok, DecodedToken#{?SCOPE_JWT_FIELD => filter_scopes(Scope, ScopePrefix)}} - end. - -filter_scopes(Scopes, <<"">>) -> Scopes; -filter_scopes(Scopes, ScopePrefix) -> - matching_scopes_without_prefix(Scopes, ScopePrefix). - -check_aud(Aud, ResourceServer) -> - case ResourceServer#resource_server.id of - <<>> -> ok; - ResourceServerId -> - case ResourceServer#resource_server.verify_aud of - true -> - case Aud of - List when is_list(List) -> - case lists:member(ResourceServerId, Aud) of - true -> ok; - false -> {error, {resource_id_not_found_in_aud, - ResourceServerId, Aud}} - end; - _ -> {error, {badarg, {aud_is_not_a_list, Aud}}} - end; - false -> ok - end - end. -%%-------------------------------------------------------------------- - + ExistingScopes = get_scope(Payload), + set_scope(Payload, AdditionalScopes ++ ExistingScopes). -spec get_expanded_scopes(map(), #resource{}) -> [binary()]. get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> Context = #{ token => Token , vhost => VHost}, - case uaa_jwt:get_scopes(Token) of + case get_scope(Token) of [] -> []; Scopes -> lists:map(fun(Scope) -> list_to_binary(parse_scope(Scope, Context)) end, Scopes) end. @@ -627,20 +550,5 @@ find_claim_in_token(Claim, Token) -> -spec tags_from(map()) -> list(atom()). tags_from(DecodedToken) -> Scopes = maps:get(?SCOPE_JWT_FIELD, DecodedToken, []), - TagScopes = matching_scopes_without_prefix(Scopes, ?TAG_SCOPE_PREFIX), + TagScopes = filter_matching_scope_prefix_and_drop_it(Scopes, ?TAG_SCOPE_PREFIX), lists:usort(lists:map(fun rabbit_data_coercion:to_atom/1, TagScopes)). - -matching_scopes_without_prefix(Scopes, PrefixPattern) -> - PatternLength = byte_size(PrefixPattern), - lists:filtermap( - fun(ScopeEl) -> - case binary:match(ScopeEl, PrefixPattern) of - {0, PatternLength} -> - ElLength = byte_size(ScopeEl), - {true, - binary:part(ScopeEl, - {PatternLength, ElLength - PatternLength})}; - _ -> false - end - end, - Scopes). 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 d81c7ded0c8f..487db36c787c 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl @@ -7,7 +7,11 @@ -module(rabbit_oauth2_scope). --export([vhost_access/2, resource_access/3, topic_access/4, concat_scopes/2]). +-export([vhost_access/2, + resource_access/3, + topic_access/4, + concat_scopes/2, + filter_matching_scope_prefix_and_drop_it/2]). -include_lib("rabbit_common/include/rabbit.hrl"). @@ -88,3 +92,21 @@ parse_resource_pattern(Pattern, Permission) -> {VhostPattern, NamePattern, RoutingKeyPattern, Permission}; _Other -> ignore end. + +-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) -> + case binary:match(ScopeEl, PrefixPattern) of + {0, PatternLength} -> + ElLength = byte_size(ScopeEl), + {true, + binary:part(ScopeEl, + {PatternLength, ElLength - PatternLength})}; + _ -> false + end + end, + Scopes). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index dec06c301f93..9c29426029f7 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -12,7 +12,7 @@ verify_signing_key/2, resolve_resource_server/1]). --export([client_id/1, sub/1, client_id/2, sub/2, get_scopes/1]). +-export([client_id/1, sub/1, client_id/2, sub/2, get_scope/1, set_scope/2]). -include("oauth2.hrl"). -include_lib("jose/include/jose_jwk.hrl"). @@ -165,9 +165,13 @@ verify_signing_key(Type, Value) -> Err -> Err end. --spec get_scopes(map()) -> binary() | list(). -get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; -get_scopes(#{}) -> []. +-spec get_scope(map()) -> binary() | list(). +get_scope(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; +get_scope(#{}) -> []. + +-spec set_scope(list(), map()) -> map(). +set_scope(Scopes, DecodedToken) -> + DecodedToken#{?SCOPE_JWT_FIELD => Scopes}. -spec client_id(map()) -> binary() | undefined. client_id(DecodedToken) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index c8b3f296e213..003e1181d631 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -11,7 +11,11 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +-include("oauth2.hrl"). +-import(rabbit_auth_backend_oauth2, [ + normalize_token_scope/2, + check_vhost_access/34]). all() -> [ @@ -1250,7 +1254,7 @@ test_own_scope(_) -> ], lists:map( fun({ScopePrefix, Src, Dest}) -> - Dest = rabbit_auth_backend_oauth2:filter_scopes(Src, ScopePrefix) + Dest = rabbit_oauth2_scope:filter_matching_scope_prefix_and_drop_it(Src, ScopePrefix) end, Examples). @@ -1326,6 +1330,11 @@ test_validate_payload_when_verify_aud_false(_) -> %% Helpers %% +verify_normalize_token_scope(Expected, Token) -> + Audience = maps:get(?AUD_JWT_FIELD, Token, none), + ResourceServer = resource_server:resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, normalize_token_scope(ResourceServer, Token)). + assert_vhost_access_granted(AuthUser, VHost) -> assert_vhost_access_response(true, AuthUser, VHost). From 54ac148daf99a31fdd169e40067880fe1e7cb8dd Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 17 Sep 2024 18:01:50 +0200 Subject: [PATCH 23/64] Fix issue and test WIP rename all token_validation to normalize_token_scope --- .../src/rabbit_auth_backend_oauth2.erl | 3 +- .../test/unit_SUITE.erl | 203 +++++++----------- 2 files changed, 81 insertions(+), 125 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 65c21ed9a07e..b751df357bf5 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 @@ -314,7 +314,6 @@ extract_scopes_from_keycloak_permissions(Acc, [_ | T]) -> extract_scopes_from_keycloak_permissions(Acc, T). - put_location_attribute(Attribute, Map) -> put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). @@ -448,7 +447,7 @@ extract_scopes_from_rich_auth_request(ResourceServer, ResourceServer#resource_server.id, FilteredPermissionsByType), ExistingScopes = get_scope(Payload), - set_scope(Payload, AdditionalScopes ++ ExistingScopes). + set_scope(AdditionalScopes ++ ExistingScopes, Payload). -spec get_expanded_scopes(map(), #resource{}) -> [binary()]. get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index 003e1181d631..a4435529f612 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -19,15 +19,14 @@ all() -> [ - test_own_scope, - test_validate_payload_with_scope_prefix, - test_validate_payload, - test_validate_payload_without_scope, - test_validate_payload_when_verify_aud_false, - - test_unsuccessful_access_without_scopes, - test_successful_access_with_a_token_with_variables_in_scopes, - test_successful_access_with_a_parsed_token, + filter_matching_scope_prefix_and_drop_it, + test_normalize_token_scopes_with_scope_prefix, + test_normalize_token_scopes, + test_normalize_token_scopes_without_scope, + + unsuccessful_access_without_scopes, + successful_access_with_a_token_with_variables_in_scopes, + successful_access_with_a_parsed_token, test_successful_access_with_a_token_that_has_tag_scopes, test_unsuccessful_access_with_a_bogus_token, test_restricted_vhost_access_with_a_valid_token, @@ -108,36 +107,6 @@ end_per_group(_, Config) -> application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), Config. -init_per_testcase(test_post_process_token_payload_complex_claims, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"additional_rabbitmq_scopes">>), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq-resource">>), - Config; - -init_per_testcase(test_validate_payload_when_verify_aud_false, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, verify_aud, false), - Config; - - - -init_per_testcase(test_post_process_payload_rich_auth_request, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_type, <<"rabbitmq-type">>), - Config; - -init_per_testcase(test_post_process_payload_rich_auth_request_using_regular_expression_with_cluster, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_type, <<"rabbitmq-type">>), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq-test">>), - Config; - -init_per_testcase(_, Config) -> - Config. - -end_per_testcase(test_post_process_token_payload_complex_claims, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, undefined), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, undefined), - Config; - -end_per_testcase(_, Config) -> - Config. %% @@ -278,8 +247,14 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste lists:foreach( fun({Case, Permissions, ExpectedScope}) -> - Payload = post_process_payload_with_rich_auth_request(<<"rabbitmq-test">>, Permissions), - ?assertEqual(lists:sort(ExpectedScope), lists:sort(maps:get(<<"scope">>, Payload)), Case) + ResourceServer = #resource_server{ + id = ?RESOURCE_SERVER_ID, + resource_server_type = ?RESOUR + } + Token0 = #{<<"authorization_details">> => Permissions]}, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(lists:sort(ExpectedScope), + lists:sort(uaa_jwt:get_scope(Token)), Case) end, Pairs). test_post_process_payload_rich_auth_request(_) -> @@ -575,17 +550,24 @@ test_post_process_payload_rich_auth_request(_) -> ], lists:foreach( - fun({Case, Permissions, ExpectedScope}) -> - Payload = post_process_payload_with_rich_auth_request(<<"rabbitmq">>, Permissions), - ?assertEqual(lists:sort(ExpectedScope), lists:sort(maps:get(<<"scope">>, Payload)), Case) + fun({Case, Permissions, ExpectedScope0}) -> + ResourceServer = #resource_server{ + id = ?RESOURCE_SERVER_ID, + resource_server_type = ?RESOURCE_SERVER_TYPE + }, + Token0 = #{<<"authorization_details">> => Permissions]}, + Token = normalize_token_scope(ResourceServer, Permissions), + ExpectedScopes = lists:sort(ExpectedScope0), + ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), + ?assertEqual(ExpectedScopes, ActualScopes, Case) end, Pairs). -post_process_payload_with_rich_auth_request(ResourceServerId, Permissions) -> +prepare_token_with_rich_authorization_details(ResourceServerId, Permissions) -> Jwk = ?UTIL_MOD:fixture_jwk(), - Token = maps:put(<<"authorization_details">>, Permissions, ?UTIL_MOD:plain_token_without_scopes_and_aud()), + {_, EncodedToken} = ?UTIL_MOD:sign_token_hs(Token, Jwk), {true, Payload} = uaa_jwt_jwt:decode_and_verify(<<"rabbitmq">>, Jwk, EncodedToken), - rabbit_auth_backend_oauth2:post_process_payload(ResourceServerId, Payload). + Payload. test_post_process_token_payload_complex_claims(_) -> Pairs = [ @@ -704,7 +686,7 @@ test_successful_access_with_a_token(_) -> assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => <<"#/foo">>}). -test_successful_access_with_a_token_with_variables_in_scopes(_) -> +successful_access_with_a_token_with_variables_in_scopes(_) -> %% Generate a token with JOSE %% Check authorization with the token %% Check user access granted by token @@ -722,7 +704,7 @@ test_successful_access_with_a_token_with_variables_in_scopes(_) -> assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => Username}). -test_successful_access_with_a_parsed_token(_) -> +successful_access_with_a_parsed_token(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), @@ -1041,7 +1023,7 @@ test_unsuccessful_access_with_a_bogus_token(_) -> ?assertMatch({refused, _, _}, rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, <<"not a token">>}])). -test_unsuccessful_access_without_scopes(_) -> +unsuccessful_access_without_scopes(_) -> Username = <<"username">>, application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), @@ -1243,7 +1225,7 @@ test_command_pem_no_kid(Config) -> rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). -test_own_scope(_) -> +filter_matching_scope_prefix_and_drop_it(_) -> Examples = [ {<<"foo.">>, [<<"foo">>, <<"foo.bar">>, <<"bar.foo">>, <<"one.two">>, <<"foobar">>, <<"foo.other.third">>], @@ -1258,83 +1240,58 @@ test_own_scope(_) -> end, Examples). -test_validate_payload_resource_server_id_mismatch(_) -> - NoKnownResourceServerId = #{<<"aud">> => [<<"foo">>, <<"bar">>], - <<"scope">> => [<<"foo">>, <<"foo.bar">>, - <<"bar.foo">>, <<"one.two">>, - <<"foobar">>, <<"foo.other.third">>]}, - EmptyAud = #{<<"aud">> => [], - <<"scope">> => [<<"foo.bar">>, <<"bar.foo">>]}, - - ?assertEqual({refused, {invalid_aud, {resource_id_not_found_in_aud, ?RESOURCE_SERVER_ID, - [<<"foo">>,<<"bar">>]}}}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, NoKnownResourceServerId, ?DEFAULT_SCOPE_PREFIX)), - - ?assertEqual({refused, {invalid_aud, {resource_id_not_found_in_aud, ?RESOURCE_SERVER_ID, []}}}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, EmptyAud, ?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(?RESOURCE_SERVER_ID, Token, ScopePrefix)) - end - , Scenarios). - -test_validate_payload(_) -> - KnownResourceServerId = #{<<"aud">> => [?RESOURCE_SERVER_ID], - <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, - <<"bar.foo">>, <<"one.two">>, - <<"foobar">>, <<"rabbitmq.other.third">>]}, - ?assertEqual({ok, #{<<"aud">> => [?RESOURCE_SERVER_ID], - <<"scope">> => [<<"bar">>, <<"other.third">>]}}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, KnownResourceServerId, ?DEFAULT_SCOPE_PREFIX)). - -test_validate_payload_without_scope(_) -> - KnownResourceServerId = #{<<"aud">> => [?RESOURCE_SERVER_ID] - }, - ?assertEqual({ok, #{<<"aud">> => [?RESOURCE_SERVER_ID] }}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, KnownResourceServerId, ?DEFAULT_SCOPE_PREFIX)). - -test_validate_payload_when_verify_aud_false(_) -> - WithoutAud = #{ - <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, - <<"bar.foo">>, <<"one.two">>, - <<"foobar">>, <<"rabbitmq.other.third">>]}, - ?assertEqual({ok, #{ - <<"scope">> => [<<"bar">>, <<"other.third">>]}}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, WithoutAud, ?DEFAULT_SCOPE_PREFIX)), - - WithAudWithUnknownResourceId = #{ - <<"aud">> => [<<"unknown">>], - <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, - <<"bar.foo">>, <<"one.two">>, - <<"foobar">>, <<"rabbitmq.other.third">>]}, - ?assertEqual({ok, #{<<"aud">> => [<<"unknown">>], - <<"scope">> => [<<"bar">>, <<"other.third">>]}}, - rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, WithAudWithUnknownResourceId, ?DEFAULT_SCOPE_PREFIX)). +test_normalize_token_scopes_with_scope_prefix(_) -> + Scenarios = [ + { + <<"">>, + #{ + ?SCOPE_JWT_FIELD => [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ] + }, + [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ] + }, + { + <<"some-prefix::">>, + #{ + ?SCOPE_JWT_FIELD => [ + <<"some-prefix::foo">>, <<"foo.bar">>, + <<"some-prefix::other.third">> ] + }, + [<<"foo">>, <<"other.third">>] + } + ], + + lists:map(fun({ ScopePrefix, Token0, ExpectedScopes}) -> + ResourceServer = #resource_server { + id = ?RESOURCE_SERVER_ID, + scope_prefix = ScopePrefix + }, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(ExpectedScopes, uaa_jwt:get_scope(Token)) + end, Scenarios). + +test_normalize_token_scope(_) -> + ResourceServer = #resource_server { + id = ?RESOURCE_SERVER_ID + }, + Token0 = #{ + <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, + <<"bar.foo">>, <<"one.two">>, + <<"foobar">>, <<"rabbitmq.other.third">>] + }, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual([<<"bar">>, <<"other.third">>], uaa_jwt:get_scope(Token)). + +test_normalize_token_scope_without_scope(_) -> + ResourceServer = #resource_server { + id = ?RESOURCE_SERVER_ID + }, + Token0 = #{ }, + ?assertEqual([], uaa_jwt:get_scope(normalize_token_scope(ResourceServer, Token0))). %% %% Helpers %% -verify_normalize_token_scope(Expected, Token) -> - Audience = maps:get(?AUD_JWT_FIELD, Token, none), - ResourceServer = resource_server:resolve_resource_server_from_audience(Audience), - ?assertEqual(Expected, normalize_token_scope(ResourceServer, Token)). - assert_vhost_access_granted(AuthUser, VHost) -> assert_vhost_access_response(true, AuthUser, VHost). From b9217aee481b18b6f94721ca914d124a3a3ea9c7 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 10:42:42 +0200 Subject: [PATCH 24/64] Fix test cases and refactor rar and keycloak functionality into their own modules --- deps/rabbitmq_auth_backend_oauth2/app.bzl | 6 + .../include/oauth2.hrl | 26 +- .../src/keycloak.erl | 41 + .../src/rabbit_auth_backend_oauth2.erl | 264 ++---- .../src/rabbit_oauth2_scope.erl | 2 +- deps/rabbitmq_auth_backend_oauth2/src/rar.erl | 174 ++++ .../src/resource_server.erl | 16 +- .../test/unit_SUITE.erl | 796 +++++++++--------- 8 files changed, 673 insertions(+), 652 deletions(-) create mode 100644 deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl create mode 100644 deps/rabbitmq_auth_backend_oauth2/src/rar.erl diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index cb9b4c7a2b8b..85cf79de8a9e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -15,6 +15,8 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_auth_backend_oauth2_app.erl", "src/oauth_provider.erl", "src/resource_server.erl", + "src/rar.erl", + "src/keycloak.erl", "src/oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -52,6 +54,8 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/resource_server.erl", "src/oauth_provider.erl", "src/oauth2_schema.erl", + "src/rar.erl", + "src/keycloak.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -100,6 +104,8 @@ def all_srcs(name = "all_srcs"): "src/oauth_provider.erl", "src/resource_server.erl", "src/oauth2_schema.erl", + "src/rar.erl", + "src/keycloak.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl index f826bae7f80e..f5d0e6559bd5 100644 --- a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -18,30 +18,10 @@ -define(AUD_JWT_FIELD, <<"aud">>). -define(SCOPE_JWT_FIELD, <<"scope">>). +-define(TAG_SCOPE_PREFIX, <<"tag:">>). %% End of Key JWT fields -%% -%% Rich Authorization Request fields -%% --define(RAR_ACTIONS_FIELD, <<"actions">>). --define(RAR_LOCATIONS_FIELD, <<"locations">>). --define(RAR_TYPE_FIELD, <<"type">>). - --define(RAR_CLUSTER_LOCATION_ATTRIBUTE, <<"cluster">>). --define(RAR_VHOST_LOCATION_ATTRIBUTE, <<"vhost">>). --define(RAR_QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). --define(RAR_EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). --define(RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). --define(RAR_LOCATION_ATTRIBUTES, [?RAR_CLUSTER_LOCATION_ATTRIBUTE, ?RAR_VHOST_LOCATION_ATTRIBUTE, - ?RAR_QUEUE_LOCATION_ATTRIBUTE, ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). - --define(RAR_ALLOWED_TAG_VALUES, [<<"monitoring">>, <<"administrator">>, <<"management">>, <<"policymaker">> ]). --define(RAR_ALLOWED_ACTION_VALUES, [<<"read">>, <<"write">>, <<"configure">>, <<"monitoring">>, - <<"administrator">>, <<"management">>, <<"policymaker">> ]). - -%% end of Rich Authorization Request fields - -record(internal_oauth_provider, { id :: oauth_provider_id(), @@ -55,9 +35,9 @@ resource_server_type :: binary() | undefined, verify_aud :: boolean(), scope_prefix :: binary(), - additional_scopes_key :: binary(), + additional_scopes_key :: binary() | undefined, preferred_username_claims :: list(), - scope_aliases :: undefined | map(), + scope_aliases :: map() | undefined, oauth_provider_id :: oauth_provider_id() }). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl b/deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl new file mode 100644 index 000000000000..081a1abd322e --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl @@ -0,0 +1,41 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(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). 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 b751df357bf5..086c458bf19c 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 @@ -28,6 +28,9 @@ get_scope/1, set_scope/2, resolve_resource_server/1]). +-import(keycloak, [has_keycloak_scopes/1, extract_scopes_from_keycloak_format/1]). +-import(rar, [extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]). + -import(rabbit_oauth2_scope, [filter_matching_scope_prefix_and_drop_it/2]). -ifdef(TEST). @@ -205,7 +208,7 @@ normalize_token_scope(ResourceServer, Payload) -> false -> Payload0 end, - Payload2 = case maps:is_key(<<"authorization">>, Payload1) of + Payload2 = case has_keycloak_scopes(Payload1) of true -> extract_scopes_from_keycloak_format(Payload1); false -> Payload1 end, @@ -215,7 +218,7 @@ normalize_token_scope(ResourceServer, Payload) -> ScopeAliases -> extract_scopes_using_scope_aliases(ScopeAliases, Payload2) end, - Payload4 = case maps:is_key(<<"authorization_details">>, Payload3) of + Payload4 = case has_rich_auth_request_scopes(Payload3) of true -> extract_scopes_from_rich_auth_request(ResourceServer, Payload3); false -> Payload3 end, @@ -259,18 +262,10 @@ has_additional_scopes_key(ResourceServer, Payload) when is_map(Payload) -> -spec extract_scopes_from_additional_scopes_key( ResourceServer :: resource_server(), Payload :: map()) -> map(). extract_scopes_from_additional_scopes_key(ResourceServer, Payload) -> - case ResourceServer#resource_server.additional_scopes_key of - undefined -> Payload; - ScopesKey -> - AdditionalScopes = extract_additional_scopes(ResourceServer, - maps:get(ScopesKey, Payload)), - case AdditionalScopes of - [] -> Payload; - _ -> - ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload, []), - maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, Payload) - end - end. + Claim = maps:get(ResourceServer#resource_server.additional_scopes_key, Payload), + AdditionalScopes = extract_additional_scopes(ResourceServer, Claim), + set_scope(AdditionalScopes ++ get_scope(Payload), Payload). + extract_additional_scopes(ResourceServer, ComplexClaim) -> ResourceServerId = ResourceServer#resource_server.id, case ComplexClaim of @@ -290,205 +285,6 @@ extract_additional_scopes(ResourceServer, ComplexClaim) -> _ -> [] end. --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">> := Authorization} = Payload) -> - AdditionalScopes = case maps:get(<<"permissions">>, Authorization, undefined) of - undefined -> []; - Permissions -> extract_scopes_from_keycloak_permissions([], Permissions) - end, - ExistingScopes = maps:get(?SCOPE_JWT_FIELD, Payload), - maps:put(?SCOPE_JWT_FIELD, AdditionalScopes ++ ExistingScopes, 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">>, 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). - - -put_location_attribute(Attribute, Map) -> - put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). - -put_attribute([Key, Value | _], Map) -> - case lists:member(Key, ?RAR_LOCATION_ATTRIBUTES) of - true -> maps:put(Key, Value, Map); - false -> Map - end; -put_attribute([_|_], Map) -> Map. - - -% convert [ <<"cluster:A">>, <<"vhost:B" >>, <<"A">>, <<"unknown:C">> ] to #{ <<"cluster">> : <<"A">>, <<"vhost">> : <<"B">> } -% filtering out non-key-value-pairs and keys which are not part of LOCATION_ATTRIBUTES -convert_attribute_list_to_attribute_map(L) -> - convert_attribute_list_to_attribute_map(L, #{}). -convert_attribute_list_to_attribute_map([H|L],Map) when is_binary(H) -> - convert_attribute_list_to_attribute_map(L, put_location_attribute(H,Map)); -convert_attribute_list_to_attribute_map([], Map) -> Map. - -build_permission_resource_path(Map) -> - Vhost = maps:get(?RAR_VHOST_LOCATION_ATTRIBUTE, Map, <<"*">>), - Resource = maps:get(?RAR_QUEUE_LOCATION_ATTRIBUTE, Map, - maps:get(?RAR_EXCHANGE_LOCATION_ATTRIBUTE, Map, <<"*">>)), - RoutingKey = maps:get(?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, Map, <<"*">>), - - <>. - -map_locations_to_permission_resource_paths(ResourceServerId, L) -> - Locations = case L of - undefined -> []; - LocationsAsList when is_list(LocationsAsList) -> - lists:map(fun(Location) -> convert_attribute_list_to_attribute_map( - binary:split(Location,<<"/">>,[global,trim_all])) end, LocationsAsList); - LocationsAsBinary when is_binary(LocationsAsBinary) -> - [convert_attribute_list_to_attribute_map( - binary:split(LocationsAsBinary,<<"/">>,[global,trim_all]))] - end, - - FilteredLocations = lists:filtermap(fun(L2) -> - case cluster_matches_resource_server_id(L2, ResourceServerId) and - legal_queue_and_exchange_values(L2) of - true -> { true, build_permission_resource_path(L2) }; - false -> false - end end, Locations), - - FilteredLocations. - -cluster_matches_resource_server_id(#{?RAR_CLUSTER_LOCATION_ATTRIBUTE := Cluster}, - ResourceServerId) -> - wildcard:match(ResourceServerId, Cluster); - -cluster_matches_resource_server_id(_,_) -> - false. - -legal_queue_and_exchange_values(#{?RAR_QUEUE_LOCATION_ATTRIBUTE := Queue, - ?RAR_EXCHANGE_LOCATION_ATTRIBUTE := Exchange}) -> - case Queue of - <<>> -> - case Exchange of - <<>> -> true; - _ -> false - end; - _ -> - case Exchange of - Queue -> true; - _ -> false - end - end; -legal_queue_and_exchange_values(_) -> true. - -map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions) -> - map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions, []). -map_rich_auth_permissions_to_scopes(_, [], Acc) -> Acc; -map_rich_auth_permissions_to_scopes(ResourceServerId, - [ #{?RAR_ACTIONS_FIELD := Actions, ?RAR_LOCATIONS_FIELD := Locations } | T ], Acc) -> - ResourcePaths = map_locations_to_permission_resource_paths(ResourceServerId, Locations), - case ResourcePaths of - [] -> map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc); - _ -> - Scopes = case Actions of - undefined -> []; - ActionsAsList when is_list(ActionsAsList) -> - build_scopes(ResourceServerId, - skip_unknown_actions(ActionsAsList), ResourcePaths); - ActionsAsBinary when is_binary(ActionsAsBinary) -> - build_scopes(ResourceServerId, - skip_unknown_actions([ActionsAsBinary]), ResourcePaths) - end, - map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc ++ Scopes) - end. - -skip_unknown_actions(Actions) -> - lists:filter(fun(A) -> lists:member(A, ?RAR_ALLOWED_ACTION_VALUES) end, Actions). - -produce_list_of_user_tag_or_action_on_resources(ResourceServerId, ActionOrUserTag, Locations) -> - case lists:member(ActionOrUserTag, ?RAR_ALLOWED_TAG_VALUES) of - true -> [<< ResourceServerId/binary, ".tag:", ActionOrUserTag/binary >>]; - _ -> build_scopes_for_action(ResourceServerId, ActionOrUserTag, Locations, []) - end. - -build_scopes_for_action(ResourceServerId, Action, [Location|Locations], Acc) -> - Scope = << ResourceServerId/binary, ".", Action/binary, ":", Location/binary >>, - build_scopes_for_action(ResourceServerId, Action, Locations, [ Scope | Acc ] ); -build_scopes_for_action(_, _, [], Acc) -> Acc. - -build_scopes(ResourceServerId, Actions, Locations) -> - lists:flatmap(fun(Action) -> - produce_list_of_user_tag_or_action_on_resources(ResourceServerId, - Action, Locations) end, Actions). - -is_recognized_permission(#{?RAR_ACTIONS_FIELD := _, ?RAR_LOCATIONS_FIELD:= _ , - ?RAR_TYPE_FIELD := Type }, ResourceServerType) -> - case ResourceServerType of - <<>> -> false; - V when V == Type -> true; - _ -> false - end; -is_recognized_permission(_, _) -> false. - - --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">> := Permissions} = Payload) -> - ResourceServerType = ResourceServer#resource_server.resource_server_type, - - FilteredPermissionsByType = lists:filter(fun(P) -> - is_recognized_permission(P, ResourceServerType) end, Permissions), - AdditionalScopes = map_rich_auth_permissions_to_scopes( - ResourceServer#resource_server.id, FilteredPermissionsByType), - - ExistingScopes = get_scope(Payload), - set_scope(AdditionalScopes ++ ExistingScopes, Payload). - --spec get_expanded_scopes(map(), #resource{}) -> [binary()]. -get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> - Context = #{ token => Token , vhost => VHost}, - case get_scope(Token) of - [] -> []; - Scopes -> lists:map(fun(Scope) -> list_to_binary(parse_scope(Scope, Context)) end, Scopes) - end. - -parse_scope(Scope, Context) -> - { Acc0, _} = lists:foldl(fun(Elem, { Acc, Stage }) -> parse_scope_part(Elem, Acc, Stage, Context) end, - { [], undefined }, re:split(Scope,"([\{.*\}])",[{return,list},trim])), - Acc0. - -parse_scope_part(Elem, Acc, Stage, Context) -> - case Stage of - error -> {Acc, error}; - undefined -> - case Elem of - "{" -> { Acc, fun capture_var_name/3}; - Value -> { Acc ++ Value, Stage} - end; - _ -> Stage(Elem, Acc, Context) - end. - -capture_var_name(Elem, Acc, #{ token := Token, vhost := Vhost}) -> - { Acc ++ resolve_scope_var(Elem, Token, Vhost), fun expect_closing_var/3}. - -expect_closing_var("}" , Acc, _Context) -> { Acc , undefined }; -expect_closing_var(_ , _Acc, _Context) -> {"", error}. - -resolve_scope_var(Elem, Token, Vhost) -> - case Elem of - "vhost" -> binary_to_list(Vhost); - _ -> - ElemAsBinary = list_to_binary(Elem), - binary_to_list(case maps:get(ElemAsBinary, Token, ElemAsBinary) of - Value when is_binary(Value) -> Value; - _ -> ElemAsBinary - end) - end. %% A token may be present in the password credential or in the rabbit_auth_backend_oauth2 %% credential. The former is the most common scenario for the first time authentication. @@ -544,7 +340,47 @@ find_claim_in_token(Claim, Token) -> _ -> false end. --define(TAG_SCOPE_PREFIX, <<"tag:">>). +-spec get_expanded_scopes(map(), #resource{}) -> [binary()]. +get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> + Context = #{ token => Token , vhost => VHost}, + case get_scope(Token) of + [] -> []; + Scopes -> lists:map(fun(Scope) -> list_to_binary(parse_scope(Scope, Context)) end, Scopes) + end. + + +parse_scope(Scope, Context) -> + { Acc0, _} = lists:foldl(fun(Elem, { Acc, Stage }) -> parse_scope_part(Elem, Acc, Stage, Context) end, + { [], undefined }, re:split(Scope,"([\{.*\}])",[{return,list},trim])), + Acc0. + +parse_scope_part(Elem, Acc, Stage, Context) -> + case Stage of + error -> {Acc, error}; + undefined -> + case Elem of + "{" -> { Acc, fun capture_var_name/3}; + Value -> { Acc ++ Value, Stage} + end; + _ -> Stage(Elem, Acc, Context) + end. + +capture_var_name(Elem, Acc, #{ token := Token, vhost := Vhost}) -> + { Acc ++ resolve_scope_var(Elem, Token, Vhost), fun expect_closing_var/3}. + +expect_closing_var("}" , Acc, _Context) -> { Acc , undefined }; +expect_closing_var(_ , _Acc, _Context) -> {"", error}. + +resolve_scope_var(Elem, Token, Vhost) -> + case Elem of + "vhost" -> binary_to_list(Vhost); + _ -> + ElemAsBinary = list_to_binary(Elem), + binary_to_list(case maps:get(ElemAsBinary, Token, ElemAsBinary) of + Value when is_binary(Value) -> Value; + _ -> ElemAsBinary + end) + end. -spec tags_from(map()) -> list(atom()). tags_from(DecodedToken) -> 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 487db36c787c..2cfcbbc01e32 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl @@ -94,9 +94,9 @@ parse_resource_pattern(Pattern, Permission) -> end. -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) -> + ct:log("filter_matching_scope_prefix_and_drop_it ~p ~p", [Scopes, PrefixPattern]), PatternLength = byte_size(PrefixPattern), lists:filtermap( fun(ScopeEl) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rar.erl b/deps/rabbitmq_auth_backend_oauth2/src/rar.erl new file mode 100644 index 000000000000..4e1c128a474c --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rar.erl @@ -0,0 +1,174 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +% Rich Authorization Request +-module(rar). + +-include("oauth2.hrl"). + +-export([extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]). +-import(uaa_jwt, [get_scope/1, set_scope/2]). + +-define(AUTHORIZATION_DETAILS_CLAIM, <<"authorization_details">>). +-define(RAR_ACTIONS_FIELD, <<"actions">>). +-define(RAR_LOCATIONS_FIELD, <<"locations">>). +-define(RAR_TYPE_FIELD, <<"type">>). + +-define(RAR_CLUSTER_LOCATION_ATTRIBUTE, <<"cluster">>). +-define(RAR_VHOST_LOCATION_ATTRIBUTE, <<"vhost">>). +-define(RAR_QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). +-define(RAR_EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). +-define(RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). +-define(RAR_LOCATION_ATTRIBUTES, [?RAR_CLUSTER_LOCATION_ATTRIBUTE, ?RAR_VHOST_LOCATION_ATTRIBUTE, + ?RAR_QUEUE_LOCATION_ATTRIBUTE, ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). + +-define(RAR_ALLOWED_TAG_VALUES, [<<"monitoring">>, <<"administrator">>, <<"management">>, <<"policymaker">> ]). +-define(RAR_ALLOWED_ACTION_VALUES, [<<"read">>, <<"write">>, <<"configure">>, <<"monitoring">>, + <<"administrator">>, <<"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) -> + ResourceServerType = ResourceServer#resource_server.resource_server_type, + + FilteredPermissionsByType = lists:filter(fun(P) -> + is_recognized_permission(P, ResourceServerType) end, Permissions), + AdditionalScopes = map_rich_auth_permissions_to_scopes( + ResourceServer#resource_server.id, FilteredPermissionsByType), + + ExistingScopes = get_scope(Payload), + set_scope(AdditionalScopes ++ ExistingScopes, Payload). + + + + + +put_location_attribute(Attribute, Map) -> + put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). + +put_attribute([Key, Value | _], Map) -> + case lists:member(Key, ?RAR_LOCATION_ATTRIBUTES) of + true -> maps:put(Key, Value, Map); + false -> Map + end; +put_attribute([_|_], Map) -> Map. + + +% convert [ <<"cluster:A">>, <<"vhost:B" >>, <<"A">>, <<"unknown:C">> ] to #{ <<"cluster">> : <<"A">>, <<"vhost">> : <<"B">> } +% filtering out non-key-value-pairs and keys which are not part of LOCATION_ATTRIBUTES +convert_attribute_list_to_attribute_map(L) -> + convert_attribute_list_to_attribute_map(L, #{}). +convert_attribute_list_to_attribute_map([H|L],Map) when is_binary(H) -> + convert_attribute_list_to_attribute_map(L, put_location_attribute(H,Map)); +convert_attribute_list_to_attribute_map([], Map) -> Map. + +build_permission_resource_path(Map) -> + Vhost = maps:get(?RAR_VHOST_LOCATION_ATTRIBUTE, Map, <<"*">>), + Resource = maps:get(?RAR_QUEUE_LOCATION_ATTRIBUTE, Map, + maps:get(?RAR_EXCHANGE_LOCATION_ATTRIBUTE, Map, <<"*">>)), + RoutingKey = maps:get(?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, Map, <<"*">>), + + <>. + +map_locations_to_permission_resource_paths(ResourceServerId, L) -> + Locations = case L of + undefined -> []; + LocationsAsList when is_list(LocationsAsList) -> + lists:map(fun(Location) -> convert_attribute_list_to_attribute_map( + binary:split(Location,<<"/">>,[global,trim_all])) end, LocationsAsList); + LocationsAsBinary when is_binary(LocationsAsBinary) -> + [convert_attribute_list_to_attribute_map( + binary:split(LocationsAsBinary,<<"/">>,[global,trim_all]))] + end, + + FilteredLocations = lists:filtermap(fun(L2) -> + case cluster_matches_resource_server_id(L2, ResourceServerId) and + legal_queue_and_exchange_values(L2) of + true -> { true, build_permission_resource_path(L2) }; + false -> false + end end, Locations), + + FilteredLocations. + +cluster_matches_resource_server_id(#{?RAR_CLUSTER_LOCATION_ATTRIBUTE := Cluster}, + ResourceServerId) -> + wildcard:match(ResourceServerId, Cluster); + +cluster_matches_resource_server_id(_,_) -> + false. + +legal_queue_and_exchange_values(#{?RAR_QUEUE_LOCATION_ATTRIBUTE := Queue, + ?RAR_EXCHANGE_LOCATION_ATTRIBUTE := Exchange}) -> + case Queue of + <<>> -> + case Exchange of + <<>> -> true; + _ -> false + end; + _ -> + case Exchange of + Queue -> true; + _ -> false + end + end; +legal_queue_and_exchange_values(_) -> true. + +map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions) -> + map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions, []). +map_rich_auth_permissions_to_scopes(_, [], Acc) -> Acc; +map_rich_auth_permissions_to_scopes(ResourceServerId, + [ #{?RAR_ACTIONS_FIELD := Actions, ?RAR_LOCATIONS_FIELD := Locations } | T ], Acc) -> + ResourcePaths = map_locations_to_permission_resource_paths(ResourceServerId, Locations), + case ResourcePaths of + [] -> map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc); + _ -> + Scopes = case Actions of + undefined -> []; + ActionsAsList when is_list(ActionsAsList) -> + build_scopes(ResourceServerId, + skip_unknown_actions(ActionsAsList), ResourcePaths); + ActionsAsBinary when is_binary(ActionsAsBinary) -> + build_scopes(ResourceServerId, + skip_unknown_actions([ActionsAsBinary]), ResourcePaths) + end, + map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc ++ Scopes) + end. + +skip_unknown_actions(Actions) -> + lists:filter(fun(A) -> lists:member(A, ?RAR_ALLOWED_ACTION_VALUES) end, Actions). + +produce_list_of_user_tag_or_action_on_resources(ResourceServerId, ActionOrUserTag, Locations) -> + case lists:member(ActionOrUserTag, ?RAR_ALLOWED_TAG_VALUES) of + true -> [<< ResourceServerId/binary, ".tag:", ActionOrUserTag/binary >>]; + _ -> build_scopes_for_action(ResourceServerId, ActionOrUserTag, Locations, []) + end. + +build_scopes_for_action(ResourceServerId, Action, [Location|Locations], Acc) -> + Scope = << ResourceServerId/binary, ".", Action/binary, ":", Location/binary >>, + build_scopes_for_action(ResourceServerId, Action, Locations, [ Scope | Acc ] ); +build_scopes_for_action(_, _, [], Acc) -> Acc. + +build_scopes(ResourceServerId, Actions, Locations) -> + lists:flatmap(fun(Action) -> + produce_list_of_user_tag_or_action_on_resources(ResourceServerId, + Action, Locations) end, Actions). + +is_recognized_permission(#{?RAR_ACTIONS_FIELD := _, ?RAR_LOCATIONS_FIELD:= _ , + ?RAR_TYPE_FIELD := Type }, ResourceServerType) -> + case ResourceServerType of + <<>> -> false; + V when V == Type -> true; + _ -> false + end; +is_recognized_permission(_, _) -> false. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index 56ed0f3866ff..56bc17a1e519 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -10,9 +10,23 @@ -include("oauth2.hrl"). -export([ - resolve_resource_server_from_audience/1 + resolve_resource_server_from_audience/1, + new_resource_server/1 ]). +-spec new_resource_server(resource_server_id()) -> resource_server(). +new_resource_server(ResourceServerId) -> + #resource_server{ + id = ResourceServerId, + resource_server_type = undefined, + verify_aud = true, + scope_prefix = erlang:iolist_to_binary([ResourceServerId, <<".">>]), + additional_scopes_key = undefined, + preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS, + scope_aliases = undefined, + oauth_provider_id = root + }. + -spec resolve_resource_server_from_audience(binary() | list() | none) -> {ok, resource_server()} | {error, aud_matched_many_resource_servers_only_one_allowed} | diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index a4435529f612..fb7b2b58a7bc 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -14,15 +14,17 @@ -include("oauth2.hrl"). -import(rabbit_auth_backend_oauth2, [ + user_login_authentication/2, + user_login_authorization/2, normalize_token_scope/2, - check_vhost_access/34]). + check_vhost_access/3]). all() -> [ filter_matching_scope_prefix_and_drop_it, - test_normalize_token_scopes_with_scope_prefix, - test_normalize_token_scopes, - test_normalize_token_scopes_without_scope, + normalize_token_scopes_with_scope_prefix, + normalize_token_scope_from_space_separated_list_in_scope_claim, + normalize_token_scope_without_scope_claim, unsuccessful_access_without_scopes, successful_access_with_a_token_with_variables_in_scopes, @@ -118,32 +120,6 @@ end_per_group(_, Config) -> -define(RESOURCE_SERVER_TYPE, <<"rabbitmq-type">>). -define(DEFAULT_SCOPE_PREFIX, <<"rabbitmq.">>). -test_post_process_token_payload(_) -> - ArgumentsExpections = [ - {{[<<"rabbitmq">>, <<"hare">>], [<<"read">>, <<"write">>, <<"configure">>]}, - {[<<"rabbitmq">>, <<"hare">>], [<<"read">>, <<"write">>, <<"configure">>]}}, - {{<<"rabbitmq hare">>, <<"read write configure">>}, - {[<<"rabbitmq">>, <<"hare">>], [<<"read">>, <<"write">>, <<"configure">>]}}, - {{<<"rabbitmq">>, <<"read">>}, - {[<<"rabbitmq">>], [<<"read">>]}} - ], - lists:foreach( - fun({{Aud, Scope}, {ExpectedAud, ExpectedScope}}) -> - Payload = post_process_token_payload(Aud, Scope), - ?assertEqual(ExpectedAud, maps:get(<<"aud">>, Payload)), - ?assertEqual(ExpectedScope, maps:get(<<"scope">>, Payload)) - end, ArgumentsExpections). - -post_process_token_payload(Audience, Scopes) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - Token = maps:put(<<"aud">>, Audience, ?UTIL_MOD:fixture_token_with_scopes(Scopes)), - {_, EncodedToken} = ?UTIL_MOD:sign_token_hs(Token, Jwk), - case rabbit_oauth2_config:find_audience_in_resource_server_ids(Audience) of - {ok, TargetResourceServerId} -> - {true, Payload} = uaa_jwt_jwt:decode_and_verify(TargetResourceServerId, Jwk, EncodedToken), - rabbit_auth_backend_oauth2:post_process_payload(TargetResourceServerId, Payload); - {error, _} = Error -> Error - end. test_post_process_token_payload_keycloak(_) -> Pairs = [ @@ -155,10 +131,13 @@ test_post_process_token_payload_keycloak(_) -> <<"scopes">> => [<<"rabbitmq-resource.read:*/*">>]}, #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, <<"rsname">> => <<"vhost1">>, - <<"scopes">> => [<<"rabbitmq-resource-read">>]}, + <<"scopes">> => [<<"rabbitmq-resource.write:vhost1/*">>]}, #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, - <<"rsname">> => <<"Default Resource">>}]}, - [<<"rabbitmq-resource.read:*/*">>, <<"rabbitmq-resource-read">>] + <<"rsname">> => <<"Default Resource">>, + <<"scopes">> => [<<"unknown-resource.write:vhost1/*">>]} + ] + }, + [<<"read:*/*">>, <<"write:vhost1/*">>] }, %% one scopes field with a string instead of an array @@ -169,10 +148,10 @@ test_post_process_token_payload_keycloak(_) -> <<"scopes">> => <<"rabbitmq-resource.read:*/*">>}, #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, <<"rsname">> => <<"vhost1">>, - <<"scopes">> => [<<"rabbitmq-resource-read">>]}, + <<"scopes">> => [<<"unknown-resource-read">>]}, #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, <<"rsname">> => <<"Default Resource">>}]}, - [<<"rabbitmq-resource.read:*/*">>, <<"rabbitmq-resource-read">>] + [<<"rabbitmq-resource.read:*/*">>] }, %% no scopes field in permissions @@ -195,19 +174,13 @@ test_post_process_token_payload_keycloak(_) -> %% missing permissions key {#{}, []} ], - lists:foreach( - fun({Authorization, ExpectedScope}) -> - Payload = post_process_payload_with_keycloak_authorization(Authorization), - ?assertEqual(ExpectedScope, maps:get(<<"scope">>, Payload)) + lists:foreach(fun({Authorization, ExpectedScope}) -> + ResourceServer = resource_server:new_resource_server(<<"rabbitmq-resource">>), + Token0 = #{<<"authorization">> => Authorization}, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token)) end, Pairs). -post_process_payload_with_keycloak_authorization(Authorization) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - Token = maps:put(<<"authorization">>, Authorization, ?UTIL_MOD:fixture_token_with_scopes([])), - {_, EncodedToken} = ?UTIL_MOD:sign_token_hs(Token, Jwk), - {true, Payload} = uaa_jwt_jwt:decode_and_verify(<<"rabbitmq">>, Jwk, EncodedToken), - rabbit_auth_backend_oauth2:post_process_payload(<<"rabbitmq">>, Payload). - test_post_process_payload_rich_auth_request_using_regular_expression_with_cluster(_) -> Pairs = [ @@ -222,7 +195,7 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste <<"actions">> => [<<"read">>] } ], - [<<"rabbitmq-test.read:*/*/*">> ] + [<<"read:*/*/*">> ] }, { "can use regular expression on any location's attribute ", @@ -231,7 +204,7 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste <<"actions">> => [<<"read">>] } ], - [<<"rabbitmq-test.read:^finance-*/*/*">> ] + [<<"read:^finance-*/*/*">> ] }, { "should filter out any location which does not match the cluster's pattern ", @@ -247,11 +220,11 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste lists:foreach( fun({Case, Permissions, ExpectedScope}) -> - ResourceServer = #resource_server{ - id = ?RESOURCE_SERVER_ID, - resource_server_type = ?RESOUR - } - Token0 = #{<<"authorization_details">> => Permissions]}, + ResourceServer0 = resource_server:new_resource_server(<<"rabbitmq-test">>), + ResourceServer = ResourceServer0#resource_server{ + resource_server_type = ?RESOURCE_SERVER_TYPE + }, + Token0 = #{<<"authorization_details">> => Permissions}, Token = normalize_token_scope(ResourceServer, Token0), ?assertEqual(lists:sort(ExpectedScope), lists:sort(uaa_jwt:get_scope(Token)), Case) @@ -259,316 +232,320 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste test_post_process_payload_rich_auth_request(_) -> - Pairs = [ - { "should merge all permissions for the current cluster", - [ - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:finance/vhost:primary-*">>], - <<"actions">> => [<<"configure">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"management">> ] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"administrator">> ] - } - ], - [ <<"rabbitmq.tag:management">>, <<"rabbitmq.tag:administrator">> ] - }, - { "should filter out those permisions whose type does not match ", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"read">>] - }, - #{<<"type">> => <<"unknown">>, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/*">> ] - }, - { "should filter out those permisions whose type is the empty string", - [ - #{<<"type">> => <<>>, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"read">>] - } - ], - [ ] - }, - { "should filter out those permisions with empty string action", - [ - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => <<>> - } - ], - [ ] - }, - { "should filter out those permisions whose locations do not refer to cluster : {resource_server_id}", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"read">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq-other">>], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/*">> ] - }, - { "should filter out those permisions whose locations' regexpr do not match the cluster : {resource_server_id} ", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbit*">>], - <<"actions">> => [<<"read">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:*">>], - <<"actions">> => [<<"write">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq-other">>], - <<"actions">> => [<<"configure">>] - } - ], - [<<"rabbitmq.read:*/*/*">>, <<"rabbitmq.write:*/*/*">> ] - }, - - { "should ignore permissions without actions", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbit*">>], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/*">>] - }, - { "should ignore permissions without locations", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"actions">> => [<<"read">>] - } - ] - ,[] - }, - { "should ignore unknown actions", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>], - <<"actions">> => [<<"read2">>, <<"read">>] - } - ] - ,[<<"rabbitmq.read:*/*/*">> ] - }, - { "should filter out locations with permissions not meant for {resource_server_id}", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq">>, <<"cluster:unknown">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/*">> ] - }, - { "should produce a scope for every (action, location) permutation for all locations meant for {resource_server_id}", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:a/*/*">>, <<"rabbitmq.read:b/*/*">> ] - }, - { "should support all known user tags ", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, <<"cluster:other">> ], - <<"actions">> => [<<"management">>, <<"policymaker">>, <<"management">>, <<"monitoring">>] - } - ], - [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">>, <<"rabbitmq.tag:management">>, <<"rabbitmq.tag:monitoring">> ] - }, - { "should produce a scope for every user tag action but only for the clusters that match {resource_server_id}", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, <<"cluster:other">> ], - <<"actions">> => [<<"management">>, <<"policymaker">>] - } - ], - [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">> ] - }, - - { "should produce as scope for every location meant for {resource_server_id} multiplied by actions", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">> ], - <<"actions">> => [<<"read">>, <<"write">>] - } - ], - [<<"rabbitmq.read:a/*/*">>, <<"rabbitmq.read:b/*/*">>, <<"rabbitmq.write:a/*/*">>, <<"rabbitmq.write:b/*/*">> ] - }, - { "should accept single value locations", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => <<"cluster:rabbitmq">>, - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/*">> ] - }, - { "should accept single value actions", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => <<"cluster:rabbitmq">>, - <<"actions">> => <<"read">> - } - ], - [<<"rabbitmq.read:*/*/*">> ] - }, - { "should merge all scopes produced by each permission", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:a">> ], - <<"actions">> => [<<"read">>] - }, - #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:b">> ], - <<"actions">> => [<<"write">>] - } - ], - [<<"rabbitmq.read:a/*/*">>, <<"rabbitmq.write:b/*/*">> ] - }, - { "can grant permission to a queue in any virtual host", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/queue:b">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/b/*">> ] - }, - { "can grant permission to an exchange in any virtual host", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/exchange:b">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/b/*">> ] - }, - { "cannot specify both exchange and queue unless they have the same value", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/queue:b/exchange:c">> ], - <<"actions">> => [<<"read">>] - } - ], - [] - }, - { "can specify exchange and queue when have same value", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/queue:*/exchange:*">> ], - <<"actions">> => [<<"read">>] - } - ], - [ <<"rabbitmq.read:*/*/*">> ] - }, - { "can specify routing-key only -> on any vhost and on any queue if that makes sense ", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/routing-key:b">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/b">> ] - }, - { "can specify vhost, queue or exchange and routing-key that combine fixed values and wildcards", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:finance-*/*-invoice/r-*">> ] - }, - { "should ignore any location's attribute other than the supported ones", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/unknown:finance-*/queue:*-invoice/routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*-invoice/r-*">> ] - }, - { "should not matter the location's attributes order", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/queue:invoices/vhost:finance/routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:finance/invoices/r-*">> ] - }, - { "should ignore locations like //", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq//routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/r-*">> ] - }, - { "should default to wildcard those attributes with empty value", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/queue:/vhost:/routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:*/*/r-*">> ] - }, - { "should ignore any location path element which is not compliant with : format", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"some-prefix-value/cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:finance-*/*-invoice/r-*">> ] - }, - { "can use regular expression on any location's attribute", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => [<<"cluster:rabbitmq/vhost:^finance-*">> ], - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:^finance-*/*/*">> ] - }, - { "can use single string value for location", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, - <<"actions">> => [<<"read">>] - } - ], - [<<"rabbitmq.read:^finance-*/*/*">> ] - }, - { "can use single string value for action", - [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, - <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, - <<"actions">> => <<"read">> - } + Pairs = [ + { "should merge all permissions for the current cluster", + [ + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:finance/vhost:primary-*">>], + <<"actions">> => [<<"configure">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"management">> ] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"administrator">> ] + } + ], + [ <<"tag:management">>, <<"tag:administrator">> ] + }, + { "should filter out those permisions whose type does not match ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => <<"unknown">>, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out those permisions whose type is the empty string", + [ + #{<<"type">> => <<>>, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + } + ], + [ ] + }, + { "should filter out those permisions with empty string action", + [ + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => <<>> + } + ], + [ ] + }, + { "should filter out those permisions whose locations do not refer to cluster : {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq-other">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out those permisions whose locations' regexpr do not match the cluster : {resource_server_id} ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbit*">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:*">>], + <<"actions">> => [<<"write">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq-other">>], + <<"actions">> => [<<"configure">>] + } + ], + [<<"read:*/*/*">>, <<"write:*/*/*">> ] + }, + { "should ignore permissions without actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbit*">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">>] + }, + { "should ignore permissions without locations", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"actions">> => [<<"read">>] + } + ], + [] + }, + { "should ignore unknown actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read2">>, <<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out locations with permissions not meant for {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>, <<"cluster:unknown">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should produce a scope for every (action, location) permutation for all locations meant for {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, + <<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:a/*/*">>, <<"read:b/*/*">> ] + }, + { "should support all known user tags ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, + <<"cluster:other">> ], + <<"actions">> => [ + <<"management">>, <<"policymaker">>, <<"management">>, + <<"monitoring">>] + } + ], + [<<"tag:management">>, <<"tag:policymaker">>, + <<"tag:management">>, <<"tag:monitoring">> ] + }, + { "should produce a scope for every user tag action but only for the clusters that match {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, + <<"cluster:other">> ], + <<"actions">> => [<<"management">>, <<"policymaker">>] + } + ], + [<<"tag:management">>, <<"tag:policymaker">> ] + }, + { "should produce as scope for every location meant for {resource_server_id} multiplied by actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"read">>, <<"write">>] + } + ], + [<<"read:a/*/*">>, <<"read:b/*/*">>, <<"write:a/*/*">>, <<"write:b/*/*">> ] + }, + { "should accept single value locations", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq">>, + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should accept single value actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq">>, + <<"actions">> => <<"read">> + } + ], + [<<"read:*/*/*">> ] + }, + { "should merge all scopes produced by each permission", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:a">> ], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"write">>] + } + ], + [<<"read:a/*/*">>, <<"write:b/*/*">> ] + }, + { "can grant permission to a queue in any virtual host", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/b/*">> ] + }, + { "can grant permission to an exchange in any virtual host", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/exchange:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/b/*">> ] + }, + { "cannot specify both exchange and queue unless they have the same value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:b/exchange:c">> ], + <<"actions">> => [<<"read">>] + } + ], + [] + }, + { "can specify exchange and queue when have same value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:*/exchange:*">> ], + <<"actions">> => [<<"read">>] + } + ], + [ <<"read:*/*/*">> ] + }, + { "can specify routing-key only -> on any vhost and on any queue if that makes sense ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/routing-key:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/b">> ] + }, + { "can specify vhost, queue or exchange and routing-key that combine fixed values and wildcards", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance-*/*-invoice/r-*">> ] + }, + { "should ignore any location's attribute other than the supported ones", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/unknown:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*-invoice/r-*">> ] + }, + { "should not matter the location's attributes order", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:invoices/vhost:finance/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance/invoices/r-*">> ] + }, + { "should ignore locations like //", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq//routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/r-*">> ] + }, + { "should default to wildcard those attributes with empty value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/queue:/vhost:/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/r-*">> ] + }, + { "should ignore any location path element which is not compliant with : format", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"some-prefix-value/cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance-*/*-invoice/r-*">> ] + }, + { "can use regular expression on any location's attribute", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:^finance-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "can use single string value for location", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, + <<"actions">> => [<<"read">>] + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "can use single string value for action", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, + <<"actions">> => <<"read">> + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "should ignore empty permission lists", + [], + [] + } ], - [<<"rabbitmq.read:^finance-*/*/*">> ] - }, - { "should ignore empty permission lists", - [], - [] - } - ], - lists:foreach( - fun({Case, Permissions, ExpectedScope0}) -> - ResourceServer = #resource_server{ - id = ?RESOURCE_SERVER_ID, + lists:foreach(fun({Case, Permissions, ExpectedScope0}) -> + ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server{ resource_server_type = ?RESOURCE_SERVER_TYPE - }, - Token0 = #{<<"authorization_details">> => Permissions]}, - Token = normalize_token_scope(ResourceServer, Permissions), - ExpectedScopes = lists:sort(ExpectedScope0), - ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), - ?assertEqual(ExpectedScopes, ActualScopes, Case) + }, + Token0 = #{<<"authorization_details">> => Permissions}, + Token = normalize_token_scope(ResourceServer, Token0), + ExpectedScopes = lists:sort(ExpectedScope0), + ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), + ?assertEqual(ExpectedScopes, ActualScopes, Case) end, Pairs). -prepare_token_with_rich_authorization_details(ResourceServerId, Permissions) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - - {_, EncodedToken} = ?UTIL_MOD:sign_token_hs(Token, Jwk), - {true, Payload} = uaa_jwt_jwt:decode_and_verify(<<"rabbitmq">>, Jwk, EncodedToken), - Payload. - test_post_process_token_payload_complex_claims(_) -> Pairs = [ %% claims in form of binary @@ -640,26 +617,27 @@ post_process_payload_with_complex_claim_authorization(ResourceServerId, Authoriz rabbit_auth_backend_oauth2:post_process_payload(ResourceServerId, Payload). test_successful_authentication_without_scopes(_) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + Jwk = ?UTIL_MOD:fixture_jwk(), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - Username = <<"username">>, - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), - {ok, #auth_user{username = Username} } = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]). + {ok, #auth_user{username = Username} } = + user_login_authentication(Username, [{password, Token}]). test_successful_authorization_without_scopes(_) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + Jwk = ?UTIL_MOD:fixture_jwk(), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - Username = <<"username">>, - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), - {ok, _ } = - rabbit_auth_backend_oauth2:user_login_authorization(Username, [{password, Token}]). + {ok, _ } = user_login_authorization(Username, [{password, Token}]). test_successful_access_with_a_token(_) -> %% Generate a token with JOSE @@ -674,11 +652,9 @@ test_successful_access_with_a_token(_) -> Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), {ok, #auth_user{username = Username} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), -% {ok, #auth_user{username = Username} = User} = -% rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token}), + user_login_authentication(Username, [{password, Token}]), - ?assertEqual(true, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"vhost">>, none)), + ?assertEqual(true, check_vhost_access(User, <<"vhost">>, none)), assert_resource_access_granted(User, VHost, <<"foo">>, configure), assert_resource_access_granted(User, VHost, <<"foo">>, write), assert_resource_access_granted(User, VHost, <<"bar">>, read), @@ -700,7 +676,7 @@ successful_access_with_a_token_with_variables_in_scopes(_) -> ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token([<<"rabbitmq.read:{vhost}/*/{sub}">>]), Username), Jwk), {ok, #auth_user{username = Username} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token}), + user_login_authentication(Username, #{password => Token}), assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => Username}). @@ -712,10 +688,10 @@ successful_access_with_a_parsed_token(_) -> Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), {ok, #auth_user{impl = Impl} } = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), {ok, _ } = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{rabbit_auth_backend_oauth2, Impl}]). + user_login_authentication(Username, [{rabbit_auth_backend_oauth2, Impl}]). test_successful_access_with_a_token_that_has_tag_scopes(_) -> @@ -727,7 +703,7 @@ test_successful_access_with_a_token_that_has_tag_scopes(_) -> [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">>]), Username), Jwk), {ok, #auth_user{username = Username, tags = [management, policymaker]}} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]). + user_login_authentication(Username, [{password, Token}]). test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), @@ -752,7 +728,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field( ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -793,7 +769,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_ ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -839,7 +815,7 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi ?UTIL_MOD:token_with_scope_alias_in_scope_field([Role1, Role2, Role3]), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -876,7 +852,7 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_fie Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), - {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_denied(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -913,7 +889,7 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_ Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), - {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -957,7 +933,7 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( ?UTIL_MOD:token_with_scope_alias_in_claim_field(Claims, [<<"unrelated">>]), Username), Jwk), - {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -995,7 +971,7 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_sco Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), - {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_denied(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -1021,7 +997,7 @@ test_unsuccessful_access_with_a_bogus_token(_) -> application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), ?assertMatch({refused, _, _}, - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, <<"not a token">>}])). + user_login_authentication(Username, [{password, <<"not a token">>}])). unsuccessful_access_without_scopes(_) -> Username = <<"username">>, @@ -1033,7 +1009,7 @@ unsuccessful_access_without_scopes(_) -> application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), {ok, #auth_user{username = Username, tags = [], impl = _CredentialsFun } = AuthUser} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), assert_vhost_access_denied(AuthUser, <<"vhost">>). @@ -1048,10 +1024,10 @@ test_restricted_vhost_access_with_a_valid_token(_) -> %% this user can authenticate successfully and access certain vhosts {ok, #auth_user{username = Username, tags = []} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), %% access to a different vhost - ?assertEqual(false, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"different vhost">>, none)). + ?assertEqual(false, check_vhost_access(User, <<"different vhost">>, none)). test_insufficient_permissions_in_a_valid_token(_) -> VHost = <<"vhost">>, @@ -1064,7 +1040,7 @@ test_insufficient_permissions_in_a_valid_token(_) -> application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), {ok, #auth_user{username = Username} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), %% access to these resources is not granted assert_resource_access_denied(User, VHost, <<"foo1">>, configure), @@ -1081,7 +1057,7 @@ test_invalid_signature(_) -> TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), ?assertMatch({refused, _, [signature_invalid]}, - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}])). + user_login_authentication(Username, [{password, Token}])). test_token_expiration(_) -> VHost = <<"vhost">>, @@ -1093,7 +1069,7 @@ test_token_expiration(_) -> TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), {ok, #auth_user{username = Username} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), + user_login_authentication(Username, [{password, Token}]), assert_resource_access_granted(User, VHost, <<"foo">>, configure), assert_resource_access_granted(User, VHost, <<"foo">>, write), @@ -1108,7 +1084,7 @@ test_token_expiration(_) -> assert_resource_access_errors(ExpectedError, User, VHost, <<"foo">>, configure), ?assertMatch({refused, _, _}, - rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}])). + user_login_authentication(Username, [{password, Token}])). test_incorrect_kid(_) -> AltKid = <<"other-token-key">>, @@ -1117,13 +1093,13 @@ test_incorrect_kid(_) -> application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, AltKid, true), ?assertMatch({refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [{error,{missing_oauth_provider_attributes, [issuer]}}]}, - rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token})). + user_login_authentication(Username, #{password => Token})). login_and_check_vhost_access(Username, Token, Vhost) -> {ok, #auth_user{username = Username} = User} = - rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token}), + user_login_authentication(Username, #{password => Token}), - ?assertEqual(true, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"vhost">>, Vhost)). + ?assertEqual(true, check_vhost_access(User, <<"vhost">>, Vhost)). test_command_json(Config) -> Username = <<"username">>, @@ -1240,7 +1216,7 @@ filter_matching_scope_prefix_and_drop_it(_) -> end, Examples). -test_normalize_token_scopes_with_scope_prefix(_) -> +normalize_token_scopes_with_scope_prefix(_) -> Scenarios = [ { <<"">>, @@ -1261,30 +1237,24 @@ test_normalize_token_scopes_with_scope_prefix(_) -> ], lists:map(fun({ ScopePrefix, Token0, ExpectedScopes}) -> - ResourceServer = #resource_server { - id = ?RESOURCE_SERVER_ID, + ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server { scope_prefix = ScopePrefix }, Token = normalize_token_scope(ResourceServer, Token0), ?assertEqual(ExpectedScopes, uaa_jwt:get_scope(Token)) end, Scenarios). -test_normalize_token_scope(_) -> - ResourceServer = #resource_server { - id = ?RESOURCE_SERVER_ID - }, +normalize_token_scope_from_space_separated_list_in_scope_claim(_) -> + ResourceServer = resource_server:new_resource_server(?RESOURCE_SERVER_ID), Token0 = #{ - <<"scope">> => [<<"foo">>, <<"rabbitmq.bar">>, - <<"bar.foo">>, <<"one.two">>, - <<"foobar">>, <<"rabbitmq.other.third">>] + ?SCOPE_JWT_FIELD => <<"foo rabbitmq.bar bar.foo one.two foobar rabbitmq.other.third">> }, Token = normalize_token_scope(ResourceServer, Token0), ?assertEqual([<<"bar">>, <<"other.third">>], uaa_jwt:get_scope(Token)). -test_normalize_token_scope_without_scope(_) -> - ResourceServer = #resource_server { - id = ?RESOURCE_SERVER_ID - }, +normalize_token_scope_without_scope_claim(_) -> + ResourceServer = resource_server:new_resource_server(?RESOURCE_SERVER_ID), Token0 = #{ }, ?assertEqual([], uaa_jwt:get_scope(normalize_token_scope(ResourceServer, Token0))). @@ -1300,7 +1270,7 @@ assert_vhost_access_denied(AuthUser, VHost) -> assert_vhost_access_response(ExpectedResult, AuthUser, VHost) -> ?assertEqual(ExpectedResult, - rabbit_auth_backend_oauth2:check_vhost_access(AuthUser, VHost, none)). + check_vhost_access(AuthUser, VHost, none)). assert_resource_access_granted(AuthUser, VHost, ResourceName, PermissionKind) -> assert_resource_access_response(true, AuthUser, VHost, ResourceName, PermissionKind). From 12b8c0db587aa981378faf86b961b749c76214e2 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 12:06:58 +0200 Subject: [PATCH 25/64] Fix all test in unit_SUITE --- deps/rabbitmq_auth_backend_oauth2/app.bzl | 6 +- .../rabbitmq_auth_backend_oauth2.schema | 18 +- .../src/oauth2_schema.erl | 12 +- .../src/rabbit_auth_backend_oauth2.erl | 4 +- .../src/rabbit_oauth2_scope.erl | 1 - .../test/unit_SUITE.erl | 244 +++++++++--------- 6 files changed, 141 insertions(+), 144 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index 85cf79de8a9e..70ff08783a13 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -105,7 +105,7 @@ def all_srcs(name = "all_srcs"): "src/resource_server.erl", "src/oauth2_schema.erl", "src/rar.erl", - "src/keycloak.erl", + "src/keycloak.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -233,9 +233,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/unit_SUITE.erl"], outs = ["test/unit_SUITE.beam"], + hdrs = ["include/oauth2.hrl"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", - deps = ["//deps/rabbit_common:erlang_app"], + deps = ["//deps/rabbit_common:erlang_app", + "//deps/oauth2_client:erlang_app"], ) erlang_bytecode( name = "wildcard_match_SUITE_beam_files", 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 7c1b116eca5b..a300ecb22e1b 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 @@ -163,15 +163,15 @@ %% auth_oauth2.resource_servers.rabbitmq.token_endpoint_params.audience %% auth_oauth2.resource_servers.rabbitmq.jkws_uri_params.appId = -{mapping, - "auth_oauth2.authorization_endpoint_params.$param", - "rabbitmq_auth_backend_oauth2.oauth_providers", - [{datatype, string}]}. - -{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", - fun(Conf) -> - oauth2_schema:translate_authorization_endpoint_params(Conf) - end}. +%%{mapping, +%% "auth_oauth2.authorization_endpoint_params.$param", +%% "rabbitmq_auth_backend_oauth2.oauth_providers", +%% [{datatype, string}]}. + +%%{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", +%% fun(Conf) -> +%% oauth2_schema:translate_authorization_endpoint_params(Conf) +%% end}. {mapping, "auth_oauth2.oauth_providers.$name.algorithms.$algorithm", diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index 93ff3669b18c..21e748f9fb98 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -11,8 +11,8 @@ -export([ translate_oauth_providers/1, translate_resource_servers/1, - translate_signing_keys/1, - translate_authorization_endpoint_params/1 + translate_signing_keys/1 %, + %%translate_authorization_endpoint_params/1 ]). extract_key_as_binary({Name,_}) -> list_to_binary(Name). @@ -64,10 +64,10 @@ translate_list_of_signing_keys(ListOfKidPath) -> end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). --spec translate_authorization_endpoint_params([{list(), binary()}]) -> map(). -translate_authorization_endpoint_params(Conf) -> - Params = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint_params", Conf), - lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, Params). +%%-spec translate_authorization_endpoint_params([{list(), binary()}]) -> map(). +%%translate_authorization_endpoint_params(Conf) -> +%% Params = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint_params", Conf), +%% lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, Params). validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of 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 086c458bf19c..f35d9aee9464 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 @@ -133,7 +133,7 @@ authenticate(_, AuthProps0) -> Token = token_from_context(AuthProps), case resolve_resource_server(Token) of {error, _} = Err0 -> - Err0; + {refused, "Authentication using OAuth 2/JWT token failed: ~tp", [Err0]}; {ResourceServer, _} = Tuple -> case check_token(Token, Tuple) of {error, _} = E -> E; @@ -189,7 +189,7 @@ check_token(Token, {ResourceServer, InternalOAuthProvider}) -> case decode_and_verify(Token, ResourceServer, InternalOAuthProvider) of {error, Reason} -> {refused, {error, Reason}}; {true, Payload} -> {ok, normalize_token_scope(ResourceServer, Payload)}; - {false, _, _} -> {refused, signature_invalid} + {false, _} -> {refused, signature_invalid} end. -spec normalize_token_scope( 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 2cfcbbc01e32..e9cc75d37d19 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl @@ -96,7 +96,6 @@ 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) -> - ct:log("filter_matching_scope_prefix_and_drop_it ~p ~p", [Scopes, PrefixPattern]), PatternLength = byte_size(PrefixPattern), lists:filtermap( fun(ScopeEl) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index fb7b2b58a7bc..18d70c5b6fd6 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -36,10 +36,9 @@ all() -> test_token_expiration, test_invalid_signature, test_incorrect_kid, - test_post_process_token_payload, - test_post_process_token_payload_keycloak, - test_post_process_payload_rich_auth_request, - test_post_process_payload_rich_auth_request_using_regular_expression_with_cluster, + 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, 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, @@ -62,7 +61,7 @@ groups() -> test_successful_authentication_without_scopes, 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, - test_post_process_token_payload_complex_claims, + normalize_token_scope_with_additional_scopes_complex_claims, test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_and_custom_scope_prefix ]} @@ -121,70 +120,67 @@ end_per_group(_, Config) -> -define(DEFAULT_SCOPE_PREFIX, <<"rabbitmq.">>). -test_post_process_token_payload_keycloak(_) -> +normalize_token_scope_with_keycloak_scopes(_) -> Pairs = [ %% common case - { - #{<<"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/*">>] - }, - - %% one scopes field with a string instead of an array - { - #{<<"permissions">> => - [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, - <<"rsname">> => <<"allvhost">>, - <<"scopes">> => <<"rabbitmq-resource.read:*/*">>}, - #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, - <<"rsname">> => <<"vhost1">>, - <<"scopes">> => [<<"unknown-resource-read">>]}, - #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, - <<"rsname">> => <<"Default Resource">>}]}, - [<<"rabbitmq-resource.read:*/*">>] + { + "common case", + #{<<"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/*">>]} + ] }, - - %% no scopes field in permissions - { - #{<<"permissions">> => - [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, - <<"rsname">> => <<"allvhost">>}, - #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, - <<"rsname">> => <<"vhost1">>}, - #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, - <<"rsname">> => <<"Default Resource">>}]}, - [] - }, - - %% no permissions - { - #{<<"permissions">> => []}, + [<<"read:*/*">>, <<"write:vhost1/*">>] + }, + { + "one scopes field with a string instead of an array", + #{<<"permissions">> => + [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, + <<"rsname">> => <<"allvhost">>, + <<"scopes">> => <<"rabbitmq-resource.read:*/*">>}, + #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, + <<"rsname">> => <<"vhost1">>, + <<"scopes">> => [<<"unknown-resource-read">>]}, + #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, + <<"rsname">> => <<"Default Resource">>}]}, + [<<"read:*/*">>] + }, + { + "no scopes field in permissions", + #{<<"permissions">> => + [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, + <<"rsname">> => <<"allvhost">>}, + #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, + <<"rsname">> => <<"vhost1">>}, + #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, + <<"rsname">> => <<"Default Resource">>}]}, + [] + }, + { + "no permissions", + #{<<"permissions">> => []}, [] - }, - %% missing permissions key - {#{}, []} + }, + {"missing permissions key", #{}, []} ], - lists:foreach(fun({Authorization, ExpectedScope}) -> + + lists:foreach(fun({Case, Authorization, ExpectedScope}) -> ResourceServer = resource_server:new_resource_server(<<"rabbitmq-resource">>), Token0 = #{<<"authorization">> => Authorization}, Token = normalize_token_scope(ResourceServer, Token0), - ?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token)) + ?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token), Case) end, Pairs). -test_post_process_payload_rich_auth_request_using_regular_expression_with_cluster(_) -> +normalize_token_scope_with_rich_auth_request_using_regular_expression_with_cluster(_) -> Pairs = [ - { "should filter out those permisions whose locations do not refer to cluster : {resource_server_id}", [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, <<"locations">> => [<<"cluster:rabbitmq-test">>], @@ -230,7 +226,7 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste lists:sort(uaa_jwt:get_scope(Token)), Case) end, Pairs). -test_post_process_payload_rich_auth_request(_) -> +normalize_token_scope_with_rich_auth_request(_) -> Pairs = [ { "should merge all permissions for the current cluster", @@ -546,75 +542,75 @@ test_post_process_payload_rich_auth_request(_) -> ?assertEqual(ExpectedScopes, ActualScopes, Case) end, Pairs). -test_post_process_token_payload_complex_claims(_) -> +normalize_token_scope_with_additional_scopes_complex_claims(_) -> Pairs = [ - %% claims in form of binary - { - <<"rabbitmq.rabbitmq-resource.read:*/* rabbitmq.rabbitmq-resource-read">>, - [<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>] - }, - %% claims in form of binary - empty result - {<<>>, []}, - %% claims in form of list - { - [<<"rabbitmq.rabbitmq-resource.read:*/*">>, + { + "claims in form of binary", + <<"rabbitmq.rabbitmq-resource.read:*/* rabbitmq.rabbitmq-resource-read">>, + [<<"read:*/*">>] + }, + {"claims in form of binary - empty result", <<>>, []}, + { + "claims in form of list", + [<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq2.rabbitmq-resource-read">>], - [<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq2.rabbitmq-resource-read">>] - }, - %% claims in form of list - empty result - {[], []}, - %% claims are map with list content - { - #{<<"rabbitmq">> => - [<<"rabbitmq-resource.read:*/*">>, - <<"rabbitmq-resource-read">>], - <<"rabbitmq3">> => - [<<"rabbitmq-resource.write:*/*">>, - <<"rabbitmq-resource-write">>]}, - [<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>] - }, - %% claims are map with list content - empty result - { - #{<<"rabbitmq2">> => - [<<"rabbitmq-resource.read:*/*">>, - <<"rabbitmq-resource-read">>]}, - [] - }, - %% 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">>] - }, - %% claims are map with binary content - empty result - { - #{<<"rabbitmq2">> => <<"rabbitmq-resource.read:*/* rabbitmq-resource-read">>}, [] - }, - %% claims are map with empty binary content - empty result - { - #{<<"rabbitmq">> => <<>>}, [] - }, - %% claims are map with empty list content - empty result - { - #{<<"rabbitmq">> => []}, [] + [<<"read:*/*">>] + }, + {"claims in form of list - empty result", [], []}, + { + "claims are map with list content", + #{<<"rabbitmq">> => + [<<"rabbitmq-resource.read:*/*">>, + <<"rabbitmq-resource-read">>], + <<"rabbitmq3">> => + [<<"rabbitmq-resource.write:*/*">>, + <<"rabbitmq-resource-write">>]}, + [<<"read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>] + }, + { + "claims are map with list content - empty result", + #{<<"rabbitmq2">> => + [<<"rabbitmq-resource.read:*/*">>, + <<"rabbitmq-resource-read">>]}, + [] + }, + { + "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">>] + }, + { + "claims are map with binary content - empty result", + #{<<"rabbitmq2">> => <<"rabbitmq-resource.read:*/* rabbitmq-resource-read">>}, [] + }, + { + "claims are map with empty binary content - empty result", + #{<<"rabbitmq">> => <<>>}, [] + }, + { + "claims are map with empty list content - empty result", + #{<<"rabbitmq">> => []}, [] + }, + { + "no extra claims provided", + [], [] + }, + { + "no extra claims provided", #{}, [] + }], + lists:foreach(fun({Case, Authorization, ExpectedScope0}) -> + ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server{ + scope_prefix = <<"rabbitmq.rabbitmq-resource.">>, + additional_scopes_key = <<"custom-key">> }, - %% no extra claims provided - {[], []}, - %% no extra claims provided - {#{}, []} - ], - lists:foreach( - fun({Authorization, ExpectedScope}) -> - Payload = post_process_payload_with_complex_claim_authorization(<<"rabbitmq-resource">>, Authorization), - ?assertEqual(ExpectedScope, maps:get(<<"scope">>, Payload)) - end, Pairs). - -post_process_payload_with_complex_claim_authorization(ResourceServerId, Authorization) -> - Jwk = ?UTIL_MOD:fixture_jwk(), - Token = maps:put(<<"additional_rabbitmq_scopes">>, Authorization, ?UTIL_MOD:fixture_token_with_scopes([])), - {_, EncodedToken} = ?UTIL_MOD:sign_token_hs(Token, Jwk), - {true, Payload} = uaa_jwt_jwt:decode_and_verify(Jwk, EncodedToken), - rabbit_auth_backend_oauth2:post_process_payload(ResourceServerId, Payload). + Token0 = #{<<"custom-key">> => Authorization}, + Token = normalize_token_scope(ResourceServer, Token0), + ExpectedScopes = lists:sort(ExpectedScope0), + ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), + ?assertEqual(ExpectedScopes, ActualScopes, Case) + end, Pairs). test_successful_authentication_without_scopes(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), From 7792b70c13ed6c6ddfe3d7f10045cadb5f46b9bf Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 12:20:01 +0200 Subject: [PATCH 26/64] Fix dialyzer errors --- .../src/rabbit_auth_backend_oauth2.erl | 2 -- .../src/resource_server.erl | 15 ++++++++------- 2 files changed, 8 insertions(+), 9 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 f35d9aee9464..e7bceabe7cda 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 @@ -106,7 +106,6 @@ update_state(AuthUser, NewToken) -> {_, _} = Tuple -> case check_token(NewToken, Tuple) of %% avoid logging the token - {error, _} = E -> E; {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid"}; {refused, Err} -> @@ -136,7 +135,6 @@ authenticate(_, AuthProps0) -> {refused, "Authentication using OAuth 2/JWT token failed: ~tp", [Err0]}; {ResourceServer, _} = Tuple -> case check_token(Token, Tuple) of - {error, _} = E -> E; {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid", []}; {refused, Err} -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl index 56bc17a1e519..268717c20d6b 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl @@ -58,7 +58,7 @@ resolve_resource_server_from_audience(Audience) -> -spec get_root_resource_server_id() -> resource_server_id(). get_root_resource_server_id() -> - get_env(resource_server_id). + get_env(resource_server_id, <<>>). -spec get_root_resource_server() -> resource_server(). get_root_resource_server() -> @@ -80,7 +80,7 @@ get_root_resource_server() -> get_env(extra_scopes_source), DefaultScopePrefix = case ResourceServerId of - undefined -> undefined; + <<>> -> undefined; _ -> erlang:iolist_to_binary([ResourceServerId, <<".">>]) end, ScopePrefix = @@ -102,17 +102,18 @@ get_root_resource_server() -> oauth_provider_id = OAuthProviderId }. --spec get_resource_server(resource_server_id()) -> resource_server(). +-spec get_resource_server(resource_server_id()) -> resource_server() | undefined. get_resource_server(ResourceServerId) -> RootResourseServer = get_root_resource_server(), RootResourseServerId = RootResourseServer#resource_server.id, case ResourceServerId of - undefined -> undefined; + <<>> -> undefined; RootResourseServerId -> RootResourseServer; _ -> get_resource_server(ResourceServerId, RootResourseServer) end. --spec get_resource_server(resource_server_id(), resource_server()) -> resource_server(). +-spec get_resource_server(ResourceServerId :: resource_server_id(), + DefaultResourceServerSettings :: resource_server()) -> resource_server(). get_resource_server(ResourceServerId, RootResourseServer) when ResourceServerId == RootResourseServer#resource_server.id -> RootResourseServer; @@ -202,7 +203,7 @@ find_unique_resource_server_without_verify_aud() -> Map0 = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, Root#resource_server.verify_aud) end, get_env(resource_servers, #{})), Map = case {Root#resource_server.id, Root#resource_server.verify_aud} of - {undefined, _} -> Map0; + {<<>>, _} -> Map0; {_, true} -> Map0; {Id, false} -> maps:put(Id, Root, Map0) end, @@ -214,7 +215,7 @@ find_unique_resource_server_without_verify_aud() -> append(List, Value) -> case Value of - undefined -> List; + <<>> -> List; _ -> List ++ [Value] end. get_env(Par) -> From 5044e297d448cd21ea2f4f1cc6e3cbae2eed67dc Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 15:37:18 +0200 Subject: [PATCH 27/64] Add token endpoint params to schema --- deps/oauth2_client/include/types.hrl | 7 ++- .../rabbitmq_auth_backend_oauth2.schema | 63 ++++++++++++++----- .../src/oauth2_schema.erl | 29 ++++++--- .../test/oauth2_schema_SUITE.erl | 22 +++++++ 4 files changed, 99 insertions(+), 22 deletions(-) diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl index 13c61cfd2c96..42f90803efcf 100644 --- a/deps/oauth2_client/include/types.hrl +++ b/deps/oauth2_client/include/types.hrl @@ -6,7 +6,7 @@ %% %% The closest we have to a type import in Erlang --type option(T) :: rabbit_types:option(T). +-type(option(T) :: T | 'undefined'). -type oauth_provider_id() :: root | binary(). @@ -22,10 +22,15 @@ -record(oauth_provider, { id :: oauth_provider_id(), issuer :: option(uri_string:uri_string()), + discovery_endpoint_path :: option(uri_string:uri_string()), + discovery_endpoint_params :: option([tuple()]), token_endpoint :: option(uri_string:uri_string()), + token_endpoint_params :: option([tuple()]), authorization_endpoint :: option(uri_string:uri_string()), + authorization_endpoint_params :: option([tuple()]), end_session_endpoint :: option(uri_string:uri_string()), jwks_uri :: option(uri_string:uri_string()), + jwks_uri_params :: option([tuple()]), ssl_options :: option(list()) }). 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 a300ecb22e1b..251102096468 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 @@ -158,20 +158,55 @@ "rabbitmq_auth_backend_oauth2.authorization_endpoint", [{datatype, string}, {validators, ["uri", "https_uri"]}]}. -%% auth_oauth2.authorization_endpoint_params.audience -%% auth_oauth2.resource_servers.rabbitmq.authorization_endpoint_params.audience -%% auth_oauth2.resource_servers.rabbitmq.token_endpoint_params.audience -%% auth_oauth2.resource_servers.rabbitmq.jkws_uri_params.appId = - -%%{mapping, -%% "auth_oauth2.authorization_endpoint_params.$param", -%% "rabbitmq_auth_backend_oauth2.oauth_providers", -%% [{datatype, string}]}. - -%%{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", -%% fun(Conf) -> -%% oauth2_schema:translate_authorization_endpoint_params(Conf) -%% end}. +{mapping, + "auth_oauth2.authorization_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", + fun(Conf) -> + oauth2_schema:translate_endpoint_params("authorization_endpoint_params", Conf) + end}. + +{mapping, + "auth_oauth2.discovery_endpoint_path", + "rabbitmq_auth_backend_oauth2.discovery_endpoint_path", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.discovery_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.discovery_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.discovery_endpoint_params", + fun(Conf) -> + oauth2_schema:translate_endpoint_params("discovery_endpoint_params", Conf) + end}. + +{mapping, + "auth_oauth2.oauth_providers.$name.discovery_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.token_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.token_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.token_endpoint_params", + fun(Conf) -> + oauth2_schema:translate_endpoint_params("token_endpoint_params", Conf) + end}. + +{mapping, + "auth_oauth2.oauth_providers.$name.authorization_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.oauth_providers.$name.token_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. {mapping, "auth_oauth2.oauth_providers.$name.algorithms.$algorithm", diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index 21e748f9fb98..eef928dd6607 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -11,8 +11,8 @@ -export([ translate_oauth_providers/1, translate_resource_servers/1, - translate_signing_keys/1 %, - %%translate_authorization_endpoint_params/1 + translate_signing_keys/1, + translate_endpoint_params/2 ]). extract_key_as_binary({Name,_}) -> list_to_binary(Name). @@ -40,9 +40,13 @@ translate_oauth_providers(Conf) -> merge_list_of_maps([ extract_oauth_providers_properties(Settings), + extract_oauth_providers_endpoint_params(discovery_endpoint_params, Settings), + extract_oauth_providers_endpoint_params(authorization_endpoint_params, Settings), + extract_oauth_providers_endpoint_params(token_endpoint_params, Settings), extract_oauth_providers_algorithm(Settings), extract_oauth_providers_https(Settings), - extract_oauth_providers_signing_keys(Settings)]). + extract_oauth_providers_signing_keys(Settings) + ]). -spec translate_signing_keys([{list(), binary()}]) -> map(). translate_signing_keys(Conf) -> @@ -64,10 +68,13 @@ translate_list_of_signing_keys(ListOfKidPath) -> end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). -%%-spec translate_authorization_endpoint_params([{list(), binary()}]) -> map(). -%%translate_authorization_endpoint_params(Conf) -> -%% Params = cuttlefish_variable:filter_by_prefix("auth_oauth2.authorization_endpoint_params", Conf), -%% lists:map(fun({Id, Value}) -> {list_to_binary(lists:last(Id)), Value} end, Params). +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). +translate_endpoint_params(Variable, Conf) -> + Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), + ct:log("translate_endpoint_params ~p -> ~p", [Variable, Params0]), + Params = [{list_to_binary(Param), list_to_binary(V)} || + {["auth_oauth2", Name, Param], V} <- Params0], + maps:from_list(Params). validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of @@ -154,6 +161,14 @@ extract_resource_server_preferred_username_claims(Settings) -> maps:map(fun(_K,V)-> [{preferred_username_claims, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, Claims)). +extract_oauth_providers_endpoint_params(Variable, Settings) -> + KeyFun = fun extract_key_as_binary/1, + + IndexedParams = [{Name, {ParamName, list_to_binary(V)}} || + {["auth_oauth2","oauth_providers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == Variable ], + maps:map(fun(_K,V)-> [{Variable, V}] end, + maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). + extract_oauth_providers_signing_keys(Settings) -> KeyFun = fun extract_key_as_binary/1, diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 7830ce623172..68078e291bd9 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -12,6 +12,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +-import(oauth2_schema, [translate_endpoint_params/2, translate_oauth_providers/1]). all() -> [ @@ -24,6 +25,8 @@ all() -> test_oauth_providers_https, test_oauth_providers_https_with_missing_cacertfile, test_oauth_providers_signing_keys, + test_without_endpoint_params, + test_with_endpoint_params, test_without_resource_servers, test_with_one_resource_server, test_with_many_resource_servers, @@ -38,6 +41,25 @@ test_without_oauth_providers(_) -> test_without_resource_servers(_) -> #{} = oauth2_schema:translate_resource_servers([]). +test_without_endpoint_params(_) -> + #{} = translate_endpoint_params("discovery_endpoint_params", []), + #{} = translate_endpoint_params("token_endpoint_params", []), + #{} = translate_endpoint_params("authorization_endpoint_params", []). + +test_with_endpoint_params(_) -> + Conf = [ + {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, + {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"}, + {["auth_oauth2","token_endpoint_params","audience"], "some-audience"}, + {["auth_oauth2","authorization_endpoint_params","resource"], "some-resource"} + ], + #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> } = + translate_endpoint_params("discovery_endpoint_params", Conf), + #{ <<"audience">> := <<"some-audience">>} = + translate_endpoint_params("token_endpoint_params", Conf), + #{ <<"resource">> := <<"some-resource">>} = + translate_endpoint_params("authorization_endpoint_params", Conf). + test_with_one_oauth_provider(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} ], From 0d4fb55cdaef0a8a94b493dea024c49400f5195e Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 15:42:06 +0200 Subject: [PATCH 28/64] Remove unnecessary statement --- deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index eef928dd6607..6341467a35be 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -71,7 +71,6 @@ translate_list_of_signing_keys(ListOfKidPath) -> -spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), - ct:log("translate_endpoint_params ~p -> ~p", [Variable, Params0]), Params = [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", Name, Param], V} <- Params0], maps:from_list(Params). From b339714bf81e0ff4ddd27090f5cfc75c6d2b9ef5 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 18 Sep 2024 16:37:17 +0200 Subject: [PATCH 29/64] Test invalid token parameter config --- .../src/oauth2_schema.erl | 21 ++++++++--- .../test/oauth2_schema_SUITE.erl | 37 ++++++++++++++++++- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index 6341467a35be..b5e6942160a9 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -72,7 +72,7 @@ translate_list_of_signing_keys(ListOfKidPath) -> translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), Params = [{list_to_binary(Param), list_to_binary(V)} || - {["auth_oauth2", Name, Param], V} <- Params0], + {["auth_oauth2", _, Param], V} <- Params0], maps:from_list(Params). validator_file_exists(Attr, Filename) -> @@ -104,9 +104,10 @@ extract_oauth_providers_properties(Settings) -> ValueFun = fun extract_value/1, OAuthProviders = [{Name, mapOauthProviderProperty({list_to_atom(Key), list_to_binary(V)})} - || {["auth_oauth2","oauth_providers", Name, Key], V} <- Settings ], + || {["auth_oauth2", "oauth_providers", Name, Key], V} <- Settings], maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). + extract_resource_server_properties(Settings) -> KeyFun = fun extract_key_as_binary/1, ValueFun = fun extract_value/1, @@ -122,6 +123,15 @@ mapOauthProviderProperty({Key, Value}) -> jwks_uri -> validator_https_uri(Key, Value); end_session_endpoint -> validator_https_uri(Key, Value); authorization_endpoint -> validator_https_uri(Key, Value); + token_endpoint_params -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); + authorization_endpoint_params -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); + discovery_endpoint_params -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); _ -> Value end}. @@ -163,9 +173,10 @@ extract_resource_server_preferred_username_claims(Settings) -> extract_oauth_providers_endpoint_params(Variable, Settings) -> KeyFun = fun extract_key_as_binary/1, - IndexedParams = [{Name, {ParamName, list_to_binary(V)}} || - {["auth_oauth2","oauth_providers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == Variable ], - maps:map(fun(_K,V)-> [{Variable, V}] end, + IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || + {["auth_oauth2","oauth_providers", Name, EndpointVar, ParamName], V} + <- Settings, EndpointVar == atom_to_list(Variable) ], + maps:map(fun(_K,V)-> [{Variable, maps:from_list(V)}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). extract_oauth_providers_signing_keys(Settings) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 68078e291bd9..0e891d54a9f6 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -27,10 +27,13 @@ all() -> test_oauth_providers_signing_keys, test_without_endpoint_params, test_with_endpoint_params, + test_with_invalid_endpoint_params, test_without_resource_servers, test_with_one_resource_server, test_with_many_resource_servers, - test_resource_servers_attributes + test_resource_servers_attributes, + test_invalid_oauth_providers_endpoint_params, + test_without_oauth_providers_with_endpoint_params ]. @@ -46,6 +49,14 @@ test_without_endpoint_params(_) -> #{} = translate_endpoint_params("token_endpoint_params", []), #{} = translate_endpoint_params("authorization_endpoint_params", []). +test_with_invalid_endpoint_params(_) -> + try translate_endpoint_params("discovery_endpoint_params", [ + {["auth_oauth2","discovery_endpoint_params"], "some-value1"}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. + test_with_endpoint_params(_) -> Conf = [ {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, @@ -60,6 +71,30 @@ test_with_endpoint_params(_) -> #{ <<"resource">> := <<"some-resource">>} = translate_endpoint_params("authorization_endpoint_params", Conf). +test_invalid_oauth_providers_endpoint_params() -> + try oauth2_schema:translate_oauth_providers([ + {["auth_oauth2","oauth_providers", "X", "discovery_endpoint_params"], ""}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. +test_without_oauth_providers_with_endpoint_params(_) -> + Conf = [ + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], "some-value1"}, + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param2"], "some-value2"}, + {["auth_oauth2","oauth_providers", "B", "token_endpoint_params","audience"], "some-audience"}, + {["auth_oauth2","oauth_providers", "C", "authorization_endpoint_params","resource"], "some-resource"} + ], + + #{ + <<"A">> := [{discovery_endpoint_params, + #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> }}], + <<"B">> := [{token_endpoint_params, + #{ <<"audience">> := <<"some-audience">>}}], + <<"C">> := [{authorization_endpoint_params, + #{ <<"resource">> := <<"some-resource">>}}] + } = translate_oauth_providers(Conf). + test_with_one_oauth_provider(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} ], From 9f11f25b9dc6efbd57252d6da77e173a2bd14d81 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 09:36:47 +0200 Subject: [PATCH 30/64] Fix test --- deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 0e891d54a9f6..7c7afa37f41f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -71,13 +71,14 @@ test_with_endpoint_params(_) -> #{ <<"resource">> := <<"some-resource">>} = translate_endpoint_params("authorization_endpoint_params", Conf). -test_invalid_oauth_providers_endpoint_params() -> +test_invalid_oauth_providers_endpoint_params(_) -> try oauth2_schema:translate_oauth_providers([ {["auth_oauth2","oauth_providers", "X", "discovery_endpoint_params"], ""}]) of _ -> {throw, should_have_failed} catch _ -> ok end. + test_without_oauth_providers_with_endpoint_params(_) -> Conf = [ {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], "some-value1"}, From eb2fbc6d9b1341fbc1bd1ae423c1fb6013df9f5f Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 10:47:08 +0200 Subject: [PATCH 31/64] Improve format --- deps/rabbitmq_auth_backend_oauth2/src/rar.erl | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rar.erl b/deps/rabbitmq_auth_backend_oauth2/src/rar.erl index 4e1c128a474c..ee207a377092 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rar.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rar.erl @@ -9,9 +9,9 @@ -module(rar). -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]). --import(uaa_jwt, [get_scope/1, set_scope/2]). -define(AUTHORIZATION_DETAILS_CLAIM, <<"authorization_details">>). -define(RAR_ACTIONS_FIELD, <<"actions">>). @@ -23,13 +23,26 @@ -define(RAR_QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). -define(RAR_EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). -define(RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). --define(RAR_LOCATION_ATTRIBUTES, [?RAR_CLUSTER_LOCATION_ATTRIBUTE, ?RAR_VHOST_LOCATION_ATTRIBUTE, - ?RAR_QUEUE_LOCATION_ATTRIBUTE, ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). - --define(RAR_ALLOWED_TAG_VALUES, [<<"monitoring">>, <<"administrator">>, <<"management">>, <<"policymaker">> ]). --define(RAR_ALLOWED_ACTION_VALUES, [<<"read">>, <<"write">>, <<"configure">>, <<"monitoring">>, - <<"administrator">>, <<"management">>, <<"policymaker">> ]). - +-define(RAR_LOCATION_ATTRIBUTES, [ + ?RAR_CLUSTER_LOCATION_ATTRIBUTE, + ?RAR_VHOST_LOCATION_ATTRIBUTE, + ?RAR_QUEUE_LOCATION_ATTRIBUTE, + ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, + ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). + +-define(RAR_ALLOWED_TAG_VALUES, [ + <<"monitoring">>, + <<"administrator">>, + <<"management">>, + <<"policymaker">> ]). +-define(RAR_ALLOWED_ACTION_VALUES, [ + <<"read">>, + <<"write">>, + <<"configure">>, + <<"monitoring">>, + <<"administrator">>, + <<"management">>, + <<"policymaker">> ]). -spec has_rich_auth_request_scopes(Payload::map()) -> boolean(). has_rich_auth_request_scopes(Payload) -> @@ -50,10 +63,6 @@ extract_scopes_from_rich_auth_request(ResourceServer, ExistingScopes = get_scope(Payload), set_scope(AdditionalScopes ++ ExistingScopes, Payload). - - - - put_location_attribute(Attribute, Map) -> put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). From f61ba39b007dddb5b2cb331d963d72791702cf91 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 10:59:03 +0200 Subject: [PATCH 32/64] Add explicitly sub preferred_username --- selenium/test/oauth/rabbitmq.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selenium/test/oauth/rabbitmq.conf b/selenium/test/oauth/rabbitmq.conf index 02b0227d4bf8..f101bae111c0 100644 --- a/selenium/test/oauth/rabbitmq.conf +++ b/selenium/test/oauth/rabbitmq.conf @@ -10,6 +10,6 @@ auth_oauth2.resource_server_id = rabbitmq auth_oauth2.preferred_username_claims.1 = user_name auth_oauth2.preferred_username_claims.2 = preferred_username auth_oauth2.preferred_username_claims.3 = email - +auth_oauth2.preferred_username_claims.4 = sub loopback_users = none From 0de61a973c19aeab6116799619d0d448673d6683 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 20:38:22 +0200 Subject: [PATCH 33/64] WIP Build discovery_endpoint --- deps/oauth2_client/include/types.hrl | 70 +++++----- deps/oauth2_client/src/oauth2_client.erl | 157 +++++++++++++++-------- deps/oauth2_client/test/system_SUITE.erl | 18 ++- deps/oauth2_client/test/unit_SUITE.erl | 71 +++++++++- 4 files changed, 214 insertions(+), 102 deletions(-) diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl index 42f90803efcf..0592ce582a48 100644 --- a/deps/oauth2_client/include/types.hrl +++ b/deps/oauth2_client/include/types.hrl @@ -11,64 +11,62 @@ -type oauth_provider_id() :: root | binary(). -record(openid_configuration, { - issuer :: option(uri_string:uri_string()), - token_endpoint :: option(uri_string:uri_string()), - authorization_endpoint :: option(uri_string:uri_string()), - end_session_endpoint :: option(uri_string:uri_string()), - jwks_uri :: option(uri_string:uri_string()) - }). + issuer :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()) +}). -type openid_configuration() :: #openid_configuration{}. -record(oauth_provider, { - id :: oauth_provider_id(), - issuer :: option(uri_string:uri_string()), - discovery_endpoint_path :: option(uri_string:uri_string()), - discovery_endpoint_params :: option([tuple()]), - token_endpoint :: option(uri_string:uri_string()), - token_endpoint_params :: option([tuple()]), - authorization_endpoint :: option(uri_string:uri_string()), - authorization_endpoint_params :: option([tuple()]), - end_session_endpoint :: option(uri_string:uri_string()), - jwks_uri :: option(uri_string:uri_string()), - jwks_uri_params :: option([tuple()]), - ssl_options :: option(list()) - }). + id :: oauth_provider_id(), + issuer :: option(uri_string:uri_string()), + discovery_endpoint :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()), + ssl_options :: option(list()) +}). + +-type query_list() :: [{unicode:chardata(), unicode:chardata() | true}]. -type oauth_provider() :: #oauth_provider{}. -record(access_token_request, { - client_id :: string() | binary(), - client_secret :: string() | binary(), - scope :: string() | binary() | undefined, - timeout :: option(integer()) - }). + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: string() | binary() | undefined, + timeout :: option(integer()) +}). -type access_token_request() :: #access_token_request{}. -record(successful_access_token_response, { - access_token :: binary(), - token_type :: binary(), - refresh_token :: option(binary()), % A refresh token SHOULD NOT be included + access_token :: binary(), + token_type :: binary(), + refresh_token :: option(binary()), % A refresh token SHOULD NOT be included % .. for client-credentials flow. % https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 - expires_in :: option(integer()) + expires_in :: option(integer()) }). -type successful_access_token_response() :: #successful_access_token_response{}. -record(unsuccessful_access_token_response, { - error :: integer(), - error_description :: binary() | string() | undefined + error :: integer(), + error_description :: binary() | string() | undefined }). -type unsuccessful_access_token_response() :: #unsuccessful_access_token_response{}. -record(refresh_token_request, { - client_id :: string() | binary(), - client_secret :: string() | binary(), - scope :: string() | binary() | undefined, - refresh_token :: binary(), - timeout :: option(integer()) - }). + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: string() | binary() | undefined, + refresh_token :: binary(), + timeout :: option(integer()) +}). -type refresh_token_request() :: #refresh_token_request{}. diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index e7380d28f728..868354697e3a 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -8,7 +8,9 @@ -export([get_access_token/2, get_expiration_time/1, refresh_access_token/2, get_oauth_provider/1, get_oauth_provider/2, - get_openid_configuration/2, get_openid_configuration/3, + get_openid_configuration/2, + build_openid_discovery_endpoint/3, build_openid_discovery_endpoint/1, + build_openid_discovery_endpoint/2, merge_openid_configuration/2, merge_oauth_provider/2, extract_ssl_options_as_list/1, @@ -47,28 +49,64 @@ refresh_access_token(OAuthProvider, Request) -> append_paths(Path1, Path2) -> erlang:iolist_to_binary([Path1, Path2]). --spec get_openid_configuration(uri_string:uri_string(), erlang:iodata() | <<>>, +-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string()) + -> uri_string:uri_string(). +build_openid_discovery_endpoint(Issuer) -> + build_openid_discovery_endpoint(Issuer, undefined, undefined). + +-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), + OpenIdConfigurationPath :: uri_string:uri_string() | undefined) + -> uri_string:uri_string(). +build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath) -> + build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, undefined). + +-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), + OpenIdConfigurationPath :: uri_string:uri_string() | undefined, + Params :: query_list()) -> uri_string:uri_string(). + +build_openid_discovery_endpoint(Issuer, undefined, Params) -> + build_openid_discovery_endpoint(Issuer, ?DEFAULT_OPENID_CONFIGURATION_PATH, + Params); +build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, Params) -> + URLMap0 = uri_string:parse(Issuer), + OpenIdPath = ensure_leading_path_separator(OpenIdConfigurationPath), + URLMap1 = URLMap0#{ + path := case maps:get(path, URLMap0) of + "/" -> OpenIdPath; + "" -> OpenIdPath; + [] -> OpenIdPath; + P -> append_paths(drop_trailing_path_separator(P), OpenIdPath) + end + }, + uri_string:recompose( + case {Params, maps:get(query, URLMap1, undefined)} of + {undefined, undefined} -> + URLMap1; + {_, undefined} -> + URLMap1#{query => uri_string:compose_query(Params)}; + {_, Q} -> + URLMap1#{query => uri_string:compose_query(Q ++ Params)} + end). +ensure_leading_path_separator(Path) -> + case string:slice(Path, 0, 1) of + "/" -> Path; + _ -> "/" ++ Path + end. +drop_trailing_path_separator(Path) -> + case string:slice(Path, string:len(Path)-1, 1) of + "/" -> lists:droplast(Path); + _ -> Path + end. + +-spec get_openid_configuration(DiscoveryEndpoint :: uri_string:uri_string(), ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}. -get_openid_configuration(IssuerURI, OpenIdConfigurationPath, TLSOptions) -> - URLMap = uri_string:parse(IssuerURI), - Path = case maps:get(path, URLMap) of - "/" -> OpenIdConfigurationPath; - "" -> OpenIdConfigurationPath; - P -> append_paths(P, OpenIdConfigurationPath) - end, - URL = uri_string:resolve(Path, IssuerURI), - rabbit_log:debug("get_openid_configuration issuer URL ~p (~p)", [URL, +get_openid_configuration(DiscoverEndpoint, TLSOptions) -> + rabbit_log:debug("get_openid_configuration from ~p (~p)", [DiscoverEndpoint, format_ssl_options(TLSOptions)]), Options = [], - Response = httpc:request(get, {URL, []}, TLSOptions, Options), + Response = httpc:request(get, {DiscoverEndpoint, []}, TLSOptions, Options), parse_openid_configuration_response(Response). --spec get_openid_configuration(uri_string:uri_string(), ssl:tls_option() | []) -> - {ok, openid_configuration()} | {error, term()}. -get_openid_configuration(IssuerURI, TLSOptions) -> - get_openid_configuration(IssuerURI, ?DEFAULT_OPENID_CONFIGURATION_PATH, TLSOptions). -% Returns {ok, with_modidified_oauth_provider} or {ok} if oauth_provider was -% not modified -spec merge_openid_configuration(openid_configuration(), oauth_provider()) -> oauth_provider(). merge_openid_configuration(OpendIdConfiguration, OAuthProvider) -> @@ -179,43 +217,37 @@ update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) -> do_update_oauth_provider_endpoints_configuration(OAuthProvider) -> case OAuthProvider#oauth_provider.token_endpoint of - undefined -> - do_nothing; - TokenEndpoint -> - application:set_env(rabbitmq_auth_backend_oauth2, token_endpoint, TokenEndpoint) + undefined -> do_nothing; + TokenEndpoint -> set_env(token_endpoint, TokenEndpoint) end, case OAuthProvider#oauth_provider.authorization_endpoint of - undefined -> - do_nothing; - AuthzEndpoint -> - application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, AuthzEndpoint) + undefined -> do_nothing; + AuthzEndpoint -> set_env(authorization_endpoint, AuthzEndpoint) end, case OAuthProvider#oauth_provider.end_session_endpoint of - undefined -> - do_nothing; - EndSessionEndpoint -> - application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, EndSessionEndpoint) + undefined -> do_nothing; + EndSessionEndpoint -> set_env(end_session_endpoint, EndSessionEndpoint) end, - List = application:get_env(rabbitmq_auth_backend_oauth2, key_config, []), + List = get_env(key_config, []), ModifiedList = case OAuthProvider#oauth_provider.jwks_uri of undefined -> List; JwksEndPoint -> [{jwks_url, JwksEndPoint} | proplists:delete(jwks_url, List)] end, - application:set_env(rabbitmq_auth_backend_oauth2, key_config, ModifiedList), + set_env(key_config, ModifiedList), rabbit_log:debug("Updated oauth_provider details: ~p ", [ format_oauth_provider(OAuthProvider)]), OAuthProvider. do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) -> - OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), + OAuthProviders = get_env(oauth_providers, #{}), Proplist = maps:get(OAuthProviderId, OAuthProviders), ModifiedOAuthProviders = maps:put(OAuthProviderId, merge_oauth_provider(OAuthProvider, Proplist), OAuthProviders), - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, ModifiedOAuthProviders), + set_env(oauth_providers, ModifiedOAuthProviders), rabbit_log:debug("Replaced oauth_providers "), OAuthProvider. use_global_locks_on_all_nodes() -> - case application:get_env(rabbitmq_auth_backend_oauth2, use_global_locks, true) of + case get_env(use_global_locks, true) of true -> {rabbit_nodes:list_running(), rabbit_nodes:lock_retries()}; _ -> {} end. @@ -246,7 +278,7 @@ unlock(LockId) -> -spec get_oauth_provider(list()) -> {ok, oauth_provider()} | {error, any()}. get_oauth_provider(ListOfRequiredAttributes) -> - case application:get_env(rabbitmq_auth_backend_oauth2, default_oauth_provider) of + case get_env(default_oauth_provider) of undefined -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes); {ok, DefaultOauthProviderId} -> rabbit_log:debug("Using default_oauth_provider ~p", [DefaultOauthProviderId]), @@ -359,18 +391,18 @@ find_missing_attributes(#oauth_provider{} = OAuthProvider, RequiredAttributes) - intersection(Filtered, RequiredAttributes). lookup_oauth_provider_from_keyconfig() -> - Issuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined), - TokenEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined), - AuthorizationEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, undefined), - EndSessionEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, undefined), - Map = maps:from_list(application:get_env(rabbitmq_auth_backend_oauth2, key_config, [])), + Map = maps:from_list(get_env(key_config, [])), + Issuer = get_env(issuer), + DiscoverEndpoint = build_openid_discovery_endpoint(Issuer, + get_env(discovery_endpoint_path), get_env(discovery_endpoint_params)), #oauth_provider{ id = root, issuer = Issuer, + discovery_endpoint = DiscoverEndpoint, jwks_uri = maps:get(jwks_url, Map, undefined), %% jwks_url not uri . _url is the legacy name - token_endpoint = TokenEndpoint, - authorization_endpoint = AuthorizationEndpoint, - end_session_endpoint = EndSessionEndpoint, + token_endpoint = get_env(token_endpoint), + authorization_endpoint = get_env(authorization_endpoint), + end_session_endpoint = get_env(end_session_endpoint), ssl_options = extract_ssl_options_as_list(Map) }. @@ -431,7 +463,7 @@ get_verify_or_peer_verification(Ssl_options, Default) -> end. lookup_oauth_provider_config(OAuth2ProviderId) -> - case application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers) of + case get_env(oauth_providers) of undefined -> {error, oauth_providers_not_found}; {ok, MapOfProviders} when is_map(MapOfProviders) -> case maps:get(OAuth2ProviderId, MapOfProviders, undefined) of @@ -522,14 +554,28 @@ map_to_unsuccessful_access_token_response(Map) -> error_description = maps:get(?RESPONSE_ERROR_DESCRIPTION, Map, undefined) }. map_to_oauth_provider(PropList) when is_list(PropList) -> + Issuer = proplists:get_value(issuer, PropList), + DiscoveryEndpoint = build_openid_discovery_endpoint(Issuer, + proplists:get_value(discovery_endpoint_path, PropList), + proplists:get_value(discovery_endpoint_params, PropList)), #oauth_provider{ - id = proplists:get_value(id, PropList), - issuer = proplists:get_value(issuer, PropList), - token_endpoint = proplists:get_value(token_endpoint, PropList), - authorization_endpoint = proplists:get_value(authorization_endpoint, PropList, undefined), - end_session_endpoint = proplists:get_value(end_session_endpoint, PropList, undefined), - jwks_uri = proplists:get_value(jwks_uri, PropList, undefined), - ssl_options = extract_ssl_options_as_list(maps:from_list(proplists:get_value(https, PropList, []))) + id = + proplists:get_value(id, PropList), + issuer = + Issuer, + discovery_endpoint = + DiscoveryEndpoint, + token_endpoint = + proplists:get_value(token_endpoint, PropList), + authorization_endpoint = + proplists:get_value(authorization_endpoint, PropList, undefined), + end_session_endpoint = + proplists:get_value(end_session_endpoint, PropList, undefined), + jwks_uri = + proplists:get_value(jwks_uri, PropList, undefined), + ssl_options = + extract_ssl_options_as_list(maps:from_list( + proplists:get_value(https, PropList, []))) }. map_to_access_token_response(Code, Reason, Headers, Body) -> case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of @@ -581,3 +627,10 @@ format_oauth_provider(OAuthProvider) -> OAuthProvider#oauth_provider.end_session_endpoint, OAuthProvider#oauth_provider.jwks_uri, format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])). + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl index a0be9dd3976d..fd1bf98322c7 100644 --- a/deps/oauth2_client/test/system_SUITE.erl +++ b/deps/oauth2_client/test/system_SUITE.erl @@ -11,6 +11,9 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("oauth2_client.hrl"). +-import(oauth2_client, [ + build_openid_discovery_endpoint/1, + build_openid_discovery_endpoint/2]). -compile(export_all). @@ -281,7 +284,7 @@ get_openid_configuration(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, ActualOpenId} = oauth2_client:get_openid_configuration( - build_issuer("https"), + build_openid_discovery_endpoint(build_issuer("https")), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, ActualOpenId). @@ -303,7 +306,7 @@ get_openid_configuration_returns_partial_payload(Config) -> SslOptions = [{ssl, ExpectedOAuthProvider0#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( - build_issuer("https"), + build_openid_discovery_endpoint(build_issuer("https")), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). @@ -312,7 +315,7 @@ get_openid_configuration_using_path(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( - build_issuer("https", ?ISSUER_PATH), + build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH)), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId,Actual). @@ -320,8 +323,8 @@ get_openid_configuration_using_path_and_custom_endpoint(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( - build_issuer("https", ?ISSUER_PATH), - ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT, + build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). @@ -329,8 +332,8 @@ get_openid_configuration_using_custom_endpoint(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( - build_issuer("https"), - ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT, + build_openid_discovery_endpoint(build_issuer("https"), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). @@ -546,6 +549,7 @@ get_oauth_provider_given_oauth_provider_id(Config) -> %%% HELPERS + build_issuer(Scheme) -> build_issuer(Scheme, ""). build_issuer(Scheme, Path) -> diff --git a/deps/oauth2_client/test/unit_SUITE.erl b/deps/oauth2_client/test/unit_SUITE.erl index ab632ceedc68..e4216036db20 100644 --- a/deps/oauth2_client/test/unit_SUITE.erl +++ b/deps/oauth2_client/test/unit_SUITE.erl @@ -15,13 +15,16 @@ -compile(export_all). +-import(oauth2_client, [build_openid_discovery_endpoint/3]). + -define(UTIL_MOD, oauth2_client_test_util). all() -> [ - {group, ssl_options}, - {group, merge}, - {group, get_expiration_time} + build_openid_discovery_endpoint, + {group, ssl_options}, + {group, merge}, + {group, get_expiration_time} ]. groups() -> @@ -45,8 +48,38 @@ groups() -> ]} ]. +build_openid_discovery_endpoint(_) -> + Issuer = "https://issuer", + ?assertEqual(Issuer ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(Issuer, undefined, undefined)), + + IssuerWithPath = "https://issuer/v2", + ?assertEqual(IssuerWithPath ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(IssuerWithPath, undefined, undefined)), + + IssuerWithPathAndExtraPathSeparator = "https://issuer/v2/", + ?assertEqual("https://issuer/v2" ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(IssuerWithPathAndExtraPathSeparator, + undefined, undefined)), + + IssuerWithPath = "https://issuer/v2", + CustomPath = "/.well-known/other", + ?assertEqual(IssuerWithPath ++ CustomPath, + build_openid_discovery_endpoint(IssuerWithPath, CustomPath, undefined)), + + IssuerWithPath = "https://issuer/v2", + CustomPath = "/.well-known/other", + WithParams = [{"param1", "v1"}, {"param2", "v2"}], + ?assertEqual("https://issuer/v2/.well-known/other?param1=v1¶m2=v2", + build_openid_discovery_endpoint(IssuerWithPath, CustomPath, WithParams)). + + merge_oauth_provider(_) -> - OAuthProvider = #oauth_provider{id = "some_id", ssl_options = [ {verify, verify_none} ]}, + OAuthProvider = #oauth_provider{ + id = "some_id", + issuer = "https://issuer", + discovery_endpoint = "https://issuer/.well-known/openid_configuration", + ssl_options = [ {verify, verify_none} ]}, Proplist = [], Proplist1 = oauth2_client:merge_oauth_provider(OAuthProvider, Proplist), ?assertEqual([], Proplist), @@ -74,11 +107,25 @@ merge_oauth_provider(_) -> {end_session_endpoint, OAuthProvider4#oauth_provider.end_session_endpoint}, {authorization_endpoint, OAuthProvider4#oauth_provider.authorization_endpoint}, {token_endpoint, OAuthProvider4#oauth_provider.token_endpoint}], - Proplist5). + Proplist5), + + % ensure id, issuer, ssl_options and discovery_endpoint are not affected + ?assertEqual(OAuthProvider#oauth_provider.id, + OAuthProvider4#oauth_provider.id), + ?assertEqual(OAuthProvider#oauth_provider.issuer, + OAuthProvider4#oauth_provider.issuer), + ?assertEqual(OAuthProvider#oauth_provider.discovery_endpoint, + OAuthProvider4#oauth_provider.discovery_endpoint), + ?assertEqual(OAuthProvider#oauth_provider.ssl_options, + OAuthProvider4#oauth_provider.ssl_options). merge_openid_configuration(_) -> OpenIdConfiguration = #openid_configuration{}, - OAuthProvider = #oauth_provider{id = "some_id", ssl_options = [ {verify, verify_none} ]}, + OAuthProvider = #oauth_provider{ + id = "some_id", + issuer = "https://issuer", + discovery_endpoint = "https://issuer/.well-known/openid_configuration", + ssl_options = [ {verify, verify_none} ]}, OAuthProvider1 = oauth2_client:merge_openid_configuration( OpenIdConfiguration, OAuthProvider), ?assertEqual(OAuthProvider#oauth_provider.id, OAuthProvider1#oauth_provider.id), @@ -125,7 +172,17 @@ merge_openid_configuration(_) -> ?assertEqual(OpenIdConfiguration2#openid_configuration.end_session_endpoint, OAuthProvider5#oauth_provider.end_session_endpoint), ?assertEqual(OpenIdConfiguration1#openid_configuration.jwks_uri, - OAuthProvider5#oauth_provider.jwks_uri). + OAuthProvider5#oauth_provider.jwks_uri), + + % ensure id, issuer, ssl_options and discovery_endpoint are not affected + ?assertEqual(OAuthProvider#oauth_provider.id, + OAuthProvider5#oauth_provider.id), + ?assertEqual(OAuthProvider#oauth_provider.issuer, + OAuthProvider5#oauth_provider.issuer), + ?assertEqual(OAuthProvider#oauth_provider.discovery_endpoint, + OAuthProvider5#oauth_provider.discovery_endpoint), + ?assertEqual(OAuthProvider#oauth_provider.ssl_options, + OAuthProvider5#oauth_provider.ssl_options). no_ssl_options_triggers_verify_peer(_) -> From 462c7e55467ca6a5c5ff5905354e9379b79a4f19 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 21:25:44 +0200 Subject: [PATCH 34/64] Fix test case --- deps/oauth2_client/src/oauth2_client.erl | 35 +++++++++++------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index 868354697e3a..cb1e62387550 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -109,12 +109,7 @@ get_openid_configuration(DiscoverEndpoint, TLSOptions) -> -spec merge_openid_configuration(openid_configuration(), oauth_provider()) -> oauth_provider(). -merge_openid_configuration(OpendIdConfiguration, OAuthProvider) -> - OAuthProvider0 = case OpendIdConfiguration#openid_configuration.issuer of - undefined -> OAuthProvider; - Issuer -> - OAuthProvider#oauth_provider{issuer = Issuer} - end, +merge_openid_configuration(OpendIdConfiguration, OAuthProvider0) -> OAuthProvider1 = case OpendIdConfiguration#openid_configuration.token_endpoint of undefined -> OAuthProvider0; TokenEndpoint -> @@ -280,7 +275,7 @@ unlock(LockId) -> get_oauth_provider(ListOfRequiredAttributes) -> case get_env(default_oauth_provider) of undefined -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes); - {ok, DefaultOauthProviderId} -> + DefaultOauthProviderId -> rabbit_log:debug("Using default_oauth_provider ~p", [DefaultOauthProviderId]), get_oauth_provider(DefaultOauthProviderId, ListOfRequiredAttributes) end. @@ -292,12 +287,12 @@ get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> [] -> {ok, OAuthProvider}; _ = MissingAttributes -> - rabbit_log:debug("OauthProvider has following missing attributes ~p", [MissingAttributes]), - Result2 = case OAuthProvider#oauth_provider.issuer of + rabbit_log:debug("Looking up missing attributes ~p ...", [MissingAttributes]), + Result2 = case OAuthProvider#oauth_provider.discovery_endpoint of undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; - Issuer -> - rabbit_log:debug("Downloading oauth_provider using issuer ~p", [Issuer]), - case get_openid_configuration(Issuer, get_ssl_options_if_any(OAuthProvider)) of + URL -> + rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]), + case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of {ok, OpenIdConfiguration} -> {ok, update_oauth_provider_endpoints_configuration( merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; @@ -341,12 +336,12 @@ get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAu {ok, OAuthProvider}; _ = MissingAttributes -> rabbit_log:debug("OauthProvider has following missing attributes ~p", [MissingAttributes]), - Result2 = case OAuthProvider#oauth_provider.issuer of + Result2 = case OAuthProvider#oauth_provider.discovery_endpoint of undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; - Issuer -> - rabbit_log:debug("Downloading oauth_provider ~p using issuer ~p", - [OAuthProviderId, Issuer]), - case get_openid_configuration(Issuer, get_ssl_options_if_any(OAuthProvider)) of + URL -> + rabbit_log:debug("Downloading oauth_provider ~p using ~p ...", + [OAuthProviderId, URL]), + case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of {ok, OpenIdConfiguration} -> {ok, update_oauth_provider_endpoints_configuration(OAuthProviderId, merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; @@ -465,7 +460,7 @@ get_verify_or_peer_verification(Ssl_options, Default) -> lookup_oauth_provider_config(OAuth2ProviderId) -> case get_env(oauth_providers) of undefined -> {error, oauth_providers_not_found}; - {ok, MapOfProviders} when is_map(MapOfProviders) -> + MapOfProviders when is_map(MapOfProviders) -> case maps:get(OAuth2ProviderId, MapOfProviders, undefined) of undefined -> {error, {oauth_provider_not_found, OAuth2ProviderId}}; @@ -617,11 +612,13 @@ format_oauth_provider_id(Id) -> binary_to_list(Id). -spec format_oauth_provider(oauth_provider()) -> string(). format_oauth_provider(OAuthProvider) -> - lists:flatten(io_lib:format("{id: ~p, issuer: ~p, token_endpoint: ~p, " ++ + lists:flatten(io_lib:format("{id: ~p, issuer: ~p, discovery_endpoint: ~p, " ++ + " token_endpoint: ~p, " ++ "authorization_endpoint: ~p, end_session_endpoint: ~p, " ++ "jwks_uri: ~p, ssl_options: ~p }", [ format_oauth_provider_id(OAuthProvider#oauth_provider.id), OAuthProvider#oauth_provider.issuer, + OAuthProvider#oauth_provider.discovery_endpoint, OAuthProvider#oauth_provider.token_endpoint, OAuthProvider#oauth_provider.authorization_endpoint, OAuthProvider#oauth_provider.end_session_endpoint, From 9ec93c98f79923870d416b83e4b6f35683eaed5e Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Thu, 19 Sep 2024 21:48:16 +0200 Subject: [PATCH 35/64] Clean up ct:log statements WIP address a dialyzer error --- deps/oauth2_client/src/oauth2_client.erl | 16 +--------------- deps/oauth2_client/test/system_SUITE.erl | 16 +++++++--------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index cb1e62387550..35e2db93656b 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -9,8 +9,7 @@ refresh_access_token/2, get_oauth_provider/1, get_oauth_provider/2, get_openid_configuration/2, - build_openid_discovery_endpoint/3, build_openid_discovery_endpoint/1, - build_openid_discovery_endpoint/2, + build_openid_discovery_endpoint/3, merge_openid_configuration/2, merge_oauth_provider/2, extract_ssl_options_as_list/1, @@ -49,17 +48,6 @@ refresh_access_token(OAuthProvider, Request) -> append_paths(Path1, Path2) -> erlang:iolist_to_binary([Path1, Path2]). --spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string()) - -> uri_string:uri_string(). -build_openid_discovery_endpoint(Issuer) -> - build_openid_discovery_endpoint(Issuer, undefined, undefined). - --spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), - OpenIdConfigurationPath :: uri_string:uri_string() | undefined) - -> uri_string:uri_string(). -build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath) -> - build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, undefined). - -spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), OpenIdConfigurationPath :: uri_string:uri_string() | undefined, Params :: query_list()) -> uri_string:uri_string(). @@ -72,8 +60,6 @@ build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, Params) -> OpenIdPath = ensure_leading_path_separator(OpenIdConfigurationPath), URLMap1 = URLMap0#{ path := case maps:get(path, URLMap0) of - "/" -> OpenIdPath; - "" -> OpenIdPath; [] -> OpenIdPath; P -> append_paths(drop_trailing_path_separator(P), OpenIdPath) end diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl index fd1bf98322c7..1d105393ecab 100644 --- a/deps/oauth2_client/test/system_SUITE.erl +++ b/deps/oauth2_client/test/system_SUITE.erl @@ -12,8 +12,7 @@ -include_lib("oauth2_client.hrl"). -import(oauth2_client, [ - build_openid_discovery_endpoint/1, - build_openid_discovery_endpoint/2]). + build_openid_discovery_endpoint/3]). -compile(export_all). @@ -150,7 +149,6 @@ init_per_group(_, Config) -> get_http_oauth_server_expectations(TestCase, Config) -> case ?config(TestCase, Config) of undefined -> - ct:log("get_openid_configuration_http_expectation : ~p", [get_openid_configuration_http_expectation(TestCase)]), [ {token_endpoint, build_http_mock_behaviour(build_http_access_token_request(), build_http_200_access_token_response())}, {get_openid_configuration, get_openid_configuration_http_expectation(TestCase)} @@ -247,7 +245,6 @@ init_per_testcase(TestCase, Config) -> case ?config(group, Config) of https -> - ct:log("Start https with expectations ~p", [ListOfExpectations]), start_https_oauth_server(?AUTH_PORT, ?config(rmq_certsdir, Config), ListOfExpectations); _ -> @@ -280,6 +277,12 @@ end_per_group(with_default_oauth_provider, Config) -> end_per_group(_, Config) -> Config. +build_openid_discovery_endpoint(Issuer) -> + build_openid_discovery_endpoint(Issuer, undefined, undefined). + +build_openid_discovery_endpoint(Issuer, Path) -> + build_openid_discovery_endpoint(Issuer, Path, undefined). + get_openid_configuration(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], @@ -468,7 +471,6 @@ verify_get_oauth_provider_returns_default_oauth_provider(DefaultOAuthProviderId) {ok, OAuthProvider2} = oauth2_client:get_oauth_provider(DefaultOAuthProviderId, [issuer, token_endpoint, jwks_uri]), - ct:log("verify_get_oauth_provider_returns_default_oauth_provider ~p vs ~p", [OAuthProvider1, OAuthProvider2]), ?assertEqual(OAuthProvider1, OAuthProvider2). get_oauth_provider(Config) -> @@ -622,8 +624,6 @@ start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations {'_', [{Path, oauth_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} ]), - ct:log("start_https_oauth_server with expectation list : ~p -> dispatch: ~p", - [Expectations, Dispatch]), {ok, _} = cowboy:start_tls( mock_http_auth_listener, [{port, Port}, @@ -634,8 +634,6 @@ start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations start_https_oauth_server(Port, CertsDir, #{request := #{path := Path}} = Expected) -> Dispatch = cowboy_router:compile([{'_', [{Path, oauth_http_mock, Expected}]}]), - ct:log("start_https_oauth_server with expectation : ~p -> dispatch: ~p", - [Expected, Dispatch]), {ok, _} = cowboy:start_tls( mock_http_auth_listener, [{port, Port}, From 06edb55dbdf768bf2ef7ed823cee22ec78bcea2a Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 20 Sep 2024 09:58:12 +0200 Subject: [PATCH 36/64] Fix dialyzer error --- deps/oauth2_client/src/oauth2_client.erl | 162 ++++++++++++----------- 1 file changed, 85 insertions(+), 77 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index 35e2db93656b..f31bbe445083 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -17,8 +17,10 @@ ]). -include("oauth2_client.hrl"). + -spec get_access_token(oauth_provider(), access_token_request()) -> - {ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}. + {ok, successful_access_token_response()} | + {error, unsuccessful_access_token_response() | any()}. get_access_token(OAuthProvider, Request) -> rabbit_log:debug("get_access_token using OAuthProvider:~p and client_id:~p", [OAuthProvider, Request#access_token_request.client_id]), @@ -33,7 +35,8 @@ get_access_token(OAuthProvider, Request) -> parse_access_token_response(Response). -spec refresh_access_token(oauth_provider(), refresh_token_request()) -> - {ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}. + {ok, successful_access_token_response()} | + {error, unsuccessful_access_token_response() | any()}. refresh_access_token(OAuthProvider, Request) -> URL = OAuthProvider#oauth_provider.token_endpoint, Header = [], @@ -50,8 +53,9 @@ append_paths(Path1, Path2) -> -spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), OpenIdConfigurationPath :: uri_string:uri_string() | undefined, - Params :: query_list()) -> uri_string:uri_string(). + Params :: query_list()) -> uri_string:uri_string() | undefined. +build_openid_discovery_endpoint(undefined, _, _) -> undefined; build_openid_discovery_endpoint(Issuer, undefined, Params) -> build_openid_discovery_endpoint(Issuer, ?DEFAULT_OPENID_CONFIGURATION_PATH, Params); @@ -95,23 +99,23 @@ get_openid_configuration(DiscoverEndpoint, TLSOptions) -> -spec merge_openid_configuration(openid_configuration(), oauth_provider()) -> oauth_provider(). -merge_openid_configuration(OpendIdConfiguration, OAuthProvider0) -> - OAuthProvider1 = case OpendIdConfiguration#openid_configuration.token_endpoint of +merge_openid_configuration(OpenId, OAuthProvider0) -> + OAuthProvider1 = case OpenId#openid_configuration.token_endpoint of undefined -> OAuthProvider0; TokenEndpoint -> OAuthProvider0#oauth_provider{token_endpoint = TokenEndpoint} end, - OAuthProvider2 = case OpendIdConfiguration#openid_configuration.authorization_endpoint of + OAuthProvider2 = case OpenId#openid_configuration.authorization_endpoint of undefined -> OAuthProvider1; AuthorizationEndpoint -> OAuthProvider1#oauth_provider{authorization_endpoint = AuthorizationEndpoint} end, - OAuthProvider3 = case OpendIdConfiguration#openid_configuration.end_session_endpoint of + OAuthProvider3 = case OpenId#openid_configuration.end_session_endpoint of undefined -> OAuthProvider2; EndSessionEndpoint -> OAuthProvider2#oauth_provider{end_session_endpoint = EndSessionEndpoint} end, - case OpendIdConfiguration#openid_configuration.jwks_uri of + case OpenId#openid_configuration.jwks_uri of undefined -> OAuthProvider3; JwksUri -> OAuthProvider3#oauth_provider{jwks_uri = JwksUri} @@ -146,7 +150,8 @@ parse_openid_configuration_response({error, Reason}) -> parse_openid_configuration_response({ok,{{_,Code,Reason}, Headers, Body}}) -> map_response_to_openid_configuration(Code, Reason, Headers, Body). map_response_to_openid_configuration(Code, Reason, Headers, Body) -> - case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of + case decode_body(proplists:get_value("content-type", Headers, + ?CONTENT_JSON), Body) of {error, {error, InternalError}} -> {error, InternalError}; {error, _} = Error -> @@ -162,13 +167,16 @@ map_to_openid_configuration(Map) -> #openid_configuration{ issuer = maps:get(?RESPONSE_ISSUER, Map), token_endpoint = maps:get(?RESPONSE_TOKEN_ENDPOINT, Map, undefined), - authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, Map, undefined), - end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, Map, undefined), + authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, + Map, undefined), + end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, + Map, undefined), jwks_uri = maps:get(?RESPONSE_JWKS_URI, Map, undefined) }. -spec get_expiration_time(successful_access_token_response()) -> - {ok, [{expires_in, integer() }| {exp, integer() }]} | {error, missing_exp_field}. + {ok, [{expires_in, integer() }| {exp, integer() }]} | + {error, missing_exp_field}. get_expiration_time(#successful_access_token_response{expires_in = ExpiresInSec, access_token = AccessToken}) -> case ExpiresInSec of @@ -188,15 +196,8 @@ update_oauth_provider_endpoints_configuration(OAuthProvider) -> unlock(LockId) end. -update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) -> - LockId = lock(), - try do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) of - V -> V - after - unlock(LockId) - end. - -do_update_oauth_provider_endpoints_configuration(OAuthProvider) -> +do_update_oauth_provider_endpoints_configuration(OAuthProvider) when + OAuthProvider#oauth_provider.id == root -> case OAuthProvider#oauth_provider.token_endpoint of undefined -> do_nothing; TokenEndpoint -> set_env(token_endpoint, TokenEndpoint) @@ -215,10 +216,12 @@ do_update_oauth_provider_endpoints_configuration(OAuthProvider) -> JwksEndPoint -> [{jwks_url, JwksEndPoint} | proplists:delete(jwks_url, List)] end, set_env(key_config, ModifiedList), - rabbit_log:debug("Updated oauth_provider details: ~p ", [ format_oauth_provider(OAuthProvider)]), - OAuthProvider. + rabbit_log:debug("Updated oauth_provider details: ~p ", + [format_oauth_provider(OAuthProvider)]), + OAuthProvider; -do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) -> +do_update_oauth_provider_endpoints_configuration(OAuthProvider) -> + OAuthProviderId = OAuthProvider#oauth_provider.id, OAuthProviders = get_env(oauth_providers, #{}), Proplist = maps:get(OAuthProviderId, OAuthProviders), ModifiedOAuthProviders = maps:put(OAuthProviderId, @@ -241,7 +244,8 @@ lock() -> false -> undefined end; {Nodes, Retries} -> - case global:set_lock({oauth2_config_lock, rabbitmq_auth_backend_oauth2}, Nodes, Retries) of + case global:set_lock({oauth2_config_lock, rabbitmq_auth_backend_oauth2}, + Nodes, Retries) of true -> rabbitmq_auth_backend_oauth2; false -> undefined end @@ -252,8 +256,10 @@ unlock(LockId) -> undefined -> ok; Value -> case use_global_locks_on_all_nodes() of - {} -> global:del_lock({oauth2_config_lock, Value}); - {Nodes, _Retries} -> global:del_lock({oauth2_config_lock, Value}, Nodes) + {} -> + global:del_lock({oauth2_config_lock, Value}); + {Nodes, _Retries} -> + global:del_lock({oauth2_config_lock, Value}, Nodes) end end. @@ -262,52 +268,70 @@ get_oauth_provider(ListOfRequiredAttributes) -> case get_env(default_oauth_provider) of undefined -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes); DefaultOauthProviderId -> - rabbit_log:debug("Using default_oauth_provider ~p", [DefaultOauthProviderId]), + rabbit_log:debug("Using default_oauth_provider ~p", + [DefaultOauthProviderId]), get_oauth_provider(DefaultOauthProviderId, ListOfRequiredAttributes) end. +-spec download_oauth_provider(oauth_provider()) -> {ok, oauth_provider()} | + {error, any()}. +download_oauth_provider(OAuthProvider) -> + case OAuthProvider#oauth_provider.discovery_endpoint of + undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; + URL -> + rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]), + case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of + {ok, OpenIdConfiguration} -> + {ok, update_oauth_provider_endpoints_configuration( + merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; + {error, _} = Error2 -> Error2 + end + end. + +ensure_oauth_provider_has_attributes(OAuthProvider, ListOfRequiredAttributes) -> + case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of + [] -> + rabbit_log:debug("Resolved oauth_provider ~p", + [format_oauth_provider(OAuthProvider)]), + {ok, OAuthProvider}; + _ = Attrs -> + {error, {missing_oauth_provider_attributes, Attrs}} + end. + get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> OAuthProvider = lookup_oauth_provider_from_keyconfig(), - rabbit_log:debug("Using oauth_provider ~p from keyconfig", [format_oauth_provider(OAuthProvider)]), + rabbit_log:debug("Using oauth_provider ~p from keyconfig", + [format_oauth_provider(OAuthProvider)]), case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of [] -> {ok, OAuthProvider}; _ = MissingAttributes -> - rabbit_log:debug("Looking up missing attributes ~p ...", [MissingAttributes]), - Result2 = case OAuthProvider#oauth_provider.discovery_endpoint of - undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; - URL -> - rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]), - case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of - {ok, OpenIdConfiguration} -> - {ok, update_oauth_provider_endpoints_configuration( - merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; - {error, _} = Error2 -> Error2 - end - end, - case Result2 of - {ok, OAuthProvider2} -> - case find_missing_attributes(OAuthProvider2, ListOfRequiredAttributes) of - [] -> - rabbit_log:debug("Resolved oauth_provider ~p", [format_oauth_provider(OAuthProvider)]), - {ok, OAuthProvider2}; - _ = Attrs-> - {error, {missing_oauth_provider_attributes, Attrs}} - end; - {error, _} = Error3 -> Error3 + rabbit_log:debug("Looking up missing attributes ~p ...", + [MissingAttributes]), + case download_oauth_provider(OAuthProvider) of + {ok, OAuthProvider2} -> + ensure_oauth_provider_has_attributes(OAuthProvider2, + ListOfRequiredAttributes); + {error, _} = Error3 -> + Error3 end end. --spec get_oauth_provider(oauth_provider_id(), list()) -> {ok, oauth_provider()} | {error, any()}. +-spec get_oauth_provider(oauth_provider_id(), list()) -> {ok, oauth_provider()} | + {error, any()}. get_oauth_provider(root, ListOfRequiredAttributes) -> get_oauth_provider(ListOfRequiredAttributes); -get_oauth_provider(OAuth2ProviderId, ListOfRequiredAttributes) when is_list(OAuth2ProviderId) -> - get_oauth_provider(list_to_binary(OAuth2ProviderId), ListOfRequiredAttributes); +get_oauth_provider(OAuth2ProviderId, ListOfRequiredAttributes) + when is_list(OAuth2ProviderId) -> + get_oauth_provider(list_to_binary(OAuth2ProviderId), + ListOfRequiredAttributes); -get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAuthProviderId) -> - rabbit_log:debug("get_oauth_provider ~p with at least these attributes: ~p", [OAuthProviderId, ListOfRequiredAttributes]), +get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) + when is_binary(OAuthProviderId) -> + rabbit_log:debug("get_oauth_provider ~p with at least these attributes: ~p", + [OAuthProviderId, ListOfRequiredAttributes]), case lookup_oauth_provider_config(OAuthProviderId) of {error, _} = Error0 -> rabbit_log:debug("Failed to find oauth_provider ~p configuration due to ~p", @@ -322,28 +346,12 @@ get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAu {ok, OAuthProvider}; _ = MissingAttributes -> rabbit_log:debug("OauthProvider has following missing attributes ~p", [MissingAttributes]), - Result2 = case OAuthProvider#oauth_provider.discovery_endpoint of - undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; - URL -> - rabbit_log:debug("Downloading oauth_provider ~p using ~p ...", - [OAuthProviderId, URL]), - case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of - {ok, OpenIdConfiguration} -> - {ok, update_oauth_provider_endpoints_configuration(OAuthProviderId, - merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; - {error, _} = Error2 -> Error2 - end - end, - case Result2 of + case download_oauth_provider(OAuthProvider) of {ok, OAuthProvider2} -> - case find_missing_attributes(OAuthProvider2, ListOfRequiredAttributes) of - [] -> - rabbit_log:debug("Resolved oauth_provider ~p", [format_oauth_provider(OAuthProvider)]), - {ok, OAuthProvider2}; - _ = Attrs-> - {error, {missing_oauth_provider_attributes, Attrs}} - end; - {error, _} = Error3 -> Error3 + ensure_oauth_provider_has_attributes(OAuthProvider2, + ListOfRequiredAttributes); + {error, _} = Error3 -> + Error3 end end end. From b2532e0c1dcdfcbc43e38549652f942110a59f99 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 20 Sep 2024 12:10:26 +0200 Subject: [PATCH 37/64] Modify management schema to be able to set extra parameters for authorize and token endpoints --- deps/oauth2_client/include/types.hrl | 3 +- deps/oauth2_client/src/oauth2_client.erl | 59 +++++++++----- deps/oauth2_client/test/system_SUITE.erl | 18 +++++ .../rabbitmq_auth_backend_oauth2.schema | 27 +------ .../src/oauth2_schema.erl | 10 +-- .../src/uaa_jwks.erl | 2 +- .../test/oauth2_schema_SUITE.erl | 32 +++----- deps/rabbitmq_management/BUILD.bazel | 5 ++ deps/rabbitmq_management/app.bzl | 12 +++ .../priv/schema/rabbitmq_management.schema | 60 +++++++-------- .../src/rabbit_mgmt_schema.erl | 64 ++++++++++++++++ .../test/rabbit_mgmt_schema_SUITE.erl | 76 +++++++++++++++++++ 12 files changed, 259 insertions(+), 109 deletions(-) create mode 100644 deps/rabbitmq_management/src/rabbit_mgmt_schema.erl create mode 100644 deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl index 0592ce582a48..ba73552a24fd 100644 --- a/deps/oauth2_client/include/types.hrl +++ b/deps/oauth2_client/include/types.hrl @@ -37,7 +37,8 @@ -record(access_token_request, { client_id :: string() | binary(), client_secret :: string() | binary(), - scope :: string() | binary() | undefined, + scope :: option(string() | binary()), + extra_parameters :: option(query_list()), timeout :: option(integer()) }). diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index f31bbe445083..e69d55e1abad 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -470,33 +470,50 @@ ensure_oauth_provider_has_id_property(OAuth2ProviderId, OAuth2Provider) -> end. build_access_token_request_body(Request) -> - uri_string:compose_query([ - grant_type_request_parameter(?CLIENT_CREDENTIALS_GRANT_TYPE), - client_id_request_parameter(Request#access_token_request.client_id), - client_secret_request_parameter(Request#access_token_request.client_secret)] - ++ scope_request_parameter_or_default(Request#access_token_request.scope, [])). + uri_string:compose_query( + append_extra_parameters(Request, + append_scope_request_parameter(Request#access_token_request.scope, [ + grant_type_request_parameter(?CLIENT_CREDENTIALS_GRANT_TYPE), + client_id_request_parameter( + Request#access_token_request.client_id), + client_secret_request_parameter( + Request#access_token_request.client_secret)]))). build_refresh_token_request_body(Request) -> - uri_string:compose_query([ - grant_type_request_parameter(?REFRESH_TOKEN_GRANT_TYPE), - refresh_token_request_parameter(Request#refresh_token_request.refresh_token), - client_id_request_parameter(Request#refresh_token_request.client_id), - client_secret_request_parameter(Request#refresh_token_request.client_secret)] - ++ scope_request_parameter_or_default(Request#refresh_token_request.scope, [])). + uri_string:compose_query( + append_scope_request_parameter(Request#refresh_token_request.scope, [ + grant_type_request_parameter(?REFRESH_TOKEN_GRANT_TYPE), + refresh_token_request_parameter(Request), + client_id_request_parameter(Request#refresh_token_request.client_id), + client_secret_request_parameter( + Request#refresh_token_request.client_secret)])). grant_type_request_parameter(Type) -> {?REQUEST_GRANT_TYPE, Type}. -client_id_request_parameter(Client_id) -> - {?REQUEST_CLIENT_ID, binary_to_list(Client_id)}. -client_secret_request_parameter(Client_secret) -> - {?REQUEST_CLIENT_SECRET, binary_to_list(Client_secret)}. -refresh_token_request_parameter(RefreshToken) -> - {?REQUEST_REFRESH_TOKEN, RefreshToken}. -scope_request_parameter_or_default(Scope, Default) -> + +client_id_request_parameter(ClientId) -> + {?REQUEST_CLIENT_ID, + binary_to_list(ClientId)}. + +client_secret_request_parameter(ClientSecret) -> + {?REQUEST_CLIENT_SECRET, + binary_to_list(ClientSecret)}. + +refresh_token_request_parameter(Request) -> + {?REQUEST_REFRESH_TOKEN, Request#refresh_token_request.refresh_token}. + +append_scope_request_parameter(Scope, QueryList) -> case Scope of - undefined -> Default; - <<>> -> Default; - Scope -> [{?REQUEST_SCOPE, Scope}] + undefined -> QueryList; + <<>> -> QueryList; + Scope -> [{?REQUEST_SCOPE, Scope} | QueryList] + end. + +append_extra_parameters(Request, QueryList) -> + case Request#access_token_request.extra_parameters of + undefined -> QueryList; + [] -> QueryList; + Params -> Params ++ QueryList end. get_ssl_options_if_any(OAuthProvider) -> diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl index 1d105393ecab..8caccd0145cd 100644 --- a/deps/oauth2_client/test/system_SUITE.erl +++ b/deps/oauth2_client/test/system_SUITE.erl @@ -33,6 +33,7 @@ all() -> groups() -> [ + {with_all_oauth_provider_settings, [], [ {group, verify_get_oauth_provider} ]}, @@ -402,6 +403,23 @@ grants_access_token(Config) -> ?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType), ?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken). +grants_access_token_optional_parameters(Config) -> + #{request := #{parameters := Parameters}, + response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } + = lookup_expectation(token_endpoint, Config), + + AccessTokenRequest0 = build_access_token_request(Parameters), + AccessTokenRequest = AccessTokenRequest0#access_token_request{ + scope = "some-scope", + extra_parameters = [{"param1", "value1"}] + }, + {ok, #successful_access_token_response{access_token = AccessToken, + token_type = TokenType} } = + oauth2_client:get_access_token(?config(oauth_provider, Config), + AccessTokenRequest), + ?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType), + ?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken). + grants_refresh_token(Config) -> #{request := #{parameters := Parameters}, response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } 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 251102096468..c7cab672f331 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 @@ -158,16 +158,6 @@ "rabbitmq_auth_backend_oauth2.authorization_endpoint", [{datatype, string}, {validators, ["uri", "https_uri"]}]}. -{mapping, - "auth_oauth2.authorization_endpoint_params.$param", - "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", - [{datatype, string}]}. - -{translation, "rabbitmq_auth_backend_oauth2.authorization_endpoint_params", - fun(Conf) -> - oauth2_schema:translate_endpoint_params("authorization_endpoint_params", Conf) - end}. - {mapping, "auth_oauth2.discovery_endpoint_path", "rabbitmq_auth_backend_oauth2.discovery_endpoint_path", @@ -189,22 +179,7 @@ [{datatype, string}]}. {mapping, - "auth_oauth2.token_endpoint_params.$param", - "rabbitmq_auth_backend_oauth2.token_endpoint_params", - [{datatype, string}]}. - -{translation, "rabbitmq_auth_backend_oauth2.token_endpoint_params", - fun(Conf) -> - oauth2_schema:translate_endpoint_params("token_endpoint_params", Conf) - end}. - -{mapping, - "auth_oauth2.oauth_providers.$name.authorization_endpoint_params.$param", - "rabbitmq_auth_backend_oauth2.oauth_providers", - [{datatype, string}]}. - -{mapping, - "auth_oauth2.oauth_providers.$name.token_endpoint_params.$param", + "auth_oauth2.oauth_providers.$name.discovery_endpoint_path", "rabbitmq_auth_backend_oauth2.oauth_providers", [{datatype, string}]}. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index b5e6942160a9..c24430bd87e2 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -41,8 +41,6 @@ translate_oauth_providers(Conf) -> merge_list_of_maps([ extract_oauth_providers_properties(Settings), extract_oauth_providers_endpoint_params(discovery_endpoint_params, Settings), - extract_oauth_providers_endpoint_params(authorization_endpoint_params, Settings), - extract_oauth_providers_endpoint_params(token_endpoint_params, Settings), extract_oauth_providers_algorithm(Settings), extract_oauth_providers_https(Settings), extract_oauth_providers_signing_keys(Settings) @@ -122,13 +120,7 @@ mapOauthProviderProperty({Key, Value}) -> token_endpoint -> validator_https_uri(Key, Value); jwks_uri -> validator_https_uri(Key, Value); end_session_endpoint -> validator_https_uri(Key, Value); - authorization_endpoint -> validator_https_uri(Key, Value); - token_endpoint_params -> - cuttlefish:invalid(io_lib:format( - "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); - authorization_endpoint_params -> - cuttlefish:invalid(io_lib:format( - "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); + authorization_endpoint -> validator_https_uri(Key, Value); discovery_endpoint_params -> cuttlefish:invalid(io_lib:format( "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl index edd81902da15..fd6c0b1cfc24 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl @@ -1,7 +1,7 @@ -module(uaa_jwks). -export([get/2]). --spec get(string() | binary(), term()) -> {ok, term()} | {error, term()}. +-spec get(uri_string:uri_string(), list()) -> {ok, term()} | {error, term()}. get(JwksUrl, SslOptions) -> Options = [{timeout, 60000}] ++ [{ssl, SslOptions}], httpc:request(get, {JwksUrl, []}, Options, []). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 7c7afa37f41f..3f581a847069 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -45,9 +45,7 @@ test_without_resource_servers(_) -> #{} = oauth2_schema:translate_resource_servers([]). test_without_endpoint_params(_) -> - #{} = translate_endpoint_params("discovery_endpoint_params", []), - #{} = translate_endpoint_params("token_endpoint_params", []), - #{} = translate_endpoint_params("authorization_endpoint_params", []). + #{} = translate_endpoint_params("oauth_discovery_endpoint_params", []). test_with_invalid_endpoint_params(_) -> try translate_endpoint_params("discovery_endpoint_params", [ @@ -60,16 +58,10 @@ test_with_invalid_endpoint_params(_) -> test_with_endpoint_params(_) -> Conf = [ {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, - {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"}, - {["auth_oauth2","token_endpoint_params","audience"], "some-audience"}, - {["auth_oauth2","authorization_endpoint_params","resource"], "some-resource"} + {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"} ], #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> } = - translate_endpoint_params("discovery_endpoint_params", Conf), - #{ <<"audience">> := <<"some-audience">>} = - translate_endpoint_params("token_endpoint_params", Conf), - #{ <<"resource">> := <<"some-resource">>} = - translate_endpoint_params("authorization_endpoint_params", Conf). + translate_endpoint_params("discovery_endpoint_params", Conf). test_invalid_oauth_providers_endpoint_params(_) -> try oauth2_schema:translate_oauth_providers([ @@ -83,17 +75,15 @@ test_without_oauth_providers_with_endpoint_params(_) -> Conf = [ {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], "some-value1"}, {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param2"], "some-value2"}, - {["auth_oauth2","oauth_providers", "B", "token_endpoint_params","audience"], "some-audience"}, - {["auth_oauth2","oauth_providers", "C", "authorization_endpoint_params","resource"], "some-resource"} + {["auth_oauth2","oauth_providers", "B", "discovery_endpoint_params","param3"], "some-value3"} ], #{ <<"A">> := [{discovery_endpoint_params, #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> }}], - <<"B">> := [{token_endpoint_params, - #{ <<"audience">> := <<"some-audience">>}}], - <<"C">> := [{authorization_endpoint_params, - #{ <<"resource">> := <<"some-resource">>}}] + <<"B">> := [{discovery_endpoint_params, + #{ <<"param3">> := <<"some-value3">>}} + ] } = translate_oauth_providers(Conf). test_with_one_oauth_provider(_) -> @@ -110,11 +100,13 @@ test_with_one_resource_server(_) -> test_with_many_oauth_providers(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","uaa","issuer"],"https://uaa"} + {["auth_oauth2","oauth_providers","uaa","issuer"],"https://uaa"}, + {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"],"/some-path"} ], - #{<<"keycloak">> := [{issuer, <<"https://keycloak">>} + #{<<"keycloak">> := [{issuer, <<"https://keycloak">>} ], - <<"uaa">> := [{issuer, <<"https://uaa">>} + <<"uaa">> := [{issuer, <<"https://uaa">>}, + {discovery_endpoint_path, <<"/some-path">>} ] } = oauth2_schema:translate_oauth_providers(Conf). diff --git a/deps/rabbitmq_management/BUILD.bazel b/deps/rabbitmq_management/BUILD.bazel index 6b560bb7059e..2d0677b21fac 100644 --- a/deps/rabbitmq_management/BUILD.bazel +++ b/deps/rabbitmq_management/BUILD.bazel @@ -130,6 +130,11 @@ rabbitmq_suite( ], ) +rabbitmq_suite( + name = "rabbit_mgmt_schema_SUITE", + size = "small" +) + rabbitmq_integration_suite( name = "clustering_prop_SUITE", size = "large", diff --git a/deps/rabbitmq_management/app.bzl b/deps/rabbitmq_management/app.bzl index 7fd01cd065c8..4e197d13f2b9 100644 --- a/deps/rabbitmq_management/app.bzl +++ b/deps/rabbitmq_management/app.bzl @@ -30,6 +30,7 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_mgmt_load_definitions.erl", "src/rabbit_mgmt_login.erl", "src/rabbit_mgmt_nodes.erl", + "src/rabbit_mgmt_schema.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", "src/rabbit_mgmt_stats.erl", @@ -163,6 +164,7 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/rabbit_mgmt_load_definitions.erl", "src/rabbit_mgmt_login.erl", "src/rabbit_mgmt_nodes.erl", + "src/rabbit_mgmt_schema.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", "src/rabbit_mgmt_stats.erl", @@ -387,6 +389,7 @@ def all_srcs(name = "all_srcs"): "src/rabbit_mgmt_load_definitions.erl", "src/rabbit_mgmt_login.erl", "src/rabbit_mgmt_nodes.erl", + "src/rabbit_mgmt_schema.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", "src/rabbit_mgmt_stats.erl", @@ -495,6 +498,15 @@ def all_srcs(name = "all_srcs"): ) def test_suite_beam_files(name = "test_suite_beam_files"): + erlang_bytecode( + name = "rabbit_mgmt_schema_SUITE_beam_files", + testonly = True, + srcs = ["test/rabbit_mgmt_schema_SUITE.erl"], + outs = ["test/rabbit_mgmt_schema_SUITE.beam"], + app_name = "rabbitmq_management", + erlc_opts = "//:test_erlc_opts", + deps = ["@proper//:erlang_app"], + ) erlang_bytecode( name = "cache_SUITE_beam_files", testonly = True, diff --git a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema index 396e6b537321..a4aaf057d926 100644 --- a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema +++ b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema @@ -472,6 +472,26 @@ end}. {mapping, "management.oauth_response_type", "rabbitmq_management.oauth_response_type", [{datatype, string}]}. +%% Configure OAuth2 authorization_endpoint additional request parameters +{mapping, "management.oauth_authorization_endpoint_params.$name", + "rabbitmq_management.oauth_authorization_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_management.oauth_authorization_endpoint_params", + fun(Conf) -> + rabbit_mgmt_schema:translate_endpoint_params("oauth_authorization_endpoint_params", Conf) + end}. + +%% Configure OAuth2 token_endpoint additional request parameters +{mapping, "management.oauth_token_endpoint_params.$name", + "rabbitmq_management.oauth_token_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_management.oauth_token_endpoint_params", + fun(Conf) -> + rabbit_mgmt_schema:translate_endpoint_params("oauth_token_endpoint_params", Conf) + end}. + %% The scopes RabbitMq should claim during the authorization flow. Defaults to "openid profile" {mapping, "management.oauth_scopes", "rabbitmq_management.oauth_scopes", [{datatype, string}]}. @@ -513,8 +533,6 @@ end}. [{datatype, string}] }. - - {mapping, "management.oauth_resource_servers.$name.oauth_client_id", "rabbitmq_management.oauth_resource_servers", @@ -533,7 +551,6 @@ end}. [{datatype, string}] }. - {mapping, "management.oauth_resource_servers.$name.oauth_scopes", "rabbitmq_management.oauth_resource_servers", @@ -551,36 +568,17 @@ end}. "rabbitmq_management.oauth_resource_servers", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. +{mapping, "management.oauth_resource_servers.$name.authorization_endpoint_params.$name", + ""rabbitmq_management.oauth_resource_servers", + [{datatype, string}]}. + +{mapping, "management.oauth_resource_servers.$name.token_endpoint_params.$name", + ""rabbitmq_management.oauth_resource_servers", + [{datatype, string}]}. + {translation, "rabbitmq_management.oauth_resource_servers", fun(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("management.oauth_resource_servers", Conf), - ResourceServers = [{Name, {list_to_atom(Key), V}} || {["management","oauth_resource_servers", Name, Key], V} <- Settings ], - KeyFun = fun({Name,_}) -> list_to_binary(Name) end, - ValueFun = fun({_,V}) -> V end, - NewGroup = maps:groups_from_list(KeyFun, ValueFun, ResourceServers), - ListOrSingleFun = fun(K, List) -> - case K of - key_config -> proplists:get_all_values(K, List); - _ -> - case proplists:lookup_all(K, List) of - [One] -> proplists:get_value(K, List); - [One|_] = V -> V - end - end - end, - GroupKeyConfigFun = fun(K, List) -> - ListKeys = proplists:get_keys(List), - [ {K,ListOrSingleFun(K,List)} || K <- ListKeys ] - end, - NewGroupTwo = maps:map(GroupKeyConfigFun, NewGroup), - IndexByIdOrElseNameFun = fun(K, V, NewMap) -> - case proplists:get_value(id, V) of - undefined -> maps:put(K, V, NewMap); - ID when is_binary(ID) -> maps:put(ID, V, NewMap); - ID -> maps:put(list_to_binary(ID), V, NewMap) - end - end, - maps:fold(IndexByIdOrElseNameFun,#{}, NewGroupTwo) + rabbit_mgmt_schema:translate_resource_servers(Conf) end}. %% =========================================================================== diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl new file mode 100644 index 000000000000..518f5133ad53 --- /dev/null +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -0,0 +1,64 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_mgmt_schema). + + +-export([ + translate_oauth_resource_servers/1, + translate_endpoint_params/2 +]). + +extract_key_as_binary({Name,_}) -> list_to_binary(Name). +extract_value({_Name,V}) -> V. + +-spec translate_oauth_resource_servers([{list(), binary()}]) -> map(). +translate_oauth_resource_servers(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + "management.oauth_resource_servers", Conf), + Map = merge_list_of_maps([ + extract_resource_server_properties(Settings), + extract_resource_server_endpoint_params(oauth_authorization_endpoint_params, Settings), + extract_resource_server_endpoint_params(oauth_token_endpoint_params, Settings) + ]), + Map0 = maps:map(fun(K,V) -> + case proplists:get_value(id, V) of + undefined -> V ++ [{id, K}]; + _ -> V + end end, Map), + ResourceServers = maps:values(Map0), + lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, + ResourceServers). + +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). +translate_endpoint_params(Variable, Conf) -> + Params0 = cuttlefish_variable:filter_by_prefix("management." ++ Variable, Conf), + Params = [{list_to_binary(Param), list_to_binary(V)} || + {["management", _, Param], V} <- Params0], + maps:from_list(Params). + +merge_list_of_maps(ListOfMaps) -> + lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, + Elem, AccIn) end, #{}, ListOfMaps). + + +extract_resource_server_properties(Settings) -> + KeyFun = fun extract_key_as_binary/1, + ValueFun = fun extract_value/1, + + OAuthProviders = [{Name, {list_to_atom(Key), list_to_binary(V)}} + || {["management","oauth_resource_servers", Name, Key], V} <- Settings ], + maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). + +extract_resource_server_endpoint_params(Variable, Settings) -> + KeyFun = fun extract_key_as_binary/1, + + IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || + {["management","oauth_resource_servers", Name, EndpointVar, ParamName], V} + <- Settings, EndpointVar == atom_to_list(Variable) ], + maps:map(fun(_K,V)-> [{Variable, maps:from_list(V)}] end, + maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). \ No newline at end of file diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl new file mode 100644 index 000000000000..7faa6aac307b --- /dev/null +++ b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl @@ -0,0 +1,76 @@ +%% 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-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% +-module(rabbit_mgmt_schema_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-import(rabbit_mgmt_schema, [translate_endpoint_params/2, translate_oauth_resource_servers/1]). + +all() -> + [ + test_empty_endpoint_params, + test_invalid_endpoint_params, + test_translate_endpoint_params, + test_with_one_resource_server, + test_with_many_resource_servers + ]. + + +test_empty_endpoint_params(_) -> + #{} = translate_endpoint_params("oauth_authorization_endpoint_params", []), + #{} = translate_endpoint_params("oauth_token_endpoint_params", []). + +test_invalid_endpoint_params(_) -> + try translate_endpoint_params("oauth_authorization_endpoint_params", [ + {["param1","param2"], "some-value1"}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. + +test_translate_endpoint_params(_) -> + #{ <<"param1">> := <<"some-value1">> } = + translate_endpoint_params("oauth_authorization_endpoint_params", [ + {["management","oauth_authorization_endpoint_params","param1"], "some-value1"} + ]). + +test_with_one_resource_server(_) -> + Conf = [ + {["management","oauth_resource_servers","rabbitmq1","id"],"rabbitmq1"} + ], + #{ + <<"rabbitmq1">> := [ + {id, <<"rabbitmq1">>} + ] + } = translate_oauth_resource_servers(Conf). + +test_with_many_resource_servers(_) -> + Conf = [ + {["management","oauth_resource_servers","keycloak","label"],"Keycloak"}, + {["management","oauth_resource_servers","uaa","label"],"Uaa"} + ], + #{ + <<"keycloak">> := [ + {label, <<"Keycloak">>}, + {id, <<"keycloak">>} + ], + <<"uaa">> := [ + {label, <<"Uaa">>}, + {id, <<"uaa">>} + ] + } = translate_oauth_resource_servers(Conf). + + +cert_filename(Conf) -> + string:concat(?config(data_dir, Conf), "certs/cert.pem"). + +sort_settings(MapOfListOfSettings) -> + maps:map(fun(_K,List) -> + lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, MapOfListOfSettings). From c7681c974b2cce0eb5d77df21549a87d5e79d2f4 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 20 Sep 2024 12:40:10 +0200 Subject: [PATCH 38/64] Send new params to management ui --- deps/oauth2_client/src/oauth2_client.erl | 9 +++++++-- deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl | 4 +++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index e69d55e1abad..a6a75f0bbefa 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -77,12 +77,17 @@ build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, Params) -> {_, Q} -> URLMap1#{query => uri_string:compose_query(Q ++ Params)} end). -ensure_leading_path_separator(Path) -> +ensure_leading_path_separator(Path) when is_binary(Path) -> + ensure_leading_path_separator(binary:bin_to_list(Path)); +ensure_leading_path_separator(Path) when is_list(Path) -> case string:slice(Path, 0, 1) of "/" -> Path; _ -> "/" ++ Path end. -drop_trailing_path_separator(Path) -> +drop_trailing_path_separator(Path) when is_binary(Path) -> + drop_trailing_path_separator(binary:bin_to_list(Path)); +drop_trailing_path_separator("") -> ""; +drop_trailing_path_separator(Path) when is_list(Path) -> case string:slice(Path, string:len(Path)-1, 1) of "/" -> lists:droplast(Path); _ -> Path diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index cc3f0b3f486f..854bb8784a7c 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -129,7 +129,9 @@ produce_auth_settings(MgtResourceServers, ManagementProps) -> case proplists:get_value(oauth_initiated_logon_type, ManagementProps, sp_initiated) of sp_initiated -> {}; idp_initiated -> {oauth_initiated_logon_type, <<"idp_initiated">>} - end + end, + to_tuple(oauth_authorization_endpoint_params, ManagementProps), + to_tuple(oauth_token_endpoint_params, ManagementProps) ]) end. From 81342dfbed07a9b4b0de1a01c92eea1387c08c5a Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 20 Sep 2024 17:12:39 +0200 Subject: [PATCH 39/64] WIP Elminate defaults and take from config Add javascript unit tests given that amount of javascript code it is difficult to get good coverage with just end-to-end tests The tests are not running yet because i need to learn how to use Babel to convert ES5 modules into NodeJs modules otherwise it is not possible because all the source modules use ES5 modules whereas tests run from node.js which requires CommonJS --- deps/rabbitmq_management/.gitignore | 11 +-- .../priv/www/js/oidc-oauth/helper.js | 73 ++++++++----------- .../src/rabbit_mgmt_wm_auth.erl | 4 + deps/rabbitmq_management/test/js/.babelrc | 3 + deps/rabbitmq_management/test/js/package.json | 35 +++++++++ .../test/js/test/oidc-oauth/helper.test.js | 22 ++++++ .../test/rabbit_mgmt_wm_auth_SUITE.erl | 2 +- 7 files changed, 97 insertions(+), 53 deletions(-) create mode 100644 deps/rabbitmq_management/test/js/.babelrc create mode 100644 deps/rabbitmq_management/test/js/package.json create mode 100644 deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js diff --git a/deps/rabbitmq_management/.gitignore b/deps/rabbitmq_management/.gitignore index 96463fa9b670..e44f8b646fac 100644 --- a/deps/rabbitmq_management/.gitignore +++ b/deps/rabbitmq_management/.gitignore @@ -2,12 +2,5 @@ test/config_schema_SUITE_data/schema/ -selenium/node_modules -selenium/package-lock.json -selenium/screens/*/* -selenium/logs -selenium/suites/logs/* -selenium/suites/screens/* -selenium/test/oauth/*/h2/*.trace.db -selenium/test/oauth/*/h2/*.lock.db -selenium/*/target/* +test/js/node_modules +test/js/package-lock.json \ No newline at end of file diff --git a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js index 0df8b3d056d5..db0a46b654d8 100644 --- a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js +++ b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js @@ -133,40 +133,41 @@ export function oauth_initiate(oauth) { } return oauth; } -function oauth_initialize_user_manager(resource_server) { - let oidcSettings = { - userStore: new oidc.WebStorageStateStore({ store: window.localStorage }), - authority: resource_server.oauth_provider_url, - client_id: resource_server.oauth_client_id, - response_type: resource_server.oauth_response_type, - scope: resource_server.oauth_scopes, -// resource: resource_server.id, - redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", - post_logout_redirect_uri: rabbit_base_uri() + "/", - - automaticSilentRenew: true, - revokeAccessTokenOnSignout: true, - extraQueryParams: { - audience: resource_server.id, // required by oauth0 - }, - }; - if (resource_server.end_session_endpoint != "") { - oidcSettings.metadataSeed = { - end_session_endpoint: resource_server.end_session_endpoint - } - } - if (resource_server.oauth_client_secret != "") { - oidcSettings.client_secret = resource_server.oauth_client_secret; - } - if (resource_server.oauth_metadata_url != "") { - oidcSettings.metadataUrl = resource_server.oauth_metadata_url; +export function oidc_settings_from(resource_server) { + let oidcSettings = { + userStore: new oidc.WebStorageStateStore({ store: window.localStorage }), + authority: resource_server.oauth_provider_url, + metadataUrl: resource_server.oauth_metadata_url, + client_id: resource_server.oauth_client_id, + response_type: resource_server.oauth_response_type, + scope: resource_server.oauth_scopes, + redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", + post_logout_redirect_uri: rabbit_base_uri() + "/", + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true + } + if (resource_server.end_session_endpoint != "") { + oidcSettings.metadataSeed = { + end_session_endpoint: resource_server.end_session_endpoint } + } + if (resource_server.oauth_client_secret != "") { + oidcSettings.client_secret = resource_server.oauth_client_secret + } + if (resource_server.authorization_endpoint_params != "") { + oidcSettings.extraQueryParams = resource_server.authorization_endpoint_params + } + if (resource_server.token_endpoint_params != "") { + oidcSettings.extraTokenParams = resource_server.token_endpoint_params + } + return oidcSettings +} +function oauth_initialize_user_manager(resource_server) { oidc.Log.setLevel(oidc.Log.DEBUG); oidc.Log.setLogger(console); - mgr = new oidc.UserManager(oidcSettings); -// oauth.readiness_url = mgr.settings.metadataUrl; + mgr = new oidc.UserManager(oidc_settings_from(resource_server)) _management_logger = new oidc.Logger("Management"); @@ -212,20 +213,6 @@ export function oauth_initialize(authSettings) { return oauth; } -function log() { - message = "" - Array.prototype.forEach.call(arguments, function(msg) { - if (msg instanceof Error) { - msg = "Error: " + msg.message; - } - else if (typeof msg !== "string") { - msg = JSON.stringify(msg, null, 2); - } - message += msg - }); - _management_logger.info(message) -} - function oauth_is_logged_in() { return mgr.getUser().then(user => { if (!user) { diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 854bb8784a7c..8b249f3429bf 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -77,6 +77,10 @@ getAllDeclaredOauth2Resources(OAuth2BackendProps) -> undefined -> OAuth2Resources; Id -> maps:put(Id, [{id, Id}], OAuth2Resources) end. +buildRootResourceServerIfAny(Props) -> + [ {id, proplists:get_value(resource_server_id, Props) }, + {oauth_client_id, proplists:get_value(oauth_client_id, Props)}, + {oauth_client_id, proplists:get_value(oauth_client_id, Props)} ]. authSettings() -> ManagementProps = application:get_all_env(rabbitmq_management), diff --git a/deps/rabbitmq_management/test/js/.babelrc b/deps/rabbitmq_management/test/js/.babelrc new file mode 100644 index 000000000000..1320b9a3272a --- /dev/null +++ b/deps/rabbitmq_management/test/js/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/deps/rabbitmq_management/test/js/package.json b/deps/rabbitmq_management/test/js/package.json new file mode 100644 index 000000000000..0748d98ba9c0 --- /dev/null +++ b/deps/rabbitmq_management/test/js/package.json @@ -0,0 +1,35 @@ +{ + "type":"module", + "dependencies": { + + + "json": "^11.0.0", + + + "mocha": "^10.7.3" + + }, + + "scripts": { + + + "test": "mocha --recursive --trace-warnings --require @babel/register" + + }, + + "devDependencies": { + + + "@babel/cli": "^7.25.6", + + + "@babel/core": "^7.25.2", + + + "@babel/preset-env": "^7.25.4", + + + "@babel/register": "^7.24.6" + + } +} diff --git a/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js b/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js new file mode 100644 index 000000000000..88431a0c9498 --- /dev/null +++ b/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js @@ -0,0 +1,22 @@ +const assert = require('assert') +import oidc_settings_from from '../../../../priv/www/js/oidc-oauth/helper.js' + +describe('oidc_settings_from', function () { + describe('single root resource', function () { + + describe('with minimum required settings', function () { + var resource = { + oauth_client_id : "some-client", + oauth_provider_url : "https://someurl", + oauth_metadata_url : "https://someurl/extra" + } + var oidc_settings = oidc_settings_from(resource) + + it('oidc_settings should have client_id ', function () { + assert.equal(resource.oauth_provider_url, oidc_settings.authority) + assert.equal(resource.oauth_metadata_url, oidc_settings.metadataUrl) + assert.equal(resource.oauth_client_id, oidc_settings.client_id) + }) + }) + }) +}) \ No newline at end of file diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index d47350d2b926..224555da7195 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -40,7 +40,7 @@ groups() -> should_return_disabled_auth_settings, {with_root_issuer_url1, [], [ {with_resource_server_id_rabbit, [], [ - should_return_disabled_auth_settings, + should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_enabled, should_return_oauth_client_id_z, From 0e80bfb89e0821ad4789b387008294138e160ba2 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 23 Sep 2024 12:07:27 +0200 Subject: [PATCH 40/64] Add auth and token endpoint params to authSettings --- .../priv/schema/rabbitmq_management.schema | 4 +- .../src/rabbit_mgmt_wm_auth.erl | 44 ++++++++++++++----- .../test/rabbit_mgmt_wm_auth_SUITE.erl | 35 ++++++++++++++- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema index a4aaf057d926..e297f765fe49 100644 --- a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema +++ b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema @@ -568,11 +568,11 @@ end}. "rabbitmq_management.oauth_resource_servers", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. -{mapping, "management.oauth_resource_servers.$name.authorization_endpoint_params.$name", +{mapping, "management.oauth_resource_servers.$name.oauth_authorization_endpoint_params.$name", ""rabbitmq_management.oauth_resource_servers", [{datatype, string}]}. -{mapping, "management.oauth_resource_servers.$name.token_endpoint_params.$name", +{mapping, "management.oauth_resource_servers.$name.oauth_token_endpoint_params.$name", ""rabbitmq_management.oauth_resource_servers", [{datatype, string}]}. diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 8b249f3429bf..25df31ae9d38 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -25,6 +25,18 @@ variances(Req, Context) -> content_types_provided(ReqData, Context) -> {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}. +merge_property(Key, List, MapIn) -> + case proplists:get_value(Key, List) of + undefined -> MapIn; + V0 -> MapIn#{Key => V0} + end. + +extract_oauth_provider_info_props_as_map(ManagementProps) -> + lists:foldl(fun(K, Acc) -> + merge_property(K, ManagementProps, Acc) end, #{}, [oauth_provider_url, + oauth_metadata_url, oauth_authorization_endpoint_params, + oauth_token_endpoint_params]). + merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, ManagementProps) -> OAuthProviderResult = case proplists:get_value(oauth_provider_id, OAuthResourceServer) of undefined -> oauth2_client:get_oauth_provider([issuer]); @@ -35,15 +47,17 @@ merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, ManagementProp {error, _} -> #{} end, OAuthProviderInfo1 = maps:merge(OAuthProviderInfo0, - case proplists:get_value(oauth_provider_url, ManagementProps) of - undefined -> #{}; - V1 -> #{oauth_provider_url => V1} - end), + extract_oauth_provider_info_props_as_map(ManagementProps)), maps:merge(OAuthProviderInfo1, proplists:to_map(MgtResourceServer)). oauth_provider_to_map(OAuthProvider) -> % only include issuer and end_session_endpoint for now. The other endpoints are resolved by oidc-client library - Map0 = #{ oauth_provider_url => OAuthProvider#oauth_provider.issuer }, + Map0 = case OAuthProvider#oauth_provider.issuer of + undefined -> #{}; + Issuer -> #{ oauth_provider_url => Issuer, + oauth_metadata_url => OAuthProvider#oauth_provider.discovery_endpoint + } + end, case OAuthProvider#oauth_provider.end_session_endpoint of undefined -> Map0; V -> maps:put(end_session_endpoint, V, Map0) @@ -75,12 +89,22 @@ getAllDeclaredOauth2Resources(OAuth2BackendProps) -> OAuth2Resources = proplists:get_value(resource_servers, OAuth2BackendProps, #{}), case proplists:get_value(resource_server_id, OAuth2BackendProps) of undefined -> OAuth2Resources; - Id -> maps:put(Id, [{id, Id}], OAuth2Resources) + Id -> maps:put(Id, buildRootResourceServerIfAny(Id, OAuth2BackendProps), + OAuth2Resources) end. -buildRootResourceServerIfAny(Props) -> - [ {id, proplists:get_value(resource_server_id, Props) }, - {oauth_client_id, proplists:get_value(oauth_client_id, Props)}, - {oauth_client_id, proplists:get_value(oauth_client_id, Props)} ]. +buildRootResourceServerIfAny(Id, Props) -> + [ {id, Id}, + {oauth_client_id, + proplists:get_value(oauth_client_id, Props)}, + {oauth_client_secret, + proplists:get_value(oauth_client_secret, Props)}, + {oauth_response_type, + proplists:get_value(oauth_response_type, Props)}, + {authorization_endpoint_params, + proplists:get_value(authorization_endpoint_params, Props)}, + {token_endpoint_params, + proplists:get_value(token_endpoint_params, Props)} + ]. authSettings() -> ManagementProps = application:get_all_env(rabbitmq_management), diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index 224555da7195..ed955f21db81 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -74,8 +74,13 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_mgt_oauth_provider_url_url1, + should_return_mgt_oauth_metadata_url_url1, {with_mgt_oauth_provider_url_url0, [], [ - should_return_mgt_oauth_provider_url_url0 + should_return_mgt_oauth_provider_url_url0, + should_return_mgt_oauth_metadata_url_url1, + {with_mgt_oauth_metadata_url_url0, [], [ + should_return_mgt_oauth_metadata_url_url0 + ]} ]} ]} ]} @@ -299,10 +304,15 @@ init_per_suite(Config) -> {idp2, <<"idp2">>}, {idp3, <<"idp3">>}, {idp1_url, <<"https://idp1">>}, + {idp1_meta_url, <<"https://idp1/.well-known/openid-configuration">>}, {idp2_url, <<"https://idp2">>}, + {idp2_meta_url, <<"https://idp2/.well-known/openid-configuration">>}, {idp3_url, <<"https://idp3">>}, + {idp3_meta_url, <<"https://idp3/.well-known/openid-configuration">>}, {url0, <<"https://url0">>}, + {meta_url0, <<"https://url0/.well-known/openid-configuration">>}, {url1, <<"https://url1">>}, + {meta_url1, <<"https://url1/.well-known/openid-configuration">>}, {logout_url_0, <<"https://logout_0">>}, {logout_url_1, <<"https://logout_1">>}, {logout_url_2, <<"https://logout_2">>}, @@ -340,6 +350,9 @@ init_per_group(with_mgt_oauth_client_secret_q, Config) -> init_per_group(with_mgt_oauth_provider_url_url0, Config) -> application:set_env(rabbitmq_management, oauth_provider_url, ?config(url0, Config)), Config; +init_per_group(with_mgt_oauth_metadata_url_url0, Config) -> + application:set_env(rabbitmq_management, oauth_metadata_url, ?config(meta_url0, Config)), + Config; init_per_group(with_root_issuer_url1, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, issuer, ?config(url1, Config)), Config; @@ -542,6 +555,14 @@ should_return_mgt_oauth_provider_url_url1(Config) -> assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), Config, rabbit, oauth_provider_url, url1). +should_return_mgt_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + Config, rabbit, oauth_metadata_url, meta_url1). + +should_return_mgt_oauth_metadata_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + Config, rabbit, oauth_metadata_url, meta_url0). + should_return_mgt_oauth_provider_url_url0(Config) -> assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), Config, rabbit, oauth_provider_url, url0). @@ -585,6 +606,10 @@ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url1(Config) assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), Config, rabbit, oauth_provider_url, url1). +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + Config, rabbit, oauth_provider_url, url1 ). + should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0(Config) -> assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), Config, rabbit, oauth_provider_url, url0). @@ -617,9 +642,9 @@ should_not_return_oauth_scopes(_Config) -> should_return_oauth_enabled(_Config) -> Actual = rabbit_mgmt_wm_auth:authSettings(), - log(Actual), ?assertEqual(true, proplists:get_value(oauth_enabled, Actual)). + should_return_oauth_idp_initiated_logon(_Config) -> Actual = rabbit_mgmt_wm_auth:authSettings(), ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). @@ -699,6 +724,12 @@ assertEqual_on_attribute_for_oauth_resource_server(Actual, Config, ConfigKey, At end, ?assertEqual(Value, proplists:get_value(Attribute, OauthResource)). +assert_attribute_is_defined_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute) -> + log(Actual), + OAuthResourceServers = proplists:get_value(oauth_resource_servers, Actual), + OauthResource = maps:get(?config(ConfigKey, Config), OAuthResourceServers), + ?assertEqual(true, proplists:is_defined(Attribute, OauthResource)). + assert_attribute_not_defined_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute) -> log(Actual), OAuthResourceServers = proplists:get_value(oauth_resource_servers, Actual), From 94a9cf6729e5f73f91531db559a6efd404b9dc5a Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 23 Sep 2024 14:17:37 +0200 Subject: [PATCH 41/64] Test authSettings with extra endpoint params --- .../src/rabbit_mgmt_wm_auth.erl | 40 ++- .../test/rabbit_mgmt_wm_auth_SUITE.erl | 275 ++++++++++++------ 2 files changed, 216 insertions(+), 99 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 25df31ae9d38..ffa1ac8a6582 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -139,8 +139,17 @@ filter_mgt_resource_servers_without_oauth_client_id_for_sp_initiated(MgtResource filter_mgt_resource_servers_without_oauth_provider_url(MgtResourceServers) -> maps:filter(fun(_K1,V1) -> maps:is_key(oauth_provider_url, V1) end, MgtResourceServers). +ensure_oauth_resource_server_properties_are_binaries(Key, Value) -> + case Key of + oauth_authorization_endpoint_params -> Value; + oauth_token_endpoint_params -> Value; + _ -> to_binary(Value) + end. + produce_auth_settings(MgtResourceServers, ManagementProps) -> - ConvertValuesToBinary = fun(_K,V) -> [ {K1, to_binary(V1)} || {K1,V1} <- maps:to_list(V) ] end, + ConvertValuesToBinary = fun(_K,V) -> [ + {K1, ensure_oauth_resource_server_properties_are_binaries(K1, V1)} || {K1,V1} + <- maps:to_list(V)] end, FilteredMgtResourceServers = filter_mgt_resource_servers_without_oauth_provider_url( filter_mgt_resource_servers_without_oauth_client_id_for_sp_initiated(MgtResourceServers, ManagementProps)), @@ -150,7 +159,7 @@ produce_auth_settings(MgtResourceServers, ManagementProps) -> filter_empty_properties([ {oauth_enabled, true}, {oauth_resource_servers, maps:map(ConvertValuesToBinary, FilteredMgtResourceServers)}, - to_tuple(oauth_disable_basic_auth, ManagementProps, true), + to_tuple(oauth_disable_basic_auth, ManagementProps, fun to_binary/1, true), to_tuple(oauth_client_id, ManagementProps), to_tuple(oauth_client_secret, ManagementProps), to_tuple(oauth_scopes, ManagementProps), @@ -158,8 +167,8 @@ produce_auth_settings(MgtResourceServers, ManagementProps) -> sp_initiated -> {}; idp_initiated -> {oauth_initiated_logon_type, <<"idp_initiated">>} end, - to_tuple(oauth_authorization_endpoint_params, ManagementProps), - to_tuple(oauth_token_endpoint_params, ManagementProps) + to_tuple(oauth_authorization_endpoint_params, ManagementProps, undefined, undefined), + to_tuple(oauth_token_endpoint_params, ManagementProps, undefined, undefined) ]) end. @@ -171,6 +180,7 @@ filter_empty_properties(ListOfProperties) -> end end, ListOfProperties). +to_binary(Value) when is_boolean(Value)-> Value; to_binary(Value) -> rabbit_data_coercion:to_binary(Value). to_json(ReqData, Context) -> @@ -188,9 +198,19 @@ is_invalid(List) -> end end, List). to_tuple(Key, Proplist) -> - case proplists:is_defined(Key, Proplist) of - true -> {Key, rabbit_data_coercion:to_binary(proplists:get_value(Key, Proplist))}; - false -> {} - end. -to_tuple(Key, Proplist, DefaultValue) -> - {Key, proplists:get_value(Key, Proplist, DefaultValue)}. + to_tuple(Key, Proplist, fun to_binary/1, undefined). + +to_tuple(Key, Proplist, ConvertFun, DefaultValue) -> + case proplists:is_defined(Key, Proplist) of + true -> + {Key, case ConvertFun of + undefined -> proplists:get_value(Key, Proplist); + _ -> ConvertFun(proplists:get_value(Key, Proplist)) + end + }; + false -> + case DefaultValue of + undefined -> {}; + _ -> {Key, proplists:get_value(Key, Proplist, DefaultValue)} + end + end. diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index ed955f21db81..604f5cc9b12c 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -9,7 +9,8 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). - +-import(application, [set_env/3, unset_env/2]). +-import(rabbit_mgmt_wm_auth, [authSettings/0]). -compile(export_all). all() -> @@ -24,7 +25,8 @@ all() -> {group, verify_oauth_initiated_logon_type_for_sp_initiated}, {group, verify_oauth_initiated_logon_type_for_idp_initiated}, {group, verify_oauth_disable_basic_auth}, - {group, verify_oauth_scopes} + {group, verify_oauth_scopes}, + {group, verify_extra_endpoint_params} ]. groups() -> @@ -91,6 +93,7 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_mgt_oauth_provider_url_idp1_url, + should_return_mgt_oauth_matadata_url_idp1_url, {with_root_issuer_url1, [], [ should_return_mgt_oauth_provider_url_idp1_url ]}, @@ -175,14 +178,21 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url1, + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, should_return_oauth_resource_server_a_with_oauth_provider_url_url1, + should_return_oauth_resource_server_a_with_oauth_metadata_url_url1, {with_mgt_oauth_provider_url_url0, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, should_return_oauth_resource_server_a_with_oauth_provider_url_url0, + should_return_oauth_resource_server_a_with_oauth_metadata_url_url1, {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, - should_return_oauth_resource_server_a_with_oauth_provider_url_url1 - ]} + should_return_oauth_resource_server_a_with_oauth_provider_url_url1, + {with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url0, [], [ + should_return_oauth_resource_server_a_with_oauth_metadata_url_url0 + ]} + ]} ]} ]} ]} @@ -193,14 +203,16 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url, + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url, {with_root_issuer_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url ]}, {with_mgt_oauth_provider_url_url0, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url, {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, - should_return_oauth_resource_server_a_with_oauth_provider_url_url1 + should_return_oauth_resource_server_a_with_oauth_provider_url_url1 ]} ]} ]} @@ -292,6 +304,24 @@ groups() -> ]} ]} ]} + ]}, + {verify_extra_endpoint_params, [], [ + {with_resource_server_id_rabbit, [], [ + {with_root_issuer_url1, [], [ + {with_oauth_enabled, [], [ + {with_mgt_oauth_client_id_z, [], [ + should_return_mgt_oauth_resource_rabbit_without_authorization_endpoint_params, + should_return_mgt_oauth_resource_rabbit_without_token_endpoint_params, + {with_authorization_endpoint_params_0, [], [ + should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0 + ]}, + {with_token_endpoint_params_0, [], [ + should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0 + ]} + ]} + ]} + ]} + ]} ]} ]. @@ -304,11 +334,11 @@ init_per_suite(Config) -> {idp2, <<"idp2">>}, {idp3, <<"idp3">>}, {idp1_url, <<"https://idp1">>}, - {idp1_meta_url, <<"https://idp1/.well-known/openid-configuration">>}, + {meta_idp1_url, <<"https://idp1/.well-known/openid-configuration">>}, {idp2_url, <<"https://idp2">>}, - {idp2_meta_url, <<"https://idp2/.well-known/openid-configuration">>}, + {meta_idp2_url, <<"https://idp2/.well-known/openid-configuration">>}, {idp3_url, <<"https://idp3">>}, - {idp3_meta_url, <<"https://idp3/.well-known/openid-configuration">>}, + {meta_idp3_url, <<"https://idp3/.well-known/openid-configuration">>}, {url0, <<"https://url0">>}, {meta_url0, <<"https://url0/.well-known/openid-configuration">>}, {url1, <<"https://url1">>}, @@ -322,6 +352,10 @@ init_per_suite(Config) -> {w, <<"w">>}, {z, <<"z">>}, {x, <<"x">>}, + {authorization_params_0, [{<<"a-param0">>, <<"value0">>}]}, + {authorization_params_1, [{<<"a-param1">>, <<"value1">>}]}, + {token_params_0, [{<<"t-param0">>, <<"value0">>}]}, + {token_params_1, [{<<"t-param1">>, <<"value1">>}]}, {admin_mgt, <<"admin mgt">>}, {read_write, <<"read write">>} | Config]. @@ -329,44 +363,44 @@ end_per_suite(_Config) -> ok. init_per_group(with_oauth_disabled, Config) -> - application:set_env(rabbitmq_management, oauth_enabled, false), + set_env(rabbitmq_management, oauth_enabled, false), Config; init_per_group(with_oauth_enabled, Config) -> - application:set_env(rabbitmq_management, oauth_enabled, true), + set_env(rabbitmq_management, oauth_enabled, true), Config; init_per_group(with_resource_server_id_rabbit, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?config(rabbit, Config)), + set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?config(rabbit, Config)), Config; init_per_group(with_mgt_oauth_client_id_z, Config) -> - application:set_env(rabbitmq_management, oauth_client_id, ?config(z, Config)), + set_env(rabbitmq_management, oauth_client_id, ?config(z, Config)), Config; init_per_group(with_mgt_resource_server_a_with_client_secret_w, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_client_secret, ?config(w, Config)), Config; init_per_group(with_mgt_oauth_client_secret_q, Config) -> - application:set_env(rabbitmq_management, oauth_client_secret, ?config(q, Config)), + set_env(rabbitmq_management, oauth_client_secret, ?config(q, Config)), Config; init_per_group(with_mgt_oauth_provider_url_url0, Config) -> - application:set_env(rabbitmq_management, oauth_provider_url, ?config(url0, Config)), + set_env(rabbitmq_management, oauth_provider_url, ?config(url0, Config)), Config; init_per_group(with_mgt_oauth_metadata_url_url0, Config) -> - application:set_env(rabbitmq_management, oauth_metadata_url, ?config(meta_url0, Config)), + set_env(rabbitmq_management, oauth_metadata_url, ?config(meta_url0, Config)), Config; init_per_group(with_root_issuer_url1, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, issuer, ?config(url1, Config)), + set_env(rabbitmq_auth_backend_oauth2, issuer, ?config(url1, Config)), Config; init_per_group(with_oauth_scopes_admin_mgt, Config) -> - application:set_env(rabbitmq_management, oauth_scopes, ?config(admin_mgt, Config)), + set_env(rabbitmq_management, oauth_scopes, ?config(admin_mgt, Config)), Config; init_per_group(with_oauth_scopes_write_read, Config) -> - application:set_env(rabbitmq_management, oauth_scopes, ?config(write_read, Config)), + set_env(rabbitmq_management, oauth_scopes, ?config(write_read, Config)), Config; init_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> - application:set_env(rabbitmq_management, oauth_initiated_logon_type, idp_initiated), + set_env(rabbitmq_management, oauth_initiated_logon_type, idp_initiated), Config; init_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> - application:set_env(rabbitmq_management, oauth_initiated_logon_type, sp_initiated), + set_env(rabbitmq_management, oauth_initiated_logon_type, sp_initiated), Config; init_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_sp_initiated, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, @@ -377,10 +411,10 @@ init_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_idp_in ?config(a, Config), oauth_initiated_logon_type, idp_initiated), Config; init_per_group(with_oauth_disable_basic_auth_false, Config) -> - application:set_env(rabbitmq_management, oauth_disable_basic_auth, false), + set_env(rabbitmq_management, oauth_disable_basic_auth, false), Config; init_per_group(with_oauth_providers_idp1_idp2, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{ + set_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{ ?config(idp1, Config) => [ { issuer, ?config(idp1_url, Config)} ], ?config(idp2, Config) => [ { issuer, ?config(idp2_url, Config)} ] }), @@ -401,18 +435,22 @@ init_per_group(with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, Co set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_provider_url, ?config(url1, Config)), Config; +init_per_group(with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url0, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_metadata_url, ?config(meta_url0, Config)), + Config; init_per_group(with_mgt_resource_server_a_with_client_id_x, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_client_id, ?config(x, Config)), Config; init_per_group(with_default_oauth_provider_idp1, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp1, Config)), + set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp1, Config)), Config; init_per_group(with_default_oauth_provider_idp3, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp3, Config)), + set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp3, Config)), Config; init_per_group(with_root_end_session_endpoint_0, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, ?config(logout_url_0, Config)), + set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, ?config(logout_url_0, Config)), Config; init_per_group(with_end_session_endpoint_for_idp1_1, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, @@ -422,53 +460,65 @@ init_per_group(with_end_session_endpoint_for_idp2_2, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, ?config(idp2, Config), end_session_endpoint, ?config(logout_url_2, Config)), Config; - init_per_group(with_oauth_provider_idp2_for_resource_server_a, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, resource_servers, ?config(a, Config), oauth_provider_id, ?config(idp2, Config)), Config; +init_per_group(with_authorization_endpoint_params_0, Config) -> + set_env(rabbitmq_management, oauth_authorization_endpoint_params, + ?config(authorization_params_0, Config)), + Config; +init_per_group(with_token_endpoint_params_0, Config) -> + set_env(rabbitmq_management, oauth_token_endpoint_params, + ?config(token_params_0, Config)), + Config; + init_per_group(_, Config) -> Config. end_per_group(with_oauth_providers_idp1_idp2, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), + unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), Config; end_per_group(with_mgt_oauth_client_secret_q, Config) -> - application:unset_env(rabbitmq_management, oauth_client_secret), + unset_env(rabbitmq_management, oauth_client_secret), Config; end_per_group(with_oauth_scopes_admin_mgt, Config) -> - application:unset_env(rabbitmq_management, oauth_scopes), + unset_env(rabbitmq_management, oauth_scopes), Config; end_per_group(with_oauth_scopes_write_read, Config) -> - application:unset_env(rabbitmq_management, oauth_scopes), + unset_env(rabbitmq_management, oauth_scopes), Config; end_per_group(with_oauth_disabled, Config) -> - application:unset_env(rabbitmq_management, oauth_enabled), + unset_env(rabbitmq_management, oauth_enabled), Config; end_per_group(with_oauth_enabled, Config) -> - application:unset_env(rabbitmq_management, oauth_enabled), + unset_env(rabbitmq_management, oauth_enabled), Config; end_per_group(with_oauth_disable_basic_auth_false, Config) -> - application:unset_env(rabbitmq_management, oauth_disable_basic_auth), + unset_env(rabbitmq_management, oauth_disable_basic_auth), Config; end_per_group(with_resource_server_id_rabbit, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), + unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), Config; end_per_group(with_mgt_oauth_provider_url_url0, Config) -> - application:unset_env(rabbitmq_management, oauth_provider_url), + unset_env(rabbitmq_management, oauth_provider_url), + Config; +end_per_group(with_mgt_oauth_metadata_url_url0, Config) -> + unset_env(rabbitmq_management, oauth_metadata_url), Config; end_per_group(with_root_issuer_url1, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, issuer), + unset_env(rabbitmq_auth_backend_oauth2, issuer), + unset_env(rabbitmq_auth_backend_oauth2, discovery_endpoint), Config; end_per_group(with_mgt_oauth_client_id_z, Config) -> - application:unset_env(rabbitmq_management, oauth_client_id), + unset_env(rabbitmq_management, oauth_client_id), Config; end_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> - application:unset_env(rabbitmq_management, oauth_initiated_logon_type), + unset_env(rabbitmq_management, oauth_initiated_logon_type), Config; end_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> - application:unset_env(rabbitmq_management, oauth_initiated_logon_type), + unset_env(rabbitmq_management, oauth_initiated_logon_type), Config; end_per_group(with_mgt_resource_server_a_with_client_secret_w, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, @@ -490,6 +540,10 @@ end_per_group(with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, Con remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_provider_url), Config; +end_per_group(with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url0, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_metadata_url), + Config; end_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_sp_initiated, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_initiated_logon_type), @@ -503,13 +557,13 @@ end_per_group(with_mgt_resource_server_a_with_client_id_x, Config) -> ?config(a, Config), oauth_client_id), Config; end_per_group(with_default_oauth_provider_idp1, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), + unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), Config; end_per_group(with_default_oauth_provider_idp3, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), + unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), Config; end_per_group(with_root_end_session_endpoint_0, Config) -> - application:unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint), + unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint), Config; end_per_group(with_end_session_endpoint_for_idp1_1, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, @@ -523,6 +577,13 @@ end_per_group(with_oauth_provider_idp2_for_resource_server_a, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_auth_backend_oauth2, resource_servers, ?config(a, Config), oauth_provider_id), Config; +end_per_group(with_authorization_endpoint_params_0, Config) -> + unset_env(rabbitmq_management, oauth_authorization_endpoint_params), + Config; +end_per_group(with_token_endpoint_params_0, Config) -> + unset_env(rabbitmq_management, oauth_token_endpoint_params), + Config; + end_per_group(_, Config) -> Config. @@ -532,163 +593,199 @@ end_per_group(_, Config) -> %% Test cases. %% ------------------------------------------------------------------- should_not_return_oauth_client_secret(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(false, proplists:is_defined(oauth_client_secret, Actual)). should_return_oauth_client_secret_q(Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(?config(q, Config), proplists:get_value(oauth_client_secret, Actual)). should_return_oauth_resource_server_a_with_client_id_x(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_client_id, x). should_return_oauth_resource_server_a_with_client_secret_w(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_client_secret, w). should_not_return_oauth_resource_server_a_with_client_secret(Config) -> - assert_attribute_not_defined_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), Config, a, oauth_client_secret). should_return_mgt_oauth_provider_url_idp1_url(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, idp1_url). +should_return_mgt_oauth_matadata_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_idp1_url). + should_return_mgt_oauth_provider_url_url1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, url1). should_return_mgt_oauth_metadata_url_url1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_metadata_url, meta_url1). should_return_mgt_oauth_metadata_url_url0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_metadata_url, meta_url0). should_return_mgt_oauth_provider_url_url0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, url0). should_return_oauth_scopes_admin_mgt(Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(?config(admin_mgt, Config), proplists:get_value(oauth_scopes, Actual)). should_return_mgt_oauth_resource_server_a_with_scopes_read_write(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, scopes, read_write). should_return_disabled_auth_settings(_Config) -> - [{oauth_enabled, false}] = rabbit_mgmt_wm_auth:authSettings(). + [{oauth_enabled, false}] = authSettings(). should_return_mgt_resource_server_a_oauth_provider_url_url0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_provider_url, url0). should_return_mgt_oauth_resource_server_a_with_client_id_x(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_client_id, x). should_return_oauth_resource_server_a_with_oauth_provider_url_idp1_url(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_provider_url, idp1_url). should_return_oauth_resource_server_a_with_oauth_provider_url_url1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_provider_url, url1). +should_return_oauth_resource_server_a_with_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_metadata_url, meta_url1). + +should_return_oauth_resource_server_a_with_oauth_metadata_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_metadata_url, meta_url0). + should_return_oauth_resource_server_a_with_oauth_provider_url_url0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_provider_url, url0). should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, idp1_url). +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_idp1_url). + should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, url1). should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), - Config, rabbit, oauth_provider_url, url1 ). + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url1 ). should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_provider_url, url0). +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url0). + should_not_return_oauth_initiated_logon_type(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(false, proplists:is_defined(oauth_initiated_logon_type, Actual)). should_return_oauth_initiated_logon_type_idp_initiated(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). should_not_return_oauth_resource_server_a(Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), assert_not_defined_oauth_resource_server(Actual, Config, a). should_not_return_oauth_resource_server_a_with_oauth_initiated_logon_type(Config) -> - assert_attribute_not_defined_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), Config, a, oauth_initiated_logon_type). should_return_oauth_resource_server_a_with_oauth_initiated_logon_type_idp_initiated(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_initiated_logon_type, <<"idp_initiated">>). should_return_oauth_resource_server_a_with_oauth_initiated_logon_type_sp_initiated(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, oauth_initiated_logon_type, <<"sp_initiated">>). should_not_return_oauth_scopes(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(false, proplists:is_defined(scopes, Actual)). should_return_oauth_enabled(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(true, proplists:get_value(oauth_enabled, Actual)). should_return_oauth_idp_initiated_logon(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). should_return_oauth_disable_basic_auth_true(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(true, proplists:get_value(oauth_disable_basic_auth, Actual)). should_return_oauth_disable_basic_auth_false(_Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(false, proplists:get_value(oauth_disable_basic_auth, Actual)). should_return_oauth_client_id_z(Config) -> - Actual = rabbit_mgmt_wm_auth:authSettings(), + Actual = authSettings(), ?assertEqual(?config(z, Config), proplists:get_value(oauth_client_id, Actual)). should_not_return_end_session_endpoint(Config) -> - assert_attribute_not_defined_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), Config, rabbit, end_session_endpoint). should_return_end_session_endpoint_0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, end_session_endpoint, ?config(logout_url_0, Config)). should_return_end_session_endpoint_1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, end_session_endpoint, ?config(logout_url_1, Config)). should_return_oauth_resource_server_a_without_end_session_endpoint(Config) -> - assert_attribute_not_defined_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), Config, a, end_session_endpoint). should_return_oauth_resource_server_a_with_end_session_endpoint_0(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, end_session_endpoint, ?config(logout_url_0, Config)). should_return_oauth_resource_server_a_with_end_session_endpoint_1(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, end_session_endpoint, ?config(logout_url_1, Config)). should_return_oauth_resource_server_a_with_end_session_endpoint_2(Config) -> - assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, a, end_session_endpoint, ?config(logout_url_2, Config)). +should_return_mgt_oauth_resource_rabbit_without_authorization_endpoint_params(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_authorization_endpoint_params). + +should_return_mgt_oauth_resource_rabbit_without_token_endpoint_params(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_token_endpoint_params). + +should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_authorization_endpoint_params, authorization_params_0). + +should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_token_endpoint_params, token_params_0). + %% ------------------------------------------------------------------- %% Utility/helper functions %% ------------------------------------------------------------------- @@ -702,16 +799,16 @@ remove_entry_from_env_variable(Application, EnvVar, Key) -> Map = application:get_env(Application, EnvVar, #{}), NewMap = maps:remove(Key, Map), case maps:size(NewMap) of - 0 -> application:unset_env(Application, EnvVar); - _ -> application:set_env(Application, EnvVar, NewMap) + 0 -> unset_env(Application, EnvVar); + _ -> set_env(Application, EnvVar, NewMap) end. remove_attribute_from_entry_from_env_variable(Application, EnvVar, Key, Attribute) -> Map = application:get_env(Application, EnvVar, #{}), Proplist = proplists:delete(Attribute, maps:get(Key, Map, [])), NewMap = delete_key_with_empty_proplist(Key, maps:put(Key, Proplist, Map)), case maps:size(NewMap) of - 0 -> application:unset_env(Application, EnvVar); - _ -> application:set_env(Application, EnvVar, NewMap) + 0 -> unset_env(Application, EnvVar); + _ -> set_env(Application, EnvVar, NewMap) end. assertEqual_on_attribute_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute, ConfigValue) -> @@ -746,7 +843,7 @@ set_attribute_in_entry_for_env_variable(Application, EnvVar, Key, Attribute, Val ct:log("set_attribute_in_entry_for_env_variable before ~p", [Map]), Map1 = maps:put(Key, [ { Attribute, Value} | maps:get(Key, Map, []) ], Map), ct:log("set_attribute_in_entry_for_env_variable after ~p", [Map1]), - application:set_env(Application, EnvVar, Map1). + set_env(Application, EnvVar, Map1). log(AuthSettings) -> logEnvVars(), From 33da3767a3eb817a43f34031b2a4b32efea88306 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 23 Sep 2024 14:47:24 +0200 Subject: [PATCH 42/64] Teet extra token parans for additioal resource servers --- .../test/rabbit_mgmt_wm_auth_SUITE.erl | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index 604f5cc9b12c..97b02acb182a 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -317,6 +317,15 @@ groups() -> ]}, {with_token_endpoint_params_0, [], [ should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0 + ]}, + {with_resource_server_a, [], [ + {with_mgt_resource_server_a_with_authorization_endpoint_params_1, [], [ + should_return_mgt_oauth_resource_a_with_authorization_endpoint_params_1 + ]}, + {with_mgt_resource_server_a_with_token_endpoint_params_1, [], [ + should_return_mgt_oauth_resource_a_with_token_endpoint_params_1 + ]} + ]} ]} ]} @@ -472,6 +481,14 @@ init_per_group(with_token_endpoint_params_0, Config) -> set_env(rabbitmq_management, oauth_token_endpoint_params, ?config(token_params_0, Config)), Config; +init_per_group(with_mgt_resource_server_a_with_authorization_endpoint_params_1, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_authorization_endpoint_params, ?config(authorization_params_1, Config)), + Config; +init_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_token_endpoint_params, ?config(token_params_1, Config)), + Config; init_per_group(_, Config) -> @@ -583,6 +600,14 @@ end_per_group(with_authorization_endpoint_params_0, Config) -> end_per_group(with_token_endpoint_params_0, Config) -> unset_env(rabbitmq_management, oauth_token_endpoint_params), Config; +end_per_group(with_mgt_resource_server_a_with_authorization_endpoint_params_1, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_authorization_endpoint_params), + Config; +end_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_token_endpoint_params), + Config; end_per_group(_, Config) -> @@ -786,6 +811,14 @@ should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0(Config) -> assertEqual_on_attribute_for_oauth_resource_server(authSettings(), Config, rabbit, oauth_token_endpoint_params, token_params_0). +should_return_mgt_oauth_resource_a_with_authorization_endpoint_params_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_authorization_endpoint_params, authorization_params_1). + +should_return_mgt_oauth_resource_a_with_token_endpoint_params_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_token_endpoint_params, token_params_1). + %% ------------------------------------------------------------------- %% Utility/helper functions %% ------------------------------------------------------------------- From 6d0e195957733195d9a08e5e6346e2aef0a556fe Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 23 Sep 2024 19:04:53 +0200 Subject: [PATCH 43/64] Fix schema issues And fix selenium script to run rabbitrmq locally --- .../src/oauth2_schema.erl | 5 +- .../rabbitmq_auth_backend_oauth2.snippets | 6 ++ .../test/oauth2_schema_SUITE.erl | 4 +- .../priv/schema/rabbitmq_management.schema | 14 ++--- .../src/rabbit_mgmt_schema.erl | 25 ++++---- .../src/rabbit_mgmt_wm_auth.erl | 57 ++++++++++--------- .../rabbitmq_management.snippets | 33 ++++++++--- .../test/rabbit_mgmt_schema_SUITE.erl | 26 ++++----- .../test/rabbit_mgmt_wm_auth_SUITE.erl | 36 ++++++------ selenium/bin/gen-env-file | 1 + selenium/test/multi-oauth/env.local | 2 +- selenium/test/oauth/env.local | 2 +- .../rabbitmq.keycloak-mgt-oauth-provider.conf | 1 + 13 files changed, 122 insertions(+), 90 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index c24430bd87e2..dd16a480e012 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -69,8 +69,7 @@ translate_list_of_signing_keys(ListOfKidPath) -> -spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), - Params = [{list_to_binary(Param), list_to_binary(V)} || - {["auth_oauth2", _, Param], V} <- Params0], + Params = [{Param, V} || {["auth_oauth2", _, Param], V} <- Params0], maps:from_list(Params). validator_file_exists(Attr, Filename) -> @@ -120,7 +119,7 @@ mapOauthProviderProperty({Key, Value}) -> token_endpoint -> validator_https_uri(Key, Value); jwks_uri -> validator_https_uri(Key, Value); end_session_endpoint -> validator_https_uri(Key, Value); - authorization_endpoint -> validator_https_uri(Key, Value); + authorization_endpoint -> validator_https_uri(Key, Value); discovery_endpoint_params -> cuttlefish:invalid(io_lib:format( "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); 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 a76c0cdf1a23..582888332f27 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 @@ -18,6 +18,8 @@ auth_oauth2.https.depth = 5 auth_oauth2.https.fail_if_no_peer_cert = false auth_oauth2.https.hostname_verification = wildcard + auth_oauth2.discovery_endpoint_path = /.well-known/openid-configuration + auth_oauth2.discovery_endpoint_params.param1 = value1 auth_oauth2.https.crl_check = true auth_oauth2.algorithms.1 = HS256 auth_oauth2.algorithms.2 = RS256", @@ -30,6 +32,10 @@ {preferred_username_claims, [<<"user_name">>, <<"username">>, <<"email">>]}, {verify_aud, true}, {issuer, "https://my-jwt-issuer"}, + {discovery_endpoint_path, "/.well-known/openid-configuration"}, + {discovery_endpoint_params, #{ + "param1" => "value1" + }}, {key_config, [ {default_key, <<"id1">>}, {signing_keys, diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 3f581a847069..049314bb3f72 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -60,7 +60,7 @@ test_with_endpoint_params(_) -> {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"} ], - #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> } = + #{ "param1" := "some-value1", "param2" := "some-value2" } = translate_endpoint_params("discovery_endpoint_params", Conf). test_invalid_oauth_providers_endpoint_params(_) -> @@ -103,7 +103,7 @@ test_with_many_oauth_providers(_) -> {["auth_oauth2","oauth_providers","uaa","issuer"],"https://uaa"}, {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"],"/some-path"} ], - #{<<"keycloak">> := [{issuer, <<"https://keycloak">>} + #{<<"keycloak">> := [{issuer, <<"https://keycloak">>} ], <<"uaa">> := [{issuer, <<"https://uaa">>}, {discovery_endpoint_path, <<"/some-path">>} diff --git a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema index e297f765fe49..244a46261465 100644 --- a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema +++ b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema @@ -473,7 +473,7 @@ end}. [{datatype, string}]}. %% Configure OAuth2 authorization_endpoint additional request parameters -{mapping, "management.oauth_authorization_endpoint_params.$name", +{mapping, "management.oauth_authorization_endpoint_params.$name", "rabbitmq_management.oauth_authorization_endpoint_params", [{datatype, string}]}. @@ -483,7 +483,7 @@ end}. end}. %% Configure OAuth2 token_endpoint additional request parameters -{mapping, "management.oauth_token_endpoint_params.$name", +{mapping, "management.oauth_token_endpoint_params.$name", "rabbitmq_management.oauth_token_endpoint_params", [{datatype, string}]}. @@ -568,17 +568,17 @@ end}. "rabbitmq_management.oauth_resource_servers", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. -{mapping, "management.oauth_resource_servers.$name.oauth_authorization_endpoint_params.$name", - ""rabbitmq_management.oauth_resource_servers", +{mapping, "management.oauth_resource_servers.$name.oauth_authorization_endpoint_params.$name", + "rabbitmq_management.oauth_resource_servers", [{datatype, string}]}. -{mapping, "management.oauth_resource_servers.$name.oauth_token_endpoint_params.$name", - ""rabbitmq_management.oauth_resource_servers", +{mapping, "management.oauth_resource_servers.$name.oauth_token_endpoint_params.$name", + "rabbitmq_management.oauth_resource_servers", [{datatype, string}]}. {translation, "rabbitmq_management.oauth_resource_servers", fun(Conf) -> - rabbit_mgmt_schema:translate_resource_servers(Conf) + rabbit_mgmt_schema:translate_oauth_resource_servers(Conf) end}. %% =========================================================================== diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl index 518f5133ad53..443b777f5380 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -13,6 +13,7 @@ translate_endpoint_params/2 ]). +extract_key({Name,_}) -> Name. extract_key_as_binary({Name,_}) -> list_to_binary(Name). extract_value({_Name,V}) -> V. @@ -20,6 +21,7 @@ extract_value({_Name,V}) -> V. translate_oauth_resource_servers(Conf) -> Settings = cuttlefish_variable:filter_by_prefix( "management.oauth_resource_servers", Conf), + rabbit_log:debug("Settings: ~p", [Settings]), Map = merge_list_of_maps([ extract_resource_server_properties(Settings), extract_resource_server_endpoint_params(oauth_authorization_endpoint_params, Settings), @@ -37,9 +39,7 @@ translate_oauth_resource_servers(Conf) -> -spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("management." ++ Variable, Conf), - Params = [{list_to_binary(Param), list_to_binary(V)} || - {["management", _, Param], V} <- Params0], - maps:from_list(Params). + Params = [{Param, list_to_binary(V)} || {["management", _, Param], V} <- Params0]. merge_list_of_maps(ListOfMaps) -> lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, @@ -47,18 +47,23 @@ merge_list_of_maps(ListOfMaps) -> extract_resource_server_properties(Settings) -> - KeyFun = fun extract_key_as_binary/1, + KeyFun = fun extract_key/1, ValueFun = fun extract_value/1, - OAuthProviders = [{Name, {list_to_atom(Key), list_to_binary(V)}} + OAuthProviders = [{Name, {list_to_atom(Key), V}} || {["management","oauth_resource_servers", Name, Key], V} <- Settings ], - maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). + rabbit_log:debug("extract_resource_server_properties ~p", [Settings]), + Result = maps:groups_from_list(KeyFun, ValueFun, OAuthProviders), + rabbit_log:debug("extract_resource_server_properties -> ~p", [Result]), + + Result. extract_resource_server_endpoint_params(Variable, Settings) -> - KeyFun = fun extract_key_as_binary/1, + KeyFun = fun extract_key/1, - IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || + rabbit_log:debug("extract_resource_server_endpoint_params ~p ~p", [Variable, Settings]), + IndexedParams = [{Name, {ParamName, list_to_binary(V)}} || {["management","oauth_resource_servers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == atom_to_list(Variable) ], - maps:map(fun(_K,V)-> [{Variable, maps:from_list(V)}] end, - maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). \ No newline at end of file + maps:map(fun(_K,V)-> [{Variable, V}] end, + maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index ffa1ac8a6582..8b2f30e4c5ad 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -25,16 +25,16 @@ variances(Req, Context) -> content_types_provided(ReqData, Context) -> {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}. -merge_property(Key, List, MapIn) -> - case proplists:get_value(Key, List) of +merge_property(Key, List, MapIn) -> + case proplists:get_value(Key, List) of undefined -> MapIn; V0 -> MapIn#{Key => V0} end. extract_oauth_provider_info_props_as_map(ManagementProps) -> - lists:foldl(fun(K, Acc) -> - merge_property(K, ManagementProps, Acc) end, #{}, [oauth_provider_url, - oauth_metadata_url, oauth_authorization_endpoint_params, + lists:foldl(fun(K, Acc) -> + merge_property(K, ManagementProps, Acc) end, #{}, [oauth_provider_url, + oauth_metadata_url, oauth_authorization_endpoint_params, oauth_token_endpoint_params]). merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, ManagementProps) -> @@ -46,19 +46,19 @@ merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, ManagementProp {ok, OAuthProvider} -> oauth_provider_to_map(OAuthProvider); {error, _} -> #{} end, - OAuthProviderInfo1 = maps:merge(OAuthProviderInfo0, + OAuthProviderInfo1 = maps:merge(OAuthProviderInfo0, extract_oauth_provider_info_props_as_map(ManagementProps)), maps:merge(OAuthProviderInfo1, proplists:to_map(MgtResourceServer)). oauth_provider_to_map(OAuthProvider) -> % only include issuer and end_session_endpoint for now. The other endpoints are resolved by oidc-client library - Map0 = case OAuthProvider#oauth_provider.issuer of + Map0 = case OAuthProvider#oauth_provider.issuer of undefined -> #{}; Issuer -> #{ oauth_provider_url => Issuer, - oauth_metadata_url => OAuthProvider#oauth_provider.discovery_endpoint + oauth_metadata_url => OAuthProvider#oauth_provider.discovery_endpoint } end, - case OAuthProvider#oauth_provider.end_session_endpoint of + case OAuthProvider#oauth_provider.end_session_endpoint of undefined -> Map0; V -> maps:put(end_session_endpoint, V, Map0) end. @@ -80,7 +80,7 @@ extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) -> MgtResources = maps:map( fun(K,V) -> merge_oauth_provider_info(maps:get(K, OAuth2Resources, #{}), V, ManagementProps) end, skip_disabled_mgt_resource_servers(MgtResources1)), - case maps:size(MgtResources) of + case maps:size(MgtResources) of 0 -> {}; _ -> {MgtResources} end. @@ -89,21 +89,21 @@ getAllDeclaredOauth2Resources(OAuth2BackendProps) -> OAuth2Resources = proplists:get_value(resource_servers, OAuth2BackendProps, #{}), case proplists:get_value(resource_server_id, OAuth2BackendProps) of undefined -> OAuth2Resources; - Id -> maps:put(Id, buildRootResourceServerIfAny(Id, OAuth2BackendProps), + Id -> maps:put(Id, buildRootResourceServerIfAny(Id, OAuth2BackendProps), OAuth2Resources) end. buildRootResourceServerIfAny(Id, Props) -> - [ {id, Id}, - {oauth_client_id, - proplists:get_value(oauth_client_id, Props)}, + [ {id, Id}, + {oauth_client_id, + proplists:get_value(oauth_client_id, Props)}, {oauth_client_secret, proplists:get_value(oauth_client_secret, Props)}, - {oauth_response_type, + {oauth_response_type, proplists:get_value(oauth_response_type, Props)}, - {authorization_endpoint_params, + {authorization_endpoint_params, proplists:get_value(authorization_endpoint_params, Props)}, - {token_endpoint_params, - proplists:get_value(token_endpoint_params, Props)} + {token_endpoint_params, + proplists:get_value(token_endpoint_params, Props)} ]. authSettings() -> @@ -114,7 +114,10 @@ authSettings() -> false -> [{oauth_enabled, false}]; true -> case extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) of - {MgtResources} -> produce_auth_settings(MgtResources, ManagementProps); + {MgtResources} -> + Settings = produce_auth_settings(MgtResources, ManagementProps), + rabbit_log:debug("authSettings: ~p", [Settings]), + Settings; {} -> [{oauth_enabled, false}] end end. @@ -137,18 +140,18 @@ filter_mgt_resource_servers_without_oauth_client_id_for_sp_initiated(MgtResource end. filter_mgt_resource_servers_without_oauth_provider_url(MgtResourceServers) -> - maps:filter(fun(_K1,V1) -> maps:is_key(oauth_provider_url, V1) end, MgtResourceServers). + maps:filter(fun(_K1,V1) -> maps:is_key(oauth_provider_url, V1) end, MgtResourceServers). ensure_oauth_resource_server_properties_are_binaries(Key, Value) -> - case Key of + case Key of oauth_authorization_endpoint_params -> Value; oauth_token_endpoint_params -> Value; _ -> to_binary(Value) end. produce_auth_settings(MgtResourceServers, ManagementProps) -> - ConvertValuesToBinary = fun(_K,V) -> [ - {K1, ensure_oauth_resource_server_properties_are_binaries(K1, V1)} || {K1,V1} + ConvertValuesToBinary = fun(_K,V) -> [ + {K1, ensure_oauth_resource_server_properties_are_binaries(K1, V1)} || {K1,V1} <- maps:to_list(V)] end, FilteredMgtResourceServers = filter_mgt_resource_servers_without_oauth_provider_url( filter_mgt_resource_servers_without_oauth_client_id_for_sp_initiated(MgtResourceServers, ManagementProps)), @@ -202,14 +205,14 @@ to_tuple(Key, Proplist) -> to_tuple(Key, Proplist, ConvertFun, DefaultValue) -> case proplists:is_defined(Key, Proplist) of - true -> - {Key, case ConvertFun of + true -> + {Key, case ConvertFun of undefined -> proplists:get_value(Key, Proplist); _ -> ConvertFun(proplists:get_value(Key, Proplist)) end }; - false -> - case DefaultValue of + false -> + case DefaultValue of undefined -> {}; _ -> {Key, proplists:get_value(Key, Proplist, DefaultValue)} end diff --git a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets index d26639620bb8..bcd787a7c8f4 100644 --- a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets +++ b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets @@ -621,15 +621,23 @@ management.oauth_client_id = rabbitmq_client_code management.oauth_client_secret = rabbitmq_client_secret management.oauth_scopes = openid profile rabbitmq.* + management.oauth_authorization_endpoint_params.param1 = value1 + management.oauth_token_endpoint_params.param2 = value2 management.oauth_initiated_logon_type = idp_initiated", [ {rabbitmq_management, [ + {oauth_authorization_endpoint_params, [ + {"param1", <<"value1">>} + ]}, {oauth_enabled, true}, {oauth_provider_url, "http://localhost:8080"}, {oauth_client_id, "rabbitmq_client_code"}, {oauth_client_secret, "rabbitmq_client_secret"}, {oauth_scopes, "openid profile rabbitmq.*"}, - {oauth_initiated_logon_type, idp_initiated} + {oauth_initiated_logon_type, idp_initiated}, + {oauth_token_endpoint_params, [ + {"param2", <<"value2">>} + ]} ]} ], [rabbitmq_management] }, @@ -640,7 +648,9 @@ management.oauth_resource_servers.1.label = One management.oauth_resource_servers.1.oauth_client_id = one management.oauth_resource_servers.1.oauth_scopes = openid profile rabbitmq.* + management.oauth_resource_servers.1.oauth_token_endpoint_params.param2 = value2 management.oauth_resource_servers.2.oauth_provider_url = http://two + management.oauth_resource_servers.2.oauth_authorization_endpoint_params.param1 = value1 management.oauth_resource_servers.2.id = resource-two management.oauth_resource_servers.2.oauth_client_id = two management.oauth_resource_servers.3.oauth_initiated_logon_type = idp_initiated @@ -650,21 +660,28 @@ {oauth_enabled, true}, {oauth_resource_servers, #{ - <<"resource-one">> => [ + "3" => [ + {oauth_provider_url, "http://three"}, + {oauth_initiated_logon_type, idp_initiated}, + {id, "3"} + ], + "resource-one" => [ + {oauth_token_endpoint_params, [ + {"param2", <<"value2">>} + ]}, {oauth_scopes, "openid profile rabbitmq.*"}, {oauth_client_id, "one"}, - {id, "resource-one"}, {label, "One"}, + {id, "resource-one"}, {oauth_provider_url, "http://one:8080"} ], - <<"resource-two">> => [ + "resource-two" => [ + {oauth_authorization_endpoint_params, [ + {"param1", <<"value1">>} + ]}, {oauth_client_id, "two"}, {id, "resource-two"}, {oauth_provider_url, "http://two"} - ], - <<"3">> => [ - {oauth_initiated_logon_type, idp_initiated}, - {oauth_provider_url, "http://three"} ] } } diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl index 7faa6aac307b..bc35bbcaee51 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl @@ -14,7 +14,7 @@ -import(rabbit_mgmt_schema, [translate_endpoint_params/2, translate_oauth_resource_servers/1]). all() -> - [ + [ test_empty_endpoint_params, test_invalid_endpoint_params, test_translate_endpoint_params, @@ -25,7 +25,7 @@ all() -> test_empty_endpoint_params(_) -> #{} = translate_endpoint_params("oauth_authorization_endpoint_params", []), - #{} = translate_endpoint_params("oauth_token_endpoint_params", []). + #{} = translate_endpoint_params("oauth_token_endpoint_params", []). test_invalid_endpoint_params(_) -> try translate_endpoint_params("oauth_authorization_endpoint_params", [ @@ -35,8 +35,8 @@ test_invalid_endpoint_params(_) -> _ -> ok end. -test_translate_endpoint_params(_) -> - #{ <<"param1">> := <<"some-value1">> } = +test_translate_endpoint_params(_) -> + #{ "param1" := "some-value1" } = translate_endpoint_params("oauth_authorization_endpoint_params", [ {["management","oauth_authorization_endpoint_params","param1"], "some-value1"} ]). @@ -44,10 +44,10 @@ test_translate_endpoint_params(_) -> test_with_one_resource_server(_) -> Conf = [ {["management","oauth_resource_servers","rabbitmq1","id"],"rabbitmq1"} - ], + ], #{ - <<"rabbitmq1">> := [ - {id, <<"rabbitmq1">>} + "rabbitmq1" := [ + {id, "rabbitmq1"} ] } = translate_oauth_resource_servers(Conf). @@ -57,13 +57,13 @@ test_with_many_resource_servers(_) -> {["management","oauth_resource_servers","uaa","label"],"Uaa"} ], #{ - <<"keycloak">> := [ - {label, <<"Keycloak">>}, - {id, <<"keycloak">>} + "keycloak" := [ + {label, "Keycloak"}, + {id, "keycloak"} ], - <<"uaa">> := [ - {label, <<"Uaa">>}, - {id, <<"uaa">>} + "uaa" := [ + {label, "Uaa"}, + {id, "uaa"} ] } = translate_oauth_resource_servers(Conf). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index 97b02acb182a..886f5d43be0b 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -26,7 +26,7 @@ all() -> {group, verify_oauth_initiated_logon_type_for_idp_initiated}, {group, verify_oauth_disable_basic_auth}, {group, verify_oauth_scopes}, - {group, verify_extra_endpoint_params} + {group, verify_extra_endpoint_params} ]. groups() -> @@ -42,7 +42,7 @@ groups() -> should_return_disabled_auth_settings, {with_root_issuer_url1, [], [ {with_resource_server_id_rabbit, [], [ - should_return_disabled_auth_settings, + should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_enabled, should_return_oauth_client_id_z, @@ -110,7 +110,7 @@ groups() -> {with_resource_server_id_rabbit, [], [ {with_root_issuer_url1, [], [ {with_oauth_enabled, [], [ - {with_mgt_oauth_client_id_z, [], [ + {with_mgt_oauth_client_id_z, [], [ should_not_return_end_session_endpoint, {with_root_end_session_endpoint_0, [], [ should_return_end_session_endpoint_0 @@ -120,7 +120,7 @@ groups() -> ]}, {with_oauth_providers_idp1_idp2, [], [ {with_default_oauth_provider_idp1, [], [ - {with_oauth_enabled, [], [ + {with_oauth_enabled, [], [ {with_mgt_oauth_client_id_z, [], [ should_not_return_end_session_endpoint, {with_end_session_endpoint_for_idp1_1, [], [ @@ -149,7 +149,7 @@ groups() -> should_return_oauth_resource_server_a_without_end_session_endpoint, {with_root_end_session_endpoint_0, [], [ should_return_end_session_endpoint_0, - should_return_oauth_resource_server_a_with_end_session_endpoint_0 + should_return_oauth_resource_server_a_with_end_session_endpoint_0 ]}, {with_oauth_providers_idp1_idp2, [], [ {with_default_oauth_provider_idp1, [], [ @@ -159,11 +159,11 @@ groups() -> {with_oauth_provider_idp2_for_resource_server_a, [], [ {with_end_session_endpoint_for_idp2_2, [], [ should_return_oauth_resource_server_a_with_end_session_endpoint_2 - ]} + ]} ]} ]} ]} - ]} + ]} ]} ]} ]} @@ -190,9 +190,9 @@ groups() -> should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, should_return_oauth_resource_server_a_with_oauth_provider_url_url1, {with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url0, [], [ - should_return_oauth_resource_server_a_with_oauth_metadata_url_url0 + should_return_oauth_resource_server_a_with_oauth_metadata_url_url0 ]} - ]} + ]} ]} ]} ]} @@ -212,7 +212,7 @@ groups() -> should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url, {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, - should_return_oauth_resource_server_a_with_oauth_provider_url_url1 + should_return_oauth_resource_server_a_with_oauth_provider_url_url1 ]} ]} ]} @@ -221,7 +221,7 @@ groups() -> ]} ]} ]} - ]}, + ]}, {verify_oauth_initiated_logon_type_for_sp_initiated, [], [ should_return_disabled_auth_settings, {with_resource_server_id_rabbit, [], [ @@ -313,7 +313,7 @@ groups() -> should_return_mgt_oauth_resource_rabbit_without_authorization_endpoint_params, should_return_mgt_oauth_resource_rabbit_without_token_endpoint_params, {with_authorization_endpoint_params_0, [], [ - should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0 + should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0 ]}, {with_token_endpoint_params_0, [], [ should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0 @@ -330,7 +330,7 @@ groups() -> ]} ]} ]} - ]} + ]} ]} ]. @@ -361,10 +361,10 @@ init_per_suite(Config) -> {w, <<"w">>}, {z, <<"z">>}, {x, <<"x">>}, - {authorization_params_0, [{<<"a-param0">>, <<"value0">>}]}, - {authorization_params_1, [{<<"a-param1">>, <<"value1">>}]}, - {token_params_0, [{<<"t-param0">>, <<"value0">>}]}, - {token_params_1, [{<<"t-param1">>, <<"value1">>}]}, + {authorization_params_0, [{"a-param0", "value0"}]}, + {authorization_params_1, [{"a-param1", "value1"}]}, + {token_params_0, [{"t-param0", "value0"}]}, + {token_params_1, [{"t-param1", "value1"}]}, {admin_mgt, <<"admin mgt">>}, {read_write, <<"read write">>} | Config]. @@ -750,7 +750,7 @@ should_return_oauth_enabled(_Config) -> Actual = authSettings(), ?assertEqual(true, proplists:get_value(oauth_enabled, Actual)). - + should_return_oauth_idp_initiated_logon(_Config) -> Actual = authSettings(), ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). diff --git a/selenium/bin/gen-env-file b/selenium/bin/gen-env-file index 60c4b4bfc50d..731cefcecb8b 100755 --- a/selenium/bin/gen-env-file +++ b/selenium/bin/gen-env-file @@ -13,6 +13,7 @@ generate_env_file() { mkdir -p $parentdir echo "#!/usr/bin/env bash" > $ENV_FILE echo "set -u" >> $ENV_FILE + echo "export SELENIUM=${SCRIPT}/.." >> $ENV_FILE declare -a FILE_ARRAY for f in $($SCRIPT/find-template-files $FIND_PATH "env") diff --git a/selenium/test/multi-oauth/env.local b/selenium/test/multi-oauth/env.local index c61124da53a7..3ae2df57c061 100644 --- a/selenium/test/multi-oauth/env.local +++ b/selenium/test/multi-oauth/env.local @@ -1 +1 @@ -export OAUTH_SERVER_CONFIG_BASEDIR=test +export OAUTH_SERVER_CONFIG_BASEDIR=${SELENIUM}/test diff --git a/selenium/test/oauth/env.local b/selenium/test/oauth/env.local index c61124da53a7..3ae2df57c061 100644 --- a/selenium/test/oauth/env.local +++ b/selenium/test/oauth/env.local @@ -1 +1 @@ -export OAUTH_SERVER_CONFIG_BASEDIR=test +export OAUTH_SERVER_CONFIG_BASEDIR=${SELENIUM}/test diff --git a/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf b/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf index 9e6e55f94073..b9e65845d55e 100644 --- a/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf +++ b/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf @@ -1,2 +1,3 @@ # uaa requires a secret in order to renew tokens management.oauth_provider_url = ${KEYCLOAK_URL} +management.oauth_authorization_endpoint_params.resource = rabbitmq From 6e74d8b60ec2d120e4088a236c05c11d12e24526 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 11:49:21 +0200 Subject: [PATCH 44/64] Always use list() type for urls --- .../src/oauth2_schema.erl | 21 +++++++++--- .../rabbitmq_auth_backend_oauth2.snippets | 22 ++++++++----- .../test/oauth2_schema_SUITE.erl | 33 ++++++++++--------- .../src/rabbit_mgmt_schema.erl | 4 +-- .../rabbitmq_management.snippets | 6 ++-- .../test/rabbit_mgmt_schema_SUITE.erl | 6 ++-- .../test/rabbit_mgmt_wm_auth_SUITE.erl | 8 ++--- 7 files changed, 60 insertions(+), 40 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index dd16a480e012..ce7a6f60d6b9 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -69,8 +69,7 @@ translate_list_of_signing_keys(ListOfKidPath) -> -spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), - Params = [{Param, V} || {["auth_oauth2", _, Param], V} <- Params0], - maps:from_list(Params). + [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", _, Param], V} <- Params0]. validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of @@ -81,10 +80,21 @@ validator_file_exists(Attr, Filename) -> cuttlefish:invalid(io_lib:format( "Invalid attribute (~p) value: file ~p does not exist or cannot be read by the node", [Attr, Filename])) end. + +validator_uri(Attr, Uri) when is_binary(Uri) -> + validator_uri(Attr, binary_to_list(Uri)); +validator_uri(Attr, Uri) when is_list(Uri) -> + case uri_string:parse(Uri) of + {error, _, _} = Error -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: ~p (~p)", [Attr, Uri, Error])); + _ -> Uri + end. + validator_https_uri(Attr, Uri) when is_binary(Uri) -> - list_to_binary(validator_https_uri(Attr, binary_to_list(Uri))); + validator_https_uri(Attr, binary_to_list(Uri)); -validator_https_uri(Attr, Uri) -> +validator_https_uri(Attr, Uri) when is_list(Uri) -> case string:nth_lexeme(Uri, 1, "://") == "https" of true -> Uri; false -> @@ -120,6 +130,7 @@ mapOauthProviderProperty({Key, Value}) -> jwks_uri -> validator_https_uri(Key, Value); end_session_endpoint -> validator_https_uri(Key, Value); authorization_endpoint -> validator_https_uri(Key, Value); + discovery_endpoint_path -> validator_uri(Key, Value); discovery_endpoint_params -> cuttlefish:invalid(io_lib:format( "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); @@ -167,7 +178,7 @@ extract_oauth_providers_endpoint_params(Variable, Settings) -> IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || {["auth_oauth2","oauth_providers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == atom_to_list(Variable) ], - maps:map(fun(_K,V)-> [{Variable, maps:from_list(V)}] end, + maps:map(fun(_K,V)-> [{Variable, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). extract_oauth_providers_signing_keys(Settings) -> 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 582888332f27..4638312ecb52 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 @@ -33,9 +33,9 @@ {verify_aud, true}, {issuer, "https://my-jwt-issuer"}, {discovery_endpoint_path, "/.well-known/openid-configuration"}, - {discovery_endpoint_params, #{ - "param1" => "value1" - }}, + {discovery_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, {key_config, [ {default_key, <<"id1">>}, {signing_keys, @@ -142,6 +142,8 @@ auth_oauth2.oauth_providers.keycloak.https.depth = 2 auth_oauth2.oauth_providers.keycloak.default_key = token-key auth_oauth2.oauth_providers.keycloak.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem + auth_oauth2.oauth_providers.keycloak.discovery_endpoint_path = /.well-known/openid-configuration + auth_oauth2.oauth_providers.keycloak.discovery_endpoint_params.param1 = value1 auth_oauth2.oauth_providers.keycloak.algorithms.1 = HS256 auth_oauth2.oauth_providers.keycloak.algorithms.2 = RS256", [ @@ -166,14 +168,18 @@ {cacertfile, "test/config_schema_SUITE_data/certs/cacert.pem"} ]}, {algorithms, [<<"HS256">>, <<"RS256">>]}, + {discovery_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, + {discovery_endpoint_path, "/.well-known/openid-configuration"}, {default_key, <<"token-key">>}, - {end_session_endpoint, <<"https://keycloak/logout">>}, - {authorization_endpoint, <<"https://keycloak/authorize">>}, - {jwks_uri, <<"https://keycloak/keys">>}, - {token_endpoint, <<"https://keycloak/token">>} + {end_session_endpoint, "https://keycloak/logout"}, + {authorization_endpoint, "https://keycloak/authorize"}, + {jwks_uri, "https://keycloak/keys"}, + {token_endpoint, "https://keycloak/token"} ], <<"uaa">> => [ - {issuer, <<"https://uaa">>} + {issuer, "https://uaa"} ] } diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl index 049314bb3f72..05705f649ca6 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl @@ -45,7 +45,7 @@ test_without_resource_servers(_) -> #{} = oauth2_schema:translate_resource_servers([]). test_without_endpoint_params(_) -> - #{} = translate_endpoint_params("oauth_discovery_endpoint_params", []). + [] = translate_endpoint_params("oauth_discovery_endpoint_params", []). test_with_invalid_endpoint_params(_) -> try translate_endpoint_params("discovery_endpoint_params", [ @@ -60,7 +60,7 @@ test_with_endpoint_params(_) -> {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"} ], - #{ "param1" := "some-value1", "param2" := "some-value2" } = + [ {<<"param1">>, <<"some-value1">>}, {<<"param2">>, <<"some-value2">>} ] = translate_endpoint_params("discovery_endpoint_params", Conf). test_invalid_oauth_providers_endpoint_params(_) -> @@ -79,17 +79,20 @@ test_without_oauth_providers_with_endpoint_params(_) -> ], #{ - <<"A">> := [{discovery_endpoint_params, - #{ <<"param1">> := <<"some-value1">>, <<"param2">> := <<"some-value2">> }}], - <<"B">> := [{discovery_endpoint_params, - #{ <<"param3">> := <<"some-value3">>}} - ] + <<"A">> := [{discovery_endpoint_params, [ + {<<"param1">>, <<"some-value1">>}, + {<<"param2">>, <<"some-value2">>} + ]}], + <<"B">> := [{discovery_endpoint_params, [ + {<<"param3">>, <<"some-value3">>} + ]}] + } = translate_oauth_providers(Conf). test_with_one_oauth_provider(_) -> Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} ], - #{<<"keycloak">> := [{issuer, <<"https://rabbit">>}] + #{<<"keycloak">> := [{issuer, "https://rabbit"}] } = oauth2_schema:translate_oauth_providers(Conf). test_with_one_resource_server(_) -> @@ -103,10 +106,10 @@ test_with_many_oauth_providers(_) -> {["auth_oauth2","oauth_providers","uaa","issuer"],"https://uaa"}, {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"],"/some-path"} ], - #{<<"keycloak">> := [{issuer, <<"https://keycloak">>} + #{<<"keycloak">> := [{issuer, "https://keycloak"} ], - <<"uaa">> := [{issuer, <<"https://uaa">>}, - {discovery_endpoint_path, <<"/some-path">>} + <<"uaa">> := [{issuer, "https://uaa"}, + {discovery_endpoint_path, "/some-path"} ] } = oauth2_schema:translate_oauth_providers(Conf). @@ -126,7 +129,7 @@ test_oauth_providers_attributes(_) -> {["auth_oauth2","oauth_providers","keycloak","default_key"],"token-key"} ], #{<<"keycloak">> := [{default_key, <<"token-key">>}, - {issuer, <<"https://keycloak">>} + {issuer, "https://keycloak"} ] } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). @@ -173,7 +176,7 @@ test_oauth_providers_algorithms(_) -> {["auth_oauth2","oauth_providers","keycloak","algorithms","1"],"RS256"} ], #{<<"keycloak">> := [{algorithms, [<<"RS256">>, <<"HS256">>]}, - {issuer, <<"https://keycloak">>} + {issuer, "https://keycloak"} ] } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). @@ -196,7 +199,7 @@ test_oauth_providers_https(Conf) -> {fail_if_no_peer_cert, true}, {cacertfile, _CaCertFile} ]}, - {issuer, <<"https://keycloak">>} + {issuer, "https://keycloak"} ] } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)). @@ -216,7 +219,7 @@ test_oauth_providers_signing_keys(Conf) -> {["auth_oauth2","oauth_providers","keycloak","signing_keys","2"], cert_filename(Conf)}, {["auth_oauth2","oauth_providers","keycloak","signing_keys","1"], cert_filename(Conf)} ], - #{<<"keycloak">> := [{issuer, <<"https://keycloak">>}, + #{<<"keycloak">> := [{issuer, "https://keycloak"}, {signing_keys, SigningKeys} ] } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)), diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl index 443b777f5380..96fccf66146a 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -39,7 +39,7 @@ translate_oauth_resource_servers(Conf) -> -spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("management." ++ Variable, Conf), - Params = [{Param, list_to_binary(V)} || {["management", _, Param], V} <- Params0]. + [{list_to_binary(Param), list_to_binary(V)} || {["management", _, Param], V} <- Params0]. merge_list_of_maps(ListOfMaps) -> lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, @@ -62,7 +62,7 @@ extract_resource_server_endpoint_params(Variable, Settings) -> KeyFun = fun extract_key/1, rabbit_log:debug("extract_resource_server_endpoint_params ~p ~p", [Variable, Settings]), - IndexedParams = [{Name, {ParamName, list_to_binary(V)}} || + IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || {["management","oauth_resource_servers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == atom_to_list(Variable) ], maps:map(fun(_K,V)-> [{Variable, V}] end, diff --git a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets index bcd787a7c8f4..fba6a98dd572 100644 --- a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets +++ b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets @@ -627,7 +627,7 @@ [ {rabbitmq_management, [ {oauth_authorization_endpoint_params, [ - {"param1", <<"value1">>} + {<<"param1">>, <<"value1">>} ]}, {oauth_enabled, true}, {oauth_provider_url, "http://localhost:8080"}, @@ -667,7 +667,7 @@ ], "resource-one" => [ {oauth_token_endpoint_params, [ - {"param2", <<"value2">>} + {<<"param2">>, <<"value2">>} ]}, {oauth_scopes, "openid profile rabbitmq.*"}, {oauth_client_id, "one"}, @@ -677,7 +677,7 @@ ], "resource-two" => [ {oauth_authorization_endpoint_params, [ - {"param1", <<"value1">>} + {<<"param1">>, <<"value1">>} ]}, {oauth_client_id, "two"}, {id, "resource-two"}, diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl index bc35bbcaee51..4ffc3190bfc2 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl @@ -24,8 +24,8 @@ all() -> test_empty_endpoint_params(_) -> - #{} = translate_endpoint_params("oauth_authorization_endpoint_params", []), - #{} = translate_endpoint_params("oauth_token_endpoint_params", []). + [] = translate_endpoint_params("oauth_authorization_endpoint_params", []), + [] = translate_endpoint_params("oauth_token_endpoint_params", []). test_invalid_endpoint_params(_) -> try translate_endpoint_params("oauth_authorization_endpoint_params", [ @@ -36,7 +36,7 @@ test_invalid_endpoint_params(_) -> end. test_translate_endpoint_params(_) -> - #{ "param1" := "some-value1" } = + [ {<<"param1">>, <<"some-value1">>} ] = translate_endpoint_params("oauth_authorization_endpoint_params", [ {["management","oauth_authorization_endpoint_params","param1"], "some-value1"} ]). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index 886f5d43be0b..d5a13fa75c3b 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -361,10 +361,10 @@ init_per_suite(Config) -> {w, <<"w">>}, {z, <<"z">>}, {x, <<"x">>}, - {authorization_params_0, [{"a-param0", "value0"}]}, - {authorization_params_1, [{"a-param1", "value1"}]}, - {token_params_0, [{"t-param0", "value0"}]}, - {token_params_1, [{"t-param1", "value1"}]}, + {authorization_params_0, [{<<"a-param0">>, <<"value0">>}]}, + {authorization_params_1, [{<<"a-param1">>, <<"value1">>}]}, + {token_params_0, [{<<"t-param0">>, <<"value0">>}]}, + {token_params_1, [{<<"t-param1">>, <<"value1">>}]}, {admin_mgt, <<"admin mgt">>}, {read_write, <<"read write">>} | Config]. From 4b7f8b28e2c11ff0c2ca17c781026c4d212b2693 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 13:15:45 +0200 Subject: [PATCH 45/64] Fix schema mapping issues And location of cert files when running multioauth test suites locally --- .../src/rabbit_mgmt_schema.erl | 17 ++++++----- .../src/rabbit_mgmt_wm_auth.erl | 3 ++ .../rabbitmq_management.snippets | 28 +++++++++---------- .../test/rabbit_mgmt_schema_SUITE.erl | 16 +++++------ .../test/rabbit_mgmt_wm_auth_SUITE.erl | 19 ++++++++++++- .../test/multi-oauth/env.local.devkeycloak | 2 +- .../test/multi-oauth/env.local.prodkeycloak | 2 +- 7 files changed, 53 insertions(+), 34 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl index 96fccf66146a..14c359621494 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -21,7 +21,6 @@ extract_value({_Name,V}) -> V. translate_oauth_resource_servers(Conf) -> Settings = cuttlefish_variable:filter_by_prefix( "management.oauth_resource_servers", Conf), - rabbit_log:debug("Settings: ~p", [Settings]), Map = merge_list_of_maps([ extract_resource_server_properties(Settings), extract_resource_server_endpoint_params(oauth_authorization_endpoint_params, Settings), @@ -45,23 +44,23 @@ merge_list_of_maps(ListOfMaps) -> lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, Elem, AccIn) end, #{}, ListOfMaps). +convert_list_to_binary(V) when is_list(V) -> + list_to_binary(V); +convert_list_to_binary(V) -> + V. extract_resource_server_properties(Settings) -> - KeyFun = fun extract_key/1, + KeyFun = fun extract_key_as_binary/1, ValueFun = fun extract_value/1, - OAuthProviders = [{Name, {list_to_atom(Key), V}} + OAuthResourceServers = [{Name, {list_to_atom(Key), convert_list_to_binary(V)}} || {["management","oauth_resource_servers", Name, Key], V} <- Settings ], - rabbit_log:debug("extract_resource_server_properties ~p", [Settings]), - Result = maps:groups_from_list(KeyFun, ValueFun, OAuthProviders), - rabbit_log:debug("extract_resource_server_properties -> ~p", [Result]), + maps:groups_from_list(KeyFun, ValueFun, OAuthResourceServers). - Result. extract_resource_server_endpoint_params(Variable, Settings) -> - KeyFun = fun extract_key/1, + KeyFun = fun extract_key_as_binary/1, - rabbit_log:debug("extract_resource_server_endpoint_params ~p ~p", [Variable, Settings]), IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || {["management","oauth_resource_servers", Name, EndpointVar, ParamName], V} <- Settings, EndpointVar == atom_to_list(Variable) ], diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 8b2f30e4c5ad..c477464867e0 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -80,6 +80,9 @@ extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) -> MgtResources = maps:map( fun(K,V) -> merge_oauth_provider_info(maps:get(K, OAuth2Resources, #{}), V, ManagementProps) end, skip_disabled_mgt_resource_servers(MgtResources1)), + rabbit_log:debug("ManagementProps: ~p", [ManagementProps]), + rabbit_log:debug("extract_oauth2_and_mgt_resources OAuth2Resources: ~p, MgtResources0: ~p MgtResources1: ~p MgtResources: ~p", + [OAuth2Resources, MgtResources0, MgtResources1, MgtResources]), case maps:size(MgtResources) of 0 -> {}; _ -> {MgtResources} diff --git a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets index fba6a98dd572..1208f4ddad0f 100644 --- a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets +++ b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets @@ -636,7 +636,7 @@ {oauth_scopes, "openid profile rabbitmq.*"}, {oauth_initiated_logon_type, idp_initiated}, {oauth_token_endpoint_params, [ - {"param2", <<"value2">>} + {<<"param2">>, <<"value2">>} ]} ]} ], [rabbitmq_management] @@ -660,28 +660,28 @@ {oauth_enabled, true}, {oauth_resource_servers, #{ - "3" => [ - {oauth_provider_url, "http://three"}, + <<"3">> => [ + {oauth_provider_url, <<"http://three">>}, {oauth_initiated_logon_type, idp_initiated}, - {id, "3"} + {id, <<"3">>} ], - "resource-one" => [ + <<"resource-one">> => [ {oauth_token_endpoint_params, [ {<<"param2">>, <<"value2">>} ]}, - {oauth_scopes, "openid profile rabbitmq.*"}, - {oauth_client_id, "one"}, - {label, "One"}, - {id, "resource-one"}, - {oauth_provider_url, "http://one:8080"} + {oauth_scopes, <<"openid profile rabbitmq.*">>}, + {oauth_client_id, <<"one">>}, + {label, <<"One">>}, + {id, <<"resource-one">>}, + {oauth_provider_url, <<"http://one:8080">>} ], - "resource-two" => [ + <<"resource-two">> => [ {oauth_authorization_endpoint_params, [ {<<"param1">>, <<"value1">>} ]}, - {oauth_client_id, "two"}, - {id, "resource-two"}, - {oauth_provider_url, "http://two"} + {oauth_client_id, <<"two">>}, + {id, <<"resource-two">>}, + {oauth_provider_url, <<"http://two">>} ] } } diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl index 4ffc3190bfc2..47c369978cb9 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl @@ -46,8 +46,8 @@ test_with_one_resource_server(_) -> {["management","oauth_resource_servers","rabbitmq1","id"],"rabbitmq1"} ], #{ - "rabbitmq1" := [ - {id, "rabbitmq1"} + <<"rabbitmq1">> := [ + {id, <<"rabbitmq1">>} ] } = translate_oauth_resource_servers(Conf). @@ -57,13 +57,13 @@ test_with_many_resource_servers(_) -> {["management","oauth_resource_servers","uaa","label"],"Uaa"} ], #{ - "keycloak" := [ - {label, "Keycloak"}, - {id, "keycloak"} + <<"keycloak">> := [ + {label, <<"Keycloak">>}, + {id, <<"keycloak">>} ], - "uaa" := [ - {label, "Uaa"}, - {id, "uaa"} + <<"uaa">> := [ + {label, <<"Uaa">>}, + {id, <<"uaa">>} ] } = translate_oauth_resource_servers(Conf). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index d5a13fa75c3b..970630b6aaf6 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -22,6 +22,7 @@ all() -> {group, verify_mgt_oauth_provider_url_with_single_resource_and_another_resource}, {group, verify_end_session_endpoint_with_single_resource}, {group, verify_end_session_endpoint_with_single_resource_and_another_resource}, + {group, verify_multi_resource_and_provider}, {group, verify_oauth_initiated_logon_type_for_sp_initiated}, {group, verify_oauth_initiated_logon_type_for_idp_initiated}, {group, verify_oauth_disable_basic_auth}, @@ -31,6 +32,22 @@ all() -> groups() -> [ + + {verify_multi_resource_and_provider, [], [ + {with_oauth_enabled, [], [ + {with_oauth_providers_idp1_idp2, [], [ + {with_default_oauth_provider_idp1, [], [ + {with_resource_server_a, [], [ + should_return_disabled_auth_settings, + {with_mgt_resource_server_a_with_client_id_x, [], [ + should_return_oauth_enabled, + should_return_oauth_resource_server_a_with_client_id_x + ]} + ]} + ]} + ]} + ]} + ]}, {without_any_settings, [], [ should_return_disabled_auth_settings ]}, @@ -325,7 +342,6 @@ groups() -> {with_mgt_resource_server_a_with_token_endpoint_params_1, [], [ should_return_mgt_oauth_resource_a_with_token_endpoint_params_1 ]} - ]} ]} ]} @@ -452,6 +468,7 @@ init_per_group(with_mgt_resource_server_a_with_client_id_x, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_client_id, ?config(x, Config)), Config; + init_per_group(with_default_oauth_provider_idp1, Config) -> set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp1, Config)), Config; diff --git a/selenium/test/multi-oauth/env.local.devkeycloak b/selenium/test/multi-oauth/env.local.devkeycloak index 8e5a2f2e9285..1a2b7cb0c286 100644 --- a/selenium/test/multi-oauth/env.local.devkeycloak +++ b/selenium/test/multi-oauth/env.local.devkeycloak @@ -1,2 +1,2 @@ export DEVKEYCLOAK_URL=https://localhost:8442/realms/dev -export DEVKEYCLOAK_CA_CERT=test/multi-oauth/devkeycloak/ca_certificate.pem +export DEVKEYCLOAK_CA_CERT=${SELENIUM}/test/multi-oauth/devkeycloak/ca_certificate.pem diff --git a/selenium/test/multi-oauth/env.local.prodkeycloak b/selenium/test/multi-oauth/env.local.prodkeycloak index c636bf8fcd55..2a2e9845c704 100644 --- a/selenium/test/multi-oauth/env.local.prodkeycloak +++ b/selenium/test/multi-oauth/env.local.prodkeycloak @@ -1,2 +1,2 @@ export PRODKEYCLOAK_URL=https://localhost:8443/realms/prod -export PRODKEYCLOAK_CA_CERT=test/multi-oauth/prodkeycloak/ca_certificate.pem +export PRODKEYCLOAK_CA_CERT=${SELENIUM}/test/multi-oauth/prodkeycloak/ca_certificate.pem From c1e8279743df1644a5fb6e502262bdcfbea46026 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 13:19:32 +0200 Subject: [PATCH 46/64] Remove function --- deps/rabbitmq_management/src/rabbit_mgmt_schema.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl index 14c359621494..105022a42c54 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -13,7 +13,6 @@ translate_endpoint_params/2 ]). -extract_key({Name,_}) -> Name. extract_key_as_binary({Name,_}) -> list_to_binary(Name). extract_value({_Name,V}) -> V. From 966d5d49b14df7c657bd489215b32ccb0571ff88 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 14:41:30 +0200 Subject: [PATCH 47/64] Fix fucntion signature --- deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl | 2 +- deps/rabbitmq_management/src/rabbit_mgmt_schema.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl index ce7a6f60d6b9..32649d385f9f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl @@ -66,7 +66,7 @@ translate_list_of_signing_keys(ListOfKidPath) -> end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). --spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> [{binary(), binary()}]. translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", _, Param], V} <- Params0]. diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl index 105022a42c54..19e973a47748 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -34,7 +34,7 @@ translate_oauth_resource_servers(Conf) -> lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, ResourceServers). --spec translate_endpoint_params(list(), [{list(), binary()}]) -> map(). +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> [{binary(), binary()}]. translate_endpoint_params(Variable, Conf) -> Params0 = cuttlefish_variable:filter_by_prefix("management." ++ Variable, Conf), [{list_to_binary(Param), list_to_binary(V)} || {["management", _, Param], V} <- Params0]. From 4142b737388421909a7588c6b47ad8c7b155c101 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 16:57:15 +0200 Subject: [PATCH 48/64] Fix issue initializing oidc-client --- .../priv/www/js/oidc-oauth/helper.js | 20 +++++++++++++------ .../src/rabbit_mgmt_wm_auth.erl | 8 ++++---- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js index db0a46b654d8..12c8c99a002f 100644 --- a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js +++ b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js @@ -72,6 +72,14 @@ function auth_settings_apply_defaults(authSettings) { if (!resource_server.oauth_metadata_url) { resource_server.oauth_metadata_url = authSettings.metadata_url } + if (!resource_server.oauth_authorization_endpoint_params) { + resource_server.oauth_authorization_endpoint_params = + authSettings.oauth_authorization_endpoint_params + } + if (!resource_server.oauth_token_endpoint_params) { + resource_server.oauth_token_endpoint_params = + authSettings.oauth_token_endpoint_params + } resource_server.id = resource_server_id authSettings.resource_servers.push(resource_server) } @@ -144,7 +152,7 @@ export function oidc_settings_from(resource_server) { redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", post_logout_redirect_uri: rabbit_base_uri() + "/", automaticSilentRenew: true, - revokeAccessTokenOnSignout: true + revokeAccessTokenOnSignout: true } if (resource_server.end_session_endpoint != "") { oidcSettings.metadataSeed = { @@ -154,16 +162,16 @@ export function oidc_settings_from(resource_server) { if (resource_server.oauth_client_secret != "") { oidcSettings.client_secret = resource_server.oauth_client_secret } - if (resource_server.authorization_endpoint_params != "") { - oidcSettings.extraQueryParams = resource_server.authorization_endpoint_params + if (resource_server.oauth_authorization_endpoint_params) { + oidcSettings.extraQueryParams = resource_server.oauth_authorization_endpoint_params } - if (resource_server.token_endpoint_params != "") { - oidcSettings.extraTokenParams = resource_server.token_endpoint_params + if (resource_server.oauth_token_endpoint_params) { + oidcSettings.extraTokenParams = resource_server.oauth_token_endpoint_params } return oidcSettings } -function oauth_initialize_user_manager(resource_server) { +function oauth_initialize_user_manager(resource_server) { oidc.Log.setLevel(oidc.Log.DEBUG); oidc.Log.setLogger(console); diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index c477464867e0..8b34a190deca 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -103,10 +103,10 @@ buildRootResourceServerIfAny(Id, Props) -> proplists:get_value(oauth_client_secret, Props)}, {oauth_response_type, proplists:get_value(oauth_response_type, Props)}, - {authorization_endpoint_params, - proplists:get_value(authorization_endpoint_params, Props)}, - {token_endpoint_params, - proplists:get_value(token_endpoint_params, Props)} + {oauth_authorization_endpoint_params, + proplists:get_value(oauth_authorization_endpoint_params, Props)}, + {oauth_token_endpoint_params, + proplists:get_value(oauth_token_endpoint_params, Props)} ]. authSettings() -> From 252b02c0eb4aceb5cd3b2bffce59b8f238d51c13 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 24 Sep 2024 17:06:49 +0200 Subject: [PATCH 49/64] Remove unnecessary log statements --- deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 8b34a190deca..481736662270 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -80,9 +80,6 @@ extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) -> MgtResources = maps:map( fun(K,V) -> merge_oauth_provider_info(maps:get(K, OAuth2Resources, #{}), V, ManagementProps) end, skip_disabled_mgt_resource_servers(MgtResources1)), - rabbit_log:debug("ManagementProps: ~p", [ManagementProps]), - rabbit_log:debug("extract_oauth2_and_mgt_resources OAuth2Resources: ~p, MgtResources0: ~p MgtResources1: ~p MgtResources: ~p", - [OAuth2Resources, MgtResources0, MgtResources1, MgtResources]), case maps:size(MgtResources) of 0 -> {}; _ -> {MgtResources} @@ -117,10 +114,7 @@ authSettings() -> false -> [{oauth_enabled, false}]; true -> case extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) of - {MgtResources} -> - Settings = produce_auth_settings(MgtResources, ManagementProps), - rabbit_log:debug("authSettings: ~p", [Settings]), - Settings; + {MgtResources} -> produce_auth_settings(MgtResources, ManagementProps), {} -> [{oauth_enabled, false}] end end. From a882f8a37c3e662a4f3f794a862bd0952359fd3e Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Wed, 25 Sep 2024 07:26:40 +0200 Subject: [PATCH 50/64] Fix error --- deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index 481736662270..c8db33e1d778 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -114,7 +114,7 @@ authSettings() -> false -> [{oauth_enabled, false}]; true -> case extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) of - {MgtResources} -> produce_auth_settings(MgtResources, ManagementProps), + {MgtResources} -> produce_auth_settings(MgtResources, ManagementProps); {} -> [{oauth_enabled, false}] end end. From 16cccd31811089062f2fad86da0934dc0e757708 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 27 Sep 2024 15:06:24 +0200 Subject: [PATCH 51/64] Remove some spaces --- deps/oauth2_client/test/system_SUITE.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl index 8caccd0145cd..97ae8a4a5e5a 100644 --- a/deps/oauth2_client/test/system_SUITE.erl +++ b/deps/oauth2_client/test/system_SUITE.erl @@ -328,8 +328,7 @@ get_openid_configuration_using_path_and_custom_endpoint(Config) -> SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH), - ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), - SslOptions), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). get_openid_configuration_using_custom_endpoint(Config) -> @@ -337,8 +336,7 @@ get_openid_configuration_using_custom_endpoint(Config) -> SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( build_openid_discovery_endpoint(build_issuer("https"), - ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), - SslOptions), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). From f56324e72c55870295647453021fe0f9f595325c Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 27 Sep 2024 15:54:58 +0200 Subject: [PATCH 52/64] Remove wrong file --- .../src/oauth2_client.hrl | 47 ------------------- 1 file changed, 47 deletions(-) delete mode 100644 deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl b/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl deleted file mode 100644 index 24534dc136f4..000000000000 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_client.hrl +++ /dev/null @@ -1,47 +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) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. -%% - --include("types.hrl"). - -% define access token request common constants - --define(DEFAULT_HTTP_TIMEOUT, 60000). - -% Refresh tome this number of seconds before expires_in token's attribute --define(REFRESH_IN_BEFORE_EXPIRES_IN, 5). - --define(DEFAULT_OPENID_CONFIGURATION_PATH, "/.well-known/openid-configuration"). - -% define access token request constants --define(CONTENT_URLENCODED, "application/x-www-form-urlencoded"). --define(CONTENT_JSON, "application/json"). --define(REQUEST_GRANT_TYPE, "grant_type"). --define(CLIENT_CREDENTIALS_GRANT_TYPE, "client_credentials"). --define(REFRESH_TOKEN_GRANT_TYPE, "refresh_token"). - --define(REQUEST_CLIENT_ID, "client_id"). --define(REQUEST_CLIENT_SECRET, "client_secret"). --define(REQUEST_SCOPE, "scope"). --define(REQUEST_REFRESH_TOKEN, "refresh_token"). - -% define access token response constants --define(BEARER_TOKEN_TYPE, <<"Bearer">>). - --define(RESPONSE_ACCESS_TOKEN, <<"access_token">>). --define(RESPONSE_TOKEN_TYPE, <<"token_type">>). --define(RESPONSE_EXPIRES_IN, <<"expires_in">>). --define(RESPONSE_REFRESH_TOKEN, <<"refresh_token">>). - --define(RESPONSE_ERROR, <<"error">>). --define(RESPONSE_ERROR_DESCRIPTION, <<"error_description">>). - --define(RESPONSE_ISSUER, <<"issuer">>). --define(RESPONSE_TOKEN_ENDPOINT, <<"token_endpoint">>). --define(RESPONSE_AUTHORIZATION_ENDPOINT, <<"authorization_endpoint">>). --define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>). --define(RESPONSE_JWKS_URI, <<"jwks_uri">>). --define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>). From ea6f194eb3810f6f05f1be12776eb26a657af344 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Thu, 3 Oct 2024 22:57:29 -0400 Subject: [PATCH 53/64] OAuth 2 client: sync option/1 with rabbit_types, add a comment --- deps/oauth2_client/include/types.hrl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl index ba73552a24fd..622cae22202c 100644 --- a/deps/oauth2_client/include/types.hrl +++ b/deps/oauth2_client/include/types.hrl @@ -5,8 +5,9 @@ %% Copyright (c) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. %% -%% The closest we have to a type import in Erlang --type(option(T) :: T | 'undefined'). +%% Matches the option type in rabbit_types without introducing a dependency +%% on that module and RabbitMQ core (rabbit_common) +-type(option(T) :: T | 'none' | 'undefined'). -type oauth_provider_id() :: root | binary(). @@ -47,9 +48,10 @@ -record(successful_access_token_response, { access_token :: binary(), token_type :: binary(), - refresh_token :: option(binary()), % A refresh token SHOULD NOT be included - % .. for client-credentials flow. - % https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 + %% Note: a refresh token SHOULD NOT be included + %% ... for client-credentials flow. + %% See https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 + refresh_token :: option(binary()), expires_in :: option(integer()) }). From d25e0f8e88c0990d62353a590adca55acf953880 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 4 Oct 2024 12:56:21 +0200 Subject: [PATCH 54/64] Refactoring - Use rabbit_oauth2 prefix for modules which do not have it - Ensure most lines stick to 80 column --- deps/rabbitmq_auth_backend_oauth2/app.bzl | 48 +- .../rabbitmq_auth_backend_oauth2.schema | 10 +- .../src/rabbit_auth_backend_oauth2.erl | 4 +- ...eycloak.erl => rabbit_oauth2_keycloak.erl} | 2 +- ...rovider.erl => rabbit_oauth2_provider.erl} | 2 +- .../src/{rar.erl => rabbit_oauth2_rar.erl} | 2 +- ....erl => rabbit_oauth2_resource_server.erl} | 2 +- ...h2_schema.erl => rabbit_oauth2_schema.erl} | 65 ++- .../src/uaa_jwt.erl | 4 +- .../test/jwks_SUITE.erl | 474 ++++++++++-------- ...E.erl => rabbit_oauth2_provider_SUITE.erl} | 23 +- ...> rabbit_oauth2_resource_server_SUITE.erl} | 4 +- ...ITE.erl => rabbit_oauth2_schema_SUITE.erl} | 192 ++++--- .../certs/cacert.pem | 0 .../certs/cert.pem | 0 .../certs/key.pem | 0 .../test/unit_SUITE.erl | 261 ++++++---- 17 files changed, 639 insertions(+), 454 deletions(-) rename deps/rabbitmq_auth_backend_oauth2/src/{keycloak.erl => rabbit_oauth2_keycloak.erl} (98%) rename deps/rabbitmq_auth_backend_oauth2/src/{oauth_provider.erl => rabbit_oauth2_provider.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/src/{rar.erl => rabbit_oauth2_rar.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/src/{resource_server.erl => rabbit_oauth2_resource_server.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/src/{oauth2_schema.erl => rabbit_oauth2_schema.erl} (86%) rename deps/rabbitmq_auth_backend_oauth2/test/{oauth_provider_SUITE.erl => rabbit_oauth2_provider_SUITE.erl} (96%) rename deps/rabbitmq_auth_backend_oauth2/test/{resource_server_SUITE.erl => rabbit_oauth2_resource_server_SUITE.erl} (99%) rename deps/rabbitmq_auth_backend_oauth2/test/{oauth2_schema_SUITE.erl => rabbit_oauth2_schema_SUITE.erl} (54%) rename deps/rabbitmq_auth_backend_oauth2/test/{oauth2_schema_SUITE_data => rabbit_oauth2_schema_SUITE_data}/certs/cacert.pem (100%) rename deps/rabbitmq_auth_backend_oauth2/test/{oauth2_schema_SUITE_data => rabbit_oauth2_schema_SUITE_data}/certs/cert.pem (100%) rename deps/rabbitmq_auth_backend_oauth2/test/{oauth2_schema_SUITE_data => rabbit_oauth2_schema_SUITE_data}/certs/key.pem (100%) diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index 70ff08783a13..a74d5bfe38e1 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -13,11 +13,11 @@ 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/oauth_provider.erl", - "src/resource_server.erl", - "src/rar.erl", - "src/keycloak.erl", - "src/oauth2_schema.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_keycloak.erl", + "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -51,11 +51,11 @@ 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/resource_server.erl", - "src/oauth_provider.erl", - "src/oauth2_schema.erl", - "src/rar.erl", - "src/keycloak.erl", + "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_schema.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_keycloak.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -101,11 +101,11 @@ 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/oauth_provider.erl", - "src/resource_server.erl", - "src/oauth2_schema.erl", - "src/rar.erl", - "src/keycloak.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_resource_server.erl", + "src/rabbit_oauth2_schema.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_keycloak.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", "src/uaa_jwt.erl", @@ -169,10 +169,10 @@ def test_suite_beam_files(name = "test_suite_beam_files"): deps = ["//deps/rabbit_common:erlang_app"], ) erlang_bytecode( - name = "oauth2_schema_SUITE_beam_files", + name = "rabbit_oauth2_schema_SUITE_beam_files", testonly = True, - srcs = ["test/oauth2_schema_SUITE.erl"], - outs = ["test/oauth2_schema_SUITE.beam"], + srcs = ["test/rabbit_oauth2_schema_SUITE.erl"], + outs = ["test/rabbit_oauth2_schema_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/rabbit_common:erlang_app"], @@ -248,20 +248,20 @@ def test_suite_beam_files(name = "test_suite_beam_files"): erlc_opts = "//:test_erlc_opts", ) erlang_bytecode( - name = "oauth_provider_SUITE_beam_files", + name = "rabbit_oauth2_provider_SUITE_beam_files", testonly = True, - srcs = ["test/oauth_provider_SUITE.erl"], - outs = ["test/oauth_provider_SUITE.beam"], + srcs = ["test/rabbit_oauth2_provider_SUITE.erl"], + outs = ["test/rabbit_oauth2_provider_SUITE.beam"], hdrs = ["include/oauth2.hrl"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], ) erlang_bytecode( - name = "resource_server_SUITE_beam_files", + name = "rabbit_oauth2_resource_server_SUITE_beam_files", testonly = True, - srcs = ["test/resource_server_SUITE.erl"], - outs = ["test/resource_server_SUITE.beam"], + srcs = ["test/rabbit_oauth2_resource_server_SUITE.erl"], + outs = ["test/rabbit_oauth2_resource_server_SUITE.beam"], hdrs = ["include/oauth2.hrl"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", 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 c7cab672f331..5379f87560de 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 @@ -130,7 +130,7 @@ {translation, "rabbitmq_auth_backend_oauth2.key_config.signing_keys", fun(Conf) -> - oauth2_schema:translate_signing_keys(Conf) + rabbit_oauth2_schema:translate_signing_keys(Conf) end}. {mapping, @@ -170,7 +170,7 @@ {translation, "rabbitmq_auth_backend_oauth2.discovery_endpoint_params", fun(Conf) -> - oauth2_schema:translate_endpoint_params("discovery_endpoint_params", Conf) + rabbit_oauth2_schema:translate_endpoint_params("discovery_endpoint_params", Conf) end}. {mapping, @@ -190,7 +190,7 @@ {translation, "rabbitmq_auth_backend_oauth2.oauth_providers", fun(Conf) -> - oauth2_schema:translate_oauth_providers(Conf) + rabbit_oauth2_schema:translate_oauth_providers(Conf) end}. {mapping, @@ -327,7 +327,7 @@ {translation, "rabbitmq_auth_backend_oauth2.oauth_providers", fun(Conf) -> - oauth2_schema:translate_oauth_providers(Conf) + rabbit_oauth2_schema:translate_oauth_providers(Conf) end}. {mapping, @@ -369,5 +369,5 @@ {translation, "rabbitmq_auth_backend_oauth2.resource_servers", fun(Conf) -> - oauth2_schema:translate_resource_servers(Conf) + rabbit_oauth2_schema:translate_resource_servers(Conf) end}. 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 e7bceabe7cda..27874000b00a 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 @@ -28,8 +28,8 @@ get_scope/1, set_scope/2, resolve_resource_server/1]). --import(keycloak, [has_keycloak_scopes/1, extract_scopes_from_keycloak_format/1]). --import(rar, [extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/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_scope, [filter_matching_scope_prefix_and_drop_it/2]). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl similarity index 98% rename from deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl index 081a1abd322e..79c056a808a8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/keycloak.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(keycloak). +-module(rabbit_oauth2_keycloak). -include("oauth2.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl index 7eaa20aa8268..2891af5a8b8d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth_provider.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(oauth_provider). +-module(rabbit_oauth2_provider). -include("oauth2.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rar.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/rar.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl index ee207a377092..d8a2c36f8325 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rar.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl @@ -6,7 +6,7 @@ %% % Rich Authorization Request --module(rar). +-module(rabbit_oauth2_rar). -include("oauth2.hrl"). -import(uaa_jwt, [get_scope/1, set_scope/2]). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl index 268717c20d6b..84675df7c96d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/resource_server.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(resource_server). +-module(rabbit_oauth2_resource_server). -include("oauth2.hrl"). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl similarity index 86% rename from deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl rename to deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl index 32649d385f9f..72642a43dc1e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(oauth2_schema). +-module(rabbit_oauth2_schema). -export([ @@ -20,7 +20,8 @@ extract_value({_Name,V}) -> V. -spec translate_resource_servers([{list(), binary()}]) -> map(). translate_resource_servers(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.resource_servers", Conf), + Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.resource_servers", + Conf), Map = merge_list_of_maps([ extract_resource_server_properties(Settings), extract_resource_server_preferred_username_claims(Settings) @@ -31,16 +32,19 @@ translate_resource_servers(Conf) -> _ -> V end end, Map), ResourceServers = maps:values(Map0), - lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, + lists:foldl(fun(Elem,AccMap) -> + maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, ResourceServers). -spec translate_oauth_providers([{list(), binary()}]) -> map(). translate_oauth_providers(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.oauth_providers", Conf), + Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.oauth_providers", + Conf), merge_list_of_maps([ extract_oauth_providers_properties(Settings), - extract_oauth_providers_endpoint_params(discovery_endpoint_params, Settings), + extract_oauth_providers_endpoint_params(discovery_endpoint_params, + Settings), extract_oauth_providers_algorithm(Settings), extract_oauth_providers_https(Settings), extract_oauth_providers_signing_keys(Settings) @@ -48,8 +52,10 @@ translate_oauth_providers(Conf) -> -spec translate_signing_keys([{list(), binary()}]) -> map(). translate_signing_keys(Conf) -> - Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.signing_keys", Conf), - ListOfKidPath = lists:map(fun({Id, Path}) -> {list_to_binary(lists:last(Id)), Path} end, Settings), + Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.signing_keys", + Conf), + ListOfKidPath = lists:map(fun({Id, Path}) -> { + list_to_binary(lists:last(Id)), Path} end, Settings), translate_list_of_signing_keys(ListOfKidPath). -spec translate_list_of_signing_keys([{list(), list()}]) -> map(). @@ -61,15 +67,20 @@ translate_list_of_signing_keys(ListOfKidPath) -> string:trim(Bin, trailing, "\n"); _Error -> %% this throws and makes Cuttlefish treak the key as invalid - cuttlefish:invalid("file does not exist or cannot be read by the node") + cuttlefish:invalid("file does not exist or cannot be " ++ + "read by the node") end end, - maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). + maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, + maps:from_list(ListOfKidPath)). --spec translate_endpoint_params(list(), [{list(), binary()}]) -> [{binary(), binary()}]. +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> + [{binary(), binary()}]. translate_endpoint_params(Variable, Conf) -> - Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, Conf), - [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", _, Param], V} <- Params0]. + Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, + Conf), + [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", _, Param], V} + <- Params0]. validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of @@ -78,7 +89,8 @@ validator_file_exists(Attr, Filename) -> _Error -> %% this throws and makes Cuttlefish treak the key as invalid cuttlefish:invalid(io_lib:format( - "Invalid attribute (~p) value: file ~p does not exist or cannot be read by the node", [Attr, Filename])) + "Invalid attribute (~p) value: file ~p does not exist or " ++ + "cannot be read by the node", [Attr, Filename])) end. validator_uri(Attr, Uri) when is_binary(Uri) -> @@ -99,7 +111,8 @@ validator_https_uri(Attr, Uri) when is_list(Uri) -> true -> Uri; false -> cuttlefish:invalid(io_lib:format( - "Invalid attribute (~p) value: uri ~p must be a valid https uri", [Attr, Uri])) + "Invalid attribute (~p) value: uri ~p must be a valid https uri", + [Attr, Uri])) end. merge_list_of_maps(ListOfMaps) -> @@ -110,7 +123,8 @@ extract_oauth_providers_properties(Settings) -> KeyFun = fun extract_key_as_binary/1, ValueFun = fun extract_value/1, - OAuthProviders = [{Name, mapOauthProviderProperty({list_to_atom(Key), list_to_binary(V)})} + OAuthProviders = [ + {Name, mapOauthProviderProperty({list_to_atom(Key), list_to_binary(V)})} || {["auth_oauth2", "oauth_providers", Name, Key], V} <- Settings], maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). @@ -133,7 +147,8 @@ mapOauthProviderProperty({Key, Value}) -> discovery_endpoint_path -> validator_uri(Key, Value); discovery_endpoint_params -> cuttlefish:invalid(io_lib:format( - "Invalid attribute (~p) value: should be a map of Key,Value pairs", [Key])); + "Invalid attribute (~p) value: should be a map of Key,Value pairs", + [Key])); _ -> Value end}. @@ -144,7 +159,8 @@ extract_oauth_providers_https(Settings) -> {["auth_oauth2","oauth_providers", Name, "https", Key], V} <- Settings ], maps:map(fun(_K,V)-> [{https, V}] end, - maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end, AttributesPerProvider)). + maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end, + AttributesPerProvider)). mapHttpProperty({Key, Value}) -> {Key, case Key of @@ -156,8 +172,10 @@ extract_oauth_providers_algorithm(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedAlgorithms = [{Name, {Index, list_to_binary(V)}} || - {["auth_oauth2","oauth_providers", Name, "algorithms", Index], V} <- Settings ], - SortedAlgorithms = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, IndexedAlgorithms), + {["auth_oauth2","oauth_providers", Name, "algorithms", Index], V} + <- Settings ], + SortedAlgorithms = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, + IndexedAlgorithms), Algorithms = [{Name, V} || {Name, {_I, V}} <- SortedAlgorithms], maps:map(fun(_K,V)-> [{algorithms, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, Algorithms)). @@ -166,8 +184,10 @@ extract_resource_server_preferred_username_claims(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedClaims = [{Name, {Index, list_to_binary(V)}} || - {["auth_oauth2","resource_servers", Name, "preferred_username_claims", Index], V} <- Settings ], - SortedClaims = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, IndexedClaims), + {["auth_oauth2","resource_servers", Name, "preferred_username_claims", + Index], V} <- Settings ], + SortedClaims = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, + IndexedClaims), Claims = [{Name, V} || {Name, {_I, V}} <- SortedClaims], maps:map(fun(_K,V)-> [{preferred_username_claims, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, Claims)). @@ -185,6 +205,7 @@ extract_oauth_providers_signing_keys(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedSigningKeys = [{Name, {list_to_binary(Kid), list_to_binary(V)}} || - {["auth_oauth2","oauth_providers", Name, "signing_keys", Kid], V} <- Settings ], + {["auth_oauth2","oauth_providers", Name, "signing_keys", Kid], V} + <- Settings ], maps:map(fun(_K,V)-> [{signing_keys, translate_list_of_signing_keys(V)}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedSigningKeys)). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index 9c29426029f7..46a46cd41176 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -23,9 +23,9 @@ format_ssl_options/1, format_oauth_provider_id/1, get_oauth_provider/2]). --import(resource_server, [ +-import(rabbit_oauth2_resource_server, [ resolve_resource_server_from_audience/1]). --import(oauth_provider, [ +-import(rabbit_oauth2_provider, [ add_signing_key/2, get_signing_key/2, get_internal_oauth_provider/1, replace_signing_keys/2]). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl index db4de4d8a677..c3f324063535 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl @@ -13,9 +13,20 @@ -include_lib("amqp_client/include/amqp_client.hrl"). -include_lib("eunit/include/eunit.hrl"). --import(rabbit_ct_client_helpers, [close_connection/1, close_channel/1, - open_unmanaged_connection/4, open_unmanaged_connection/5, - close_connection_and_channel/2]). +-import(rabbit_ct_client_helpers, [ + close_connection/1, + close_channel/1, + open_unmanaged_connection/4, + open_unmanaged_connection/5, + close_connection_and_channel/2 +]). +-import(rabbit_ct_helpers, [ + set_config/2, + get_config/2, get_config/3 +]). +-import(rabbit_ct_broker_helpers, [ + rpc/5 +]). -import(rabbit_mgmt_test_util, [amqp_port/1]). all() -> @@ -159,21 +170,23 @@ end_per_suite(Config) -> ] ++ rabbit_ct_broker_helpers:teardown_steps()). init_per_group(no_peer_verification, Config) -> - KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(non_strict_jwks_url, Config)}, {peer_verification, verify_none}]), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), - rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); + KeyConfig = set_config(?config(key_config, Config), [ + {jwks_url, ?config(non_strict_jwks_url, Config)}, + {peer_verification, verify_none} + ]), + ok = rpc_set_env(Config,key_config, KeyConfig), + set_config(Config, {key_config, KeyConfig}); init_per_group(without_kid, Config) -> - rabbit_ct_helpers:set_config(Config, [{include_kid, false}]); + set_config(Config, [{include_kid, false}]); init_per_group(with_resource_servers_rabbitmq1_with_oauth_provider_A, Config) -> - ResourceServersConfig0 = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, resource_servers, #{}]), - Resource0 = maps:get(<<"rabbitmq1">>, ResourceServersConfig0, [{id, <<"rabbitmq1">>}]), - ResourceServersConfig1 = maps:put(<<"rabbitmq1">>, [{oauth_provider_id, <<"A">>} | Resource0], ResourceServersConfig0), - - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, resource_servers, ResourceServersConfig1]); + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), + Resource0 = maps:get(<<"rabbitmq1">>, + ResourceServersConfig0, [{id, <<"rabbitmq1">>}]), + ResourceServersConfig1 = maps:put(<<"rabbitmq1">>, + [{oauth_provider_id, <<"A">>} | Resource0], ResourceServersConfig0), + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1); init_per_group(with_oauth_providers_A_B_and_C, Config) -> OAuthProviders = #{ @@ -190,58 +203,50 @@ init_per_group(with_oauth_providers_A_B_and_C, Config) -> {https, [{verify, verify_none}]} ] }, - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders), Config; init_per_group(with_default_oauth_provider_B, Config) -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, default_oauth_provider, <<"B">>]); + ok = rpc_set_env(Config, default_oauth_provider, <<"B">>); + init_per_group(with_oauth_providers_A_with_default_key, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"A">>, OAuthProviders0, []), OAuthProviders1 = maps:put(<<"A">>, [ - {default_key, ?UTIL_MOD:token_key(?config(fixture_jwksA, Config))} | OAuthProvider], - OAuthProviders0), + {default_key, ?UTIL_MOD:token_key(?config(fixture_jwksA, Config))} + | OAuthProvider], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), Config; init_per_group(with_oauth_provider_A_with_jwks_with_one_signing_key, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"A">>, OAuthProviders0, []), - OAuthProviders1 = maps:put(<<"A">>, [{jwks_uri, strict_jwks_url(Config, "/jwksA")} | OAuthProvider], + OAuthProviders1 = maps:put(<<"A">>, [ + {jwks_uri, strict_jwks_url(Config, "/jwksA")} | OAuthProvider], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), Config; init_per_group(with_resource_servers_rabbitmq2, Config) -> - ResourceServersConfig0 = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, resource_servers, #{}]), - Resource0 = maps:get(<<"rabbitmq2">>, ResourceServersConfig0, [{id, <<"rabbitmq2">>}]), - ResourceServersConfig1 = maps:put(<<"rabbitmq2">>, Resource0, ResourceServersConfig0), - - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, resource_servers, ResourceServersConfig1]); + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), + Resource0 = maps:get(<<"rabbitmq2">>, ResourceServersConfig0, + [{id, <<"rabbitmq2">>}]), + ResourceServersConfig1 = maps:put(<<"rabbitmq2">>, Resource0, + ResourceServersConfig0), + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1); init_per_group(with_oauth_providers_B_with_default_key_static_key, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"B">>, OAuthProviders0, []), OAuthProviders1 = maps:put(<<"B">>, [ {default_key, ?UTIL_MOD:token_key(?config(fixture_staticB, Config))} | proplists:delete(default_key, OAuthProvider)], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config,oauth_providers, OAuthProviders1), Config; init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"C">>, OAuthProviders0, []), Jwks1 = ?config(fixture_staticC_1, Config), Jwks2 = ?config(fixture_staticC_2, Config), @@ -249,16 +254,14 @@ init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> ?UTIL_MOD:token_key(Jwks1) => {json, Jwks1}, ?UTIL_MOD:token_key(Jwks2) => {json, Jwks2} }, - OAuthProviders1 = maps:put(<<"C">>, [{signing_keys, SigningKeys} | OAuthProvider], - OAuthProviders0), + OAuthProviders1 = maps:put(<<"C">>, [ + {signing_keys, SigningKeys} | OAuthProvider], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), Config; init_per_group(with_root_oauth_provider_with_two_static_keys_and_one_jwks_key, Config) -> - KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, key_config, []]), + KeyConfig = rpc_get_env(Config, key_config, []), Jwks1 = ?config(fixture_static_1, Config), Jwks2 = ?config(fixture_static_2, Config), SigningKeys = #{ @@ -267,28 +270,25 @@ init_per_group(with_root_oauth_provider_with_two_static_keys_and_one_jwks_key, C }, KeyConfig1 = [{signing_keys, SigningKeys}, {jwks_url, strict_jwks_url(Config, "/jwks")}| KeyConfig], - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), - + ok = rpc_set_env(Config, key_config, KeyConfig1), Config; init_per_group(with_root_oauth_provider_with_default_key_1, Config) -> - KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, key_config, []]), - KeyConfig1 = [{default_key, ?UTIL_MOD:token_key(?config(fixture_static_1, Config))} | KeyConfig], - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = [ + {default_key, ?UTIL_MOD:token_key(?config(fixture_static_1, Config))} + | KeyConfig], + ok = rpc_set_env(Config, key_config, KeyConfig1), Config; init_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> - KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, key_config, []]), - KeyConfig1 = [{default_key, ?UTIL_MOD:token_key(?config(fixture_jwk, Config))} | KeyConfig], - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = [ + {default_key, ?UTIL_MOD:token_key(?config(fixture_jwk, Config))} + | KeyConfig], + ok = rpc_set_env(Config, key_config, KeyConfig1), Config; init_per_group(with_oauth_provider_B_with_one_static_key_and_jwks_with_two_signing_keys, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"B">>, OAuthProviders0, []), Jwks = ?config(fixture_staticB, Config), SigningKeys = #{ @@ -299,63 +299,55 @@ init_per_group(with_oauth_provider_B_with_one_static_key_and_jwks_with_two_signi {jwks_uri, strict_jwks_url(Config, "/jwksB")} | OAuthProvider], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), Config; init_per_group(with_resource_servers_rabbitmq3_with_oauth_provider_C, Config) -> - ResourceServersConfig0 = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, resource_servers, #{}]), + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), Resource0 = maps:get(<<"rabbitmq3">>, ResourceServersConfig0, [ {id, <<"rabbitmq3">>},{oauth_provider_id, <<"C">>}]), - ResourceServersConfig1 = maps:put(<<"rabbitmq3">>, Resource0, ResourceServersConfig0), + ResourceServersConfig1 = maps:put(<<"rabbitmq3">>, Resource0, + ResourceServersConfig0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, resource_servers, ResourceServersConfig1]); + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1); init_per_group(with_oauth_providers_C_with_default_key_static_key_1, Config) -> - {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, oauth_providers]), + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), OAuthProvider = maps:get(<<"C">>, OAuthProviders0, []), Jwks = ?config(fixture_staticC_1, Config), OAuthProviders1 = maps:put(<<"C">>, [ {default_key, ?UTIL_MOD:token_key(Jwks)} | OAuthProvider], OAuthProviders0), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), Config; init_per_group(_Group, Config) -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, resource_server_id, ?RESOURCE_SERVER_ID]), + ok = rpc_set_env(Config, resource_server_id, ?RESOURCE_SERVER_ID), Config. end_per_group(without_kid, Config) -> rabbit_ct_helpers:delete_config(Config, include_kid); end_per_group(no_peer_verification, Config) -> - KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(strict_jwks_url, Config)}, {peer_verification, verify_peer}]), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), - rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); + KeyConfig = set_config(?config(key_config, Config), [ + {jwks_url, ?config(strict_jwks_url, Config)}, + {peer_verification, verify_peer}]), + ok = rpc_set_env(Config, key_config, KeyConfig), + set_config(Config, {key_config, KeyConfig}); end_per_group(with_default_oauth_provider_B, Config) -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env, - [rabbitmq_auth_backend_oauth2, default_oauth_provider]); + ok = rpc_unset_env(Config, default_oauth_provider); end_per_group(with_root_oauth_provider_with_default_key_1, Config) -> - KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, key_config, []]), + KeyConfig = rpc_get_env(Config, key_config, []), KeyConfig1 = proplists:delete(default_key, KeyConfig), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), + ok = rpc_set_env(Config, key_config, KeyConfig1), Config; end_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> - KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, - [rabbitmq_auth_backend_oauth2, key_config, []]), + KeyConfig = rpc_get_env(Config, key_config, []), KeyConfig1 = proplists:delete(default_key, KeyConfig), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), + ok = rpc_set_env(Config, key_config, KeyConfig1), Config; end_per_group(_Group, Config) -> @@ -363,44 +355,50 @@ end_per_group(_Group, Config) -> add_vhosts(Config) -> %% The broker is managed by {init,end}_per_testcase(). - lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:add_vhost(Config, Value) end, - [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). + lists:foreach(fun(Value) -> + rabbit_ct_broker_helpers:add_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). %rabbit_ct_helpers:set_config(Config, []). delete_vhosts(Config) -> %% The broker is managed by {init,end}_per_testcase(). - lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:delete_vhost(Config, Value) end, - [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). + lists:foreach(fun(Value) -> + rabbit_ct_broker_helpers:delete_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). -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 -> +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 -> rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost1">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -init_per_testcase(Testcase, Config) when Testcase =:= test_failed_token_refresh_case1 orelse - Testcase =:= test_failed_token_refresh_case2 -> +init_per_testcase(Testcase, Config) when + Testcase =:= test_failed_token_refresh_case1 orelse + Testcase =:= test_failed_token_refresh_case2 -> rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost4">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse - Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse - Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, extra_scopes_source, ?EXTRA_SCOPES_SOURCE]), +init_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> + ok = rpc_set_env(Config, extra_scopes_source, ?EXTRA_SCOPES_SOURCE), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_algorithm_restriction -> +init_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_algorithm_restriction -> KeyConfig = ?config(key_config, Config), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, [{algorithms, [<<"HS256">>]} | KeyConfig]]), + ok = rpc_set_env(Config, key_config, [{algorithms, [<<"HS256">>]} | KeyConfig]), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -init_per_testcase(Testcase, Config) when Testcase =:= test_failed_connection_with_algorithm_restriction -> +init_per_testcase(Testcase, Config) when + Testcase =:= test_failed_connection_with_algorithm_restriction -> KeyConfig = ?config(key_config, Config), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, [{algorithms, [<<"RS256">>]} | KeyConfig]]), + ok = rpc_set_env(Config, key_config, [{algorithms, [<<"RS256">>]} | KeyConfig]), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; @@ -408,25 +406,28 @@ init_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_started(Config, Testcase), Config. -end_per_testcase(Testcase, Config) when Testcase =:= test_failed_token_refresh_case1 orelse - Testcase =:= test_failed_token_refresh_case2 -> +end_per_testcase(Testcase, Config) when + Testcase =:= test_failed_token_refresh_case1 orelse + Testcase =:= test_failed_token_refresh_case2 -> rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost4">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse - Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse - Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> +end_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env, [rabbitmq_auth_backend_oauth2, extra_scopes_source]), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; -end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_algorithm_restriction orelse - Testcase =:= test_failed_connection_with_algorithm_restriction -> +end_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_algorithm_restriction orelse + Testcase =:= test_failed_connection_with_algorithm_restriction -> rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, ?config(key_config, Config)]), + ok = rpc_set_env(Config, key_config, ?config(key_config, Config)), rabbit_ct_helpers:testcase_finished(Config, Testcase), Config; @@ -436,10 +437,9 @@ end_per_testcase(Testcase, Config) -> Config. preconfigure_node(Config) -> - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbit, auth_backends, [rabbit_auth_backend_oauth2]]), - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, resource_server_id, ?RESOURCE_SERVER_ID]), + ok = rpc(Config, 0, application, set_env, + [rabbit, auth_backends, [rabbit_auth_backend_oauth2]]), + ok = rpc_set_env(Config, resource_server_id, ?RESOURCE_SERVER_ID), add_vhosts(Config), Config. @@ -477,25 +477,23 @@ start_jwks_server(Config0) -> KeyConfig = [{jwks_url, StrictJwksUrl}, {peer_verification, verify_peer}, {cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])}], - ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, - [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), - rabbit_ct_helpers:set_config(Config, - [ - {non_strict_jwks_url, NonStrictJwksUrl}, - {strict_jwks_url, StrictJwksUrl}, - {key_config, KeyConfig}, - {fixture_static_1, Jwk7}, - {fixture_static_2, Jwk8}, - {fixture_staticB, Jwk4}, - {fixture_staticC_1, Jwk5}, - {fixture_staticC_2, Jwk6}, - {fixture_jwksB_1, Jwk1}, - {fixture_jwksB_2, Jwk3}, - {fixture_jwksA, Jwk}, - {fixture_jwk, Jwk}, - {fixture_jwks_1, [Jwk1, Jwk3]}, - {fixture_jwks_2, [Jwk2]} - ]). + ok = rpc_set_env(Config, key_config, KeyConfig), + set_config(Config, [ + {non_strict_jwks_url, NonStrictJwksUrl}, + {strict_jwks_url, StrictJwksUrl}, + {key_config, KeyConfig}, + {fixture_static_1, Jwk7}, + {fixture_static_2, Jwk8}, + {fixture_staticB, Jwk4}, + {fixture_staticC_1, Jwk5}, + {fixture_staticC_2, Jwk6}, + {fixture_jwksB_1, Jwk1}, + {fixture_jwksB_2, Jwk3}, + {fixture_jwksA, Jwk}, + {fixture_jwk, Jwk}, + {fixture_jwks_1, [Jwk1, Jwk3]}, + {fixture_jwks_2, [Jwk2]} + ]). strict_jwks_url(Config) -> strict_jwks_url(Config, "/jwks"). strict_jwks_url(Config, Path) -> @@ -517,54 +515,63 @@ generate_valid_token(Config, Scopes) -> generate_valid_token(Config, Scopes, undefined). generate_valid_token(Config, Scopes, Audience) -> - Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of + Jwk = + case get_config(Config, fixture_jwk) of undefined -> ?UTIL_MOD:fixture_jwk(); Value -> Value end, generate_valid_token(Config, Jwk, Scopes, Audience). generate_valid_token(Config, Jwk, Scopes, Audience) -> - Token = case Audience of - undefined -> ?UTIL_MOD:fixture_token_with_scopes(Scopes); - DefinedAudience -> maps:put(<<"aud">>, DefinedAudience, ?UTIL_MOD:fixture_token_with_scopes(Scopes)) + Token = + case Audience of + undefined -> + ?UTIL_MOD:fixture_token_with_scopes(Scopes); + DefinedAudience -> + maps:put(<<"aud">>, DefinedAudience, + ?UTIL_MOD:fixture_token_with_scopes(Scopes)) end, IncludeKid = rabbit_ct_helpers:get_config(Config, include_kid, true), ?UTIL_MOD:sign_token_hs(Token, Jwk, IncludeKid). generate_valid_token_with_extra_fields(Config, ExtraFields) -> - Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of - undefined -> ?UTIL_MOD:fixture_jwk(); - Value -> Value - end, + Jwk = + case rabbit_ct_helpers:get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, Token = maps:merge(?UTIL_MOD:fixture_token_with_scopes([]), ExtraFields), - ?UTIL_MOD:sign_token_hs(Token, Jwk, rabbit_ct_helpers:get_config(Config, include_kid, true)). + ?UTIL_MOD:sign_token_hs(Token, Jwk, + rabbit_ct_helpers:get_config(Config, include_kid, true)). generate_expired_token(Config) -> generate_expired_token(Config, ?UTIL_MOD:full_permission_scopes()). generate_expired_token(Config, Scopes) -> - Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of - undefined -> ?UTIL_MOD:fixture_jwk(); - Value -> Value - end, + Jwk = + case get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, ?UTIL_MOD:sign_token_hs(?UTIL_MOD:expired_token_with_scopes(Scopes), Jwk, - rabbit_ct_helpers:get_config(Config, include_kid, true)). + get_config(Config, include_kid, true)). generate_expirable_token(Config, Seconds) -> generate_expirable_token(Config, ?UTIL_MOD:full_permission_scopes(), Seconds). generate_expirable_token(Config, Scopes, Seconds) -> - Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of - undefined -> ?UTIL_MOD:fixture_jwk(); - Value -> Value - end, + Jwk = + case get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, Expiration = os:system_time(seconds) + Seconds, - ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_scopes_and_expiration(Scopes, Expiration), - Jwk, rabbit_ct_helpers:get_config(Config, include_kid, true)). + ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_scopes_and_expiration( + Scopes, Expiration), Jwk, get_config(Config, include_kid, true)). preconfigure_token(Config) -> Token = generate_valid_token(Config), - rabbit_ct_helpers:set_config(Config, {fixture_jwt, Token}). + set_config(Config, {fixture_jwt, Token}). %% @@ -682,7 +689,7 @@ test_unsuccessful_connection_for_rabbitmq_audience_signed_by_root_oauth_provider ?assertMatch({error, {auth_failure, _}}, open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token)). test_successful_connection_with_a_full_permission_token_and_all_defaults(Config) -> - {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + {_Algo, Token} = get_config(Config, fixture_jwt), verify_queue_declare_with_token(Config, Token). verify_queue_declare_with_token(Config, Token) -> @@ -734,10 +741,12 @@ test_successful_queue_declaration_using_multiple_keys_and_audiences(Config) -> test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost(Config) -> - {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost1/*">>, - <<"rabbitmq.write:vhost1/*">>, - <<"rabbitmq.read:vhost1/*">>]), - Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token), + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, + Token), {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), @@ -758,7 +767,13 @@ test_successful_connection_with_simple_strings_for_aud_and_scope(Config) -> test_successful_connection_with_complex_claim_as_a_map(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, - #{<<"additional_rabbitmq_scopes">> => #{<<"rabbitmq">> => [<<"configure:*/*">>, <<"read:*/*">>, <<"write:*/*">>]}} + #{<<"additional_rabbitmq_scopes">> => #{ + <<"rabbitmq">> => [ + <<"configure:*/*">>, + <<"read:*/*">>, + <<"write:*/*">> + ]} + } ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -769,7 +784,11 @@ test_successful_connection_with_complex_claim_as_a_map(Config) -> test_successful_connection_with_complex_claim_as_a_list(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, - #{<<"additional_rabbitmq_scopes">> => [<<"rabbitmq.configure:*/*">>, <<"rabbitmq.read:*/*">>, <<"rabbitmq.write:*/*">>]} + #{<<"additional_rabbitmq_scopes">> => [ + <<"rabbitmq.configure:*/*">>, + <<"rabbitmq.read:*/*">>, + <<"rabbitmq.write:*/*">> + ]} ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -780,7 +799,8 @@ test_successful_connection_with_complex_claim_as_a_list(Config) -> test_successful_connection_with_complex_claim_as_a_binary(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, - #{<<"additional_rabbitmq_scopes">> => <<"rabbitmq.configure:*/* rabbitmq.read:*/* rabbitmq.write:*/*">>} + #{<<"additional_rabbitmq_scopes">> => + <<"rabbitmq.configure:*/* rabbitmq.read:*/* rabbitmq.write:*/*">>} ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -815,79 +835,94 @@ test_successful_connection_with_keycloak_token(Config) -> test_successful_token_refresh(Config) -> Duration = 5, - {_Algo, Token} = generate_expirable_token(Config, [<<"rabbitmq.configure:vhost1/*">>, - <<"rabbitmq.write:vhost1/*">>, - <<"rabbitmq.read:vhost1/*">>], - Duration), - Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token), + {_Algo, Token} = generate_expirable_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">> + ], Duration), + Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), - {_Algo2, Token2} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost1/*">>, - <<"rabbitmq.write:vhost1/*">>, - <<"rabbitmq.read:vhost1/*">>]), + {_Algo2, Token2} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), ?UTIL_MOD:wait_for_token_to_expire(timer:seconds(Duration)), - ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, <<"token refresh">>)), - + ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, + <<"token refresh">>)), {ok, Ch2} = amqp_connection:open_channel(Conn), - #'queue.declare_ok'{queue = _} = - amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), - #'queue.declare_ok'{queue = _} = - amqp_channel:call(Ch2, #'queue.declare'{exclusive = true}), + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, + #'queue.declare'{exclusive = true}), + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch2, + #'queue.declare'{exclusive = true}), amqp_channel:close(Ch2), close_connection_and_channel(Conn, Ch). test_successful_connection_with_algorithm_restriction(Config) -> - {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + {_Algo, Token} = get_config(Config, fixture_jwt), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), - #'queue.declare_ok'{queue = _} = - amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, + #'queue.declare'{exclusive = true}), close_connection_and_channel(Conn, Ch). test_failed_connection_with_expired_token(Config) -> - {_Algo, Token} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost1/*">>, - <<"rabbitmq.write:vhost1/*">>, - <<"rabbitmq.read:vhost1/*">>]), + {_Algo, Token} = generate_expired_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), ?assertMatch({error, {auth_failure, _}}, - open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token)). + open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, Token)). test_failed_connection_with_a_non_token(Config) -> ?assertMatch({error, {auth_failure, _}}, - open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, <<"a-non-token-value">>)). + open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, <<"a-non-token-value">>)). test_failed_connection_with_a_token_with_insufficient_vhost_permission(Config) -> - {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:alt-vhost/*">>, - <<"rabbitmq.write:alt-vhost/*">>, - <<"rabbitmq.read:alt-vhost/*">>]), + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:alt-vhost/*">>, + <<"rabbitmq.write:alt-vhost/*">>, + <<"rabbitmq.read:alt-vhost/*">>]), ?assertEqual({error, not_allowed}, - open_unmanaged_connection(Config, 0, <<"off-limits-vhost">>, <<"username">>, Token)). + open_unmanaged_connection(Config, 0, <<"off-limits-vhost">>, + <<"username">>, Token)). 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*">>, - <<"rabbitmq.read:vhost2/jwt*">>]), - Conn = open_unmanaged_connection(Config, 0, <<"vhost2">>, <<"username">>, Token), + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost2/jwt*">>, + <<"rabbitmq.write:vhost2/jwt*">>, + <<"rabbitmq.read:vhost2/jwt*">>]), + 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 = <<"alt-prefix.eq.1">>, exclusive = true})), + amqp_channel:call(Ch, #'queue.declare'{queue = <<"alt-prefix.eq.1">>, + exclusive = true})), close_connection(Conn). test_failed_token_refresh_case1(Config) -> - {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost4/*">>, - <<"rabbitmq.write:vhost4/*">>, - <<"rabbitmq.read:vhost4/*">>]), - Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, Token), + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, + Token), {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), - {_Algo2, Token2} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost4/*">>, - <<"rabbitmq.write:vhost4/*">>, - <<"rabbitmq.read:vhost4/*">>]), + {_Algo2, Token2} = generate_expired_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), %% the error is communicated asynchronously via a connection-level error - ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, <<"token refresh">>)), + ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, + <<"token refresh">>)), {ok, Ch2} = amqp_connection:open_channel(Conn), ?assertExit({{shutdown, {server_initiated_close, 403, _}}, _}, @@ -896,16 +931,19 @@ test_failed_token_refresh_case1(Config) -> close_connection(Conn). test_failed_token_refresh_case2(Config) -> - {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost4/*">>, - <<"rabbitmq.write:vhost4/*">>, - <<"rabbitmq.read:vhost4/*">>]), - Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, Token), + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, + <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), %% the error is communicated asynchronously via a connection-level error - ?assertEqual(ok, amqp_connection:update_secret(Conn, <<"not-a-token-^^^^5%">>, <<"token refresh">>)), + ?assertEqual(ok, amqp_connection:update_secret(Conn, <<"not-a-token-^^^^5%">>, + <<"token refresh">>)), ?assertExit({{shutdown, {connection_closing, {server_initiated_close, 530, _}}}, _}, amqp_connection:open_channel(Conn)), @@ -913,6 +951,20 @@ test_failed_token_refresh_case2(Config) -> close_connection(Conn). test_failed_connection_with_algorithm_restriction(Config) -> - {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), + {_Algo, Token} = get_config(Config, fixture_jwt), ?assertMatch({error, {auth_failure, _}}, open_unmanaged_connection(Config, 0, <<"username">>, Token)). + +%%% HELPERS +rpc_unset_env(Config, Par) -> + rpc(Config, 0, application, unset_env, + [rabbitmq_auth_backend_oauth2, Par]). +rpc_set_env(Config, Par, Val) -> + rpc(Config, 0, application, set_env, + [rabbitmq_auth_backend_oauth2, Par, Val]). +rpc_get_env(Config, Par) -> + rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par]). +rpc_get_env(Config, Par, Default) -> + rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par, Default]). \ No newline at end of file diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl similarity index 96% rename from deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl index 12fb06d054ec..9f830585aa18 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth_provider_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(oauth_provider_SUITE). +-module(rabbit_oauth2_provider_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -17,7 +17,7 @@ -define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). -define(AUTH_PORT, 8000). --import(oauth_provider, [ +-import(rabbit_oauth2_provider, [ get_internal_oauth_provider/0,get_internal_oauth_provider/1, add_signing_key/2, add_signing_key/3, replace_signing_keys/1, replace_signing_keys/2, @@ -237,22 +237,27 @@ call_get_env(Config, Par, Def) -> [rabbitmq_auth_backend_oauth2, Par, Def]). call_add_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, add_signing_key, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + add_signing_key, Args). call_get_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, get_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + get_signing_keys, Args). call_get_signing_keys(Config) -> call_get_signing_keys(Config, []). call_get_signing_key(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, get_signing_key, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + get_signing_key, Args). call_add_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, add_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + add_signing_keys, Args). call_replace_signing_keys(Config, Args) -> - rabbit_ct_broker_helpers:rpc(Config, 0, oauth_provider, replace_signing_keys, Args). + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + replace_signing_keys, Args). %% ----- Test cases @@ -474,15 +479,13 @@ start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations {'_', [{Path, oauth2_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} ]), - ct:log("start_https_oauth_server (port:~p) with expectation list : ~p -> dispatch: ~p", [Port, Expectations, Dispatch]), {ok, Pid} = cowboy:start_tls( mock_http_auth_listener, [{port, Port}, {certfile, filename:join([CertsDir, "server", "cert.pem"])}, {keyfile, filename:join([CertsDir, "server", "key.pem"])} ], - #{env => #{dispatch => Dispatch}}), - ct:log("Started on Port ~p and pid ~p", [ranch:get_port(mock_http_auth_listener), Pid]). + #{env => #{dispatch => Dispatch}}). build_url_to_oauth_provider(Path) -> uri_string:recompose(#{scheme => "https", diff --git a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl similarity index 99% rename from deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl index dba11c6b4c98..3e1fb745b6ec 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/resource_server_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl @@ -5,7 +5,7 @@ %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(resource_server_SUITE). +-module(rabbit_oauth2_resource_server_SUITE). -compile(export_all). -include_lib("common_test/include/ct.hrl"). @@ -19,7 +19,7 @@ -define(OAUTH_PROVIDER_B,<<"B">>). -import(oauth2_client, [get_oauth_provider/2]). --import(resource_server, [resolve_resource_server_from_audience/1]). +-import(rabbit_oauth2_resource_server, [resolve_resource_server_from_audience/1]). all() -> [ diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl similarity index 54% rename from deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl index 05705f649ca6..ccf1b3a0f6ac 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl @@ -4,7 +4,7 @@ %% %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% --module(oauth2_schema_SUITE). +-module(rabbit_oauth2_schema_SUITE). -compile(export_all). @@ -12,7 +12,11 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). --import(oauth2_schema, [translate_endpoint_params/2, translate_oauth_providers/1]). +-import(rabbit_oauth2_schema, [ + translate_endpoint_params/2, + translate_oauth_providers/1, + translate_resource_servers/1 +]). all() -> [ @@ -39,10 +43,10 @@ all() -> test_without_oauth_providers(_) -> - #{} = oauth2_schema:translate_oauth_providers([]). + #{} = translate_oauth_providers([]). test_without_resource_servers(_) -> - #{} = oauth2_schema:translate_resource_servers([]). + #{} = translate_resource_servers([]). test_without_endpoint_params(_) -> [] = translate_endpoint_params("oauth_discovery_endpoint_params", []). @@ -64,7 +68,7 @@ test_with_endpoint_params(_) -> translate_endpoint_params("discovery_endpoint_params", Conf). test_invalid_oauth_providers_endpoint_params(_) -> - try oauth2_schema:translate_oauth_providers([ + try translate_oauth_providers([ {["auth_oauth2","oauth_providers", "X", "discovery_endpoint_params"], ""}]) of _ -> {throw, should_have_failed} catch @@ -73,11 +77,13 @@ test_invalid_oauth_providers_endpoint_params(_) -> test_without_oauth_providers_with_endpoint_params(_) -> Conf = [ - {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], "some-value1"}, - {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param2"], "some-value2"}, - {["auth_oauth2","oauth_providers", "B", "discovery_endpoint_params","param3"], "some-value3"} + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], + "some-value1"}, + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param2"], + "some-value2"}, + {["auth_oauth2","oauth_providers", "B", "discovery_endpoint_params","param3"], + "some-value3"} ], - #{ <<"A">> := [{discovery_endpoint_params, [ {<<"param1">>, <<"some-value1">>}, @@ -90,107 +96,143 @@ test_without_oauth_providers_with_endpoint_params(_) -> } = translate_oauth_providers(Conf). test_with_one_oauth_provider(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} - ], + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"],"https://rabbit"} + ], #{<<"keycloak">> := [{issuer, "https://rabbit"}] - } = oauth2_schema:translate_oauth_providers(Conf). + } = translate_oauth_providers(Conf). test_with_one_resource_server(_) -> - Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"} - ], + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"} + ], #{<<"rabbitmq1">> := [{id, <<"rabbitmq1">>}] - } = oauth2_schema:translate_resource_servers(Conf). + } = translate_resource_servers(Conf). test_with_many_oauth_providers(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","uaa","issuer"],"https://uaa"}, - {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"],"/some-path"} - ], + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","uaa","issuer"], + "https://uaa"}, + {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"], + "/some-path"} + ], #{<<"keycloak">> := [{issuer, "https://keycloak"} ], <<"uaa">> := [{issuer, "https://uaa"}, {discovery_endpoint_path, "/some-path"} ] - } = oauth2_schema:translate_oauth_providers(Conf). + } = translate_oauth_providers(Conf). test_with_many_resource_servers(_) -> - Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"}, - {["auth_oauth2","resource_servers","rabbitmq2","id"],"rabbitmq2"} - ], + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"], "rabbitmq1"}, + {["auth_oauth2","resource_servers","rabbitmq2","id"], "rabbitmq2"} + ], #{<<"rabbitmq1">> := [{id, <<"rabbitmq1">>} ], <<"rabbitmq2">> := [{id, <<"rabbitmq2">>} ] - } = oauth2_schema:translate_resource_servers(Conf). + } = translate_resource_servers(Conf). test_oauth_providers_attributes(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","default_key"],"token-key"} - ], + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","default_key"], + "token-key"} + ], #{<<"keycloak">> := [{default_key, <<"token-key">>}, {issuer, "https://keycloak"} ] - } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). + } = sort_settings(translate_oauth_providers(Conf)). test_resource_servers_attributes(_) -> - Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1xxx"}, - {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"],"somescope."}, - {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"],"roles"}, - {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"],"userid"}, - {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"],"groupid"} - ], + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"], + "rabbitmq1xxx"}, + {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"], + "somescope."}, + {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"], + "roles"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"], + "userid"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"], + "groupid"} + ], #{<<"rabbitmq1xxx">> := [{additional_scopes_key, <<"roles">>}, {id, <<"rabbitmq1xxx">>}, {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, {scope_prefix, <<"somescope.">>} ] - } = sort_settings(oauth2_schema:translate_resource_servers(Conf)), + } = sort_settings(translate_resource_servers(Conf)), Conf2 = [ - {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"],"somescope."}, - {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"],"roles"}, - {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"],"userid"}, - {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"],"groupid"} - ], + {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"], + "somescope."}, + {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"], + "roles"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"], + "userid"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"], + "groupid"} + ], #{<<"rabbitmq1">> := [{additional_scopes_key, <<"roles">>}, {id, <<"rabbitmq1">>}, {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, {scope_prefix, <<"somescope.">>} ] - } = sort_settings(oauth2_schema:translate_resource_servers(Conf2)). + } = sort_settings(translate_resource_servers(Conf2)). test_oauth_providers_attributes_with_invalid_uri(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"http://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","default_key"],"token-key"} - ], - try sort_settings(oauth2_schema:translate_oauth_providers(Conf)) of + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "http://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","default_key"], + "token-key"} + ], + try sort_settings(translate_oauth_providers(Conf)) of _ -> {throw, should_have_failed} catch _ -> ok end. test_oauth_providers_algorithms(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","algorithms","2"],"HS256"}, - {["auth_oauth2","oauth_providers","keycloak","algorithms","1"],"RS256"} - ], + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","algorithms","2"], + "HS256"}, + {["auth_oauth2","oauth_providers","keycloak","algorithms","1"], + "RS256"} + ], #{<<"keycloak">> := [{algorithms, [<<"RS256">>, <<"HS256">>]}, {issuer, "https://keycloak"} ] - } = sort_settings(oauth2_schema:translate_oauth_providers(Conf)). + } = sort_settings(translate_oauth_providers(Conf)). test_oauth_providers_https(Conf) -> - CuttlefishConf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","https","verify"],verify_none}, - {["auth_oauth2","oauth_providers","keycloak","https","peer_verification"],verify_peer}, - {["auth_oauth2","oauth_providers","keycloak","https","depth"],2}, - {["auth_oauth2","oauth_providers","keycloak","https","hostname_verification"],wildcard}, - {["auth_oauth2","oauth_providers","keycloak","https","crl_check"],false}, - {["auth_oauth2","oauth_providers","keycloak","https","fail_if_no_peer_cert"],true}, - {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"],cert_filename(Conf)} - ], + CuttlefishConf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","https","verify"], + verify_none}, + {["auth_oauth2","oauth_providers","keycloak","https","peer_verification"], + verify_peer}, + {["auth_oauth2","oauth_providers","keycloak","https","depth"], + 2}, + {["auth_oauth2","oauth_providers","keycloak","https","hostname_verification"], + wildcard}, + {["auth_oauth2","oauth_providers","keycloak","https","crl_check"], + false}, + {["auth_oauth2","oauth_providers","keycloak","https","fail_if_no_peer_cert"], + true}, + {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"], + cert_filename(Conf)} + ], #{<<"keycloak">> := [{https, [{verify, verify_none}, {peer_verification, verify_peer}, {depth, 2}, @@ -201,36 +243,44 @@ test_oauth_providers_https(Conf) -> ]}, {issuer, "https://keycloak"} ] - } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)). + } = sort_settings(translate_oauth_providers(CuttlefishConf)). test_oauth_providers_https_with_missing_cacertfile(_) -> - Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"],"/non-existent.pem"} - ], - try sort_settings(oauth2_schema:translate_oauth_providers(Conf)) of + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"], + "/non-existent.pem"} + ], + try sort_settings(translate_oauth_providers(Conf)) of _ -> {throw, should_have_failed} catch _ -> ok end. test_oauth_providers_signing_keys(Conf) -> - CuttlefishConf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, - {["auth_oauth2","oauth_providers","keycloak","signing_keys","2"], cert_filename(Conf)}, - {["auth_oauth2","oauth_providers","keycloak","signing_keys","1"], cert_filename(Conf)} - ], + CuttlefishConf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","signing_keys","2"], + cert_filename(Conf)}, + {["auth_oauth2","oauth_providers","keycloak","signing_keys","1"], + cert_filename(Conf)} + ], #{<<"keycloak">> := [{issuer, "https://keycloak"}, {signing_keys, SigningKeys} ] - } = sort_settings(oauth2_schema:translate_oauth_providers(CuttlefishConf)), + } = sort_settings(translate_oauth_providers(CuttlefishConf)), ct:log("SigningKey: ~p", [SigningKeys]), #{<<"1">> := {pem, <<"I'm not a certificate">>}, <<"2">> := {pem, <<"I'm not a certificate">>} - } = SigningKeys. + } = SigningKeys. cert_filename(Conf) -> string:concat(?config(data_dir, Conf), "certs/cert.pem"). sort_settings(MapOfListOfSettings) -> maps:map(fun(_K,List) -> - lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, MapOfListOfSettings). + lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, + MapOfListOfSettings). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cacert.pem b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cacert.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cacert.pem rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cacert.pem diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cert.pem b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cert.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/cert.pem rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/cert.pem diff --git a/deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/key.pem b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/key.pem similarity index 100% rename from deps/rabbitmq_auth_backend_oauth2/test/oauth2_schema_SUITE_data/certs/key.pem rename to deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE_data/certs/key.pem diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index 18d70c5b6fd6..aaeb0b929601 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -18,6 +18,9 @@ user_login_authorization/2, normalize_token_scope/2, check_vhost_access/3]). +-import(rabbit_oauth2_resource_server, [ + new_resource_server/1 +]). all() -> [ @@ -77,7 +80,7 @@ end_per_suite(Config) -> Env = ?config(env, Config), lists:foreach( fun({K, V}) -> - application:set_env(rabbitmq_auth_backend_oauth2, K, V) + set_env(K, V) end, Env), rabbit_ct_helpers:run_teardown_steps(Config). @@ -95,7 +98,7 @@ init_per_group(with_rabbitmq_node, Config) -> rabbit_ct_helpers:run_steps(Config2, rabbit_ct_broker_helpers:setup_steps()); init_per_group(with_resource_server_id, Config) -> - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(resource_server_id, <<"rabbitmq">>), Config; init_per_group(_, Config) -> @@ -172,7 +175,7 @@ normalize_token_scope_with_keycloak_scopes(_) -> ], lists:foreach(fun({Case, Authorization, ExpectedScope}) -> - ResourceServer = resource_server:new_resource_server(<<"rabbitmq-resource">>), + ResourceServer = new_resource_server(<<"rabbitmq-resource">>), Token0 = #{<<"authorization">> => Authorization}, Token = normalize_token_scope(ResourceServer, Token0), ?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token), Case) @@ -216,7 +219,7 @@ normalize_token_scope_with_rich_auth_request_using_regular_expression_with_clust lists:foreach( fun({Case, Permissions, ExpectedScope}) -> - ResourceServer0 = resource_server:new_resource_server(<<"rabbitmq-test">>), + ResourceServer0 = new_resource_server(<<"rabbitmq-test">>), ResourceServer = ResourceServer0#resource_server{ resource_server_type = ?RESOURCE_SERVER_TYPE }, @@ -531,7 +534,7 @@ normalize_token_scope_with_rich_auth_request(_) -> ], lists:foreach(fun({Case, Permissions, ExpectedScope0}) -> - ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), ResourceServer = ResourceServer0#resource_server{ resource_server_type = ?RESOURCE_SERVER_TYPE }, @@ -600,7 +603,7 @@ normalize_token_scope_with_additional_scopes_complex_claims(_) -> "no extra claims provided", #{}, [] }], lists:foreach(fun({Case, Authorization, ExpectedScope0}) -> - ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), ResourceServer = ResourceServer0#resource_server{ scope_prefix = <<"rabbitmq.rabbitmq-resource.">>, additional_scopes_key = <<"custom-key">> @@ -627,7 +630,7 @@ test_successful_authentication_without_scopes(_) -> test_successful_authorization_without_scopes(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( @@ -641,11 +644,12 @@ test_successful_access_with_a_token(_) -> %% Check user access granted by token Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), VHost = <<"vhost">>, Username = <<"username">>, - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), {ok, #auth_user{username = Username} = User} = user_login_authentication(Username, [{password, Token}]), @@ -656,7 +660,8 @@ test_successful_access_with_a_token(_) -> assert_resource_access_granted(User, VHost, <<"bar">>, read), assert_resource_access_granted(User, VHost, custom, <<"bar">>, read), - assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => <<"#/foo">>}). + assert_topic_access_granted(User, VHost, <<"bar">>, read, + #{routing_key => <<"#/foo">>}). successful_access_with_a_token_with_variables_in_scopes(_) -> %% Generate a token with JOSE @@ -664,25 +669,28 @@ successful_access_with_a_token_with_variables_in_scopes(_) -> %% Check user access granted by token Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), VHost = <<"my-vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs( - ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token([<<"rabbitmq.read:{vhost}/*/{sub}">>]), Username), + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( + [<<"rabbitmq.read:{vhost}/*/{sub}">>]), Username), Jwk), {ok, #auth_user{username = Username} = User} = user_login_authentication(Username, #{password => Token}), - assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => Username}). + assert_topic_access_granted(User, VHost, <<"bar">>, read, + #{routing_key => Username}). successful_access_with_a_parsed_token(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), Username = <<"username">>, - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), {ok, #auth_user{impl = Impl} } = user_login_authentication(Username, [{password, Token}]), @@ -693,10 +701,12 @@ successful_access_with_a_parsed_token(_) -> test_successful_access_with_a_token_that_has_tag_scopes(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), Username = <<"username">>, - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( - [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">>]), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( + [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">>]), + Username), Jwk), {ok, #auth_user{username = Username, tags = [management, policymaker]}} = user_login_authentication(Username, [{password, Token}]). @@ -704,9 +714,9 @@ test_successful_access_with_a_token_that_has_tag_scopes(_) -> 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}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), Alias = <<"client-alias-1">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ Alias => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -744,10 +754,10 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field( test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_and_custom_scope_prefix(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, scope_prefix, <<>>), + set_env(key_config, UaaEnv), + set_env(scope_prefix, <<>>), Alias = <<"client-alias-1">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ Alias => [ <<"configure:vhost/one">>, <<"write:vhost/two">>, @@ -785,11 +795,11 @@ 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(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), Role1 = <<"client-aliases-1">>, Role2 = <<"client-aliases-2">>, Role3 = <<"client-aliases-3">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ Role1 => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.tag:management">> @@ -808,7 +818,8 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( - ?UTIL_MOD:token_with_scope_alias_in_scope_field([Role1, Role2, Role3]), Username), Jwk), + ?UTIL_MOD:token_with_scope_alias_in_scope_field([Role1, Role2, Role3]), + Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = user_login_authentication(Username, [{password, Token}]), @@ -830,10 +841,10 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), Alias = <<"client-alias-33">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ <<"non-existent-alias-23948sdkfjsdof8">> => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -867,10 +878,10 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_fie test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), Alias = <<"client-alias-1">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ Alias => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -883,7 +894,8 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_ VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( - ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), + Username), Jwk), {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), @@ -904,12 +916,12 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), Role1 = <<"client-aliases-1">>, Role2 = <<"client-aliases-2">>, Role3 = <<"client-aliases-3">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ Role1 => [ <<"rabbitmq.configure:vhost/one">> ], @@ -927,7 +939,8 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc Username = <<"username">>, Claims = [Role1, Role2, Role3], Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( - ?UTIL_MOD:token_with_scope_alias_in_claim_field(Claims, [<<"unrelated">>]), Username), Jwk), + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Claims, [<<"unrelated">>]), + Username), Jwk), {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_granted(AuthUser, VHost), @@ -948,11 +961,11 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), + set_env(resource_server_id, <<"rabbitmq">>), Alias = <<"client-alias-11">>, - application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ + set_env(scope_aliases, #{ <<"non-existent-client-alias-9238923789">> => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -965,7 +978,8 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_sco VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( - ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), + Username), Jwk), {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_denied(AuthUser, VHost), @@ -985,38 +999,40 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_sco test_unsuccessful_access_with_a_bogus_token(_) -> Username = <<"username">>, - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(resource_server_id, <<"rabbitmq">>), Jwk0 = ?UTIL_MOD:fixture_jwk(), Jwk = Jwk0#{<<"k">> => <<"bm90b2tlbmtleQ">>}, UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), - ?assertMatch({refused, _, _}, - user_login_authentication(Username, [{password, <<"not a token">>}])). + ?assertMatch({refused, _, _}, user_login_authentication(Username, + [{password, <<"not a token">>}])). unsuccessful_access_without_scopes(_) -> Username = <<"username">>, - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(resource_server_id, <<"rabbitmq">>), Jwk = ?UTIL_MOD:fixture_jwk(), - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:token_without_scopes(), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:token_without_scopes(), Username), Jwk), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), - {ok, #auth_user{username = Username, tags = [], impl = _CredentialsFun } = AuthUser} = - user_login_authentication(Username, [{password, Token}]), + {ok, #auth_user{username = Username, tags = [], impl = _CredentialsFun } + = AuthUser} = user_login_authentication(Username, [{password, Token}]), assert_vhost_access_denied(AuthUser, <<"vhost">>). test_restricted_vhost_access_with_a_valid_token(_) -> Username = <<"username">>, - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(resource_server_id, <<"rabbitmq">>), Jwk = ?UTIL_MOD:fixture_jwk(), - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), %% this user can authenticate successfully and access certain vhosts {ok, #auth_user{username = Username, tags = []} = User} = @@ -1028,12 +1044,13 @@ test_restricted_vhost_access_with_a_valid_token(_) -> test_insufficient_permissions_in_a_valid_token(_) -> VHost = <<"vhost">>, Username = <<"username">>, - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(resource_server_id, <<"rabbitmq">>), Jwk = ?UTIL_MOD:fixture_jwk(), - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + set_env(key_config, UaaEnv), {ok, #auth_user{username = Username} = User} = user_login_authentication(Username, [{password, Token}]), @@ -1041,15 +1058,16 @@ test_insufficient_permissions_in_a_valid_token(_) -> %% access to these resources is not granted assert_resource_access_denied(User, VHost, <<"foo1">>, configure), assert_resource_access_denied(User, VHost, <<"bar">>, write), - assert_topic_access_refused(User, VHost, <<"bar">>, read, #{routing_key => <<"foo/#">>}). + assert_topic_access_refused(User, VHost, <<"bar">>, read, + #{routing_key => <<"foo/#">>}). test_invalid_signature(_) -> Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), WrongJwk = ?UTIL_MOD:fixture_jwk("wrong", <<"GawgguFyGrWKav7AX4VKUg">>), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, WrongJwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), ?assertMatch({refused, _, [signature_invalid]}, @@ -1060,8 +1078,8 @@ test_token_expiration(_) -> Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], - application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), {ok, #auth_user{username = Username} = User} = @@ -1076,7 +1094,8 @@ test_token_expiration(_) -> ?UTIL_MOD:wait_for_token_to_expire(), #{<<"exp">> := Exp} = TokenData, - ExpectedError = "Provided JWT token has expired at timestamp " ++ integer_to_list(Exp) ++ " (validated at " ++ integer_to_list(Exp) ++ ")", + ExpectedError = "Provided JWT token has expired at timestamp " ++ + integer_to_list(Exp) ++ " (validated at " ++ integer_to_list(Exp) ++ ")", assert_resource_access_errors(ExpectedError, User, VHost, <<"foo">>, configure), ?assertMatch({refused, _, _}, @@ -1086,9 +1105,14 @@ test_incorrect_kid(_) -> AltKid = <<"other-token-key">>, Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), - application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, AltKid, true), - ?assertMatch({refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [{error,{missing_oauth_provider_attributes, [issuer]}}]}, + set_env(resource_server_id, + <<"rabbitmq">>), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, + AltKid, true), + ?assertMatch( + {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", + [{error,{missing_oauth_provider_attributes, [issuer]}}]}, user_login_authentication(Username, #{password => Token})). login_and_check_vhost_access(Username, Token, Vhost) -> @@ -1104,9 +1128,12 @@ test_command_json(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], - #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), json => Json}), - Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), - rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). + #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), + json => Json}), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). test_username_from(_) -> Pairs = [ @@ -1143,7 +1170,8 @@ test_username_from(_) -> lists:foreach( fun( {Comment, PreferredUsernameClaims, Token, ExpectedUsername}) -> - ActualUsername = rabbit_auth_backend_oauth2:username_from(PreferredUsernameClaims, Token), + ActualUsername = rabbit_auth_backend_oauth2:username_from( + PreferredUsernameClaims, Token), ?assertEqual(ExpectedUsername, ActualUsername, Comment) end, Pairs). @@ -1160,10 +1188,13 @@ test_command_pem_file(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], - #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem_file => PublicKeyFile}), + #{node => rabbit_ct_broker_helpers:get_node_config( + Config, 0, nodename), pem_file => PublicKeyFile}), - Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:fixture_token(), Jwk, <<"token-key">>), - rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). + Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:fixture_token(), + Jwk, <<"token-key">>), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). test_command_pem(Config) -> @@ -1176,10 +1207,13 @@ test_command_pem(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], - #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem => Pem}), + #{node => rabbit_ct_broker_helpers:get_node_config( + Config, 0, nodename), pem => Pem}), - Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, <<"token-key">>), - rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). + Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk, <<"token-key">>), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). test_command_pem_no_kid(Config) -> Username = <<"username">>, @@ -1191,10 +1225,13 @@ test_command_pem_no_kid(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], - #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem => Pem}), + #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), + pem => Pem}), - Token = ?UTIL_MOD:sign_token_no_kid(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), - rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). + Token = ?UTIL_MOD:sign_token_no_kid(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). filter_matching_scope_prefix_and_drop_it(_) -> @@ -1208,7 +1245,8 @@ filter_matching_scope_prefix_and_drop_it(_) -> ], lists:map( fun({ScopePrefix, Src, Dest}) -> - Dest = rabbit_oauth2_scope:filter_matching_scope_prefix_and_drop_it(Src, ScopePrefix) + Dest = rabbit_oauth2_scope:filter_matching_scope_prefix_and_drop_it( + Src, ScopePrefix) end, Examples). @@ -1233,7 +1271,7 @@ normalize_token_scopes_with_scope_prefix(_) -> ], lists:map(fun({ ScopePrefix, Token0, ExpectedScopes}) -> - ResourceServer0 = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), ResourceServer = ResourceServer0#resource_server { scope_prefix = ScopePrefix }, @@ -1242,7 +1280,7 @@ normalize_token_scopes_with_scope_prefix(_) -> end, Scenarios). normalize_token_scope_from_space_separated_list_in_scope_claim(_) -> - ResourceServer = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = new_resource_server(?RESOURCE_SERVER_ID), Token0 = #{ ?SCOPE_JWT_FIELD => <<"foo rabbitmq.bar bar.foo one.two foobar rabbitmq.other.third">> }, @@ -1250,7 +1288,7 @@ normalize_token_scope_from_space_separated_list_in_scope_claim(_) -> ?assertEqual([<<"bar">>, <<"other.third">>], uaa_jwt:get_scope(Token)). normalize_token_scope_without_scope_claim(_) -> - ResourceServer = resource_server:new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = new_resource_server(?RESOURCE_SERVER_ID), Token0 = #{ }, ?assertEqual([], uaa_jwt:get_scope(normalize_token_scope(ResourceServer, Token0))). @@ -1258,6 +1296,9 @@ normalize_token_scope_without_scope_claim(_) -> %% Helpers %% +set_env(Par, Var) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Var). + assert_vhost_access_granted(AuthUser, VHost) -> assert_vhost_access_response(true, AuthUser, VHost). @@ -1269,45 +1310,63 @@ assert_vhost_access_response(ExpectedResult, AuthUser, VHost) -> check_vhost_access(AuthUser, VHost, none)). assert_resource_access_granted(AuthUser, VHost, ResourceName, PermissionKind) -> - assert_resource_access_response(true, AuthUser, VHost, ResourceName, PermissionKind). + assert_resource_access_response(true, AuthUser, VHost, ResourceName, + PermissionKind). assert_resource_access_denied(AuthUser, VHost, ResourceName, PermissionKind) -> - assert_resource_access_response(false, AuthUser, VHost, ResourceName, PermissionKind). + assert_resource_access_response(false, AuthUser, VHost, ResourceName, + PermissionKind). -assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceName, PermissionKind) -> - assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, ResourceName, PermissionKind). +assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceName, + PermissionKind) -> + assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, + ResourceName, PermissionKind). -assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceName, PermissionKind) -> +assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceName, + PermissionKind) -> ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_resource_access( AuthUser, rabbit_misc:r(VHost, queue, ResourceName), PermissionKind, #{})). -assert_resource_access_granted(AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> - assert_resource_access_response(true, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind). +assert_resource_access_granted(AuthUser, VHost, ResourceKind, ResourceName, + PermissionKind) -> + assert_resource_access_response(true, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind). -assert_resource_access_denied(AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> - assert_resource_access_response(false, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind). +assert_resource_access_denied(AuthUser, VHost, ResourceKind, ResourceName, + PermissionKind) -> + assert_resource_access_response(false, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind). -assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> - assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind). +assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind) -> + assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, + ResourceKind, ResourceName, PermissionKind). -assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> +assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind) -> ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_resource_access( AuthUser, rabbit_misc:r(VHost, ResourceKind, ResourceName), PermissionKind, #{})). -assert_topic_access_granted(AuthUser, VHost, ResourceName, PermissionKind, AuthContext) -> - assert_topic_access_response(true, AuthUser, VHost, ResourceName, PermissionKind, AuthContext). +assert_topic_access_granted(AuthUser, VHost, ResourceName, PermissionKind, + AuthContext) -> + assert_topic_access_response(true, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext). -assert_topic_access_refused(AuthUser, VHost, ResourceName, PermissionKind, AuthContext) -> - assert_topic_access_response(false, AuthUser, VHost, ResourceName, PermissionKind, AuthContext). +assert_topic_access_refused(AuthUser, VHost, ResourceName, PermissionKind, + AuthContext) -> + assert_topic_access_response(false, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext). -assert_topic_access_response(ExpectedResult, AuthUser, VHost, ResourceName, PermissionKind, AuthContext) -> - ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_topic_access( +assert_topic_access_response(ExpectedResult, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext) -> + ?assertEqual(ExpectedResult, + rabbit_auth_backend_oauth2:check_topic_access( AuthUser, #resource{virtual_host = VHost, kind = topic, From 0ec415a419a1daf6f8af1928bb0f8a4ec530e7d7 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 7 Oct 2024 10:04:53 +0200 Subject: [PATCH 55/64] Fix bazel misconfiguration --- deps/rabbitmq_auth_backend_oauth2/BUILD.bazel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 71529eca5e3b..14a77fb5d3e4 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -113,7 +113,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "oauth_provider_SUITE", + name = "rabbit_oauth2_provider_SUITE", additional_beam = [ "test/oauth2_http_mock.beam", ], From 743f663520874bda0d0e5c837614f48b258409c4 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Mon, 7 Oct 2024 10:20:38 +0200 Subject: [PATCH 56/64] Fix bazel configuration --- deps/rabbitmq_auth_backend_oauth2/BUILD.bazel | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 14a77fb5d3e4..741bd873135d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -123,7 +123,7 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( - name = "resource_server_SUITE" + name = "rabbit_oauth2_resource_server_SUITE" ) rabbitmq_integration_suite( @@ -149,7 +149,7 @@ rabbitmq_suite( ) rabbitmq_suite( - name = "oauth2_schema_SUITE", + name = "rabbit_oauth2_schema_SUITE", size = "medium", deps = [ "//deps/rabbit_common:erlang_app", From 21a4a250d54a1a9d7658e4c7dad0394cd6afde14 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 8 Oct 2024 07:26:10 +0200 Subject: [PATCH 57/64] Run selenium job with Makefile --- .../workflows/test-management-ui-for-pr.yaml | 21 ++++------------- .github/workflows/test-management-ui.yaml | 23 ++++--------------- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index 98ec573b739d..76243556618d 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -42,23 +42,10 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} - - name: Configure Bazel - run: | - if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then - cat << EOF >> user.bazelrc - build --remote_cache=https://storage.googleapis.com/${{ secrets.REMOTE_CACHE_BUCKET_NAME }} - build --google_default_credentials - - build --remote_download_toplevel - EOF - fi - cat << EOF >> user.bazelrc - build --color=yes - EOF - - name: Build & Load RabbitMQ OCI run: | - bazelisk run packaging/docker-image:rabbitmq-amd64 + make package-generic-unix + make docker-image - name: Configure Docker Network run: | @@ -71,8 +58,8 @@ jobs: - name: Run full ui suites on a standalone rabbitmq server run: | - RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ - ${SELENIUM_DIR}/run-suites.sh + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq-$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/full-suite mv /tmp/selenium/* /tmp/full-suite mkdir -p /tmp/full-suite/logs diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml index b05a80cb4e91..861857a4cfe9 100644 --- a/.github/workflows/test-management-ui.yaml +++ b/.github/workflows/test-management-ui.yaml @@ -56,23 +56,10 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} - - name: Configure Bazel - run: | - if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then - cat << EOF >> user.bazelrc - build --remote_cache=https://storage.googleapis.com/${{ secrets.REMOTE_CACHE_BUCKET_NAME }} - build --google_default_credentials - - build --remote_download_toplevel - EOF - fi - cat << EOF >> user.bazelrc - build --color=yes - EOF - - name: Build & Load RabbitMQ OCI run: | - bazelisk run packaging/docker-image:rabbitmq-amd64 + make package-generic-unix + make docker-image - name: Configure Docker Network run: | @@ -84,9 +71,9 @@ jobs: docker build -t mocha-test --target test . - name: Run short ui suite on a 3-node rabbitmq cluster - run: | - RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ - ADDON_PROFILES=cluster ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + run: | + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq-$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/short-suite mv /tmp/selenium/* /tmp/short-suite mkdir -p /tmp/short-suite/logs From e1e101db9b9dbce873e036a06d48c229096cb143 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 8 Oct 2024 07:46:26 +0200 Subject: [PATCH 58/64] Fix issue with docker image name --- .github/workflows/test-management-ui-for-pr.yaml | 2 +- .github/workflows/test-management-ui.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index 76243556618d..231f090523c4 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -59,7 +59,7 @@ jobs: - name: Run full ui suites on a standalone rabbitmq server run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') - RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq-$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/full-suite mv /tmp/selenium/* /tmp/full-suite mkdir -p /tmp/full-suite/logs diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml index 861857a4cfe9..b81a0c6438ce 100644 --- a/.github/workflows/test-management-ui.yaml +++ b/.github/workflows/test-management-ui.yaml @@ -73,7 +73,7 @@ jobs: - name: Run short ui suite on a 3-node rabbitmq cluster run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') - RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq-$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/short-suite mv /tmp/selenium/* /tmp/short-suite mkdir -p /tmp/short-suite/logs From d98eb17c2c00ac325518caad54b86db433d5d284 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Tue, 8 Oct 2024 08:02:41 +0200 Subject: [PATCH 59/64] Move also selenium authz tests to Makefile --- .github/workflows/test-authnz.yaml | 20 ++++--------------- .../workflows/test-management-ui-for-pr.yaml | 3 ++- .github/workflows/test-management-ui.yaml | 3 ++- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test-authnz.yaml b/.github/workflows/test-authnz.yaml index 2b0342b03823..05f807179ecc 100644 --- a/.github/workflows/test-authnz.yaml +++ b/.github/workflows/test-authnz.yaml @@ -62,23 +62,10 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} - - name: Configure Bazel - run: | - if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then - cat << EOF >> user.bazelrc - build --remote_cache=https://storage.googleapis.com/${{ secrets.REMOTE_CACHE_BUCKET_NAME }} - build --google_default_credentials - - build --remote_download_toplevel - EOF - fi - cat << EOF >> user.bazelrc - build --color=yes - EOF - - name: Build & Load RabbitMQ OCI run: | - bazelisk run packaging/docker-image:rabbitmq-amd64 + make package-generic-unix + make docker-image - name: Configure Docker Network run: | @@ -91,7 +78,8 @@ jobs: - name: Run Suites run: | - RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ ${SELENIUM_DIR}/run-suites.sh full-suite-authnz-messaging - name: Upload Test Artifacts diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index 231f090523c4..358ff5571e5d 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -59,7 +59,8 @@ jobs: - name: Run full ui suites on a standalone rabbitmq server run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') - RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ + ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/full-suite mv /tmp/selenium/* /tmp/full-suite mkdir -p /tmp/full-suite/logs diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml index b81a0c6438ce..76fe452e10ed 100644 --- a/.github/workflows/test-management-ui.yaml +++ b/.github/workflows/test-management-ui.yaml @@ -73,7 +73,8 @@ jobs: - name: Run short ui suite on a 3-node rabbitmq cluster run: | IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') - RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ + ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/short-suite mv /tmp/selenium/* /tmp/short-suite mkdir -p /tmp/short-suite/logs From 545abce10fd179bdfeb118fffe7bcce1f5bb08ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Thu, 26 Sep 2024 16:43:55 +0200 Subject: [PATCH 60/64] CQ: Fix shared store scanner missing messages It was still possible, although rare, to have message store files lose message data, when the following conditions were met: * the message data contains byte values 255 (255 is used as an OK marker after a message) * the message is located after a 0-filled hole in the file * the length of the data is at least 4096 bytes and if we misread it (as detailed below) we encounter a 255 byte where we expect the OK marker The trick for the code to previously misread the length can be explained as follow: A message is stored in the following format: <> With MsgId always being 16 bytes in length. So Len is always at least 16, if the message data Msg is empty. But technically it never is. Now if we have a zero filled hole just before this message, we may end up with this: <<0, Len:64, MsgIdAndMsg:Len/unit:8, 255>> When we are scanning we are testing bytes to see if there is a message there or not. We look for a Len that gives us byte 255 after MsgIdAndMsg. Len of value 4096 looks like this in binary: <<0:48, 16, 0>> Problem is if we have leading zeroes, Len may look like this: <<0, 0:48, 16, 0>> If we take the first 64 bits we get a potential length of 16. We look at the byte after the next 16 bytes. If it is 255, we think this is a message and skip by this amount of bytes, and mistakenly miss the real message. Solving this by changing the file format would be simple enough, but we don't have the luxury to afford that. A different solution was found, which is to combine file scanning with checking that the message exists in the message store index (populated from queues at startup, and kept up to date over the life time of the store). Then we know for sure that the message above doesn't exist, because the MsgId won't be found in the index. If it is, then the file number and offset will not match, and the check will fail. There remains a small chance that we get it wrong during dirty recovery. Only a better file format would improve that. --- deps/rabbit/src/rabbit_msg_store.erl | 179 ++++++++++++----------- deps/rabbit/test/backing_queue_SUITE.erl | 23 ++- 2 files changed, 108 insertions(+), 94 deletions(-) diff --git a/deps/rabbit/src/rabbit_msg_store.erl b/deps/rabbit/src/rabbit_msg_store.erl index b28506ab2ab8..efd8d53a0507 100644 --- a/deps/rabbit/src/rabbit_msg_store.erl +++ b/deps/rabbit/src/rabbit_msg_store.erl @@ -16,7 +16,7 @@ -export([compact_file/2, truncate_file/4, delete_file/2]). %% internal --export([scan_file_for_valid_messages/1]). %% salvage tool +-export([scan_file_for_valid_messages/1, scan_file_for_valid_messages/2]). %% salvage tool -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, prioritise_call/4, prioritise_cast/3, @@ -1472,31 +1472,28 @@ list_sorted_filenames(Dir, Ext) -> -define(SCAN_BLOCK_SIZE, 4194304). %% 4MB -scan_file_for_valid_messages(Dir, FileName) -> - scan_file_for_valid_messages(form_filename(Dir, FileName)). - +%% Exported as a salvage tool. Not as accurate as node recovery +%% because it doesn't have the queue index. scan_file_for_valid_messages(Path) -> + scan_file_for_valid_messages(Path, fun(Obj) -> {valid, Obj} end). + +scan_file_for_valid_messages(Path, Fun) -> case file:open(Path, [read, binary, raw]) of {ok, Fd} -> {ok, FileSize} = file:position(Fd, eof), {ok, _} = file:position(Fd, bof), - Messages = scan(<<>>, Fd, 0, FileSize, #{}, []), + Messages = scan(<<>>, Fd, Fun, 0, FileSize, #{}, []), ok = file:close(Fd), - case Messages of - [] -> - {ok, [], 0}; - [{_, TotalSize, Offset}|_] -> - {ok, Messages, Offset + TotalSize} - end; + {ok, Messages}; {error, enoent} -> - {ok, [], 0}; + {ok, []}; {error, Reason} -> {error, {unable_to_scan_file, filename:basename(Path), Reason}} end. -scan(Buffer, Fd, Offset, FileSize, MsgIdsFound, Acc) -> +scan(Buffer, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) -> case file:read(Fd, ?SCAN_BLOCK_SIZE) of eof -> Acc; @@ -1505,12 +1502,12 @@ scan(Buffer, Fd, Offset, FileSize, MsgIdsFound, Acc) -> <<>> -> Data0; _ -> <> end, - scan_data(Data, Fd, Offset, FileSize, MsgIdsFound, Acc) + scan_data(Data, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) end. %% Message might have been found. scan_data(<> = Data, - Fd, Offset, FileSize, MsgIdsFound, Acc) + Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) when Size >= 16 -> <> = MsgIdAndMsg, case MsgIdsFound of @@ -1519,26 +1516,37 @@ scan_data(<> = Data, %% simply be a coincidence. Try the next byte. #{MsgIdInt := true} -> <<_, Rest2/bits>> = Data, - scan_data(Rest2, Fd, Offset + 1, FileSize, MsgIdsFound, Acc); + scan_data(Rest2, Fd, Fun, Offset + 1, FileSize, MsgIdsFound, Acc); %% Data looks to be a message. _ -> %% Avoid sub-binary construction. MsgId = <>, TotalSize = Size + 9, - scan_data(Rest, Fd, Offset + TotalSize, FileSize, - MsgIdsFound#{MsgIdInt => true}, - [{MsgId, TotalSize, Offset}|Acc]) + case Fun({MsgId, TotalSize, Offset}) of + %% Confirmed to be a message by the provided fun. + {valid, Entry} -> + scan_data(Rest, Fd, Fun, Offset + TotalSize, FileSize, + MsgIdsFound#{MsgIdInt => true}, [Entry|Acc]); + %% Confirmed to be a message but we don't need it anymore. + previously_valid -> + scan_data(Rest, Fd, Fun, Offset + TotalSize, FileSize, + MsgIdsFound#{MsgIdInt => true}, Acc); + %% Not a message, try the next byte. + invalid -> + <<_, Rest2/bits>> = Data, + scan_data(Rest2, Fd, Fun, Offset + 1, FileSize, MsgIdsFound, Acc) + end end; %% This might be the start of a message. -scan_data(<> = Data, Fd, Offset, FileSize, MsgIdsFound, Acc) +scan_data(<> = Data, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) when byte_size(Rest) < Size + 1, Size < FileSize - Offset -> - scan(Data, Fd, Offset, FileSize, MsgIdsFound, Acc); -scan_data(Data, Fd, Offset, FileSize, MsgIdsFound, Acc) + scan(Data, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc); +scan_data(Data, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) when byte_size(Data) < 8 -> - scan(Data, Fd, Offset, FileSize, MsgIdsFound, Acc); + scan(Data, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc); %% This is definitely not a message. Try the next byte. -scan_data(<<_, Rest/bits>>, Fd, Offset, FileSize, MsgIdsFound, Acc) -> - scan_data(Rest, Fd, Offset + 1, FileSize, MsgIdsFound, Acc). +scan_data(<<_, Rest/bits>>, Fd, Fun, Offset, FileSize, MsgIdsFound, Acc) -> + scan_data(Rest, Fd, Fun, Offset + 1, FileSize, MsgIdsFound, Acc). %%---------------------------------------------------------------------------- %% Ets index @@ -1742,47 +1750,39 @@ build_index(false, {MsgRefDeltaGen, MsgRefDeltaGenInit}, build_index_worker(Gatherer, #msstate { index_ets = IndexEts, dir = Dir }, File, Files) -> - FileName = filenum_to_name(File), + Path = form_filename(Dir, filenum_to_name(File)), rabbit_log:debug("Rebuilding message location index from ~ts (~B file(s) remaining)", - [form_filename(Dir, FileName), length(Files)]), + [Path, length(Files)]), %% The scan function already dealt with duplicate messages - %% within the file. We then get messages in reverse order. - {ok, Messages, FileSize} = - scan_file_for_valid_messages(Dir, FileName), - %% Valid messages are in file order so the last message is - %% the last message from the list. - {ValidMessages, ValidTotalSize} = - lists:foldl( - fun (Obj = {MsgId, TotalSize, Offset}, {VMAcc, VTSAcc}) -> - %% Fan-out may result in the same message data in multiple - %% files so we have to guard against it. - case index_lookup(IndexEts, MsgId) of - #msg_location { file = undefined } = StoreEntry -> - ok = index_update(IndexEts, StoreEntry #msg_location { - file = File, offset = Offset, - total_size = TotalSize }), - {[Obj | VMAcc], VTSAcc + TotalSize}; - _ -> - {VMAcc, VTSAcc} - end - end, {[], 0}, Messages), - FileSize1 = - case Files of - %% if it's the last file, we'll truncate to remove any - %% rubbish above the last valid message. This affects the - %% file size. - [] -> case ValidMessages of - [] -> 0; - _ -> {_MsgId, TotalSize, Offset} = - lists:last(ValidMessages), - Offset + TotalSize - end; - [_|_] -> FileSize - end, + %% within the file, and only returns valid messages (we do + %% the index lookup in the fun). But we get messages in reverse order. + {ok, Messages} = scan_file_for_valid_messages(Path, + fun (Obj = {MsgId, TotalSize, Offset}) -> + %% Fan-out may result in the same message data in multiple + %% files so we have to guard against it. + case index_lookup(IndexEts, MsgId) of + #msg_location { file = undefined } = StoreEntry -> + ok = index_update(IndexEts, StoreEntry #msg_location { + file = File, offset = Offset, + total_size = TotalSize }), + {valid, Obj}; + _ -> + invalid + end + end), + ValidTotalSize = lists:foldl(fun({_, TotalSize, _}, Acc) -> Acc + TotalSize end, 0, Messages), + %% Any file may have rubbish at the end of it that we will want truncated. + %% Note that the last message in the file is the first in the list. + FileSize = case Messages of + [] -> + 0; + [{_, TotalSize, Offset}|_] -> + Offset + TotalSize + end, ok = gatherer:in(Gatherer, #file_summary { file = File, valid_total_size = ValidTotalSize, - file_size = FileSize1, + file_size = FileSize, locked = false }), ok = gatherer:finish(Gatherer). @@ -1933,7 +1933,7 @@ compact_file(File, State = #gc_state { index_ets = IndexEts, %% Load the messages. It's possible to get 0 messages here; %% that's OK. That means we have little to do as the file is %% about to be deleted. - {Messages, _} = scan_and_vacuum_message_file(File, State), + Messages = scan_and_vacuum_message_file(File, State), %% Blank holes. We must do this first otherwise the file is left %% with data that may confuse the code (for example data that looks %% like a message, isn't a message, but spans over a real message). @@ -2087,7 +2087,7 @@ delete_file(File, State = #gc_state { file_summary_ets = FileSummaryEts, _ -> [#file_summary{ valid_total_size = 0, file_size = FileSize }] = ets:lookup(FileSummaryEts, File), - {[], 0} = scan_and_vacuum_message_file(File, State), + [] = scan_and_vacuum_message_file(File, State), ok = file:delete(form_filename(Dir, filenum_to_name(File))), true = ets:delete(FileSummaryEts, File), rabbit_log:debug("Deleted empty file number ~tp; reclaimed ~tp bytes", [File, FileSize]), @@ -2096,28 +2096,31 @@ delete_file(File, State = #gc_state { file_summary_ets = FileSummaryEts, scan_and_vacuum_message_file(File, #gc_state{ index_ets = IndexEts, dir = Dir }) -> %% Messages here will be end-of-file at start-of-list - {ok, Messages, _FileSize} = - scan_file_for_valid_messages(Dir, filenum_to_name(File)), - %% foldl will reverse so will end up with msgs in ascending offset order - lists:foldl( - fun ({MsgId, TotalSize, Offset}, Acc = {List, Size}) -> - case index_lookup(IndexEts, MsgId) of - #msg_location { file = File, total_size = TotalSize, - offset = Offset, ref_count = 0 } = Entry -> - index_delete_object(IndexEts, Entry), - Acc; - #msg_location { file = File, total_size = TotalSize, - offset = Offset } = Entry -> - {[ Entry | List ], TotalSize + Size}; - %% Fan-out may remove the entry but also write a new - %% entry in a different file when it needs to write - %% a message and the existing reference is in a file - %% that's about to be deleted. So we explicitly accept - %% these cases and ignore this message. - #msg_location { file = OtherFile, total_size = TotalSize } - when File =/= OtherFile -> - Acc; - not_found -> - Acc - end - end, {[], 0}, Messages). + Path = form_filename(Dir, filenum_to_name(File)), + {ok, Messages} = scan_file_for_valid_messages(Path, + fun ({MsgId, TotalSize, Offset}) -> + case index_lookup(IndexEts, MsgId) of + #msg_location { file = File, total_size = TotalSize, + offset = Offset, ref_count = 0 } = Entry -> + index_delete_object(IndexEts, Entry), + %% The message was valid, but since we have now deleted + %% it due to having no ref_count, it becomes invalid. + %% We still want to let the scan function skip though. + previously_valid; + #msg_location { file = File, total_size = TotalSize, + offset = Offset } = Entry -> + {valid, Entry}; + %% Fan-out may remove the entry but also write a new + %% entry in a different file when it needs to write + %% a message and the existing reference is in a file + %% that's about to be deleted. So we explicitly accept + %% these cases and ignore this message. + #msg_location { file = OtherFile, total_size = TotalSize } + when File =/= OtherFile -> + invalid; + not_found -> + invalid + end + end), + %% @todo Do we really need to reverse messages? + lists:reverse(Messages). diff --git a/deps/rabbit/test/backing_queue_SUITE.erl b/deps/rabbit/test/backing_queue_SUITE.erl index 2b4ce444c991..845cdc17ef56 100644 --- a/deps/rabbit/test/backing_queue_SUITE.erl +++ b/deps/rabbit/test/backing_queue_SUITE.erl @@ -630,6 +630,22 @@ msg_store_file_scan1(Config) -> %% Messages with no content. ok = Scan([{bin, <<0:64, "deadbeefdeadbeef", 255>>}]), ok = Scan([{msg, gen_id(), <<>>}]), + %% Tricky messages. + %% + %% These only get properly detected when the index is populated. + %% In this test case we simulate the index with a fun. + TrickyScan = fun (Blocks, Expected, Fun) -> + Path = gen_msg_file(Config, Blocks), + Result = rabbit_msg_store:scan_file_for_valid_messages(Path, Fun), + case Result of + Expected -> ok; + _ -> {expected, Expected, got, Result} + end + end, + ok = TrickyScan( + [{bin, <<0, 0:48, 17, 17, "idididididididid", 255, 0:4352/unit:8, 255>>}], + {ok, [{<<"idididididididid">>, 4378, 1}]}, + fun(Obj = {<<"idididididididid">>, 4378, 1}) -> {valid, Obj}; (_) -> invalid end), %% All good!! passed. @@ -662,12 +678,7 @@ gen_msg_file(Config, Blocks) -> gen_result(Blocks) -> Messages = gen_result(Blocks, 0, []), - case Messages of - [] -> - {ok, [], 0}; - [{_, TotalSize, Offset}|_] -> - {ok, Messages, Offset + TotalSize} - end. + {ok, Messages}. gen_result([], _, Acc) -> Acc; From 541f053979aa239370723b96884103ab87e4a7ff Mon Sep 17 00:00:00 2001 From: Simon Unge Date: Mon, 7 Oct 2024 20:23:50 +0000 Subject: [PATCH 61/64] Dependency Recon updated from 2.5.3 to 2.5.6 --- MODULE.bazel | 4 ++-- bazel/BUILD.recon | 38 ++++++++++++++++---------------------- rabbitmq-components.mk | 2 +- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 5211632962cc..4721a077d58b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -277,8 +277,8 @@ erlang_package.hex_package( erlang_package.hex_package( name = "recon", build_file = "@rabbitmq-server//bazel:BUILD.recon", - sha256 = "6c6683f46fd4a1dfd98404b9f78dcabc7fcd8826613a89dcb984727a8c3099d7", - version = "2.5.3", + sha256 = "96c6799792d735cc0f0fd0f86267e9d351e63339cbe03df9d162010cefc26bb0", + version = "2.5.6", ) erlang_package.hex_package( diff --git a/bazel/BUILD.recon b/bazel/BUILD.recon index 9a2eb6cc8baa..35d78a04b4de 100644 --- a/bazel/BUILD.recon +++ b/bazel/BUILD.recon @@ -25,17 +25,9 @@ erlang_bytecode( "src/recon_rec.erl", "src/recon_trace.erl", ], - outs = [ - "ebin/recon.beam", - "ebin/recon_alloc.beam", - "ebin/recon_lib.beam", - "ebin/recon_map.beam", - "ebin/recon_rec.beam", - "ebin/recon_trace.beam", - ], - hdrs = [], + hdrs = [":public_and_private_hdrs"], app_name = "recon", - beam = [], + dest = "ebin", erlc_opts = "//:erlc_opts", ) @@ -57,20 +49,11 @@ filegroup( ], ) -filegroup( - name = "private_hdrs", - srcs = [], -) +filegroup(name = "private_hdrs") -filegroup( - name = "public_hdrs", - srcs = [], -) +filegroup(name = "public_hdrs") -filegroup( - name = "priv", - srcs = [], -) +filegroup(name = "priv") filegroup( name = "licenses", @@ -96,8 +79,12 @@ filegroup( erlang_app( name = "erlang_app", srcs = [":all_srcs"], + hdrs = [":public_hdrs"], app_name = "recon", beam_files = [":beam_files"], + extra_apps = ["syntax_tools"], + license_files = [":license_files"], + priv = [":priv"], ) alias( @@ -105,3 +92,10 @@ alias( actual = ":erlang_app", visibility = ["//visibility:public"], ) + +filegroup( + name = "license_files", + srcs = [ + "LICENSE", + ], +) diff --git a/rabbitmq-components.mk b/rabbitmq-components.mk index b6361f61d0cd..51ae1961dfc2 100644 --- a/rabbitmq-components.mk +++ b/rabbitmq-components.mk @@ -52,7 +52,7 @@ dep_osiris = git https://github.com/rabbitmq/osiris v1.8.3 dep_prometheus = hex 4.11.0 dep_ra = hex 2.14.0 dep_ranch = hex 2.1.0 -dep_recon = hex 2.5.3 +dep_recon = hex 2.5.6 dep_redbug = hex 2.0.7 dep_systemd = hex 0.6.1 dep_thoas = hex 1.0.0 From d63d70c36cefe20e5f72adfc066264c53d47be19 Mon Sep 17 00:00:00 2001 From: GitHub Date: Tue, 8 Oct 2024 04:02:25 +0000 Subject: [PATCH 62/64] bazel run gazelle --- deps/rabbit/BUILD.bazel | 2 +- deps/rabbitmq_ct_helpers/app.bzl | 6 ++++++ moduleindex.yaml | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/deps/rabbit/BUILD.bazel b/deps/rabbit/BUILD.bazel index d9910dc90e14..8ce54e6f584b 100644 --- a/deps/rabbit/BUILD.bazel +++ b/deps/rabbit/BUILD.bazel @@ -1257,10 +1257,10 @@ rabbitmq_integration_suite( rabbitmq_integration_suite( name = "amqp_address_SUITE", - shard_count = 2, additional_beam = [ ":test_amqp_utils_beam", ], + shard_count = 2, runtime_deps = [ "//deps/rabbitmq_amqp_client:erlang_app", ], diff --git a/deps/rabbitmq_ct_helpers/app.bzl b/deps/rabbitmq_ct_helpers/app.bzl index 7f56b8dfcbab..a2f85973d675 100644 --- a/deps/rabbitmq_ct_helpers/app.bzl +++ b/deps/rabbitmq_ct_helpers/app.bzl @@ -11,7 +11,9 @@ def all_beam_files(name = "all_beam_files"): name = "other_beam", testonly = True, srcs = [ + "src/ct_master_fork.erl", "src/cth_log_redirect_any_domains.erl", + "src/cth_parallel_ct_detect_failure.erl", "src/rabbit_control_helper.erl", "src/rabbit_ct_broker_helpers.erl", "src/rabbit_ct_config_schema.erl", @@ -37,7 +39,9 @@ def all_test_beam_files(name = "all_test_beam_files"): name = "test_other_beam", testonly = True, srcs = [ + "src/ct_master_fork.erl", "src/cth_log_redirect_any_domains.erl", + "src/cth_parallel_ct_detect_failure.erl", "src/rabbit_control_helper.erl", "src/rabbit_ct_broker_helpers.erl", "src/rabbit_ct_config_schema.erl", @@ -99,7 +103,9 @@ def all_srcs(name = "all_srcs"): name = "srcs", testonly = True, srcs = [ + "src/ct_master_fork.erl", "src/cth_log_redirect_any_domains.erl", + "src/cth_parallel_ct_detect_failure.erl", "src/rabbit_control_helper.erl", "src/rabbit_ct_broker_helpers.erl", "src/rabbit_ct_config_schema.erl", diff --git a/moduleindex.yaml b/moduleindex.yaml index 1ce6bae902c0..08b3bdc8d0c7 100755 --- a/moduleindex.yaml +++ b/moduleindex.yaml @@ -864,7 +864,9 @@ rabbitmq_consistent_hash_exchange: rabbitmq_ct_client_helpers: - rabbit_ct_client_helpers rabbitmq_ct_helpers: +- ct_master_fork - cth_log_redirect_any_domains +- cth_parallel_ct_detect_failure - rabbit_control_helper - rabbit_ct_broker_helpers - rabbit_ct_config_schema From e7f82a53ba0ff9d7e783f38af3e88a7ed2c024f1 Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 8 Oct 2024 07:09:03 -0400 Subject: [PATCH 63/64] OAuth 2: add a missing dependency on rabbitmq_cli --- deps/rabbitmq_auth_backend_oauth2/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/Makefile b/deps/rabbitmq_auth_backend_oauth2/Makefile index 1066e7be8271..19262056e74d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/Makefile +++ b/deps/rabbitmq_auth_backend_oauth2/Makefile @@ -7,7 +7,7 @@ export BUILD_WITHOUT_QUIC LOCAL_DEPS = inets public_key BUILD_DEPS = rabbit_common -DEPS = rabbit cowlib jose base64url oauth2_client +DEPS = rabbit cowlib jose base64url oauth2_client rabbitmq_cli TEST_DEPS = cowboy rabbitmq_web_dispatch rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client rabbitmq_web_mqtt emqtt rabbitmq_amqp_client PLT_APPS += rabbitmqctl From c15f19fe830a8c9a0be65c127b65ae28310d200f Mon Sep 17 00:00:00 2001 From: Michael Klishin Date: Tue, 8 Oct 2024 07:11:43 -0400 Subject: [PATCH 64/64] OAuth 2: CLI is a build time dependency, not a runtime one --- deps/rabbitmq_auth_backend_oauth2/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/rabbitmq_auth_backend_oauth2/Makefile b/deps/rabbitmq_auth_backend_oauth2/Makefile index 19262056e74d..ce2bdbd048ac 100644 --- a/deps/rabbitmq_auth_backend_oauth2/Makefile +++ b/deps/rabbitmq_auth_backend_oauth2/Makefile @@ -6,8 +6,8 @@ BUILD_WITHOUT_QUIC=1 export BUILD_WITHOUT_QUIC LOCAL_DEPS = inets public_key -BUILD_DEPS = rabbit_common -DEPS = rabbit cowlib jose base64url oauth2_client rabbitmq_cli +BUILD_DEPS = rabbit_common rabbitmq_cli +DEPS = rabbit cowlib jose base64url oauth2_client TEST_DEPS = cowboy rabbitmq_web_dispatch rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client rabbitmq_web_mqtt emqtt rabbitmq_amqp_client PLT_APPS += rabbitmqctl