Skip to content

Commit 638dc59

Browse files
Merge pull request #5456 from rabbitmq/mergify/bp/v3.11.x/pr-5319
HTTP API: allow connections to be listed and closed by username (backport #5319)
2 parents 5bbdbb0 + 8e5f20e commit 638dc59

File tree

5 files changed

+110
-5
lines changed

5 files changed

+110
-5
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,18 @@ <h2>Reference</h2>
331331
DELETEing to provide a reason.
332332
</td>
333333
</tr>
334+
<tr>
335+
<td>X</td>
336+
<td></td>
337+
<td>X</td>
338+
<td></td>
339+
<td class="path">/api/connections/username/<i>username</i></td>
340+
<td>
341+
A list of all open connections for a specific username. Use pagination parameters to filter connections.
342+
DELETEing a resource will close all the connections for a username. Optionally set the
343+
"X-Reason" header when DELETEing to provide a reason.
344+
</td>
345+
</tr>
334346
<tr>
335347
<td>X</td>
336348
<td></td>

deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ dispatcher() ->
114114
{"/vhost-limits/:vhost", rabbit_mgmt_wm_limits, []},
115115
{"/connections", rabbit_mgmt_wm_connections, []},
116116
{"/connections/:connection", rabbit_mgmt_wm_connection, []},
117+
{"/connections/username/:username", rabbit_mgmt_wm_connection_user_name, []},
117118
{"/connections/:connection/channels", rabbit_mgmt_wm_connection_channels, []},
118119
{"/channels", rabbit_mgmt_wm_channels, []},
119120
{"/channels/:channel", rabbit_mgmt_wm_channel, []},

deps/rabbitmq_management/src/rabbit_mgmt_wm_connection.erl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ resource_exists(ReqData, Context) ->
3737
to_json(ReqData, Context) ->
3838
case rabbit_mgmt_util:disable_stats(ReqData) of
3939
false ->
40-
rabbit_mgmt_util:reply(
41-
maps:from_list(rabbit_mgmt_format:strip_pids(conn_stats(ReqData))), ReqData, Context);
40+
ConnStats = conn_stats(ReqData),
41+
ConnStatsWithoutPids = rabbit_mgmt_format:strip_pids(ConnStats),
42+
ReplyData = maps:from_list(ConnStatsWithoutPids),
43+
rabbit_mgmt_util:reply(ReplyData, ReqData, Context);
4244
true ->
43-
rabbit_mgmt_util:reply([{name, rabbit_mgmt_util:id(connection, ReqData)}],
44-
ReqData, Context)
45+
ReplyData = [{name, rabbit_mgmt_util:id(connection, ReqData)}],
46+
rabbit_mgmt_util:reply(ReplyData, ReqData, Context)
4547
end.
4648

4749
delete_resource(ReqData, Context) ->
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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-2022 VMware, Inc. or its affiliates. All rights reserved.
6+
%%
7+
8+
-module(rabbit_mgmt_wm_connection_user_name).
9+
10+
-export([init/2, to_json/2, content_types_provided/2,
11+
is_authorized/2, allowed_methods/2, delete_resource/2]).
12+
-export([variances/2]).
13+
14+
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
15+
-include_lib("rabbit_common/include/rabbit.hrl").
16+
17+
%%--------------------------------------------------------------------
18+
19+
init(Req, _State) ->
20+
{cowboy_rest, rabbit_mgmt_headers:set_common_permission_headers(Req, ?MODULE), #context{}}.
21+
22+
variances(Req, Context) ->
23+
{[<<"accept-encoding">>, <<"origin">>], Req, Context}.
24+
25+
content_types_provided(ReqData, Context) ->
26+
{rabbit_mgmt_util:responder_map(to_json), ReqData, Context}.
27+
28+
allowed_methods(ReqData, Context) ->
29+
{[<<"HEAD">>, <<"GET">>, <<"DELETE">>, <<"OPTIONS">>], ReqData, Context}.
30+
31+
to_json(ReqData, Context) ->
32+
{ok, _Username, UserConns} = list_user_connections(ReqData),
33+
FilteredConns = rabbit_mgmt_util:filter_tracked_conn_list(UserConns, ReqData, Context),
34+
rabbit_mgmt_util:reply_list_or_paginate(FilteredConns, ReqData, Context).
35+
36+
delete_resource(ReqData, Context) ->
37+
delete_resource(list_user_connections(ReqData), ReqData, Context).
38+
39+
delete_resource({ok, _Username, []}, ReqData, Context) ->
40+
{true, ReqData, Context};
41+
delete_resource({ok, Username, UserConns}, ReqData, Context) ->
42+
ok = close_user_connections(UserConns, Username, ReqData),
43+
{true, ReqData, Context}.
44+
45+
is_authorized(ReqData, Context) ->
46+
try
47+
UserConns = list_user_connections(ReqData),
48+
rabbit_mgmt_util:is_authorized_user(ReqData, Context, UserConns)
49+
catch
50+
{error, invalid_range_parameters, Reason} ->
51+
rabbit_mgmt_util:bad_request(iolist_to_binary(Reason), ReqData, Context)
52+
end.
53+
54+
%%--------------------------------------------------------------------
55+
56+
list_user_connections(ReqData) ->
57+
Username = rabbit_mgmt_util:id(username, ReqData),
58+
UserConns = rabbit_connection_tracking:list_of_user(Username),
59+
{ok, Username, UserConns}.
60+
61+
close_user_connections([], _Username, _ReqData) ->
62+
ok;
63+
close_user_connections([Conn | Rest], Username, ReqData) ->
64+
ok = close_user_connection(Conn, Username, ReqData),
65+
close_user_connections(Rest, Username, ReqData).
66+
67+
close_user_connection(#tracked_connection{name = Name, pid = Pid, username = Username, type = Type}, Username, ReqData) when is_pid(Pid) ->
68+
Conn = [{name, Name}, {pid, Pid}, {user, Username}, {type, Type}],
69+
force_close_connection(ReqData, Conn, Pid);
70+
close_user_connection(#tracked_connection{pid = undefined}, _Username, _ReqData) ->
71+
ok;
72+
close_user_connection(UnexpectedConn, Username, _ReqData) ->
73+
rabbit_log:debug("~p Username: ~p", [?MODULE, Username]),
74+
rabbit_log:debug("~p unexpected connection: ~p", [?MODULE, UnexpectedConn]),
75+
ok.
76+
77+
force_close_connection(ReqData, Conn, Pid) ->
78+
Reason = case cowboy_req:header(<<"x-reason">>, ReqData) of
79+
undefined -> "Closed via management plugin";
80+
V -> binary_to_list(V)
81+
end,
82+
case proplists:get_value(type, Conn) of
83+
direct ->
84+
amqp_direct_connection:server_close(Pid, 320, Reason);
85+
network ->
86+
rabbit_networking:close_connection(Pid, Reason);
87+
_ ->
88+
% best effort, this will work for connections to the stream plugin
89+
gen_server:cast(Pid, {shutdown, Reason})
90+
end,
91+
ok.

deps/rabbitmq_management/src/rabbit_mgmt_wm_connections.erl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
-import(rabbit_misc, [pget/2]).
1515

1616
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
17-
-include_lib("rabbit_common/include/rabbit.hrl").
1817

1918
%%--------------------------------------------------------------------
2019

0 commit comments

Comments
 (0)