Skip to content

Commit e17b976

Browse files
Merge pull request rabbitmq#16584 from rabbitmq/mk-minor-management-plugin-refinements
Minor refactoring of two HTTP API areas
2 parents a7548a3 + af0ad06 commit e17b976

6 files changed

Lines changed: 555 additions & 16 deletions

File tree

deps/rabbitmq_management/include/rabbit_mgmt.hrl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@
1818
-define(OAUTH2_BOOTSTRAP_PATH, <<"js/oidc-oauth/bootstrap.js">>).
1919
-define(MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, <<"strict_auth_mechanism">>).
2020
-define(MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, <<"preferred_auth_mechanism">>).
21+
22+
-define(DEFAULT_LOGIN_ERROR_CODE, <<"not_authorised">>).
23+
24+
-define(LOGIN_ERROR_CODES,
25+
[<<"Not_Authorized">>,
26+
?DEFAULT_LOGIN_ERROR_CODE,
27+
<<"invalid_credentials">>,
28+
<<"token_invalid">>,
29+
<<"token_expired">>,
30+
<<"unsupported_mechanism">>]).

deps/rabbitmq_management/src/rabbit_mgmt_login.erl

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@
88
-module(rabbit_mgmt_login).
99

1010
-export([init/2]).
11+
-export([canonical_login_error/1, sanitise_location/1]).
1112

1213
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
1314
-include("rabbit_mgmt.hrl").
1415
-include_lib("kernel/include/logger.hrl").
1516

17+
-define(MAX_LOCATION_LEN, 1024).
18+
1619
%%--------------------------------------------------------------------
1720
%% /api/login endpoint
1821
%%
@@ -118,7 +121,7 @@ handleAccessToken(Req0, AccessToken, State) ->
118121
AccessToken,
119122
Req1, State);
120123
{false, ReqData1, Reason} ->
121-
replyWithError(Reason, ReqData1, State)
124+
reply_with_error(Reason, ReqData1, State)
122125
end.
123126

