diff options
| author | Ben Hood <0x6e6562@gmail.com> | 2009-01-22 13:47:46 +0000 |
|---|---|---|
| committer | Ben Hood <0x6e6562@gmail.com> | 2009-01-22 13:47:46 +0000 |
| commit | 25964d18a56f65e94b64dace82bb409e64dab4e9 (patch) | |
| tree | a785b370cc61158e63b198e75ad7757b75de90d4 | |
| parent | 33d0c4627618c5fbde6b2a85483ed038f4dba293 (diff) | |
| parent | f02d49ed637a9033ea0dd841522ea6417be34375 (diff) | |
| download | rabbitmq-server-git-25964d18a56f65e94b64dace82bb409e64dab4e9.tar.gz | |
Merged 20173 into default
| -rw-r--r-- | docs/rabbitmqctl.1.pod | 24 | ||||
| -rw-r--r-- | ebin/rabbit_app.in | 1 | ||||
| -rw-r--r-- | include/rabbit.hrl | 6 | ||||
| -rw-r--r-- | src/rabbit.erl | 6 | ||||
| -rw-r--r-- | src/rabbit_access_control.erl | 148 | ||||
| -rw-r--r-- | src/rabbit_channel.erl | 46 | ||||
| -rw-r--r-- | src/rabbit_control.erl | 93 | ||||
| -rw-r--r-- | src/rabbit_mnesia.erl | 6 | ||||
| -rw-r--r-- | src/rabbit_tests.erl | 33 |
9 files changed, 250 insertions, 113 deletions
diff --git a/docs/rabbitmqctl.1.pod b/docs/rabbitmqctl.1.pod index 68c26b14c9..e9b9514ef6 100644 --- a/docs/rabbitmqctl.1.pod +++ b/docs/rabbitmqctl.1.pod @@ -114,17 +114,23 @@ delete_vhost I<vhostpath> list_vhosts list all virtual hosts. -map_user_vhost I<username> I<vhostpath> - grant the user named I<username> access to the virtual host called +set_permissions [-p I<vhostpath>] I<username> I<regexp> I<regexp> + set the permissions for the user named I<username> in the virtual + host I<vhostpath>, granting them configuration access to resources + with names matching the first I<regexp> and messaging access to + resources with names matching the second I<regexp>. + +clear_permissions [-p I<vhostpath>] I<username> + remove the permissions for the user named I<username> in the + virtual host I<vhostpath>. + +list_permissions [-p I<vhostpath>] + list all the users and their permissions in the virtual host I<vhostpath>. -unmap_user_vhost I<username> I<vhostpath> - deny the user named I<username> access to the virtual host called - I<vhostpath>. - -list_user_vhost I<username> - list all the virtual hosts to which the user named I<username> has - been granted access. +list_user_permissions I<username> + list the permissions of the user named I<username> across all + virtual hosts. =head2 SERVER STATUS diff --git a/ebin/rabbit_app.in b/ebin/rabbit_app.in index e2f36c0f5f..77f9d299b7 100644 --- a/ebin/rabbit_app.in +++ b/ebin/rabbit_app.in @@ -17,4 +17,5 @@ {default_user, <<"guest">>}, {default_pass, <<"guest">>}, {default_vhost, <<"/">>}, + {default_permissions, [<<".*">>, <<".*">>]}, {memory_alarms, auto}]}]}. diff --git a/include/rabbit.hrl b/include/rabbit.hrl index d07aeaf845..8aba8a6fb6 100644 --- a/include/rabbit.hrl +++ b/include/rabbit.hrl @@ -30,7 +30,9 @@ %% -record(user, {username, password}). +-record(permission, {configuration, messaging}). -record(user_vhost, {username, virtual_host}). +-record(user_permission, {user_vhost, permission}). -record(vhost, {virtual_host, dummy}). @@ -74,6 +76,7 @@ -type(thunk(T) :: fun(() -> T)). -type(info_key() :: atom()). -type(info() :: {info_key(), any()}). +-type(regexp() :: binary()). %% this is really an abstract type, but dialyzer does not support them -type(guid() :: any()). @@ -88,6 +91,9 @@ -type(user() :: #user{username :: username(), password :: password()}). +-type(permission() :: + #permission{configuration :: regexp(), + messaging :: regexp()}). -type(amqqueue() :: #amqqueue{name :: queue_name(), durable :: bool(), diff --git a/src/rabbit.erl b/src/rabbit.erl index 30b8c39475..7ad13a7d28 100644 --- a/src/rabbit.erl +++ b/src/rabbit.erl @@ -264,9 +264,13 @@ insert_default_data() -> {ok, DefaultUser} = application:get_env(default_user), {ok, DefaultPass} = application:get_env(default_pass), {ok, DefaultVHost} = application:get_env(default_vhost), + {ok, [DefaultConfigurationPerm, DefaultMessagingPerm]} = + application:get_env(default_permissions), ok = rabbit_access_control:add_vhost(DefaultVHost), ok = rabbit_access_control:add_user(DefaultUser, DefaultPass), - ok = rabbit_access_control:map_user_vhost(DefaultUser, DefaultVHost), + ok = rabbit_access_control:set_permissions(DefaultUser, DefaultVHost, + DefaultConfigurationPerm, + DefaultMessagingPerm), ok. start_builtin_amq_applications() -> diff --git a/src/rabbit_access_control.erl b/src/rabbit_access_control.erl index 36270efddc..0d9632b5d8 100644 --- a/src/rabbit_access_control.erl +++ b/src/rabbit_access_control.erl @@ -34,11 +34,12 @@ -include("rabbit.hrl"). -export([check_login/2, user_pass_login/2, - check_vhost_access/2]). + check_vhost_access/2, check_resource_access/3]). -export([add_user/2, delete_user/1, change_password/2, list_users/0, lookup_user/1]). --export([add_vhost/1, delete_vhost/1, list_vhosts/0, list_vhost_users/1]). --export([list_user_vhosts/1, map_user_vhost/2, unmap_user_vhost/2]). +-export([add_vhost/1, delete_vhost/1, list_vhosts/0]). +-export([set_permissions/4, clear_permissions/2, + list_vhost_permissions/1, list_user_permissions/1]). %%---------------------------------------------------------------------------- @@ -47,6 +48,8 @@ -spec(check_login/2 :: (binary(), binary()) -> user()). -spec(user_pass_login/2 :: (username(), password()) -> user()). -spec(check_vhost_access/2 :: (user(), vhost()) -> 'ok'). +-spec(check_resource_access/3 :: + (username(), r(atom()), non_neg_integer()) -> 'ok'). -spec(add_user/2 :: (username(), password()) -> 'ok'). -spec(delete_user/1 :: (username()) -> 'ok'). -spec(change_password/2 :: (username(), password()) -> 'ok'). @@ -55,10 +58,12 @@ -spec(add_vhost/1 :: (vhost()) -> 'ok'). -spec(delete_vhost/1 :: (vhost()) -> 'ok'). -spec(list_vhosts/0 :: () -> [vhost()]). --spec(list_vhost_users/1 :: (vhost()) -> [username()]). --spec(list_user_vhosts/1 :: (username()) -> [vhost()]). --spec(map_user_vhost/2 :: (username(), vhost()) -> 'ok'). --spec(unmap_user_vhost/2 :: (username(), vhost()) -> 'ok'). +-spec(set_permissions/4 :: (username(), vhost(), regexp(), regexp()) -> 'ok'). +-spec(clear_permissions/2 :: (username(), vhost()) -> 'ok'). +-spec(list_vhost_permissions/1 :: + (vhost()) -> [{username(), regexp(), regexp()}]). +-spec(list_user_permissions/1 :: + (username()) -> [{vhost(), regexp(), regexp()}]). -endif. @@ -112,9 +117,9 @@ internal_lookup_vhost_access(Username, VHostPath) -> %% TODO: use dirty ops instead rabbit_misc:execute_mnesia_transaction( fun () -> - case mnesia:match_object( - #user_vhost{username = Username, - virtual_host = VHostPath}) of + case mnesia:read({user_permission, + #user_vhost{username = Username, + virtual_host = VHostPath}}) of [] -> not_found; [R] -> {ok, R} end @@ -131,6 +136,38 @@ check_vhost_access(#user{username = Username}, VHostPath) -> [VHostPath, Username]) end. +check_resource_access(Username, + R = #resource{kind = exchange, name = <<"">>}, + Permission) -> + check_resource_access(Username, + R#resource{name = <<"amq.default">>}, + Permission); +check_resource_access(_Username, + #resource{name = <<"amq.gen",_/binary>>}, + _Permission) -> + ok; +check_resource_access(Username, + R = #resource{virtual_host = VHostPath, name = Name}, + Permission) -> + Res = case mnesia:dirty_read({user_permission, + #user_vhost{username = Username, + virtual_host = VHostPath}}) of + [] -> + false; + [#user_permission{permission = P}] -> + case regexp:match( + binary_to_list(Name), + binary_to_list(element(Permission, P))) of + {match, _, _} -> true; + nomatch -> false + end + end, + if Res -> ok; + true -> rabbit_misc:protocol_error( + access_refused, "access to ~s refused for user '~s'", + [rabbit_misc:rs(R), Username]) + end. + add_user(Username, Password) -> R = rabbit_misc:execute_mnesia_transaction( fun () -> @@ -151,7 +188,13 @@ delete_user(Username) -> Username, fun () -> ok = mnesia:delete({user, Username}), - ok = mnesia:delete({user_vhost, Username}) + [ok = mnesia:delete_object(R) || + R <- mnesia:match_object( + #user_permission{user_vhost = #user_vhost{ + username = Username, + virtual_host = '_'}, + permission = '_'})], + ok end)), rabbit_log:info("Deleted user ~p~n", [Username]), R. @@ -220,53 +263,74 @@ internal_delete_vhost(VHostPath) -> ok = rabbit_exchange:delete(Name, false) end, rabbit_exchange:list(VHostPath)), - lists:foreach(fun (Username) -> - ok = unmap_user_vhost(Username, VHostPath) + lists:foreach(fun ({Username, _, _}) -> + ok = clear_permissions(Username, VHostPath) end, - list_vhost_users(VHostPath)), + list_vhost_permissions(VHostPath)), ok = mnesia:delete({vhost, VHostPath}), ok. list_vhosts() -> mnesia:dirty_all_keys(vhost). -list_vhost_users(VHostPath) -> - [Username || - #user_vhost{username = Username} <- - %% TODO: use dirty ops instead - rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_vhost( - VHostPath, - fun () -> mnesia:index_read(user_vhost, VHostPath, - #user_vhost.virtual_host) - end))]. - -list_user_vhosts(Username) -> - [VHostPath || - #user_vhost{virtual_host = VHostPath} <- - %% TODO: use dirty ops instead - rabbit_misc:execute_mnesia_transaction( - rabbit_misc:with_user( - Username, - fun () -> mnesia:read({user_vhost, Username}) end))]. +validate_regexp(RegexpBin) -> + Regexp = binary_to_list(RegexpBin), + case regexp:parse(Regexp) of + {ok, _} -> ok; + {error, Reason} -> throw({error, {invalid_regexp, Regexp, Reason}}) + end. -map_user_vhost(Username, VHostPath) -> +set_permissions(Username, VHostPath, ConfigurationPerm, MessagingPerm) -> + validate_regexp(ConfigurationPerm), + validate_regexp(MessagingPerm), rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( Username, VHostPath, - fun () -> - ok = mnesia:write( - #user_vhost{username = Username, - virtual_host = VHostPath}) + fun () -> ok = mnesia:write( + #user_permission{user_vhost = #user_vhost{ + username = Username, + virtual_host = VHostPath}, + permission = #permission{ + configuration = ConfigurationPerm, + messaging = MessagingPerm}}) end)). -unmap_user_vhost(Username, VHostPath) -> +clear_permissions(Username, VHostPath) -> rabbit_misc:execute_mnesia_transaction( rabbit_misc:with_user_and_vhost( Username, VHostPath, fun () -> - ok = mnesia:delete_object( - #user_vhost{username = Username, - virtual_host = VHostPath}) + ok = mnesia:delete({user_permission, + #user_vhost{username = Username, + virtual_host = VHostPath}}) end)). +list_vhost_permissions(VHostPath) -> + [{Username, ConfigurationPerm, MessagingPerm} || + {Username, _, ConfigurationPerm, MessagingPerm} <- + list_permissions(rabbit_misc:with_vhost( + VHostPath, match_user_vhost('_', VHostPath)))]. + +list_user_permissions(Username) -> + [{VHostPath, ConfigurationPerm, MessagingPerm} || + {_, VHostPath, ConfigurationPerm, MessagingPerm} <- + list_permissions(rabbit_misc:with_user( + Username, match_user_vhost(Username, '_')))]. + +list_permissions(QueryThunk) -> + [{Username, VHostPath, ConfigurationPerm, MessagingPerm} || + #user_permission{user_vhost = #user_vhost{username = Username, + virtual_host = VHostPath}, + permission = #permission{ + configuration = ConfigurationPerm, + messaging = MessagingPerm}} <- + %% TODO: use dirty ops instead + rabbit_misc:execute_mnesia_transaction(QueryThunk)]. + +match_user_vhost(Username, VHostPath) -> + fun () -> mnesia:match_object( + #user_permission{user_vhost = #user_vhost{ + username = Username, + virtual_host = VHostPath}, + permission = '_'}) + end. diff --git a/src/rabbit_channel.erl b/src/rabbit_channel.erl index 376e39c60d..39867a4b61 100644 --- a/src/rabbit_channel.erl +++ b/src/rabbit_channel.erl @@ -48,6 +48,8 @@ -define(HIBERNATE_AFTER, 1000). +-define(MAX_PERMISSION_CACHE_SIZE, 12). + %%---------------------------------------------------------------------------- -ifdef(use_specs). @@ -144,6 +146,7 @@ handle_cast({deliver, ConsumerTag, AckRequired, Msg}, noreply(State1#ch{next_tag = DeliveryTag + 1}); handle_cast({conserve_memory, Conserve}, State) -> + ok = clear_permission_cache(), ok = rabbit_writer:send_command( State#ch.writer_pid, #'channel.flow'{active = not(Conserve)}), noreply(State). @@ -152,6 +155,7 @@ handle_info({'EXIT', _Pid, Reason}, State) -> {stop, Reason, State}; handle_info(timeout, State) -> + ok = clear_permission_cache(), %% TODO: Once we drop support for R11B-5, we can change this to %% {noreply, State, hibernate}; proc_lib:hibernate(gen_server2, enter_loop, [?MODULE, [], State]). @@ -200,6 +204,32 @@ return_queue_declare_ok(State, NoWait, Q) -> {reply, Reply, NewState} end. +check_resource_access(Username, Resource, Perm) -> + V = {Resource, Perm}, + Cache = case get(permission_cache) of + undefined -> []; + Other -> Other + end, + CacheTail = + case lists:member(V, Cache) of + true -> lists:delete(V, Cache); + false -> ok = rabbit_access_control:check_resource_access( + Username, Resource, Perm), + lists:sublist(Cache, ?MAX_PERMISSION_CACHE_SIZE - 1) + end, + put(permission_cache, [V | CacheTail]), + ok. + +clear_permission_cache() -> + erase(permission_cache), + ok. + +check_configuration_permitted(Resource, #ch{ username = Username}) -> + check_resource_access(Username, Resource, #permission.configuration). + +check_messaging_permitted(Resource, #ch{ username = Username}) -> + check_resource_access(Username, Resource, #permission.messaging). + expand_queue_name_shortcut(<<>>, #ch{ most_recently_declared_queue = <<>> }) -> rabbit_misc:protocol_error( not_allowed, "no previously declared queue", []); @@ -269,6 +299,7 @@ handle_method(#'basic.publish'{exchange = ExchangeNameBin, immediate = Immediate}, Content, State = #ch{ virtual_host = VHostPath}) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), + check_messaging_permitted(ExchangeName, State), Exchange = rabbit_exchange:lookup_or_die(ExchangeName), %% We decode the content's properties here because we're almost %% certain to want to look at delivery-mode and priority. @@ -312,6 +343,7 @@ handle_method(#'basic.get'{queue = QueueNameBin, _, State = #ch{ writer_pid = WriterPid, next_tag = DeliveryTag }) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), + check_messaging_permitted(QueueName, State), case rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> rabbit_amqqueue:basic_get(Q, self(), NoAck) end) of @@ -346,6 +378,7 @@ handle_method(#'basic.consume'{queue = QueueNameBin, case dict:find(ConsumerTag, ConsumerMapping) of error -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), + check_messaging_permitted(QueueName, State), ActualConsumerTag = case ConsumerTag of <<>> -> rabbit_misc:binstring_guid("amq.ctag"); @@ -504,6 +537,7 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, _, State = #ch{ virtual_host = VHostPath }) -> CheckedType = rabbit_exchange:check_type(TypeNameBin), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), + check_configuration_permitted(ExchangeName, State), X = case rabbit_exchange:lookup(ExchangeName) of {ok, FoundX} -> FoundX; {error, not_found} -> @@ -523,6 +557,7 @@ handle_method(#'exchange.declare'{exchange = ExchangeNameBin, nowait = NoWait}, _, State = #ch{ virtual_host = VHostPath }) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), + check_configuration_permitted(ExchangeName, State), X = rabbit_exchange:lookup_or_die(ExchangeName), ok = rabbit_exchange:assert_type(X, rabbit_exchange:check_type(TypeNameBin)), return_ok(State, NoWait, #'exchange.declare_ok'{}); @@ -532,6 +567,7 @@ handle_method(#'exchange.delete'{exchange = ExchangeNameBin, nowait = NoWait}, _, State = #ch { virtual_host = VHostPath }) -> ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), + check_configuration_permitted(ExchangeName, State), case rabbit_exchange:delete(ExchangeName, IfUnused) of {error, not_found} -> rabbit_misc:protocol_error( @@ -582,9 +618,12 @@ handle_method(#'queue.declare'{queue = QueueNameBin, Other -> check_name('queue', Other) end, QueueName = rabbit_misc:r(VHostPath, queue, ActualNameBin), + check_configuration_permitted(QueueName, State), Finish(rabbit_amqqueue:declare(QueueName, Durable, AutoDelete, Args)); - Other -> Other + Other = #amqqueue{name = QueueName} -> + check_configuration_permitted(QueueName, State), + Other end, return_queue_declare_ok(State, NoWait, Q); @@ -593,6 +632,7 @@ handle_method(#'queue.declare'{queue = QueueNameBin, nowait = NoWait}, _, State = #ch{ virtual_host = VHostPath }) -> QueueName = rabbit_misc:r(VHostPath, queue, QueueNameBin), + check_configuration_permitted(QueueName, State), Q = rabbit_amqqueue:with_or_die(QueueName, fun (Q) -> Q end), return_queue_declare_ok(State, NoWait, Q); @@ -603,6 +643,7 @@ handle_method(#'queue.delete'{queue = QueueNameBin, }, _, State) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), + check_configuration_permitted(QueueName, State), case rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> rabbit_amqqueue:delete(Q, IfUnused, IfEmpty) end) of @@ -639,6 +680,7 @@ handle_method(#'queue.purge'{queue = QueueNameBin, nowait = NoWait}, _, State) -> QueueName = expand_queue_name_shortcut(QueueNameBin, State), + check_messaging_permitted(QueueName, State), {ok, PurgedMessageCount} = rabbit_amqqueue:with_or_die( QueueName, fun (Q) -> rabbit_amqqueue:purge(Q) end), @@ -688,9 +730,11 @@ binding_action(Fun, ExchangeNameBin, QueueNameBin, RoutingKey, Arguments, %% FIXME: don't allow binding to internal exchanges - %% including the one named "" ! QueueName = expand_queue_name_shortcut(QueueNameBin, State), + check_configuration_permitted(QueueName, State), ActualRoutingKey = expand_routing_key_shortcut(QueueNameBin, RoutingKey, State), ExchangeName = rabbit_misc:r(VHostPath, exchange, ExchangeNameBin), + check_configuration_permitted(ExchangeName, State), case Fun(ExchangeName, QueueName, ActualRoutingKey, Arguments) of {error, queue_not_found} -> rabbit_misc:protocol_error( diff --git a/src/rabbit_control.erl b/src/rabbit_control.erl index cbc11b4031..293cd79751 100644 --- a/src/rabbit_control.erl +++ b/src/rabbit_control.erl @@ -114,10 +114,10 @@ Available commands: delete_vhost <VHostPath> list_vhosts - map_user_vhost <UserName> <VHostPath> - unmap_user_vhost <UserName> <VHostPath> - list_user_vhosts <UserName> - list_vhost_users <VHostPath> + set_permissions [-p <VHostPath>] <UserName> <Regexp> <Regexp> + clear_permissions [-p <VHostPath>] <UserName> + list_permissions [-p <VHostPath>] + list_user_permissions <UserName> list_queues [-p <VHostPath>] [<QueueInfoItem> ...] list_exchanges [-p <VHostPath>] [<ExchangeInfoItem> ...] @@ -223,25 +223,14 @@ action(list_vhosts, Node, [], Inform) -> Inform("Listing vhosts", []), display_list(call(Node, {rabbit_access_control, list_vhosts, []})); -action(map_user_vhost, Node, Args = [_Username, _VHostPath], Inform) -> - Inform("Mapping user ~p to vhost ~p", Args), - call(Node, {rabbit_access_control, map_user_vhost, Args}); - -action(unmap_user_vhost, Node, Args = [_Username, _VHostPath], Inform) -> - Inform("Unmapping user ~p from vhost ~p", Args), - call(Node, {rabbit_access_control, unmap_user_vhost, Args}); - -action(list_user_vhosts, Node, Args = [_Username], Inform) -> - Inform("Listing vhosts for user ~p", Args), - display_list(call(Node, {rabbit_access_control, list_user_vhosts, Args})); - -action(list_vhost_users, Node, Args = [_VHostPath], Inform) -> - Inform("Listing users for vhosts ~p", Args), - display_list(call(Node, {rabbit_access_control, list_vhost_users, Args})); +action(list_user_permissions, Node, Args = [_Username], Inform) -> + Inform("Listing permissions for user ~p", Args), + display_list(call(Node, {rabbit_access_control, list_user_permissions, + Args})); action(list_queues, Node, Args, Inform) -> Inform("Listing queues", []), - {VHostArg, RemainingArgs} = parse_vhost_flag(Args), + {VHostArg, RemainingArgs} = parse_vhost_flag_bin(Args), ArgAtoms = list_replace(node, pid, default_if_empty(RemainingArgs, [name, messages])), display_info_list(rpc_call(Node, rabbit_amqqueue, info_all, @@ -250,7 +239,7 @@ action(list_queues, Node, Args, Inform) -> action(list_exchanges, Node, Args, Inform) -> Inform("Listing exchanges", []), - {VHostArg, RemainingArgs} = parse_vhost_flag(Args), + {VHostArg, RemainingArgs} = parse_vhost_flag_bin(Args), ArgAtoms = default_if_empty(RemainingArgs, [name, type]), display_info_list(rpc_call(Node, rabbit_exchange, info_all, [VHostArg, ArgAtoms]), @@ -258,7 +247,7 @@ action(list_exchanges, Node, Args, Inform) -> action(list_bindings, Node, Args, Inform) -> Inform("Listing bindings", []), - {VHostArg, _} = parse_vhost_flag(Args), + {VHostArg, _} = parse_vhost_flag_bin(Args), InfoKeys = [exchange_name, routing_key, queue_name, args], display_info_list( [lists:zip(InfoKeys, tuple_to_list(X)) || @@ -272,15 +261,37 @@ action(list_connections, Node, Args, Inform) -> default_if_empty(Args, [user, peer_address, peer_port])), display_info_list(rpc_call(Node, rabbit_networking, connection_info_all, [ArgAtoms]), - ArgAtoms). + ArgAtoms); + +action(Command, Node, Args, Inform) -> + {VHost, RemainingArgs} = parse_vhost_flag(Args), + action(Command, Node, VHost, RemainingArgs, Inform). + +action(set_permissions, Node, VHost, [Username, CPerm, MPerm], Inform) -> + Inform("Setting permissions for user ~p in vhost ~p", [Username, VHost]), + call(Node, {rabbit_access_control, set_permissions, + [Username, VHost, CPerm, MPerm]}); + +action(clear_permissions, Node, VHost, [Username], Inform) -> + Inform("Clearing permissions for user ~p in vhost ~p", [Username, VHost]), + call(Node, {rabbit_access_control, clear_permissions, [Username, VHost]}); + +action(list_permissions, Node, VHost, [], Inform) -> + Inform("Listing permissions in vhost ~p", [VHost]), + display_list(call(Node, {rabbit_access_control, list_vhost_permissions, + [VHost]})). parse_vhost_flag(Args) when is_list(Args) -> - case Args of - ["-p", VHost | RemainingArgs] -> - {list_to_binary(VHost), RemainingArgs}; - RemainingArgs -> - {<<"/">>, RemainingArgs} - end. + case Args of + ["-p", VHost | RemainingArgs] -> + {VHost, RemainingArgs}; + RemainingArgs -> + {"/", RemainingArgs} + end. + +parse_vhost_flag_bin(Args) -> + {VHost, RemainingArgs} = parse_vhost_flag(Args), + {list_to_binary(VHost), RemainingArgs}. default_if_empty(List, Default) when is_list(List) -> if List == [] -> @@ -290,21 +301,17 @@ default_if_empty(List, Default) when is_list(List) -> end. display_info_list(Results, InfoItemKeys) when is_list(Results) -> - lists:foreach( - fun (Result) -> - io:fwrite( - lists:flatten( - rabbit_misc:intersperse( - "\t", - [format_info_item(Result, X) || X <- InfoItemKeys]))), - io:nl() - end, - Results), + lists:foreach(fun (Result) -> display_row([format_info_item(Result, X) || + X <- InfoItemKeys]) + end, Results), ok; - display_info_list(Other, _) -> Other. +display_row(Row) -> + io:fwrite(lists:flatten(rabbit_misc:intersperse("\t", Row))), + io:nl(). + format_info_item(Items, Key) -> {value, Info = {Key, Value}} = lists:keysearch(Key, 1, Items), case Info of @@ -321,8 +328,10 @@ format_info_item(Items, Key) -> end. display_list(L) when is_list(L) -> - lists:foreach(fun (I) -> - io:format("~s~n", [binary_to_list(I)]) + lists:foreach(fun (I) when is_binary(I) -> + io:format("~s~n", [url_encode(I)]); + (I) when is_tuple(I) -> + display_row([url_encode(V) || V <- tuple_to_list(I)]) end, lists:sort(L)), ok; diff --git a/src/rabbit_mnesia.erl b/src/rabbit_mnesia.erl index eebb38fa61..b7f3dd0a89 100644 --- a/src/rabbit_mnesia.erl +++ b/src/rabbit_mnesia.erl @@ -102,10 +102,8 @@ force_reset() -> reset(true). table_definitions() -> [{user, [{disc_copies, [node()]}, {attributes, record_info(fields, user)}]}, - {user_vhost, [{type, bag}, - {disc_copies, [node()]}, - {attributes, record_info(fields, user_vhost)}, - {index, [virtual_host]}]}, + {user_permission, [{disc_copies, [node()]}, + {attributes, record_info(fields, user_permission)}]}, {vhost, [{disc_copies, [node()]}, {attributes, record_info(fields, vhost)}]}, {rabbit_config, [{disc_copies, [node()]}]}, diff --git a/src/rabbit_tests.erl b/src/rabbit_tests.erl index df2e71d9e6..ef390e4de6 100644 --- a/src/rabbit_tests.erl +++ b/src/rabbit_tests.erl @@ -444,17 +444,18 @@ test_user_management() -> {error, {no_such_vhost, _}} = control_action(delete_vhost, ["/testhost"]), {error, {no_such_user, _}} = - control_action(map_user_vhost, ["foo", "/"]), + control_action(set_permissions, ["foo", ".*", ".*"]), {error, {no_such_user, _}} = - control_action(unmap_user_vhost, ["foo", "/"]), + control_action(clear_permissions, ["foo"]), {error, {no_such_user, _}} = - control_action(list_user_vhosts, ["foo"]), + control_action(list_user_permissions, ["foo"]), {error, {no_such_vhost, _}} = - control_action(map_user_vhost, ["guest", "/testhost"]), - {error, {no_such_vhost, _}} = - control_action(unmap_user_vhost, ["guest", "/testhost"]), - {error, {no_such_vhost, _}} = - control_action(list_vhost_users, ["/testhost"]), + control_action(list_permissions, ["-p", "/testhost"]), + {error, {invalid_regexp, _, _}} = + control_action(set_permissions, ["guest", "+foo", ".*"]), + {error, {invalid_regexp, _, _}} = + control_action(set_permissions, ["guest", ".*", "+foo"]), + %% user creation ok = control_action(add_user, ["foo", "bar"]), {error, {user_already_exists, _}} = @@ -469,13 +470,16 @@ test_user_management() -> ok = control_action(list_vhosts, []), %% user/vhost mapping - ok = control_action(map_user_vhost, ["foo", "/testhost"]), - ok = control_action(map_user_vhost, ["foo", "/testhost"]), - ok = control_action(list_user_vhosts, ["foo"]), + ok = control_action(set_permissions, ["-p", "/testhost", + "foo", ".*", ".*"]), + ok = control_action(set_permissions, ["-p", "/testhost", + "foo", ".*", ".*"]), + ok = control_action(list_permissions, ["-p", "/testhost"]), + ok = control_action(list_user_permissions, ["foo"]), %% user/vhost unmapping - ok = control_action(unmap_user_vhost, ["foo", "/testhost"]), - ok = control_action(unmap_user_vhost, ["foo", "/testhost"]), + ok = control_action(clear_permissions, ["-p", "/testhost", "foo"]), + ok = control_action(clear_permissions, ["-p", "/testhost", "foo"]), %% vhost deletion ok = control_action(delete_vhost, ["/testhost"]), @@ -484,7 +488,8 @@ test_user_management() -> %% deleting a populated vhost ok = control_action(add_vhost, ["/testhost"]), - ok = control_action(map_user_vhost, ["foo", "/testhost"]), + ok = control_action(set_permissions, ["-p", "/testhost", + "foo", ".*", ".*"]), ok = control_action(delete_vhost, ["/testhost"]), %% user deletion |
