diff options
author | Simon MacMullen <simon@rabbitmq.com> | 2014-11-14 14:16:39 +0000 |
---|---|---|
committer | Simon MacMullen <simon@rabbitmq.com> | 2014-11-14 14:16:39 +0000 |
commit | 8a6ad3517e031b8b7b85c63ab24d062d1d647b5b (patch) | |
tree | adfd795b14087b1dc2ee7ecb1e42b73f40400f94 | |
parent | 627f4c0e15db86bdff439ccc01bcc81bb087a79f (diff) | |
download | rabbitmq-server-bug26469.tar.gz |
Tweak the APIs again, so that authz plugins aren't expected to create a #auth_user record in the first place, just whatever impl they want. Which necessitates seperate login functions for authz and authn, and if we're going to do that we might as well split the behaviours so that we have the possibility of making an authz-only plugin.bug26469
-rw-r--r-- | include/rabbit.hrl | 2 | ||||
-rw-r--r-- | src/rabbit_access_control.erl | 40 | ||||
-rw-r--r-- | src/rabbit_auth_backend_dummy.erl | 20 | ||||
-rw-r--r-- | src/rabbit_auth_backend_internal.erl | 23 | ||||
-rw-r--r-- | src/rabbit_authn_backend.erl | 49 | ||||
-rw-r--r-- | src/rabbit_authz_backend.erl (renamed from src/rabbit_auth_backend.erl) | 43 | ||||
-rw-r--r-- | src/rabbit_types.erl | 7 | ||||
-rw-r--r-- | test/src/rabbit_tests.erl | 7 |
8 files changed, 120 insertions, 71 deletions
diff --git a/include/rabbit.hrl b/include/rabbit.hrl index 86c30fc5..9cbd978e 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -17,7 +17,7 @@ %% Passed around most places -record(user, {username, tags, - authz_backends}). %% List of {Module, AuthUser} pairs + authz_backends}). %% List of {Module, AuthUserImpl} pairs %% Passed to auth backends -record(auth_user, {username, diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 0ebd2fcf..d1577432 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -73,8 +73,10 @@ check_user_login(Username, AuthProps) -> %% Same module for authN and authZ. Just take the result %% it gives us case try_authenticate(Mod, Username, AuthProps) of - {ok, ModNUser} -> user(ModNUser, {ok, [{Mod, ModNUser}]}); - Else -> Else + {ok, ModNUser = #auth_user{impl = Impl}} -> + user(ModNUser, {ok, [{Mod, Impl}]}); + Else -> + Else end; (_, {ok, User}) -> %% We've successfully authenticated. Skip to the end... @@ -87,7 +89,7 @@ check_user_login(Username, AuthProps) -> R. try_authenticate(Module, Username, AuthProps) -> - case Module:check_user_login(Username, AuthProps) of + case Module:user_login_authentication(Username, AuthProps) of {ok, AuthUser} -> {ok, AuthUser}; {error, E} -> {refused, "~s failed authenticating ~s: ~p~n", [Module, Username, E]}; @@ -96,9 +98,9 @@ try_authenticate(Module, Username, AuthProps) -> try_authorize(Modules, Username) -> lists:foldr( - fun (Module, {ok, AUsers}) -> - case Module:check_user_login(Username, []) of - {ok, AUser} -> {ok, [{Module, AUser} | AUsers]}; + 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", [Module, Username, E]}; {refused, F, A} -> {refused, F, A} @@ -107,13 +109,18 @@ try_authorize(Modules, Username) -> Error end, {ok, []}, Modules). -user(#auth_user{username = Username, tags = Tags}, {ok, ModZUsers}) -> +user(#auth_user{username = Username, tags = Tags}, {ok, ModZImpls}) -> {ok, #user{username = Username, tags = Tags, - authz_backends = ModZUsers}}; + authz_backends = ModZImpls}}; user(_AuthUser, Error) -> Error. +auth_user(#user{username = Username, tags = Tags}, Impl) -> + #auth_user{username = Username, + tags = Tags, + impl = Impl}. + check_user_loopback(Username, SockOrAddr) -> {ok, Users} = application:get_env(rabbit, loopback_users), case rabbit_net:is_loopback(SockOrAddr) @@ -122,14 +129,15 @@ check_user_loopback(Username, SockOrAddr) -> false -> not_allowed end. -check_vhost_access(#user{username = Username, - authz_backends = Modules}, VHostPath, Sock) -> +check_vhost_access(User = #user{username = Username, + authz_backends = Modules}, VHostPath, Sock) -> lists:foldl( - fun({Mod, AUser}, ok) -> + fun({Mod, Impl}, ok) -> check_access( fun() -> rabbit_vhost:exists(VHostPath) andalso - Mod:check_vhost_access(AUser, VHostPath, Sock) + Mod:check_vhost_access( + auth_user(User, Impl), VHostPath, Sock) end, Mod, "access to vhost '~s' refused for user '~s'", [VHostPath, Username]); @@ -141,14 +149,14 @@ check_resource_access(User, R = #resource{kind = exchange, name = <<"">>}, Permission) -> check_resource_access(User, R#resource{name = <<"amq.default">>}, Permission); -check_resource_access(#user{username = Username, - authz_backends = Modules}, +check_resource_access(User = #user{username = Username, + authz_backends = Modules}, Resource, Permission) -> lists:foldl( - fun({Module, AUser}, ok) -> + fun({Module, Impl}, ok) -> check_access( fun() -> Module:check_resource_access( - AUser, Resource, Permission) end, + auth_user(User, Impl), Resource, Permission) end, Module, "access to ~s refused for user '~s'", [rabbit_misc:rs(Resource), Username]); (_, Else) -> Else diff --git a/src/rabbit_auth_backend_dummy.erl b/src/rabbit_auth_backend_dummy.erl index d2905334..d2f07c1d 100644 --- a/src/rabbit_auth_backend_dummy.erl +++ b/src/rabbit_auth_backend_dummy.erl @@ -17,11 +17,12 @@ -module(rabbit_auth_backend_dummy). -include("rabbit.hrl"). --behaviour(rabbit_auth_backend). +-behaviour(rabbit_authn_backend). +-behaviour(rabbit_authz_backend). --export([description/0]). -export([user/0]). --export([check_user_login/2, check_vhost_access/3, check_resource_access/3]). +-export([user_login_authentication/2, user_login_authorization/1, + check_vhost_access/3, check_resource_access/3]). -ifdef(use_specs). @@ -33,19 +34,14 @@ %% not needed. This user can do anything AMQPish. user() -> #user{username = <<"none">>, tags = [], - authz_backends = [{?MODULE, auser()}]}. - -auser() -> #auth_user{username = <<"none">>, - tags = [], - impl = none}. + authz_backends = [{?MODULE, none}]}. %% Implementation of rabbit_auth_backend -description() -> - [{name, <<"Dummy">>}, - {description, <<"Database for the dummy user">>}]. +user_login_authentication(_, _) -> + {refused, "cannot log in conventionally as dummy user", []}. -check_user_login(_, _) -> +user_login_authorization(_) -> {refused, "cannot log in conventionally as dummy user", []}. check_vhost_access(#auth_user{}, _VHostPath, _Sock) -> true. diff --git a/src/rabbit_auth_backend_internal.erl b/src/rabbit_auth_backend_internal.erl index c8f09be9..20a5766d 100644 --- a/src/rabbit_auth_backend_internal.erl +++ b/src/rabbit_auth_backend_internal.erl @@ -17,10 +17,11 @@ -module(rabbit_auth_backend_internal). -include("rabbit.hrl"). --behaviour(rabbit_auth_backend). +-behaviour(rabbit_authn_backend). +-behaviour(rabbit_authz_backend). --export([description/0]). --export([check_user_login/2, check_vhost_access/3, check_resource_access/3]). +-export([user_login_authentication/2, user_login_authorization/1, + check_vhost_access/3, check_resource_access/3]). -export([add_user/2, delete_user/1, lookup_user/1, change_password/2, clear_password/1, @@ -76,13 +77,9 @@ %%---------------------------------------------------------------------------- %% Implementation of rabbit_auth_backend -description() -> - [{name, <<"Internal">>}, - {description, <<"Internal user / password database">>}]. - -check_user_login(Username, []) -> +user_login_authentication(Username, []) -> internal_check_user_login(Username, fun(_) -> true end); -check_user_login(Username, [{password, Cleartext}]) -> +user_login_authentication(Username, [{password, Cleartext}]) -> internal_check_user_login( Username, fun (#internal_user{password_hash = <<Salt:4/binary, Hash/binary>>}) -> @@ -90,9 +87,15 @@ check_user_login(Username, [{password, Cleartext}]) -> (#internal_user{}) -> false end); -check_user_login(Username, AuthProps) -> +user_login_authentication(Username, AuthProps) -> exit({unknown_auth_props, Username, AuthProps}). +user_login_authorization(Username) -> + case user_login_authentication(Username, []) of + {ok, #auth_user{impl = Impl}} -> {ok, Impl}; + Else -> Else + end. + internal_check_user_login(Username, Fun) -> Refused = {refused, "user '~s' - invalid credentials", [Username]}, case lookup_user(Username) of diff --git a/src/rabbit_authn_backend.erl b/src/rabbit_authn_backend.erl new file mode 100644 index 00000000..cfc3f5db --- /dev/null +++ b/src/rabbit_authn_backend.erl @@ -0,0 +1,49 @@ +%% The contents of this file are subject to the Mozilla Public License +%% Version 1.1 (the "License"); you may not use this file except in +%% compliance with the License. You may obtain a copy of the License +%% at http://www.mozilla.org/MPL/ +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and +%% limitations under the License. +%% +%% The Original Code is RabbitMQ. +%% +%% The Initial Developer of the Original Code is GoPivotal, Inc. +%% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. +%% + +-module(rabbit_authn_backend). + +-include("rabbit.hrl"). + +-ifdef(use_specs). + +%% Check a user can log in, given a username and a proplist of +%% authentication information (e.g. [{password, Password}]). If your +%% backend is not to be used for authentication, this should always +%% refuse access. +%% +%% Possible responses: +%% {ok, User} +%% Authentication succeeded, and here's the user record. +%% {error, Error} +%% Something went wrong. Log and die. +%% {refused, Msg, Args} +%% Client failed authentication. Log and die. +-callback user_login_authentication(rabbit_types:username(), [term()]) -> + {'ok', rabbit_types:auth_user()} | + {'refused', string(), [any()]} | + {'error', any()}. + +-else. + +-export([behaviour_info/1]). + +behaviour_info(callbacks) -> + [{user_login_authentication, 2}]; +behaviour_info(_Other) -> + undefined. + +-endif. diff --git a/src/rabbit_auth_backend.erl b/src/rabbit_authz_backend.erl index 315d5719..ff5f014e 100644 --- a/src/rabbit_auth_backend.erl +++ b/src/rabbit_authz_backend.erl @@ -14,56 +14,49 @@ %% Copyright (c) 2007-2014 GoPivotal, Inc. All rights reserved. %% --module(rabbit_auth_backend). +-module(rabbit_authz_backend). -include("rabbit.hrl"). -ifdef(use_specs). --export_type([auth_user/0]). - --type(auth_user() :: - #auth_user{username :: rabbit_types:username(), - tags :: [atom()], - impl :: any()}). - -%% A description proplist as with auth mechanisms, -%% exchanges. Currently unused. --callback description() -> [proplists:property()]. - -%% Check a user can log in, given a username and a proplist of -%% authentication information (e.g. [{password, Password}]). +%% Check a user can log in, when this backend is being used for +%% authorisation only. Authentication has already taken place +%% successfully, but we need to check that the user exists in this +%% backend, and initialise any impl field we will want to have passed +%% back in future calls to check_vhost_access/3 and +%% check_resource_access/3. %% %% Possible responses: -%% {ok, User} -%% Authentication succeeded, and here's the user record. +%% {ok, Impl} +%% User authorisation succeeded, and here's the impl field. %% {error, Error} %% Something went wrong. Log and die. %% {refused, Msg, Args} -%% Client failed authentication. Log and die. --callback check_user_login(rabbit_types:username(), [term()]) -> - {'ok', auth_user()} | +%% User authorisation failed. Log and die. +-callback user_login_authorization(rabbit_types:username()) -> + {'ok', any()} | {'refused', string(), [any()]} | {'error', any()}. -%% Given #user and vhost, can a user log in to a vhost? +%% Given #auth_user and vhost, can a user log in to a vhost? %% Possible responses: %% true %% false %% {error, Error} %% Something went wrong. Log and die. --callback check_vhost_access(auth_user(), +-callback check_vhost_access(rabbit_types:auth_user(), rabbit_types:vhost(), rabbit_net:socket()) -> boolean() | {'error', any()}. -%% Given #user, resource and permission, can a user access a resource? +%% Given #auth_user, resource and permission, can a user access a resource? %% %% Possible responses: %% true %% false %% {error, Error} %% Something went wrong. Log and die. --callback check_resource_access(auth_user(), +-callback check_resource_access(rabbit_types:auth_user(), rabbit_types:r(atom()), rabbit_access_control:permission_atom()) -> boolean() | {'error', any()}. @@ -73,8 +66,8 @@ -export([behaviour_info/1]). behaviour_info(callbacks) -> - [{description, 0}, {check_user_login, 2}, {check_vhost_access, 3}, - {check_resource_access, 3}]; + [{user_login_authorization, 1}, + {check_vhost_access, 3}, {check_resource_access, 3}]; behaviour_info(_Other) -> undefined. diff --git a/src/rabbit_types.erl b/src/rabbit_types.erl index 27fbae88..039568df 100644 --- a/src/rabbit_types.erl +++ b/src/rabbit_types.erl @@ -27,7 +27,7 @@ vhost/0, ctag/0, amqp_error/0, r/1, r2/2, r3/3, listener/0, binding/0, binding_source/0, binding_destination/0, amqqueue/0, exchange/0, - connection/0, protocol/0, user/0, internal_user/0, + connection/0, protocol/0, auth_user/0, user/0, internal_user/0, username/0, password/0, password_hash/0, ok/1, error/1, ok_or_error/1, ok_or_error2/2, ok_pid_or_error/0, channel_exit/0, connection_exit/0, mfargs/0, proc_name/0, @@ -131,6 +131,11 @@ -type(protocol() :: rabbit_framing:protocol()). +-type(auth_user() :: + #auth_user{username :: username(), + tags :: [atom()], + impl :: any()}). + -type(user() :: #user{username :: username(), tags :: [atom()], diff --git a/test/src/rabbit_tests.erl b/test/src/rabbit_tests.erl index e614bfd7..dcbec8f6 100644 --- a/test/src/rabbit_tests.erl +++ b/test/src/rabbit_tests.erl @@ -1294,12 +1294,7 @@ test_spawn_remote() -> user(Username) -> #user{username = Username, tags = [administrator], - authz_backends = [{rabbit_auth_backend_internal, auser(Username)}]}. - -auser(Username) -> - #auth_user{username = Username, - tags = [administrator], - impl = none}. + authz_backends = [{rabbit_auth_backend_internal, none}]}. test_confirms() -> {_Writer, Ch} = test_spawn(), |