124127
redirect_to_home_with_cookie(CookiePath, CookieName, CookieValue, Req=#{scheme := Scheme}, State) ->
@@ -143,14 +146,40 @@ get_home_uri(Req0) ->
143146
cowboy_req:uri(Req0, #{path => rabbit_mgmt_util:get_path_prefix() ++ "/", qs => undefined}).
144147

145148

146-
replyWithError(Reason, Req, State) ->
147-
Home = cowboy_req:uri(Req, #{
148-
path => rabbit_mgmt_util:get_path_prefix() ++ "/",
149-
qs => "error=" ++ Reason
149+
reply_with_error(Reason, Req, State) ->
150+
Code = canonical_login_error(Reason),
151+
QS = cow_qs:qs([{<<"error">>, Code}]),
152+
Home = cowboy_req:uri(Req, #{
153+
path => rabbit_mgmt_util:get_path_prefix() ++ "/",
154+
qs => QS
150155
}),
151-
Req2 = cowboy_req:reply(302, #{
152-
<<"Location">> => iolist_to_binary(Home)
153-
}, <<>>, Req),
156+
Location = sanitise_location(iolist_to_binary(Home)),
157+
Req2 = cowboy_req:reply(302, #{<<"Location">> => Location}, <<>>, Req),
154158
{ok, Req2, State}.
155159

156-
160+
-spec canonical_login_error(term()) -> binary().
161+
canonical_login_error(Reason) when is_binary(Reason) ->
162+
case lists:member(Reason, ?LOGIN_ERROR_CODES) of
163+
true -> Reason;
164+
false -> ?DEFAULT_LOGIN_ERROR_CODE
165+
end;
166+
canonical_login_error(Reason) when is_atom(Reason) ->
167+
canonical_login_error(atom_to_binary(Reason, utf8));
168+
canonical_login_error(Reason) when is_list(Reason) ->
169+
try unicode:characters_to_binary(Reason) of
170+
B when is_binary(B) -> canonical_login_error(B);
171+
_ -> ?DEFAULT_LOGIN_ERROR_CODE
172+
catch
173+
error:_ -> ?DEFAULT_LOGIN_ERROR_CODE
174+
end;
175+
canonical_login_error(_) ->
176+
?DEFAULT_LOGIN_ERROR_CODE.
177+
178+
-spec sanitise_location(binary()) -> binary().
179+
sanitise_location(Loc) when byte_size(Loc) > ?MAX_LOCATION_LEN ->
180+
<<"/">>;
181+
sanitise_location(Loc) ->
182+
case binary:match(Loc, [<<"\r">>, <<"\n">>]) of
183+
nomatch -> Loc;
184+
_ -> <<"/">>
185+
end.

deps/rabbitmq_management/src/rabbit_mgmt_util.erl

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
-export([filter_tracked_conn_list/3]).
3232
-export([with_decode/5, decode/1, decode/2, set_resp_header/3,
3333
args/1, read_complete_body/1, read_complete_body_with_limit/2]).
34+
-export([content_disposition_attachment/1]).
3435
-export([reply_list/3, reply_list/5, reply_list/4,
3536
sort_list/2, destination_type/1, reply_list_or_paginate/3
3637
]).
@@ -68,6 +69,7 @@
6869

6970
-define(MAX_RANGE, 500).
7071
-define(MAX_FILTER_LENGTH, 1024).
72+
-define(MAX_ATTACHMENT_FILENAME_LEN, 500).
7173

7274
-record(pagination, {page = undefined, page_size = undefined,
7375
name = undefined, use_regex = undefined}).
@@ -1138,7 +1140,54 @@ filter_tracked_conn_list(List, ReqData, #context{user = #user{username = FilterU
11381140
set_resp_header(K, V, ReqData) ->
11391141
cowboy_req:set_resp_header(K, strip_crlf(V), ReqData).
11401142

1141-
strip_crlf(Str) -> lists:append(string:tokens(Str, "\r\n")).
1143+
strip_crlf(Str) when is_list(Str) ->
1144+
lists:append(string:tokens(Str, "\r\n"));
1145+
strip_crlf(Str) when is_binary(Str) ->
1146+
binary:replace(Str, [<<"\r">>, <<"\n">>], <<>>, [global]).
1147+
1148+
-spec content_disposition_attachment(binary()) -> binary().
1149+
content_disposition_attachment(Filename0) when is_binary(Filename0) ->
1150+
Filename = case byte_size(Filename0) > ?MAX_ATTACHMENT_FILENAME_LEN of
1151+
true -> binary:part(Filename0, 0, ?MAX_ATTACHMENT_FILENAME_LEN);
1152+
false -> Filename0
1153+
end,
1154+
Ascii = ascii_fallback(Filename),
1155+
Encoded = rfc5987_encode(Filename),
1156+
<<"attachment; filename=\"", Ascii/binary,
1157+
"\"; filename*=UTF-8''", Encoded/binary>>.
1158+
1159+
-spec ascii_fallback(binary()) -> binary().
1160+
ascii_fallback(B) ->
1161+
<< <<(safe_byte(C))>> || <<C>> <= B >>.
1162+
1163+
-spec safe_byte(byte()) -> byte().
1164+
safe_byte(C) when C >= $A, C =< $Z -> C;
1165+
safe_byte(C) when C >= $a, C =< $z -> C;
1166+
safe_byte(C) when C >= $0, C =< $9 -> C;
1167+
safe_byte($.) -> $.;
1168+
safe_byte($_) -> $_;
1169+
safe_byte($-) -> $-;
1170+
safe_byte(_) -> $_.
1171+
1172+
%% RFC 5987 percent-encoding. Shares the unreserved alphabet with the
1173+
%% ASCII fallback above so both forms decode to the same character set.
1174+
-spec rfc5987_encode(binary()) -> binary().
1175+
rfc5987_encode(B) ->
1176+
<< <<(rfc5987_byte(C))/binary>> || <<C>> <= B >>.
1177+
1178+
-spec rfc5987_byte(byte()) -> binary().
1179+
rfc5987_byte(C) when C >= $A, C =< $Z -> <<C>>;
1180+
rfc5987_byte(C) when C >= $a, C =< $z -> <<C>>;
1181+
rfc5987_byte(C) when C >= $0, C =< $9 -> <<C>>;
1182+
rfc5987_byte($.) -> <<".">>;
1183+
rfc5987_byte($_) -> <<"_">>;
1184+
rfc5987_byte($-) -> <<"-">>;
1185+
rfc5987_byte(C) ->
1186+
<<"%", (hex_nibble(C bsr 4)), (hex_nibble(C band 16#0F))>>.
1187+
1188+
-spec hex_nibble(0..15) -> byte().
1189+
hex_nibble(N) when N < 10 -> $0 + N;
1190+
hex_nibble(N) -> $A + N - 10.
11421191

11431192
args([]) -> args(#{});
11441193
args(L) -> rabbit_mgmt_format:to_amqp_table(L).

deps/rabbitmq_management/src/rabbit_mgmt_wm_definitions.erl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ all_definitions(ReqData, Context) ->
8585
Result = TopLevelDefsAndMetadata ++ retain_whitelisted(Contents),
8686
ReqData1 = case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of
8787
undefined -> ReqData;
88-
Filename -> rabbit_mgmt_util:set_resp_header(
89-
<<"Content-Disposition">>,
90-
"attachment; filename=" ++
91-
binary_to_list(Filename), ReqData)
88+
Filename ->
89+
Header = rabbit_mgmt_util:content_disposition_attachment(Filename),
90+
rabbit_mgmt_util:set_resp_header(
91+
<<"Content-Disposition">>, Header, ReqData)
9292
end,
9393

9494
rabbit_mgmt_util:reply(Result, ReqData1, Context).
@@ -152,8 +152,9 @@ vhost_definitions(ReqData, VHostName, Context) ->
152152
ReqData1 = case rabbit_mgmt_util:qs_val(<<"download">>, ReqData) of
153153
undefined -> ReqData;
154154
Filename ->
155-
HeaderVal = "attachment; filename=" ++ binary_to_list(Filename),
156-
rabbit_mgmt_util:set_resp_header(<<"Content-Disposition">>, HeaderVal, ReqData)
155+
Header = rabbit_mgmt_util:content_disposition_attachment(Filename),
156+
rabbit_mgmt_util:set_resp_header(
157+
<<"Content-Disposition">>, Header, ReqData)
157158
end,
158159
rabbit_mgmt_util:reply(Result, ReqData1, Context).
159160

0 commit comments

Comments
 (0)