From 3a210d7f81bc2b5f7c0559ac64efa5c7b48d57cf Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 20 Sep 2024 12:10:26 +0200 Subject: [PATCH] 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).