From 7a642fb27b845c7bcd1695b13bc120933a310d8a Mon Sep 17 00:00:00 2001 From: Simon MacMullen Date: Mon, 22 Nov 2010 16:06:35 +0000 Subject: [PATCH] Let an auth plugin specify which vhosts a user can see. --- .../include/rabbit_mgmt.hrl | 2 +- .../src/rabbit_mgmt_format.erl | 10 ++- .../src/rabbit_mgmt_util.erl | 76 ++++++++++--------- .../src/rabbit_mgmt_wm_aliveness_test.erl | 2 +- .../src/rabbit_mgmt_wm_all_configuration.erl | 6 +- .../src/rabbit_mgmt_wm_bindings.erl | 29 +++---- .../src/rabbit_mgmt_wm_exchanges.erl | 15 ++-- .../src/rabbit_mgmt_wm_queues.erl | 15 ++-- .../src/rabbit_mgmt_wm_user.erl | 3 +- .../src/rabbit_mgmt_wm_users.erl | 2 +- .../src/rabbit_mgmt_wm_vhosts.erl | 9 +-- .../src/rabbit_mgmt_wm_whoami.erl | 3 +- 12 files changed, 90 insertions(+), 82 deletions(-) diff --git a/deps/rabbitmq_management/include/rabbit_mgmt.hrl b/deps/rabbitmq_management/include/rabbit_mgmt.hrl index 5a32a7cf1d..0e65f36416 100644 --- a/deps/rabbitmq_management/include/rabbit_mgmt.hrl +++ b/deps/rabbitmq_management/include/rabbit_mgmt.hrl @@ -18,4 +18,4 @@ %% %% Contributor(s): ______________________________________. %% --record(context, {username, password, is_admin}). +-record(context, {user, password}). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl index a2ad436bba..4d0a07d250 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_format.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_format.erl @@ -17,7 +17,7 @@ -export([format/2, print/2, pid/1, ip/1, amqp_table/1, tuple/1, timestamp/1]). -export([node_and_pid/1, protocol/1, resource/1, permissions/1, queue/1]). --export([exchange/1, user/1, binding/1, url/2, application/1]). +-export([exchange/1, user/1, internal_user/1, binding/1, url/2, application/1]). -export([pack_binding_props/2, unpack_binding_props/1, tokenise/1]). -export([args_type/1, listener/1, properties/1]). @@ -99,11 +99,17 @@ permissions({User, VHost, Conf, Write, Read}) -> {write, Write}, {read, Read}]. -user(User) -> +internal_user(User) -> [{name, User#internal_user.username}, {password_hash, base64:encode(User#internal_user.password_hash)}, {administrator, User#internal_user.is_admin}]. +user(User) -> + [{name, User#user.username}, + {administrator, User#user.is_admin}, + {auth_backend, User#user.auth_backend}]. + + listener(#listener{node = Node, protocol = Protocol, host = Host, ip_address = IPAddress, port = Port}) -> [{node, Node}, diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl index 5f2f500e33..07853b39f3 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_util.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_util.erl @@ -20,8 +20,8 @@ -export([is_authorized_vhost/2, is_authorized/3, is_authorized_user/3]). -export([bad_request/3, id/2, parse_bool/1]). -export([with_decode/4, not_found/3, amqp_request/4]). --export([all_or_one_vhost/2, with_decode_vhost/4, reply/3, filter_vhost/3]). --export([filter_user/3, with_decode/5, redirect/2, args/1, vhosts/1]). +-export([all_or_one_vhost/3, with_decode_vhost/4, reply/3, filter_vhost/3]). +-export([filter_user/3, with_decode/5, redirect/2, args/1]). -export([reply_list/3, reply_list/4, sort_list/4, destination_type/1]). -include("rabbit_mgmt.hrl"). @@ -37,14 +37,16 @@ is_authorized_admin(ReqData, Context) -> fun(#user{is_admin = IsAdmin}) -> IsAdmin end). is_authorized_vhost(ReqData, Context) -> - is_authorized(ReqData, Context, - fun(#user{username = Username}) -> - case vhost(ReqData) of - not_found -> true; - none -> true; - V -> lists:member(V, vhosts(Username)) - end - end). + is_authorized( + ReqData, Context, + fun(User) -> + case vhost(ReqData) of + not_found -> true; + none -> true; + V -> lists:member( + V, rabbit_access_control:list_vhosts(User)) + end + end). is_authorized_user(ReqData, Context, Item) -> is_authorized( @@ -58,14 +60,14 @@ is_authorized(ReqData, Context, Fun) -> ReqData, Context}, case rabbit_mochiweb_util:parse_auth_header( wrq:get_req_header("authorization", ReqData)) of - [User, Pass] -> - case rabbit_access_control:check_user_pass_login(User, Pass) of - {ok, U = #user{is_admin = IsAdmin}} -> - case Fun(U) of + [Username, Password] -> + case rabbit_access_control:check_user_pass_login(Username, + Password) of + {ok, User} -> + case Fun(User) of true -> {true, ReqData, - Context#context{username = User, - password = Pass, - is_admin = IsAdmin}}; + Context#context{user = User, + password = Password}}; false -> Unauthorized end; {refused, _} -> @@ -223,18 +225,19 @@ parse_bool(V) -> throw({error, {not_boolean, V}}). amqp_request(VHost, ReqData, Context, Method) -> try - Params = #amqp_params{username = Context#context.username, + Params = #amqp_params{username = Context#context.user#user.username, password = Context#context.password, virtual_host = VHost}, - {ok, Conn} = amqp_connection:start(direct, Params), - %% No need to check for {error, {auth_failure_likely... - %% since we will weed out failed logins in some webmachine - %% is_authorized/2 anyway. - {ok, Ch} = amqp_connection:open_channel(Conn), - amqp_channel:call(Ch, Method), - amqp_channel:close(Ch), - amqp_connection:close(Conn), - {true, ReqData, Context} + case amqp_connection:start(direct, Params) of + {ok, Conn} -> + {ok, Ch} = amqp_connection:open_channel(Conn), + amqp_channel:call(Ch, Method), + amqp_channel:close(Ch), + amqp_connection:close(Conn), + {true, ReqData, Context}; + {error, auth_failure} -> + not_authorised(<<"">>, ReqData, Context) + end catch exit:{{server_initiated_close, ?NOT_FOUND, Reason}, _} -> not_found(list_to_binary(Reason), ReqData, Context); @@ -244,28 +247,27 @@ amqp_request(VHost, ReqData, Context, Method) -> when ServerClose =:= server_initiated_close; ServerClose =:= server_initiated_hard_close -> bad_request(list_to_binary(io_lib:format("~p ~s", [Code, Reason])), - ReqData, Context) + ReqData, Context); + E:R -> io:format("~p~n", [{E,R}]) end. -all_or_one_vhost(ReqData, Fun) -> +all_or_one_vhost(ReqData, #context{ user = User }, Fun) -> case rabbit_mgmt_util:vhost(ReqData) of none -> lists:append( - [Fun(V) || V <- rabbit_access_control:list_vhosts()]); + [Fun(V) || + V <- rabbit_access_control:list_vhosts(User)]); not_found -> vhost_not_found; VHost -> Fun(VHost) end. filter_vhost(List, _ReqData, Context) -> - VHosts = vhosts(Context#context.username), + VHosts = rabbit_access_control:list_vhosts(Context#context.user), [I || I <- List, lists:member(proplists:get_value(vhost, I), VHosts)]. -vhosts(Username) -> - [VHost || {VHost, _ConfigurePerm, _WritePerm, _ReadPerm} - <- rabbit_access_control:list_user_permissions(Username)]. - -filter_user(List, _ReqData, #context{is_admin = true}) -> +filter_user(List, _ReqData, #context{user = #user{is_admin = true}}) -> List; -filter_user(List, _ReqData, #context{username = Username, is_admin = false}) -> +filter_user(List, _ReqData, + #context{user = #user{username = Username, is_admin = false}}) -> [I || I <- List, proplists:get_value(user, I) == Username]. redirect(Location, ReqData) -> diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_aliveness_test.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_aliveness_test.erl index 6d58e84ea7..a01c0c1b9b 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_aliveness_test.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_aliveness_test.erl @@ -37,7 +37,7 @@ resource_exists(ReqData, Context) -> end, ReqData, Context}. to_json(ReqData, Context) -> - Params = #amqp_params{username = Context#context.username, + Params = #amqp_params{username = Context#context.user#user.username, password = Context#context.password, virtual_host = rabbit_mgmt_util:vhost(ReqData)}, %% TODO use network connection (need to check what we're bound to) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_all_configuration.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_all_configuration.erl index 24276c1f0d..690da2c746 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_all_configuration.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_all_configuration.erl @@ -44,12 +44,12 @@ create_path(ReqData, Context) -> {"dummy", ReqData, Context}. to_json(ReqData, Context) -> - Xs = [X || X <- rabbit_mgmt_wm_exchanges:exchanges(ReqData), + Xs = [X || X <- rabbit_mgmt_wm_exchanges:exchanges(ReqData, Context), export_exchange(X)], - Qs = [Q || Q <- rabbit_mgmt_wm_queues:queues(ReqData), + Qs = [Q || Q <- rabbit_mgmt_wm_queues:queues(ReqData, Context), export_queue(Q)], QNames = [{pget(name, Q), pget(vhost, Q)} || Q <- Qs], - Bs = [B || B <- rabbit_mgmt_wm_bindings:bindings(ReqData), + Bs = [B || B <- rabbit_mgmt_wm_bindings:bindings(ReqData, Context), export_binding(B, QNames)], {ok, Vsn} = application:get_key(rabbit, vsn), rabbit_mgmt_util:reply( diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl index 4a816ff008..6d33b37092 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_bindings.erl @@ -17,7 +17,7 @@ -export([init/1, to_json/2, content_types_provided/2, is_authorized/2]). -export([allowed_methods/2, post_is_create/2, create_path/2]). -export([content_types_accepted/2, accept_content/2, resource_exists/2]). --export([bindings/1]). +-export([bindings/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -32,7 +32,7 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. resource_exists(ReqData, {Mode, Context}) -> - {case list_bindings(Mode, ReqData) of + {case list_bindings(Mode, ReqData, Context) of vhost_not_found -> false; _ -> true end, ReqData, {Mode, Context}}. @@ -50,7 +50,8 @@ post_is_create(ReqData, Context) -> {true, ReqData, Context}. to_json(ReqData, {Mode, Context}) -> - Bs = [rabbit_mgmt_format:binding(B) || B <- list_bindings(Mode, ReqData)], + Bs = [rabbit_mgmt_format:binding(B) || + B <- list_bindings(Mode, ReqData, Context)], rabbit_mgmt_util:reply_list( rabbit_mgmt_util:filter_vhost(Bs, ReqData, Context), ["vhost", "exchange", "queue", "routing_key", "properties_key"], @@ -102,24 +103,24 @@ is_authorized(ReqData, {Mode, Context}) -> %%-------------------------------------------------------------------- -bindings(ReqData) -> +bindings(ReqData, Context) -> [rabbit_mgmt_format:binding(B) || - B <- list_bindings(all, ReqData)]. + B <- list_bindings(all, ReqData, Context)]. %%-------------------------------------------------------------------- -list_bindings(all, ReqData) -> - rabbit_mgmt_util:all_or_one_vhost(ReqData, - fun (VHost) -> - rabbit_binding:list(VHost) - end); -list_bindings(exchange_source, ReqData) -> +list_bindings(all, ReqData, Context) -> + rabbit_mgmt_util:all_or_one_vhost(ReqData, Context, + fun (VHost) -> + rabbit_binding:list(VHost) + end); +list_bindings(exchange_source, ReqData, _Context) -> rabbit_binding:list_for_source(r(exchange, exchange, ReqData)); -list_bindings(exchange_destination, ReqData) -> +list_bindings(exchange_destination, ReqData, _Context) -> rabbit_binding:list_for_destination(r(exchange, exchange, ReqData)); -list_bindings(queue, ReqData) -> +list_bindings(queue, ReqData, _Context) -> rabbit_binding:list_for_destination(r(queue, destination, ReqData)); -list_bindings(source_destination, ReqData) -> +list_bindings(source_destination, ReqData, _Context) -> DestType = rabbit_mgmt_util:destination_type(ReqData), rabbit_binding:list_for_source_and_destination( r(exchange, source, ReqData), diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl index 4a6c3fe1ed..f9965437c8 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_exchanges.erl @@ -15,7 +15,7 @@ -module(rabbit_mgmt_wm_exchanges). -export([init/1, to_json/2, content_types_provided/2, is_authorized/2, - resource_exists/2, exchanges/1]). + resource_exists/2, exchanges/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -29,13 +29,13 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. resource_exists(ReqData, Context) -> - {case exchanges0(ReqData) of + {case exchanges0(ReqData, Context) of vhost_not_found -> false; _ -> true end, ReqData, Context}. to_json(ReqData, Context) -> - Xs = rabbit_mgmt_db:get_exchanges(exchanges(ReqData)), + Xs = rabbit_mgmt_db:get_exchanges(exchanges(ReqData, Context)), rabbit_mgmt_util:reply_list( rabbit_mgmt_util:filter_vhost(Xs, ReqData, Context), ReqData, Context). @@ -45,8 +45,9 @@ is_authorized(ReqData, Context) -> %%-------------------------------------------------------------------- -exchanges(ReqData) -> - [rabbit_mgmt_format:exchange(X) || X <- exchanges0(ReqData)]. +exchanges(ReqData, Context) -> + [rabbit_mgmt_format:exchange(X) || X <- exchanges0(ReqData, Context)]. -exchanges0(ReqData) -> - rabbit_mgmt_util:all_or_one_vhost(ReqData, fun rabbit_exchange:info_all/1). +exchanges0(ReqData, Context) -> + rabbit_mgmt_util:all_or_one_vhost(ReqData, Context, + fun rabbit_exchange:info_all/1). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_queues.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_queues.erl index b506cd200c..36943ac0b1 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_queues.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_queues.erl @@ -15,7 +15,7 @@ -module(rabbit_mgmt_wm_queues). -export([init/1, to_json/2, content_types_provided/2, is_authorized/2, - resource_exists/2, queues/1]). + resource_exists/2, queues/2]). -include("rabbit_mgmt.hrl"). -include_lib("webmachine/include/webmachine.hrl"). @@ -29,13 +29,13 @@ content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. resource_exists(ReqData, Context) -> - {case queues0(ReqData) of + {case queues0(ReqData, Context) of vhost_not_found -> false; _ -> true end, ReqData, Context}. to_json(ReqData, Context) -> - Qs = rabbit_mgmt_db:get_queues(queues(ReqData)), + Qs = rabbit_mgmt_db:get_queues(queues(ReqData, Context)), rabbit_mgmt_util:reply_list( rabbit_mgmt_util:filter_vhost(Qs, ReqData, Context), ReqData, Context). @@ -45,8 +45,9 @@ is_authorized(ReqData, Context) -> %%-------------------------------------------------------------------- -queues(ReqData) -> - [rabbit_mgmt_format:queue(Q) || Q <- queues0(ReqData)]. +queues(ReqData, Context) -> + [rabbit_mgmt_format:queue(Q) || Q <- queues0(ReqData, Context)]. -queues0(ReqData) -> - rabbit_mgmt_util:all_or_one_vhost(ReqData, fun rabbit_amqqueue:list/1). +queues0(ReqData, Context) -> + rabbit_mgmt_util:all_or_one_vhost(ReqData, Context, + fun rabbit_amqqueue:list/1). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_user.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_user.erl index 16d8690f67..2645bab663 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_user.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_user.erl @@ -43,7 +43,8 @@ resource_exists(ReqData, Context) -> to_json(ReqData, Context) -> {ok, User} = user(ReqData), - rabbit_mgmt_util:reply(rabbit_mgmt_format:user(User), ReqData, Context). + rabbit_mgmt_util:reply(rabbit_mgmt_format:internal_user(User), + ReqData, Context). accept_content(ReqData, Context) -> User = rabbit_mgmt_util:id(user, ReqData), diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_users.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_users.erl index bbdec46dbc..032f2fcd44 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_users.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_users.erl @@ -39,5 +39,5 @@ is_authorized(ReqData, Context) -> users() -> [begin {ok, User} = rabbit_access_control:lookup_user(U), - rabbit_mgmt_format:user(User) + rabbit_mgmt_format:internal_user(User) end || {U, _} <- rabbit_access_control:list_users()]. diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl index 79bf35a9f4..3069cd3de0 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_vhosts.erl @@ -28,12 +28,8 @@ init(_Config) -> {ok, #context{}}. content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. -to_json(ReqData, Context = #context{username = Username, - is_admin = IsAdmin}) -> - VHosts = case IsAdmin of - true -> vhosts(); - false -> format(rabbit_mgmt_util:vhosts(Username)) - end, +to_json(ReqData, Context = #context{user = User}) -> + VHosts = format(rabbit_access_control:list_vhosts(User)), rabbit_mgmt_util:reply_list(VHosts, ReqData, Context). is_authorized(ReqData, Context) -> @@ -41,6 +37,7 @@ is_authorized(ReqData, Context) -> %%-------------------------------------------------------------------- +%% This is used by export config and so should list ones in Mnesia vhosts() -> format(rabbit_access_control:list_vhosts()). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_whoami.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_whoami.erl index 554f447675..e5e24a2165 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_whoami.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_whoami.erl @@ -26,8 +26,7 @@ init(_Config) -> {ok, #context{}}. content_types_provided(ReqData, Context) -> {[{"application/json", to_json}], ReqData, Context}. -to_json(ReqData, Context = #context{username = Username}) -> - {ok, User} = rabbit_access_control:lookup_user(Username), +to_json(ReqData, Context = #context{user = User}) -> rabbit_mgmt_util:reply(rabbit_mgmt_format:user(User), ReqData, Context). is_authorized(ReqData, Context) ->