summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Sebastien Pedron <jean-sebastien@rabbitmq.com>2014-11-25 19:21:27 +0100
committerJean-Sebastien Pedron <jean-sebastien@rabbitmq.com>2014-11-25 19:21:27 +0100
commit6373af3300b850c1d4c74ca8e5f8453c3da3eb18 (patch)
treefc9cecaf2cf62954138d680135b8de707a33f915
parentbc979010866b4f1cde80ea391f6bd98d38383fbc (diff)
downloadrabbitmq-server-6373af3300b850c1d4c74ca8e5f8453c3da3eb18.tar.gz
Add more properties to the user_authentication_* notifications
Until now, the only property was {name, Username}. The added properties are: o {connection_type, network | direct} o {error, Message} (only if the authentication failed) For network connections, the following informations are added as returned by rabbit_reader:infos/2: o auth_mechanism o host o name (the property is renamed to connection_name to avoid conflict with the username) o peer_cert_issuer o peer_cert_subject o peer_cert_validity o peer_host o peer_port o protocol o ssl o ssl_cipher o ssl_protocol o vhost The notification is sent by rabbit_reader:notify_auth_result/5 and rabbit_direct:notify_auth_result/4, not by rabbit_access_control:check_user_login/2 anymore. This fixes a bug where a "user_authentication_success" event would be sent by rabbit_access_control:check_user_login/2, even if rabbit_reader:auth_phase/2 rejects the user later because the connection isn't on the loopback interface.
-rw-r--r--src/rabbit_access_control.erl31
-rw-r--r--src/rabbit_direct.erl20
-rw-r--r--src/rabbit_reader.erl55
3 files changed, 83 insertions, 23 deletions
diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl
index d1577432..41c54b07 100644
--- a/src/rabbit_access_control.erl
+++ b/src/rabbit_access_control.erl
@@ -31,10 +31,12 @@
-spec(check_user_pass_login/2 ::
(rabbit_types:username(), rabbit_types:password())
- -> {'ok', rabbit_types:user()} | {'refused', string(), [any()]}).
+ -> {'ok', rabbit_types:user()} |
+ {'refused', rabbit_types:username(), string(), [any()]}).
-spec(check_user_login/2 ::
(rabbit_types:username(), [{atom(), any()}])
- -> {'ok', rabbit_types:user()} | {'refused', string(), [any()]}).
+ -> {'ok', rabbit_types:user()} |
+ {'refused', rabbit_types:username(), string(), [any()]}).
-spec(check_user_loopback/2 :: (rabbit_types:username(),
rabbit_net:socket() | inet:ip_address())
-> 'ok' | 'not_allowed').
@@ -55,7 +57,7 @@ check_user_pass_login(Username, Password) ->
check_user_login(Username, AuthProps) ->
{ok, Modules} = application:get_env(rabbit, auth_backends),
R = lists:foldl(
- fun ({ModN, ModZs0}, {refused, _, _}) ->
+ fun ({ModN, ModZs0}, {refused, _, _, _}) ->
ModZs = case ModZs0 of
A when is_atom(A) -> [A];
L when is_list(L) -> L
@@ -69,7 +71,7 @@ check_user_login(Username, AuthProps) ->
Else ->
Else
end;
- (Mod, {refused, _, _}) ->
+ (Mod, {refused, _, _, _}) ->
%% Same module for authN and authZ. Just take the result
%% it gives us
case try_authenticate(Mod, Username, AuthProps) of
@@ -81,19 +83,17 @@ check_user_login(Username, AuthProps) ->
(_, {ok, User}) ->
%% We've successfully authenticated. Skip to the end...
{ok, User}
- end, {refused, "No modules checked '~s'", [Username]}, Modules),
- rabbit_event:notify(case R of
- {ok, _User} -> user_authentication_success;
- _ -> user_authentication_failure
- end, [{name, Username}]),
+ end,
+ {refused, Username, "No modules checked '~s'", [Username]}, Modules),
R.
try_authenticate(Module, Username, AuthProps) ->
case Module:user_login_authentication(Username, AuthProps) of
{ok, AuthUser} -> {ok, AuthUser};
- {error, E} -> {refused, "~s failed authenticating ~s: ~p~n",
+ {error, E} -> {refused, Username,
+ "~s failed authenticating ~s: ~p~n",
[Module, Username, E]};
- {refused, F, A} -> {refused, F, A}
+ {refused, F, A} -> {refused, Username, F, A}
end.
try_authorize(Modules, Username) ->
@@ -101,12 +101,13 @@ try_authorize(Modules, Username) ->
fun (Module, {ok, ModsImpls}) ->
case Module:user_login_authorization(Username) of
{ok, Impl} -> {ok, [{Module, Impl} | ModsImpls]};
- {error, E} -> {refused, "~s failed authorizing ~s: ~p~n",
+ {error, E} -> {refused, Username,
+ "~s failed authorizing ~s: ~p~n",
[Module, Username, E]};
- {refused, F, A} -> {refused, F, A}
+ {refused, F, A} -> {refused, Username, F, A}
end;
- (_, {refused, _, _} = Error) ->
- Error
+ (_, {refused, F, A}) ->
+ {refused, Username, F, A}
end, {ok, []}, Modules).
user(#auth_user{username = Username, tags = Tags}, {ok, ModZImpls}) ->
diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl
index f6140f09..9756dd49 100644
--- a/src/rabbit_direct.erl
+++ b/src/rabbit_direct.erl
@@ -83,14 +83,30 @@ connect({Username, Password}, VHost, Protocol, Pid, Infos) ->
connect0(AuthFun, VHost, Protocol, Pid, Infos) ->
case rabbit:is_running() of
true -> case AuthFun() of
- {ok, User} ->
+ {ok, User = #user{username = Username}} ->
+ notify_auth_result(Username,
+ user_authentication_success, "", []),
connect1(User, VHost, Protocol, Pid, Infos);
- {refused, _M, _A} ->
+ {refused, Username, Msg, Args} ->
+ notify_auth_result(Username,
+ user_authentication_failure, Msg, Args),
{error, {auth_failure, "Refused"}}
end;
false -> {error, broker_not_found_on_node}
end.
+notify_auth_result(Username, AuthResult, Msg, Args) ->
+ EventProps0 = [{connection_type, direct}],
+ EventProps1 = case Username of
+ none -> [{name, ''} | EventProps0];
+ _ -> [{name, Username} | EventProps0]
+ end,
+ EventProps = case Msg of
+ "" -> EventProps1;
+ _ -> [{error, lists:flatten(io_lib:format(Msg, Args))} | EventProps1]
+ end,
+ rabbit_event:notify(AuthResult, EventProps).
+
connect1(User, VHost, Protocol, Pid, Infos) ->
try rabbit_access_control:check_vhost_access(User, VHost, undefined) of
ok -> ok = pg_local:join(rabbit_direct, Pid),
diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl
index 2033dd14..a18d75d7 100644
--- a/src/rabbit_reader.erl
+++ b/src/rabbit_reader.erl
@@ -1046,9 +1046,15 @@ auth_phase(Response,
auth_state = AuthState},
sock = Sock}) ->
case AuthMechanism:handle_response(Response, AuthState) of
+ {refused, Username, Msg, Args} ->
+ auth_fail(Username, Msg, Args, Name, State);
{refused, Msg, Args} ->
- auth_fail(Msg, Args, Name, State);
+ %% Older auth mechanisms didn't return the username, even if
+ %% they reach a stage where they know it.
+ auth_fail(none, Msg, Args, Name, State);
{protocol_error, Msg, Args} ->
+ notify_auth_result(none, user_authentication_failure,
+ Msg, Args, State),
rabbit_misc:protocol_error(syntax_error, Msg, Args);
{challenge, Challenge, AuthState1} ->
Secure = #'connection.secure'{challenge = Challenge},
@@ -1057,9 +1063,12 @@ auth_phase(Response,
auth_state = AuthState1}};
{ok, User = #user{username = Username}} ->
case rabbit_access_control:check_user_loopback(Username, Sock) of
- ok -> ok;
- not_allowed -> auth_fail("user '~s' can only connect via "
- "localhost", [Username], Name, State)
+ ok ->
+ notify_auth_result(Username, user_authentication_success,
+ "", [], State);
+ not_allowed ->
+ auth_fail(Username, "user '~s' can only connect via "
+ "localhost", [Username], Name, State)
end,
Tune = #'connection.tune'{frame_max = get_env(frame_max),
channel_max = get_env(channel_max),
@@ -1071,11 +1080,14 @@ auth_phase(Response,
end.
-ifdef(use_specs).
--spec(auth_fail/4 :: (string(), [any()], binary(), #v1{}) -> no_return()).
+-spec(auth_fail/5 ::
+ (rabbit_types:username() | none, string(), [any()], binary(), #v1{}) ->
+ no_return()).
-endif.
-auth_fail(Msg, Args, AuthName,
+auth_fail(Username, Msg, Args, AuthName,
State = #v1{connection = #connection{protocol = Protocol,
capabilities = Capabilities}}) ->
+ notify_auth_result(Username, user_authentication_failure, Msg, Args, State),
AmqpError = rabbit_misc:amqp_error(
access_refused, "~s login refused: ~s",
[AuthName, io_lib:format(Msg, Args)], none),
@@ -1094,6 +1106,37 @@ auth_fail(Msg, Args, AuthName,
end,
rabbit_misc:protocol_error(AmqpError).
+notify_auth_result(Username, AuthResult, Msg, Args, State) ->
+ EventProps0 = [{connection_type, network}],
+ EventProps1 = lists:foldl(
+ fun
+ (name, Acc) -> [{connection_name, i(name, State)} | Acc];
+ (Item, Acc) -> [{Item, i(Item, State)} | Acc]
+ end, EventProps0, [
+ peer_cert_validity,
+ peer_cert_subject,
+ peer_cert_issuer,
+ ssl_cipher,
+ ssl_protocol,
+ ssl,
+ auth_mechanism,
+ protocol,
+ peer_port,
+ peer_host,
+ name,
+ vhost,
+ host
+ ]),
+ EventProps2 = case Username of
+ none -> [{name, ''} | EventProps1];
+ _ -> [{name, Username} | EventProps1]
+ end,
+ EventProps = case Msg of
+ "" -> EventProps2;
+ _ -> [{error, lists:flatten(io_lib:format(Msg, Args))} | EventProps2]
+ end,
+ rabbit_event:notify(AuthResult, EventProps).
+
%%--------------------------------------------------------------------------
infos(Items, State) -> [{Item, i(Item, State)} || Item <- Items].