Minor refactor

Improve logging
Fix an issue running selenium tests locally
WIP modify schema to configure queryParameters for
oauth2 endpoints
This commit is contained in:
Marcial Rosales 2024-09-09 19:42:40 +01:00 committed by Marcial Rosales
parent 7aca1605eb
commit 4da45996ca
14 changed files with 79 additions and 47 deletions

View File

@ -254,7 +254,7 @@ get_oauth_provider(ListOfRequiredAttributes) ->
get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) ->
OAuthProvider = lookup_oauth_provider_from_keyconfig(), 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 case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of
[] -> [] ->
{ok, OAuthProvider}; {ok, OAuthProvider};
@ -557,27 +557,27 @@ format_ssl_options(TlsOptions) ->
[] -> 0; [] -> 0;
Certs -> length(Certs) Certs -> length(Certs)
end, 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 }", [ "depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [
proplists:get_value(verify, TlsOptions), proplists:get_value(verify, TlsOptions),
proplists:get_value(fail_if_no_peer_cert, TlsOptions), proplists:get_value(fail_if_no_peer_cert, TlsOptions),
proplists:get_value(crl_check, TlsOptions), proplists:get_value(crl_check, TlsOptions),
proplists:get_value(depth, TlsOptions), proplists:get_value(depth, TlsOptions),
proplists:get_value(cacertfile, TlsOptions), proplists:get_value(cacertfile, TlsOptions),
CaCertsCount]). CaCertsCount])).
format_oauth_provider_id(root) -> "<from keyconfig>"; format_oauth_provider_id(root) -> "<from keyconfig>";
format_oauth_provider_id(Id) -> binary_to_list(Id). format_oauth_provider_id(Id) -> binary_to_list(Id).
-spec format_oauth_provider(oauth_provider()) -> string(). -spec format_oauth_provider(oauth_provider()) -> string().
format_oauth_provider(OAuthProvider) -> 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, " ++ "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), format_oauth_provider_id(OAuthProvider#oauth_provider.id),
OAuthProvider#oauth_provider.issuer, OAuthProvider#oauth_provider.issuer,
OAuthProvider#oauth_provider.token_endpoint, OAuthProvider#oauth_provider.token_endpoint,
OAuthProvider#oauth_provider.authorization_endpoint, OAuthProvider#oauth_provider.authorization_endpoint,
OAuthProvider#oauth_provider.end_session_endpoint, OAuthProvider#oauth_provider.end_session_endpoint,
OAuthProvider#oauth_provider.jwks_uri, OAuthProvider#oauth_provider.jwks_uri,
format_ssl_options(OAuthProvider#oauth_provider.ssl_options)]). format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])).

View File

@ -158,6 +158,31 @@
"rabbitmq_auth_backend_oauth2.authorization_endpoint", "rabbitmq_auth_backend_oauth2.authorization_endpoint",
[{datatype, string}, {validators, ["uri", "https_uri"]}]}. [{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, {mapping,
"auth_oauth2.https.peer_verification", "auth_oauth2.https.peer_verification",
"rabbitmq_auth_backend_oauth2.key_config.peer_verification", "rabbitmq_auth_backend_oauth2.key_config.peer_verification",
@ -333,5 +358,5 @@
{translation, "rabbitmq_auth_backend_oauth2.resource_servers", {translation, "rabbitmq_auth_backend_oauth2.resource_servers",
fun(Conf) -> fun(Conf) ->
rabbit_oauth2_schema:translate_resource_servers(Conf) rabbit_oauth2_schema:translate_resource_servers(Conf)
end}. end}.

View File

@ -11,7 +11,8 @@
-export([ -export([
translate_oauth_providers/1, translate_oauth_providers/1,
translate_resource_servers/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). extract_key_as_binary({Name,_}) -> list_to_binary(Name).
@ -63,6 +64,10 @@ translate_list_of_signing_keys(ListOfKidPath) ->
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_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) -> validator_file_exists(Attr, Filename) ->
case file:read_file(Filename) of case file:read_file(Filename) of
{ok, _} -> {ok, _} ->

View File

@ -32,7 +32,7 @@ add_signing_key(KeyId, Type, Value) ->
-spec update_jwks_signing_keys(oauth_provider()) -> ok | {error, term()}. -spec update_jwks_signing_keys(oauth_provider()) -> ok | {error, term()}.
update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl,
ssl_options = SslOptions}) -> 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]), [JwksUrl, SslOptions]),
case uaa_jwks:get(JwksUrl, SslOptions) of case uaa_jwks:get(JwksUrl, SslOptions) of
{ok, {_, _, JwksBody}} -> {ok, {_, _, JwksBody}} ->
@ -40,13 +40,13 @@ update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl,
jose:decode(erlang:iolist_to_binary(JwksBody)), []), jose:decode(erlang:iolist_to_binary(JwksBody)), []),
Keys = maps:from_list(lists:map(fun(Key) -> Keys = maps:from_list(lists:map(fun(Key) ->
{maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), {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 case rabbit_oauth2_config:replace_signing_keys(Keys, Id) of
{error, _} = Err -> Err; {error, _} = Err -> Err;
_ -> ok _ -> ok
end; end;
{error, _} = Err -> {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 Err
end. end.
@ -56,29 +56,31 @@ decode_and_verify(Token) ->
{error, _} = Err -> {error, _} = Err ->
Err; Err;
ResourceServerId -> ResourceServerId ->
OAuthProviderId = decode_and_verify(Token, ResourceServerId,
rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(ResourceServerId), rabbit_oauth2_config:get_oauth_provider_id_for_resource_server_id(
rabbit_log:debug("OAuth 2 JWT: resolved resource_server_id: ~p oauth_provider_id: ~p", ResourceServerId))
[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
end. 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) -> resolve_resource_server_id(Token) ->
case uaa_jwt_jwt:get_aud(Token) of case uaa_jwt_jwt:get_aud(Token) of
{error, _} = Error -> {error, _} = Error ->

View File

@ -12,18 +12,17 @@
-include_lib("jose/include/jose_jws.hrl"). -include_lib("jose/include/jose_jws.hrl").
decode_and_verify(OauthProviderId, Jwk, Token) -> -spec decode_and_verify(list() | undefined, map(), binary()) -> {boolean(), map()}.
Verify = decode_and_verify(Algs, Jwk, Token) ->
case rabbit_oauth2_config:get_algorithms(OauthProviderId) of Verify = case Algs of
undefined -> jose_jwt:verify(Jwk, Token); undefined -> jose_jwt:verify(Jwk, Token);
Algs -> jose_jwt:verify_strict(Jwk, Algs, Token) _ -> jose_jwt:verify_strict(Jwk, Algs, Token)
end, end,
case Verify of case Verify of
{true, #jose_jwt{fields = Fields}, _} -> {true, Fields}; {true, #jose_jwt{fields = Fields}, _} -> {true, Fields};
{false, #jose_jwt{fields = Fields}, _} -> {false, Fields} {false, #jose_jwt{fields = Fields}, _} -> {false, Fields}
end. end.
get_key_id(DefaultKey, Token) -> get_key_id(DefaultKey, Token) ->
try try
case jose_jwt:peek_protected(Token) of case jose_jwt:peek_protected(Token) of

View File

@ -52,6 +52,7 @@ start_local_rabbitmq() {
init_rabbitmq init_rabbitmq
RABBITMQ_SERVER_ROOT=$(realpath ../) RABBITMQ_SERVER_ROOT=$(realpath ../)
MOUNT_RABBITMQ_CONF="/etc/rabbitmq/rabbitmq.conf" MOUNT_RABBITMQ_CONF="/etc/rabbitmq/rabbitmq.conf"
MOUNT_ADVANCED_CONFIG="/etc/rabbitmq/advanced.config" MOUNT_ADVANCED_CONFIG="/etc/rabbitmq/advanced.config"

View File

@ -1 +1 @@
export IMPORT_DIR=test/authnz-msg-protocols/imports export IMPORT_DIR=selenium/test/authnz-msg-protocols/imports

View File

@ -1 +1 @@
export IMPORT_DIR=deps/rabbitmq_management/selenium/test/basic-auth/imports export IMPORT_DIR=selenium/test/basic-auth/imports

View File

@ -1,6 +1,6 @@
auth_backends.1 = rabbit_auth_backend_internal auth_backends.1 = rabbit_auth_backend_internal
management.login_session_timeout = 1 management.login_session_timeout = 1
load_definitions = ${IMPORT_DIR}/users.json load_definitions = ${RABBITMQ_TEST_DIR}/imports/users.json
loopback_users = none loopback_users = none

View File

@ -1 +1 @@
export OAUTH_SERVER_CONFIG_BASEDIR=deps/rabbitmq_management/selenium/test export OAUTH_SERVER_CONFIG_BASEDIR=test

View File

@ -1,2 +1,2 @@
export DEVKEYCLOAK_URL=https://localhost:8442/realms/dev 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

View File

@ -1,2 +1,2 @@
export PRODKEYCLOAK_URL=https://localhost:8443/realms/prod 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

View File

@ -1 +1 @@
export OAUTH_SERVER_CONFIG_BASEDIR=selenium/test export OAUTH_SERVER_CONFIG_BASEDIR=test

View File

@ -1,3 +1,3 @@
export KEYCLOAK_URL=https://localhost:8443/realms/test export KEYCLOAK_URL=https://localhost:8443/realms/test
export OAUTH_PROVIDER_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