Skip to content

Commit f3f9c36

Browse files
/login endpoint ready to accept login preferences
1 parent cd195a6 commit f3f9c36

File tree

6 files changed

+212
-75
lines changed

6 files changed

+212
-75
lines changed

deps/rabbitmq_management/include/rabbit_mgmt.hrl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,7 @@
1414

1515
-define(MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE, 20000000).
1616

17-
-define(OAUTH2_ACCESS_TOKEN_COOKIE_NAME, <<"access_token">>).
18-
-define(OAUTH2_ACCESS_TOKEN_COOKIE_PATH, <<"js/oidc-oauth/bootstrap.js">>).
17+
-define(OAUTH2_ACCESS_TOKEN, <<"access_token">>).
18+
-define(OAUTH2_BOOTSTRAP_PATH, <<"js/oidc-oauth/bootstrap.js">>).
19+
-define(MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, <<"strict_auth_mechanism">>).
20+
-define(MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, <<"preferred_auth_mechanism">>).

deps/rabbitmq_management/priv/www/js/main.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,6 +1135,9 @@ function update_truncate() {
11351135

11361136
function setup_visibility() {
11371137
$('div.section,div.section-hidden').each(function(_index) {
1138+
if ($(this).hasClass("disable-pref")) {
1139+
return;
1140+
}
11381141
var pref = section_pref(current_template,
11391142
$(this).children('h2').text());
11401143
var show = get_pref(pref);

deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,17 @@ export function oauth_initialize(authSettings) {
218218
"logged_in": false,
219219
"enabled" : authSettings.oauth_enabled,
220220
"resource_servers" : authSettings.resource_servers,
221-
"oauth_disable_basic_auth" : authSettings.oauth_disable_basic_auth
221+
"oauth_disable_basic_auth" : authSettings.oauth_disable_basic_auth,
222222
}
223223
if (!oauth.enabled) return oauth;
224+
224225
if (authSettings.resource_servers.length > 1 || !authSettings.oauth_disable_basic_auth) {
225226
if (authSettings.strict_auth_mechanism) {
226-
oauth = parseAuthMechanism(oauth, "strict_auth_mechanism", authSettings.strict_auth_mechanism);
227+
oauth["strict_auth_mechanism"] = authSettings.strict_auth_mechanism;
227228
}else if (authSettings.preferred_auth_mechanism) {
228-
oauth = parseAuthMechanism(oauth, "preferred_auth_mechanism", authSettings.preferred_auth_mechanism);
229+
oauth["preferred_auth_mechanism"] = authSettings.preferred_auth_mechanism;
229230
}
230231
}
231-
232232
let resource_server = null;
233233

234234
if (oauth.resource_servers.length == 1) {

deps/rabbitmq_management/priv/www/js/tmpl/login_oauth.ejs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020
2121
<b>Login with :</b>
2222
<p/>
23+
<% const OAuth2Visible = (strict_auth_mechanism === undefined || strict_auth_mechanism.type === "oauth2") ||
24+
(preferred_auth_mechanism === undefined || preferred_auth_mechanism === "oauth2"); %>
25+
<% const OAuth2Invisible = (preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type !== "oauth2"); %>
26+
<% const OAuth2Hidden = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type !== "oauth2"); %>
2327
<% const preferredResourceId = preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type === "oauth2" ? preferred_auth_mechanism.resource_id : null; %>
2428
<!-- begin login with oauth2 -->
25-
<div class="section" id="login-with-oauth2">
29+
<% if (!OAuth2Hidden) { %>
30+
<div class="section disable-pref <%= OAuth2Visible ? 'section-visible' : '' %> <%= OAuth2Invisible ? 'section-invisible' : '' %> " id="login-with-oauth2">
2631
<h2>OAuth 2.0</h2>
2732
<div class="hider">
2833
<div class="updatable">
2934
<% if (resource_servers.length == 1 && declared_resource_servers_count == 1) { %>
3035
<button id="login" onclick="oauth_initiateLogin('<%=resource_servers[0].id%>')">Click here to log in</button>
3136
<% } else { %>
3237
<form onsubmit="oauth_initiateLogin(document.getElementById('oauth2-resource').value)">
33-
<label for="oauth2-resource">Resource:</label>
38+
<label for="oauth2-resource">Resource: </label>
3439
<select id="oauth2-resource">
3540
<% for (var i = 0; i < resource_servers.length; i++) { %>
3641
<option value="<%= fmt_string(resource_servers[i].id) %>" <%= (preferredResourceId === resource_servers[i].id) ? 'selected="selected"' : '' %>>
@@ -45,12 +50,17 @@
4550
</div>
4651
</div>
4752
</div>
53+
<% } %>
4854
<!-- end login with oauth2 -->
4955
<% } %>
5056
5157
<!-- begin login with basic auth -->
52-
<% if (!oauth_disable_basic_auth && (strict_auth_mechanism === undefined || strict_auth_mechanism.type === "basic")) { %>
53-
<div class="section-hidden <%= (strict_auth_mechanism != undefined && strict_auth_mechanism.type === 'basic')? 'section-visible' : '' %> " id="login-with-basic-auth">
58+
<% const basicAuthVisible = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type === "basic") ||
59+
(preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type === "basic"); %>
60+
<% const basicAuthInvisible = (strict_auth_mechanism === undefined && preferred_auth_mechanism === undefined || (preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type !== "basic"));%>
61+
<% const basicAuthHidden = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type !== "basic"); %>
62+
<% if (!oauth_disable_basic_auth && !basicAuthHidden) { %>
63+
<div class="section disable-pref <%= basicAuthInvisible ? 'section-invisible' : ''%> <%= basicAuthVisible ? 'section-visible' : ''%> " id="login-with-basic-auth">
5464
<h2>Basic Authentication</h2>
5565
<div class="hider">
5666
<div class="updatable">

deps/rabbitmq_management/src/rabbit_mgmt_login.erl

Lines changed: 122 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,43 +11,140 @@
1111

1212
-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
1313
-include("rabbit_mgmt.hrl").
14+
-include_lib("kernel/include/logger.hrl").
1415

1516
%%--------------------------------------------------------------------
17+
%% /api/login endpoint
18+
%%
19+
%% Scenario 1 : Users come to RabbitMQ management UI with an access token
20+
%% carried in the body's (POST) field called <<"access_token">>.
21+
%% Only for POST method.
22+
%%
23+
%% The endpoint redirects the user to the home page of the management UI with a
24+
%% short-lived cookie with the name <<"access_token">> and targed to the resource/path
25+
%% <<"js/oidc-oauth/bootstrap.js">> if the token is valid and the user is authorized
26+
%% to access the management UI.
27+
%%
28+
%% Scenario 2: Users come to RabbitMQ management UI with one of these fields
29+
%% <<"strict_auth_mechanism">> or <<"preferred_auth_mechanism">> defined via
30+
%% of these methods:
31+
%% - In the body payload
32+
%% - As request parameter (Only available for GET method)
33+
%% - As request header with the prefix <<"x-">>
34+
%%
35+
%% The endpoint redirects the user to the home page of the management UI with a
36+
%% short-lived cookie with the name matching either <<"strict_auth_mechanism">>
37+
%% or <<"preferred_auth_mechanism">> (regardless if the value was set as form
38+
%% field, or request parameter or header) and targeted to the resource/path
39+
%% <<"js/oidc-oauth/bootstrap.js">>.
40+
%%
41+
%% NOTE: The short-lived token is removed once it is read by the module
42+
%% rabbit_mgmt_oauth_bootstrap.erl which attends the resource/path
43+
%% <<"js/oidc-oauth/bootstrap.js">>.
1644

1745
init(Req0, State) ->
1846
login(cowboy_req:method(Req0), Req0, State).
1947

20-
login(<<"POST">>, Req0=#{scheme := Scheme}, State) ->
48+
login(<<"POST">>, Req0, State) ->
2149
{ok, Body, _} = cowboy_req:read_urlencoded_body(Req0),
22-
AccessToken = proplists:get_value(<<"access_token">>, Body),
23-
case rabbit_mgmt_util:is_authorized_user(Req0, #context{}, <<"">>, AccessToken, false) of
24-
{true, Req1, _} ->
25-
CookieSettings = #{
26-
http_only => true,
27-
path => ?OAUTH2_ACCESS_TOKEN_COOKIE_PATH,
28-
max_age => 30,
29-
same_site => strict
30-
},
31-
SetCookie = cowboy_req:set_resp_cookie(?OAUTH2_ACCESS_TOKEN_COOKIE_NAME, AccessToken, Req1,
32-
case Scheme of
33-
<<"https">> -> CookieSettings#{ secure => true};
34-
_ -> CookieSettings
35-
end),
36-
Home = cowboy_req:uri(SetCookie, #{
37-
path => rabbit_mgmt_util:get_path_prefix() ++ "/"
38-
}),
39-
Redirect = cowboy_req:reply(302, #{
40-
<<"Location">> => iolist_to_binary(Home)
41-
}, <<>>, SetCookie),
42-
{ok, Redirect, State};
43-
{false, ReqData1, Reason} ->
44-
replyWithError(Reason, ReqData1, State)
50+
case proplists:get_value(?OAUTH2_ACCESS_TOKEN, Body) of
51+
undefined -> handleStrictOrPreferredAuthMechanism(Req0, Body, State);
52+
AccessToken -> handleAccessToken(Req0, AccessToken, State)
53+
end;
54+
55+
login(<<"GET">>, Req, State) ->
56+
Auth = case rabbit_mgmt_util:qs_val(?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Req) of
57+
undefined ->
58+
case rabbit_mgmt_util:qs_val(?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Req) of
59+
undefined -> undefined;
60+
Val -> validate_auth_mechanism({?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Val})
61+
end;
62+
Val -> validate_auth_mechanism({?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Val})
63+
end,
64+
case Auth of
65+
undefined ->
66+
case rabbit_mgmt_util:qs_val(?OAUTH2_ACCESS_TOKEN, Req) of
67+
undefined ->
68+
{ok, cowboy_req:reply(302, #{<<"Location">> => iolist_to_binary(get_home_uri(Req))},
69+
<<>>, Req), State};
70+
_ -> {ok, cowboy_req:reply(405, Req), State}
71+
end;
72+
{Type, Value} ->
73+
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH, Type, Value, Req, State)
4574
end;
4675

47-
login(_, Req0, State) ->
76+
login(M, Req0, State) ->
4877
%% Method not allowed.
78+
?LOG_ERROR("Method ~p not allowed", [M]),
4979
{ok, cowboy_req:reply(405, Req0), State}.
5080

81+
handleStrictOrPreferredAuthMechanism(Req, Body, State) ->
82+
case validate_auth_mechanism(get_auth_mechanism(Req, Body)) of
83+
undefined ->
84+
{ok, cowboy_req:reply(302, #{<<"Location">> => iolist_to_binary(get_home_uri(Req))},
85+
<<>>, Req), State};
86+
{Type, Value} ->
87+
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH, Type, Value, Req, State)
88+
end.
89+
90+
get_auth_mechanism(Req, Body) ->
91+
case get_param_or_header(?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Req, Body) of
92+
undefined ->
93+
case get_param_or_header(?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Req, Body) of
94+
undefined -> undefined;
95+
Val -> {?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Val}
96+
end;
97+
Val -> {?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Val}
98+
end.
99+
100+
validate_auth_mechanism({_, <<"oauth2:", _Id/binary>>} = Auth) -> Auth;
101+
validate_auth_mechanism({_, <<"basic">>} = Auth) -> Auth;
102+
validate_auth_mechanism({_, _}) -> undefined;
103+
validate_auth_mechanism(_) -> undefined.
104+
105+
get_param_or_header(ParamName, Req, Body) ->
106+
case proplists:get_value(ParamName, Body) of
107+
undefined ->
108+
case rabbit_mgmt_util:qs_val(ParamName, Req) of
109+
undefined -> cowboy_req:header(<<"x-", ParamName/binary>>, Req);
110+
Val -> Val
111+
end;
112+
Val -> Val
113+
end.
114+
115+
handleAccessToken(Req0, AccessToken, State) ->
116+
case rabbit_mgmt_util:is_authorized_user(Req0, #context{}, <<"">>, AccessToken, false) of
117+
{true, Req1, _} ->
118+
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH,
119+
?OAUTH2_ACCESS_TOKEN,
120+
AccessToken,
121+
Req1, State);
122+
{false, ReqData1, Reason} ->
123+
replyWithError(Reason, ReqData1, State)
124+
end.
125+
126+
redirect_to_home_with_cookie(CookiePath, CookieName, CookieValue, Req=#{scheme := Scheme}, State) ->
127+
CookieSettings0 = #{
128+
http_only => true,
129+
path => CookiePath,
130+
max_age => 30,
131+
same_site => strict
132+
},
133+
CookieSettings =
134+
case Scheme of
135+
<<"https">> -> CookieSettings0#{secure => true};
136+
_ -> CookieSettings0
137+
end,
138+
SetCookie = cowboy_req:set_resp_cookie(CookieName, CookieValue, Req, CookieSettings),
139+
Redirect = cowboy_req:reply(302, #{
140+
<<"Location">> => iolist_to_binary(get_home_uri(SetCookie))
141+
}, <<>>, SetCookie),
142+
{ok, Redirect, State}.
143+
144+
get_home_uri(Req0) ->
145+
cowboy_req:uri(Req0, #{path => rabbit_mgmt_util:get_path_prefix() ++ "/", qs => undefined}).
146+
147+
51148
replyWithError(Reason, Req, State) ->
52149
Home = cowboy_req:uri(Req, #{
53150
path => rabbit_mgmt_util:get_path_prefix() ++ "/",

0 commit comments

Comments
 (0)