Skip to content

Commit 2a6a25e

Browse files
Merge pull request #14607 from rabbitmq/mergify/bp/v4.2.x/pr-14545
Check for protected tag during user update and deletion via management API (backport #14545)
2 parents d6755e4 + 7cc9c07 commit 2a6a25e

File tree

4 files changed

+54
-10
lines changed

4 files changed

+54
-10
lines changed

deps/rabbitmq_management/src/rabbit_mgmt_util.erl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353

5454
-export([set_resp_not_found/2]).
5555

56+
-export([is_protected_user/1]).
57+
5658
-import(rabbit_misc, [pget/2]).
5759

5860
-include("rabbit_mgmt.hrl").
@@ -208,6 +210,14 @@ is_authorized_user(ReqData, Context, Username, Password, ReplyWhenFailed) ->
208210
ReplyWhenFailed,
209211
auth_config()).
210212

213+
is_protected_user(Username) ->
214+
case rabbit_auth_backend_internal:lookup_user(Username) of
215+
{ok, User} ->
216+
Tags = internal_user:get_tags(User),
217+
rabbit_web_dispatch_access_control:is_protected_user(Tags);
218+
{error, _} -> false
219+
end.
220+
211221
vhost_from_headers(ReqData) ->
212222
rabbit_web_dispatch_access_control:vhost_from_headers(ReqData).
213223

deps/rabbitmq_management/src/rabbit_mgmt_wm_user.erl

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,31 @@ to_json(ReqData, Context) ->
4646

4747
accept_content(ReqData0, Context = #context{user = #user{username = ActingUser}}) ->
4848
Username = rabbit_mgmt_util:id(user, ReqData0),
49-
rabbit_mgmt_util:with_decode(
50-
[], ReqData0, Context,
51-
fun(_, User, ReqData) ->
52-
_ = put_user(User#{name => Username}, ActingUser),
53-
{true, ReqData, Context}
54-
end).
49+
case rabbit_mgmt_util:is_protected_user(Username) of
50+
true ->
51+
rabbit_mgmt_util:bad_request(
52+
<<"User updates via API are disabled for this user">>,
53+
ReqData0, Context);
54+
false ->
55+
rabbit_mgmt_util:with_decode(
56+
[], ReqData0, Context,
57+
fun(_, User, ReqData) ->
58+
_ = put_user(User#{name => Username}, ActingUser),
59+
{true, ReqData, Context}
60+
end)
61+
end.
5562

5663
delete_resource(ReqData, Context = #context{user = #user{username = ActingUser}}) ->
5764
User = rabbit_mgmt_util:id(user, ReqData),
58-
rabbit_auth_backend_internal:delete_user(User, ActingUser),
59-
{true, ReqData, Context}.
65+
case rabbit_mgmt_util:is_protected_user(User) of
66+
true ->
67+
rabbit_mgmt_util:bad_request(
68+
<<"User deletion via API is disabled for this user">>,
69+
ReqData, Context);
70+
false ->
71+
rabbit_auth_backend_internal:delete_user(User, ActingUser),
72+
{true, ReqData, Context}
73+
end.
6074

6175
is_authorized(ReqData, Context) ->
6276
rabbit_mgmt_util:is_authorized_admin(ReqData, Context).

deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ groups() ->
7777
some_tests() ->
7878
[
7979
users_test,
80+
users_protected_test,
8081
exchanges_test,
8182
queues_test,
8283
bindings_test,
@@ -657,6 +658,21 @@ users_test(Config) ->
657658
http_get(Config, "/users/users_test", ?NOT_FOUND),
658659
passed.
659660

661+
users_protected_test(Config) ->
662+
ProtectedUser = <<"protected_user">>,
663+
rabbit_ct_broker_helpers:add_user(Config, ProtectedUser),
664+
rabbit_ct_broker_helpers:set_user_tags(Config, 0, ProtectedUser, [management, protected]),
665+
666+
%% Verify protected user cannot be updated via API
667+
http_put(Config, "/users/protected_user", [{password, <<"new_password">>},
668+
{tags, <<"management,protected">>}], ?BAD_REQUEST),
669+
670+
%% Verify protected user cannot be deleted via API
671+
http_delete(Config, "/users/protected_user", ?BAD_REQUEST),
672+
673+
rabbit_ct_broker_helpers:delete_user(Config, ProtectedUser),
674+
passed.
675+
660676
without_permissions_users_test(Config) ->
661677
assert_item(#{name => <<"guest">>, tags => [<<"administrator">>]},
662678
http_get(Config, "/whoami")),

deps/rabbitmq_web_dispatch/src/rabbit_web_dispatch_access_control.erl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
-export([id/2]).
2525
-export([not_authorised/3, halt_response/5]).
2626

27-
-export([is_admin/1, is_policymaker/1, is_monitor/1, is_mgmt_user/1]).
27+
-export([is_admin/1, is_policymaker/1, is_monitor/1, is_mgmt_user/1, is_protected_user/1]).
2828

2929
-import(rabbit_misc, [pget/2]).
3030

@@ -243,8 +243,12 @@ is_policymaker(T) -> intersects(T, [administrator, policymaker]).
243243
is_monitor(T) -> intersects(T, [administrator, monitoring]).
244244
is_mgmt_user(T) -> intersects(T, [administrator, monitoring, policymaker,
245245
management]).
246+
is_protected_user(T) -> intersects(T, [protected]).
246247

247-
intersects(A, B) -> lists:any(fun(I) -> lists:member(I, B) end, A).
248+
intersects(A, [B]) ->
249+
lists:member(B, A);
250+
intersects(A, B) ->
251+
lists:any(fun(I) -> lists:member(I, B) end, A).
248252

249253
user_matches_vhost(ReqData, User) ->
250254
case vhost(ReqData) of

0 commit comments

Comments
 (0)