diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2014-11-13 13:01:35 +0000 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2014-11-13 13:01:35 +0000 |
commit | 5c02c826bf447c07cd6ecb7bb1070b9c5ea59b7f (patch) | |
tree | 3ecce7dcc09e51f60e3b40b36e82e54403b527c0 | |
parent | bbb6650d6a529201424a10b2d7c5c002d632e374 (diff) | |
download | rabbitmq-server-5c02c826bf447c07cd6ecb7bb1070b9c5ea59b7f.tar.gz |
Import changes from https://github.com/gotthardp/rabbitmq-server/tree/multi_authorization
-rw-r--r-- | include/rabbit.hrl | 4 | ||||
-rw-r--r-- | src/rabbit_access_control.erl | 95 | ||||
-rw-r--r-- | src/rabbit_auth_backend.erl | 11 | ||||
-rw-r--r-- | src/rabbit_auth_backend_dummy.erl | 14 | ||||
-rw-r--r-- | src/rabbit_auth_backend_internal.erl | 13 | ||||
-rw-r--r-- | src/rabbit_channel.erl | 2 | ||||
-rw-r--r-- | src/rabbit_direct.erl | 2 | ||||
-rw-r--r-- | src/rabbit_reader.erl | 2 | ||||
-rw-r--r-- | src/rabbit_types.erl | 8 | ||||
-rw-r--r-- | test/src/rabbit_tests.erl | 11 |
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(), |