Skip to content

Commit f9bc22e

Browse files
committed
Add /users/$user/queues endpoint
It can be used to list queues by owner.
1 parent cb9c8ca commit f9bc22e

File tree

4 files changed

+165
-0
lines changed

4 files changed

+165
-0
lines changed

deps/rabbitmq_management/priv/www/api/index.html

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,14 @@ <h2>Reference</h2>
924924
<td class="path">/api/users/<i>user</i>/permissions</td>
925925
<td>A list of all permissions for a given user.</td>
926926
</tr>
927+
<tr>
928+
<td>X</td>
929+
<td></td>
930+
<td></td>
931+
<td></td>
932+
<td class="path">/api/users/<i>user</i>/queues</td>
933+
<td>A list of all queues owned by a given user.</td>
934+
</tr>
927935
<tr>
928936
<td>X</td>
929937
<td></td>

deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ dispatcher() ->
176176
{"/users/:user", rabbit_mgmt_wm_user, []},
177177
{"/users/:user/permissions", rabbit_mgmt_wm_permissions_user, []},
178178
{"/users/:user/topic-permissions", rabbit_mgmt_wm_topic_permissions_user, []},
179+
{"/users/:user/queues", rabbit_mgmt_wm_user_queues, []},
179180
{"/user-limits/:user/:name", rabbit_mgmt_wm_user_limit, []},
180181
{"/user-limits", rabbit_mgmt_wm_user_limits, []},
181182
{"/user-limits/:user", rabbit_mgmt_wm_user_limits, []},
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
%% This Source Code Form is subject to the terms of the Mozilla Public
2+
%% License, v. 2.0. If a copy of the MPL was not distributed with this
3+
%% file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
%%
5+
%% Copyright (c) 2007-2025 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved.
6+
%%
7+
8+
-module(rabbit_mgmt_wm_user_queues).
9+
10+
-export([init/2, to_json/2, content_types_provided/2, is_authorized/2,
11+
resource_exists/2, basic/1]).
12+
-export([variances/2]).
13+
14+
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
15+
16+
-define(BASIC_COLUMNS,
17+
["vhost",
18+
"name",
19+
"node",
20+
"durable",
21+
"auto_delete",
22+
"exclusive",
23+
"owner_pid",
24+
"arguments",
25+
"type",
26+
"pid",
27+
"state"]).
28+
29+
-define(DEFAULT_SORT, ["vhost", "name"]).
30+
31+
%%--------------------------------------------------------------------
32+
33+
init(Req, _InitialState) ->
34+
{cowboy_rest, rabbit_mgmt_headers:set_common_permission_headers(Req, ?MODULE), #context{}}.
35+
36+
variances(Req, Context) ->
37+
{[<<"accept-encoding">>, <<"origin">>], Req, Context}.
38+
39+
content_types_provided(ReqData, Context) ->
40+
{rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
41+
42+
resource_exists(ReqData, Context) ->
43+
%% just checking that the vhost requested exists
44+
{case rabbit_mgmt_util:all_or_one_vhost(ReqData, fun (_) -> [] end) of
45+
vhost_not_found -> false;
46+
_ -> true
47+
end, ReqData, Context}.
48+
49+
to_json(ReqData, Context) ->
50+
try
51+
Basic = basic_owner_and_vhost_filtered(ReqData, Context),
52+
Data = rabbit_mgmt_util:augment_resources(Basic, ?DEFAULT_SORT,
53+
?BASIC_COLUMNS, ReqData,
54+
Context, augment()),
55+
rabbit_mgmt_util:reply(Data, ReqData, Context)
56+
catch
57+
{error, invalid_range_parameters, Reason} ->
58+
rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData,
59+
Context)
60+
end.
61+
62+
is_authorized(ReqData, Context) ->
63+
rabbit_mgmt_util:is_authorized_vhost(ReqData, Context).
64+
65+
%%--------------------------------------------------------------------
66+
%% Exported functions
67+
68+
basic(ReqData) ->
69+
%% rabbit_nodes:list_running/1 is a potentially slow function that performs
70+
%% a cluster wide query with a reasonably long (10s) timeout.
71+
%% TODO: replace with faster approximate function
72+
Running = rabbit_nodes:list_running(),
73+
Ctx = #{running_nodes => Running},
74+
FmtQ = fun (Q) -> rabbit_mgmt_format:queue(Q, Ctx) end,
75+
User = rabbit_mgmt_util:id(user, ReqData),
76+
list_queues(ReqData, Running, FmtQ, FmtQ, User).
77+
78+
list_queues(ReqData, Running, FormatRunningFun, FormatDownFun, User) ->
79+
[begin
80+
Pid = amqqueue:get_pid(Q),
81+
%% only queues whose leader pid is a on a non running node
82+
%% are considered "down", all other states should be passed
83+
%% as they are and the queue type impl will decide how to
84+
%% emit them.
85+
case not rabbit_amqqueue:is_local_to_node_set(Pid, Running) of
86+
false ->
87+
FormatRunningFun(Q);
88+
true ->
89+
FormatDownFun(amqqueue:set_state(Q, down))
90+
end
91+
end || Q <- all_queues(ReqData, User)].
92+
93+
94+
%%--------------------------------------------------------------------
95+
%% Private helpers
96+
97+
augment() ->
98+
fun(Basic, ReqData) ->
99+
case rabbit_mgmt_util:disable_stats(ReqData) of
100+
false ->
101+
Stats = case rabbit_mgmt_util:columns(ReqData) of
102+
all -> basic;
103+
_ -> detailed
104+
end,
105+
rabbit_mgmt_db:augment_queues(Basic,
106+
rabbit_mgmt_util:range_ceil(ReqData),
107+
Stats);
108+
true ->
109+
Basic
110+
end
111+
end.
112+
113+
basic_owner_and_vhost_filtered(ReqData, Context) ->
114+
rabbit_mgmt_util:filter_vhost(basic(ReqData), ReqData, Context).
115+
116+
all_queues(ReqData, User) ->
117+
rabbit_mgmt_util:all_or_one_vhost(ReqData, fun (VHost) -> list_all_for_user(VHost, User) end).
118+
119+
list_all_for_user(VHost, User) ->
120+
All = rabbit_amqqueue:list_all(VHost),
121+
[Q || Q <- All,
122+
maps:get(user, amqqueue:get_options(Q)) =:= User].

deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ all_tests() -> [
174174
queue_pagination_test,
175175
queue_pagination_columns_test,
176176
queues_pagination_permissions_test,
177+
user_queues_test,
177178
samples_range_test,
178179
sorting_test,
179180
format_output_test,
@@ -2948,6 +2949,39 @@ queues_pagination_permissions_test(Config) ->
29482949
http_delete(Config, "/users/non-admin", {group, '2xx'}),
29492950
passed.
29502951

2952+
user_queues_test(Config) ->
2953+
%% "alice" has no access, "bob" and "carlos" have access.
2954+
http_put(Config, "/users/alice", [{password, <<"alice">>},
2955+
{tags, <<>>}], {group, '2xx'}),
2956+
http_put(Config, "/users/bob", [{password, <<"bob">>},
2957+
{tags, <<"management">>}], {group, '2xx'}),
2958+
http_put(Config, "/users/carlos", [{password, <<"carlos">>},
2959+
{tags, <<"management">>}], {group, '2xx'}),
2960+
2961+
Perms = [{configure, <<".*">>},
2962+
{write, <<".*">>},
2963+
{read, <<".*">>}],
2964+
http_put(Config, "/permissions/%2F/bob", Perms, {group, '2xx'}),
2965+
http_put(Config, "/permissions/%2F/carlos", Perms, {group, '2xx'}),
2966+
2967+
QArgs = #{},
2968+
http_put(Config, "/queues/%2F/bobq", QArgs, "bob","bob", {group, '2xx'}),
2969+
http_put(Config, "/queues/%2F/carlosq", QArgs, "carlos","carlos", {group, '2xx'}),
2970+
2971+
http_get(Config, "/users/bob/queues", "alice", "alice", ?NOT_AUTHORISED),
2972+
http_get(Config, "/users/carlos/queues", "alice", "alice", ?NOT_AUTHORISED),
2973+
[#{name := <<"bobq">>}] = http_get(Config, "/users/bob/queues", "bob", "bob", ?OK),
2974+
[#{name := <<"carlosq">>}] = http_get(Config, "/users/carlos/queues", "bob", "bob", ?OK),
2975+
[#{name := <<"bobq">>}] = http_get(Config, "/users/bob/queues", "carlos", "carlos", ?OK),
2976+
[#{name := <<"carlosq">>}] = http_get(Config, "/users/carlos/queues", "carlos", "carlos", ?OK),
2977+
2978+
http_delete(Config, "/queues/%2F/bobq","bob","bob", {group, '2xx'}),
2979+
http_delete(Config, "/queues/%2F/carlosq","carlos","carlos", {group, '2xx'}),
2980+
http_delete(Config, "/users/alice", {group, '2xx'}),
2981+
http_delete(Config, "/users/bob", {group, '2xx'}),
2982+
http_delete(Config, "/users/carlos", {group, '2xx'}),
2983+
passed.
2984+
29512985
samples_range_test(Config) ->
29522986
{Conn, Ch} = open_connection_and_channel(Config),
29532987

0 commit comments

Comments
 (0)