diff options
author | Marcial Rosales <mrosales@pivotal.io> | 2023-01-30 14:14:58 +0100 |
---|---|---|
committer | Marcial Rosales <mrosales@pivotal.io> | 2023-01-31 11:45:59 +0100 |
commit | 51e27f8a3fd3a7a14b884fb5e4eee36053aa9972 (patch) | |
tree | cb7e8219ab4ae1b47a33b5a51a9f2ce3e9617a67 | |
parent | b02c268632e7729f7ce809adb264e05a8d041084 (diff) | |
download | rabbitmq-server-git-51e27f8a3fd3a7a14b884fb5e4eee36053aa9972.tar.gz |
Fix issue #6909
Use the outcome from first authentication
stored in the #user.authz_backends to authenticate
subsequent attempts which occur when a session is
opened.
In particular, during the first authentication attempt
which occurs during the sasl handshake, the amqp 1.0
plugins reads and validates JWT token present in the
password field.
When a new AMQP 1.0 session is opened, the plugin creates
an internal AMQP connection which triggers a second/nth
authentication. For this second/nth authentication, the
plugin propagates as Authentication Credentials the outcome
from the first authentication which is stored in the
`#user.authz_backends`.
The Oauth2 backend first attempts to authenticate using
the password credentials else it uses the credential with the
key `rabbit_auth_backend_oauth2` which has a function which
returns the decoded token
6 files changed, 53 insertions, 11 deletions
diff --git a/deps/rabbit/src/rabbit_auth_backend_internal.erl b/deps/rabbit/src/rabbit_auth_backend_internal.erl index 93c1b9e26a..25c5852db0 100644 --- a/deps/rabbit/src/rabbit_auth_backend_internal.erl +++ b/deps/rabbit/src/rabbit_auth_backend_internal.erl @@ -92,7 +92,11 @@ user_login_authentication(Username, AuthProps) -> false end end); - false -> exit({unknown_auth_props, Username, AuthProps}) + false -> + case proplists:get_value(rabbit_auth_backend_internal, AuthProps, undefined) of + undefined -> exit({unknown_auth_props, Username, AuthProps}); + _ -> internal_check_user_login(Username, fun(_) -> true end) + end end. state_can_expire() -> false. diff --git a/deps/rabbit/src/rabbit_direct.erl b/deps/rabbit/src/rabbit_direct.erl index d8d36c5b27..b187d60f89 100644 --- a/deps/rabbit/src/rabbit_direct.erl +++ b/deps/rabbit/src/rabbit_direct.erl @@ -52,8 +52,8 @@ list() -> auth_fun({none, _}, _VHost, _ExtraAuthProps) -> fun () -> {ok, rabbit_auth_backend_dummy:user()} end; -auth_fun({Username, none}, _VHost, _ExtraAuthProps) -> - fun () -> rabbit_access_control:check_user_login(Username, []) end; +auth_fun({Username, none}, _VHost, ExtraAuthProps) -> + fun () -> rabbit_access_control:check_user_login(Username, [] ++ ExtraAuthProps) end; auth_fun({Username, Password}, VHost, ExtraAuthProps) -> fun () -> @@ -73,7 +73,8 @@ auth_fun({Username, Password}, VHost, ExtraAuthProps) -> {'auth_failure', string()} | 'access_refused'). connect(Creds, VHost, Protocol, Pid, Infos) -> - ExtraAuthProps = extract_extra_auth_props(Creds, VHost, Pid, Infos), + ExtraAuthProps = append_authz_backends(extract_extra_auth_props(Creds, VHost, Pid, Infos), Infos), + AuthFun = auth_fun(Creds, VHost, ExtraAuthProps), case rabbit_boot_state:has_reached_and_is_active(core_started) of true -> @@ -114,6 +115,12 @@ extract_extra_auth_props(Creds, VHost, Pid, Infos) -> maybe_call_connection_info_module(Protocol, Creds, VHost, Pid, Infos) end. +append_authz_backends(AuthProps, Infos) -> + case proplists:get_value(authz_backends, Infos, undefined) of + undefined -> AuthProps; + Authz_backends -> AuthProps ++ Authz_backends + end. + extract_protocol(Infos) -> case proplists:get_value(protocol, Infos, undefined) of {Protocol, _Version} -> diff --git a/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_reader.erl b/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_reader.erl index 48b9770139..674ac8252f 100644 --- a/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_reader.erl +++ b/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_reader.erl @@ -710,6 +710,7 @@ send_to_new_1_0_session(Channel, Frame, State) -> user = User}, proxy_socket = ProxySocket} = State, %% Note: the equivalent, start_channel is in channel_sup_sup + case rabbit_amqp1_0_session_sup_sup:start_session( %% NB subtract fixed frame header size ChanSupSup, {amqp10_framing, Sock, Channel, diff --git a/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_session_sup.erl b/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_session_sup.erl index dcbca00674..a530213d50 100644 --- a/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_session_sup.erl +++ b/deps/rabbitmq_amqp1_0/src/rabbit_amqp1_0_session_sup.erl @@ -29,7 +29,7 @@ %%---------------------------------------------------------------------------- start_link({amqp10_framing, Sock, Channel, FrameMax, ReaderPid, - Username, VHost, Collector, ProxySocket}) -> + User, VHost, Collector, ProxySocket}) -> {ok, SupPid} = supervisor:start_link(?MODULE, []), {ok, WriterPid} = supervisor:start_child( @@ -61,8 +61,8 @@ start_link({amqp10_framing, Sock, Channel, FrameMax, ReaderPid, id => channel, start => {rabbit_amqp1_0_session_process, start_link, [ - {Channel, ReaderPid, WriterPid, Username, VHost, FrameMax, - adapter_info(SocketForAdapterInfo), Collector} + {Channel, ReaderPid, WriterPid, User, VHost, FrameMax, + adapter_info(User, SocketForAdapterInfo), Collector} ]}, restart => transient, significant => true, @@ -86,5 +86,7 @@ init([]) -> auto_shutdown => any_significant}, {ok, {SupFlags, []}}. -adapter_info(Sock) -> - amqp_connection:socket_adapter_info(Sock, {'AMQP', "1.0"}). +adapter_info(User, Sock) -> + AdapterInfo = amqp_connection:socket_adapter_info(Sock, {'AMQP', "1.0"}), + AdapterInfo#amqp_adapter_info{additional_info = + AdapterInfo#amqp_adapter_info.additional_info ++ [{authz_backends, User#user.authz_backends}]}. 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 345b239df3..7f33a61207 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 @@ -167,7 +167,7 @@ validate_token_expiry(#{<<"exp">> := Exp}) when is_integer(Exp) -> end; validate_token_expiry(#{}) -> ok. --spec check_token(binary()) -> +-spec check_token(binary() | map()) -> {'ok', map()} | {'error', term() }| {'refused', @@ -175,6 +175,9 @@ validate_token_expiry(#{}) -> ok. {'error', term()} | {'invalid_aud', term()}}. +check_token(DecodedToken) when is_map(DecodedToken) -> + {ok, DecodedToken}; + check_token(Token) -> Settings = application:get_all_env(?APP), case uaa_jwt:decode_and_verify(Token) of @@ -535,7 +538,14 @@ get_scopes(#{?SCOPE_JWT_FIELD := Scope}) -> Scope. -spec token_from_context(map()) -> binary() | undefined. token_from_context(AuthProps) -> - maps:get(password, AuthProps, undefined). + case maps:get(password, AuthProps, undefined) of + undefined -> + case maps:get(rabbit_auth_backend_oauth2, AuthProps, undefined) of + undefined -> undefined; + Impl -> Impl() + end; + Token -> Token + end. %% Decoded tokens look like this: %% diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index f12b272b77..aa76cd3e00 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -12,6 +12,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). + all() -> [ test_own_scope, @@ -19,6 +20,7 @@ all() -> test_validate_payload, test_validate_payload_when_verify_aud_false, test_successful_access_with_a_token, + test_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, @@ -629,6 +631,22 @@ test_successful_access_with_a_token(_) -> assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => <<"#/foo">>}). +test_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), + application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), + + VHost = <<"vhost">>, + 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}]), + + {ok, _ } = + rabbit_auth_backend_oauth2:user_login_authentication(Username, [{rabbit_auth_backend_oauth2, Impl}]). + + test_successful_access_with_a_token_that_has_tag_scopes(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], |