summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon MacMullen <simon@rabbitmq.com>2014-11-13 13:01:35 +0000
committerSimon MacMullen <simon@rabbitmq.com>2014-11-13 13:01:35 +0000
commit5c02c826bf447c07cd6ecb7bb1070b9c5ea59b7f (patch)
tree3ecce7dcc09e51f60e3b40b36e82e54403b527c0
parentbbb6650d6a529201424a10b2d7c5c002d632e374 (diff)
downloadrabbitmq-server-5c02c826bf447c07cd6ecb7bb1070b9c5ea59b7f.tar.gz
Import changes from https://github.com/gotthardp/rabbitmq-server/tree/multi_authorization
-rw-r--r--include/rabbit.hrl4
-rw-r--r--src/rabbit_access_control.erl95
-rw-r--r--src/rabbit_auth_backend.erl11
-rw-r--r--src/rabbit_auth_backend_dummy.erl14
-rw-r--r--src/rabbit_auth_backend_internal.erl13
-rw-r--r--src/rabbit_channel.erl2
-rw-r--r--src/rabbit_direct.erl2
-rw-r--r--src/rabbit_reader.erl2
-rw-r--r--src/rabbit_types.erl8
-rw-r--r--test/src/rabbit_tests.erl11
10 files changed, 96 insertions, 66 deletions
diff --git a/include/rabbit.hrl b/include/rabbit.hrl
index 74e165cd..627d0479 100644
--- a/include/rabbit.hrl
+++ b/include/rabbit.hrl
@@ -16,8 +16,8 @@
-record(user, {username,
tags,
- auth_backend, %% Module this user came from
- impl %% Scratch space for that module
+ authN_backend, %% Authentication module this user came from
+ authZ_backends %% List of authorization modules
}).
-record(internal_user, {username, password_hash, tags}).
diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl
index b0a9c0d8..dcec0ff5 100644
--- a/src/rabbit_access_control.erl
+++ b/src/rabbit_access_control.erl
@@ -19,7 +19,7 @@
-include("rabbit.hrl").
-export([check_user_pass_login/2, check_user_login/2, check_user_loopback/2,
- check_vhost_access/2, check_resource_access/3]).
+ check_vhost_access/3, check_resource_access/3]).
%%----------------------------------------------------------------------------
@@ -38,8 +38,8 @@
-spec(check_user_loopback/2 :: (rabbit_types:username(),
rabbit_net:socket() | inet:ip_address())
-> 'ok' | 'not_allowed').
--spec(check_vhost_access/2 ::
- (rabbit_types:user(), rabbit_types:vhost())
+-spec(check_vhost_access/3 ::
+ (rabbit_types:user(), rabbit_types:vhost(), rabbit_net:socket())
-> 'ok' | rabbit_types:channel_exit()).
-spec(check_resource_access/3 ::
(rabbit_types:user(), rabbit_types:r(atom()), permission_atom())
@@ -58,33 +58,52 @@ check_user_login(Username, AuthProps) ->
fun ({ModN, ModZ}, {refused, _, _}) ->
%% Different modules for authN vs authZ. So authenticate
%% with authN module, then if that succeeds do
- %% passwordless (i.e pre-authenticated) login with authZ
- %% module, and use the #user{} the latter gives us.
- case try_login(ModN, Username, AuthProps) of
- {ok, _} -> try_login(ModZ, Username, []);
+ %% passwordless (i.e pre-authenticated) login with authZ.
+ case try_authenticate(ModN, Username, AuthProps) of
+ {ok, User, _AuthZ} -> try_authorize(ModZ, User, []);
Else -> Else
end;
(Mod, {refused, _, _}) ->
%% Same module for authN and authZ. Just take the result
%% it gives us
- try_login(Mod, Username, AuthProps);
- (_, {ok, User}) ->
+ try_authenticate(Mod, Username, AuthProps);
+ (_, {ok, User, AuthZ}) ->
%% We've successfully authenticated. Skip to the end...
- {ok, User}
+ {ok, User, AuthZ}
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}]),
- R.
-try_login(Module, Username, AuthProps) ->
+ case R of
+ {ok, RUser, RAuthZ} ->
+ rabbit_event:notify(user_authentication_success, [{name, Username}]),
+ %% Store the list of authorization backends
+ {ok, RUser#user{authZ_backends=RAuthZ}};
+ _ ->
+ rabbit_event:notify(user_authentication_failure, [{name, Username}]),
+ R
+ end.
+
+try_authenticate(Module, Username, AuthProps) ->
case Module:check_user_login(Username, AuthProps) of
+ {ok, User, AuthZ} -> {ok, User, [{Module, AuthZ}]};
{error, E} -> {refused, "~s failed authenticating ~s: ~p~n",
[Module, Username, E]};
Else -> Else
end.
+try_authorize(Modules, User, AuthZList) when is_list(Modules) ->
+ lists:foldr(
+ fun (Module, {ok, _User, AuthZList2}) -> try_authorize(Module, User, AuthZList2);
+ (_, {refused, _, _} = Error) -> Error
+ end, {ok, User, AuthZList}, Modules);
+
+try_authorize(Module, User = #user{username = Username}, AuthZList) ->
+ case Module:check_user_login(Username, []) of
+ {ok, _User, AuthZ} -> {ok, User, [{Module, AuthZ}|AuthZList]};
+ {error, E} -> {refused, "~s failed authorizing ~s: ~p~n",
+ [Module, Username, E]};
+ Else -> Else
+ end.
+
check_user_loopback(Username, SockOrAddr) ->
{ok, Users} = application:get_env(rabbit, loopback_users),
case rabbit_net:is_loopback(SockOrAddr)
@@ -93,29 +112,39 @@ check_user_loopback(Username, SockOrAddr) ->
false -> not_allowed
end.
-check_vhost_access(User = #user{ username = Username,
- auth_backend = Module }, VHostPath) ->
- check_access(
- fun() ->
- %% TODO this could be an andalso shortcut under >R13A
- case rabbit_vhost:exists(VHostPath) of
- false -> false;
- true -> Module:check_vhost_access(User, VHostPath)
- end
- end,
- Module, "access to vhost '~s' refused for user '~s'",
- [VHostPath, Username]).
+check_vhost_access(User = #user{ username = Username,
+ authZ_backends = Modules }, VHostPath, Sock) ->
+ lists:foldl(
+ fun({Module, Impl}, ok) ->
+ check_access(
+ fun() ->
+ %% TODO this could be an andalso shortcut under >R13A
+ case rabbit_vhost:exists(VHostPath) of
+ false -> false;
+ true -> Module:check_vhost_access(User, Impl, VHostPath, Sock)
+ end
+ end,
+ Module, "access to vhost '~s' refused for user '~s'",
+ [VHostPath, Username]);
+
+ (_, Else) -> Else
+ end, ok, Modules).
check_resource_access(User, R = #resource{kind = exchange, name = <<"">>},
Permission) ->
check_resource_access(User, R#resource{name = <<"amq.default">>},
Permission);
-check_resource_access(User = #user{username = Username, auth_backend = Module},
+check_resource_access(User = #user{username = Username, authZ_backends = Modules},
Resource, Permission) ->
- check_access(
- fun() -> Module:check_resource_access(User, Resource, Permission) end,
- Module, "access to ~s refused for user '~s'",
- [rabbit_misc:rs(Resource), Username]).
+ lists:foldl(
+ fun({Module, Impl}, ok) ->
+ check_access(
+ fun() -> Module:check_resource_access(User, Impl, Resource, Permission) end,
+ Module, "access to ~s refused for user '~s'",
+ [rabbit_misc:rs(Resource), Username]);
+
+ (_, Else) -> Else
+ end, ok, Modules).
check_access(Fun, Module, ErrStr, ErrArgs) ->
Allow = case Fun() of
diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_auth_backend.erl
index a7dd6494..99f291f1 100644
--- a/src/rabbit_auth_backend.erl
+++ b/src/rabbit_auth_backend.erl
@@ -33,7 +33,7 @@
%% {refused, Msg, Args}
%% Client failed authentication. Log and die.
-callback check_user_login(rabbit_types:username(), [term()]) ->
- {'ok', rabbit_types:user()} |
+ {'ok', rabbit_types:user(), any()} |
{'refused', string(), [any()]} |
{'error', any()}.
@@ -43,7 +43,8 @@
%% false
%% {error, Error}
%% Something went wrong. Log and die.
--callback check_vhost_access(rabbit_types:user(), rabbit_types:vhost()) ->
+-callback check_vhost_access(rabbit_types:user(), any(),
+ rabbit_types:vhost(), rabbit_net:socket()) ->
boolean() | {'error', any()}.
@@ -54,7 +55,7 @@
%% false
%% {error, Error}
%% Something went wrong. Log and die.
--callback check_resource_access(rabbit_types:user(),
+-callback check_resource_access(rabbit_types:user(), any(),
rabbit_types:r(atom()),
rabbit_access_control:permission_atom()) ->
boolean() | {'error', any()}.
@@ -64,8 +65,8 @@
-export([behaviour_info/1]).
behaviour_info(callbacks) ->
- [{description, 0}, {check_user_login, 2}, {check_vhost_access, 2},
- {check_resource_access, 3}];
+ [{description, 0}, {check_user_login, 2}, {check_vhost_access, 4},
+ {check_resource_access, 4}];
behaviour_info(_Other) ->
undefined.
diff --git a/src/rabbit_auth_backend_dummy.erl b/src/rabbit_auth_backend_dummy.erl
index 5daca368..8e2d8269 100644
--- a/src/rabbit_auth_backend_dummy.erl
+++ b/src/rabbit_auth_backend_dummy.erl
@@ -21,7 +21,7 @@
-export([description/0]).
-export([user/0]).
--export([check_user_login/2, check_vhost_access/2, check_resource_access/3]).
+-export([check_user_login/2, check_vhost_access/4, check_resource_access/4]).
-ifdef(use_specs).
@@ -31,10 +31,10 @@
%% A user to be used by the direct client when permission checks are
%% not needed. This user can do anything AMQPish.
-user() -> #user{username = <<"none">>,
- tags = [],
- auth_backend = ?MODULE,
- impl = none}.
+user() -> #user{username = <<"none">>,
+ tags = [],
+ authN_backend = ?MODULE,
+ authZ_backends = []}.
%% Implementation of rabbit_auth_backend
@@ -45,5 +45,5 @@ description() ->
check_user_login(_, _) ->
{refused, "cannot log in conventionally as dummy user", []}.
-check_vhost_access(#user{}, _VHostPath) -> true.
-check_resource_access(#user{}, #resource{}, _Permission) -> true.
+check_vhost_access(#user{}, _Impl, _VHostPath, _Sock) -> true.
+check_resource_access(#user{}, _Impl, #resource{}, _Permission) -> true.
diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl
index fd1c4e8e..5cdff985 100644
--- a/src/rabbit_auth_backend_internal.erl
+++ b/src/rabbit_auth_backend_internal.erl
@@ -20,7 +20,7 @@
-behaviour(rabbit_auth_backend).
-export([description/0]).
--export([check_user_login/2, check_vhost_access/2, check_resource_access/3]).
+-export([check_user_login/2, check_vhost_access/4, check_resource_access/4]).
-export([add_user/2, delete_user/1, lookup_user/1,
change_password/2, clear_password/1,
@@ -98,17 +98,16 @@ internal_check_user_login(Username, Fun) ->
case lookup_user(Username) of
{ok, User = #internal_user{tags = Tags}} ->
case Fun(User) of
- true -> {ok, #user{username = Username,
- tags = Tags,
- auth_backend = ?MODULE,
- impl = User}};
+ true -> {ok, #user{username = Username,
+ tags = Tags,
+ authN_backend = ?MODULE}, User};
_ -> Refused
end;
{error, not_found} ->
Refused
end.
-check_vhost_access(#user{username = Username}, VHostPath) ->
+check_vhost_access(#user{username = Username}, _Impl, VHostPath, _Sock) ->
case mnesia:dirty_read({rabbit_user_permission,
#user_vhost{username = Username,
virtual_host = VHostPath}}) of
@@ -116,7 +115,7 @@ check_vhost_access(#user{username = Username}, VHostPath) ->
[_R] -> true
end.
-check_resource_access(#user{username = Username},
+check_resource_access(#user{username = Username}, _Impl,
#resource{virtual_host = VHostPath, name = Name},
Permission) ->
case mnesia:dirty_read({rabbit_user_permission,
diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl
index 8632e1b3..2f7e234d 100644
--- a/src/rabbit_channel.erl
+++ b/src/rabbit_channel.erl
@@ -581,7 +581,7 @@ check_user_id_header(#'P_basic'{user_id = Username},
#ch{user = #user{username = Username}}) ->
ok;
check_user_id_header(
- #'P_basic'{}, #ch{user = #user{auth_backend = rabbit_auth_backend_dummy}}) ->
+ #'P_basic'{}, #ch{user = #user{authN_backend = rabbit_auth_backend_dummy}}) ->
ok;
check_user_id_header(#'P_basic'{user_id = Claimed},
#ch{user = #user{username = Actual,
diff --git a/src/rabbit_direct.erl b/src/rabbit_direct.erl
index 749a67b1..f6140f09 100644
--- a/src/rabbit_direct.erl
+++ b/src/rabbit_direct.erl
@@ -92,7 +92,7 @@ connect0(AuthFun, VHost, Protocol, Pid, Infos) ->
end.
connect1(User, VHost, Protocol, Pid, Infos) ->
- try rabbit_access_control:check_vhost_access(User, VHost) of
+ try rabbit_access_control:check_vhost_access(User, VHost, undefined) of
ok -> ok = pg_local:join(rabbit_direct, Pid),
rabbit_event:notify(connection_created, Infos),
{ok, {User, rabbit_reader:server_properties(Protocol)}}
diff --git a/src/rabbit_reader.erl b/src/rabbit_reader.erl
index ca73006a..2033dd14 100644
--- a/src/rabbit_reader.erl
+++ b/src/rabbit_reader.erl
@@ -944,7 +944,7 @@ handle_method0(#'connection.open'{virtual_host = VHostPath},
helper_sup = SupPid,
sock = Sock,
throttle = Throttle}) ->
- ok = rabbit_access_control:check_vhost_access(User, VHostPath),
+ ok = rabbit_access_control:check_vhost_access(User, VHostPath, Sock),
NewConnection = Connection#connection{vhost = VHostPath},
ok = send_on_channel0(Sock, #'connection.open_ok'{}, Protocol),
Conserve = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}),
diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl
index ba48867a..b3158cc8 100644
--- a/src/rabbit_types.erl
+++ b/src/rabbit_types.erl
@@ -132,10 +132,10 @@
-type(protocol() :: rabbit_framing:protocol()).
-type(user() ::
- #user{username :: username(),
- tags :: [atom()],
- auth_backend :: atom(),
- impl :: any()}).
+ #user{username :: username(),
+ tags :: [atom()],
+ authN_backend :: atom(),
+ authZ_backends :: [{atom(), any()}]}).
-type(internal_user() ::
#internal_user{username :: username(),
diff --git a/test/src/rabbit_tests.erl b/test/src/rabbit_tests.erl
index ef6b756b..a227f3d2 100644
--- a/test/src/rabbit_tests.erl
+++ b/test/src/rabbit_tests.erl
@@ -1292,11 +1292,12 @@ test_spawn_remote() ->
end.
user(Username) ->
- #user{username = Username,
- tags = [administrator],
- auth_backend = rabbit_auth_backend_internal,
- impl = #internal_user{username = Username,
- tags = [administrator]}}.
+ #user{username = Username,
+ tags = [administrator],
+ authN_backend = rabbit_auth_backend_internal,
+ authZ_backends = [{rabbit_auth_backend_internal,
+ #internal_user{username = Username,
+ tags = [administrator]}}]}.
test_confirms() ->
{_Writer, Ch} = test_spawn(),