WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions deps/rabbitmq_management/include/rabbit_mgmt.hrl
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@

-define(MANAGEMENT_DEFAULT_HTTP_MAX_BODY_SIZE, 20000000).

-define(OAUTH2_ACCESS_TOKEN_COOKIE_NAME, <<"access_token">>).
-define(OAUTH2_ACCESS_TOKEN_COOKIE_PATH, <<"js/oidc-oauth/bootstrap.js">>).
-define(OAUTH2_ACCESS_TOKEN, <<"access_token">>).
-define(OAUTH2_BOOTSTRAP_PATH, <<"js/oidc-oauth/bootstrap.js">>).
-define(MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, <<"strict_auth_mechanism">>).
-define(MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, <<"preferred_auth_mechanism">>).
17 changes: 11 additions & 6 deletions deps/rabbitmq_management/priv/www/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,14 @@ function startWithOAuthLogin (oauth) {
}
}
function render_login_oauth(oauth, messages) {
let formatData = {}
formatData.warnings = []
formatData.notAuthorized = false
formatData.resource_servers = oauth.resource_servers
formatData.declared_resource_servers_count = oauth.declared_resource_servers_count
formatData.oauth_disable_basic_auth = oauth.oauth_disable_basic_auth
let formatData = {};
formatData.warnings = [];
formatData.notAuthorized = false;
formatData.resource_servers = oauth.resource_servers;
formatData.declared_resource_servers_count = oauth.declared_resource_servers_count;
formatData.oauth_disable_basic_auth = oauth.oauth_disable_basic_auth;
formatData.strict_auth_mechanism = oauth.strict_auth_mechanism;
formatData.preferred_auth_mechanism = oauth.preferred_auth_mechanism;

if (Array.isArray(messages)) {
formatData.warnings = messages
Expand Down Expand Up @@ -1133,6 +1135,9 @@ function update_truncate() {

function setup_visibility() {
$('div.section,div.section-hidden').each(function(_index) {
if ($(this).hasClass("disable-pref")) {
return;
}
var pref = section_pref(current_template,
$(this).children('h2').text());
var show = get_pref(pref);
Expand Down
19 changes: 14 additions & 5 deletions deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,24 @@ function oauth_initialize_user_manager(resource_server) {
});

}

export function oauth_initialize(authSettings) {
authSettings = auth_settings_apply_defaults(authSettings);
let oauth = {
"logged_in": false,
"enabled" : authSettings.oauth_enabled,
"resource_servers" : authSettings.resource_servers,
"oauth_disable_basic_auth" : authSettings.oauth_disable_basic_auth
"oauth_disable_basic_auth" : authSettings.oauth_disable_basic_auth,
}
if (!oauth.enabled) return oauth;


if (authSettings.resource_servers.length > 1 || !authSettings.oauth_disable_basic_auth) {
if (authSettings.strict_auth_mechanism) {
oauth["strict_auth_mechanism"] = authSettings.strict_auth_mechanism;
}else if (authSettings.preferred_auth_mechanism) {
oauth["preferred_auth_mechanism"] = authSettings.preferred_auth_mechanism;
}
}
let resource_server = null;

if (oauth.resource_servers.length == 1) {
Expand Down Expand Up @@ -392,11 +400,12 @@ export function hasAnyResourceServerReady(oauth, onReadyCallback) {
warnings.push(warningMessageOAuthResources(url, notCompliantResources, " not compliant"))
}
}
oauth.declared_resource_servers_count = oauth.resource_servers.length
oauth.declared_resource_servers_count = oauth.resource_servers.length;
oauth.resource_servers = oauth.resource_servers.filter((resource) =>
!notReadyServers.includes(resource.oauth_provider_url) && !notCompliantServers.includes(resource.oauth_provider_url))
!notReadyServers.includes(resource.oauth_provider_url) && !notCompliantServers.includes(resource.oauth_provider_url));
oauth.resource_servers.sort((a, b) => a.index - b.index);

onReadyCallback(oauth, warnings)
onReadyCallback(oauth, warnings)

})
}else {
Expand Down
29 changes: 21 additions & 8 deletions deps/rabbitmq_management/priv/www/js/tmpl/login_oauth.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,33 @@
<% } %>
</div>
<% if (!notAuthorized) { %>
<% if ((typeof resource_servers == 'object' && resource_servers.length == 1) && oauth_disable_basic_auth) { %>
<% if (strict_auth_mechanism !== undefined && strict_auth_mechanism.type === "oauth2") { %>
<button id="login" onclick="oauth_initiateLogin('<%=strict_auth_mechanism.resource_id%>')">Click here to log in</button>
<% } else if ((typeof resource_servers == 'object' && resource_servers.length == 1) && oauth_disable_basic_auth) { %>
<button id="login" onclick="oauth_initiateLogin('<%=resource_servers[0].id%>')">Click here to log in</button>
<% } else if (typeof resource_servers == 'object' && resource_servers.length >= 1) { %>
<% } else if (typeof resource_servers == 'object' && resource_servers.length >= 1 && strict_auth_mechanism == undefined) { %>

<b>Login with :</b>
<p/>
<% const OAuth2Visible = (strict_auth_mechanism === undefined || strict_auth_mechanism.type === "oauth2") ||
(preferred_auth_mechanism === undefined || preferred_auth_mechanism === "oauth2"); %>
<% const OAuth2Invisible = (preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type !== "oauth2"); %>
<% const OAuth2Hidden = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type !== "oauth2"); %>
<% const preferredResourceId = preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type === "oauth2" ? preferred_auth_mechanism.resource_id : null; %>
<!-- begin login with oauth2 -->
<div class="section" id="login-with-oauth2">
<% if (!OAuth2Hidden) { %>
<div class="section disable-pref <%= OAuth2Visible ? 'section-visible' : '' %> <%= OAuth2Invisible ? 'section-invisible' : '' %> " id="login-with-oauth2">
<h2>OAuth 2.0</h2>
<div class="hider">
<div class="updatable">
<% if (resource_servers.length == 1 && declared_resource_servers_count == 1) { %>
<button id="login" onclick="oauth_initiateLogin('<%=resource_servers[0].id%>')">Click here to log in</button>
<% } else { %>
<form onsubmit="oauth_initiateLogin(document.getElementById('oauth2-resource').value)">
<label for="oauth2-resource">Resource:</label>
<label for="oauth2-resource">Resource: </label>
<select id="oauth2-resource">
<% for (var i = 0; i < resource_servers.length; i++) { %>
<option value="<%= fmt_string(resource_servers[i].id) %>">
<option value="<%= fmt_string(resource_servers[i].id) %>" <%= (preferredResourceId === resource_servers[i].id) ? 'selected="selected"' : '' %>>
<%= fmt_string(resource_servers[i].label != null ? resource_servers[i].label : resource_servers[i].id) %>
</option>
<% } %>
Expand All @@ -42,12 +50,17 @@
</div>
</div>
</div>
<% } %>
<!-- end login with oauth2 -->
<% } %>

<!-- begin login with basic auth -->
<% if (!oauth_disable_basic_auth) { %>
<div class="section-hidden" id="login-with-basic-auth">
<!-- begin login with basic auth -->
<% const basicAuthVisible = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type === "basic") ||
(preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type === "basic"); %>
<% const basicAuthInvisible = (strict_auth_mechanism === undefined && preferred_auth_mechanism === undefined || (preferred_auth_mechanism !== undefined && preferred_auth_mechanism.type !== "basic"));%>
<% const basicAuthHidden = (strict_auth_mechanism !== undefined && strict_auth_mechanism.type !== "basic"); %>
<% if (!oauth_disable_basic_auth && !basicAuthHidden) { %>
<div class="section disable-pref <%= basicAuthInvisible ? 'section-invisible' : ''%> <%= basicAuthVisible ? 'section-visible' : ''%> " id="login-with-basic-auth">
<h2>Basic Authentication</h2>
<div class="hider">
<div class="updatable">
Expand Down
145 changes: 120 additions & 25 deletions deps/rabbitmq_management/src/rabbit_mgmt_login.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,43 +11,138 @@

-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl").
-include("rabbit_mgmt.hrl").
-include_lib("kernel/include/logger.hrl").

%%--------------------------------------------------------------------
%% /api/login endpoint
%%
%% Scenario 1 : Users come to RabbitMQ management UI with an access token
%% carried in the body's (POST) field called <<"access_token">>.
%% Only for POST method.
%%
%% The endpoint redirects the user to the home page of the management UI with a
%% short-lived cookie with the name <<"access_token">> and targed to the resource/path
%% <<"js/oidc-oauth/bootstrap.js">> if the token is valid and the user is authorized
%% to access the management UI.
%%
%% Scenario 2: Users come to RabbitMQ management UI with one of these fields
%% <<"strict_auth_mechanism">> or <<"preferred_auth_mechanism">> defined via
%% of these methods:
%% - In the body payload
%% - As request parameter (Only available for GET method)
%% - As request header with the prefix <<"x-">>
%%
%% The endpoint redirects the user to the home page of the management UI with a
%% short-lived cookie with the name matching either <<"strict_auth_mechanism">>
%% or <<"preferred_auth_mechanism">> (regardless if the value was set as form
%% field, or request parameter or header) and targeted to the resource/path
%% <<"js/oidc-oauth/bootstrap.js">>.
%%
%% NOTE: The short-lived token is removed once it is read by the module
%% rabbit_mgmt_oauth_bootstrap.erl which attends the resource/path
%% <<"js/oidc-oauth/bootstrap.js">>.

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

login(<<"POST">>, Req0=#{scheme := Scheme}, State) ->
login(<<"POST">>, Req0, State) ->
{ok, Body, _} = cowboy_req:read_urlencoded_body(Req0),
AccessToken = proplists:get_value(<<"access_token">>, Body),
case rabbit_mgmt_util:is_authorized_user(Req0, #context{}, <<"">>, AccessToken, false) of
{true, Req1, _} ->
CookieSettings = #{
http_only => true,
path => ?OAUTH2_ACCESS_TOKEN_COOKIE_PATH,
max_age => 30,
same_site => strict
},
SetCookie = cowboy_req:set_resp_cookie(?OAUTH2_ACCESS_TOKEN_COOKIE_NAME, AccessToken, Req1,
case Scheme of
<<"https">> -> CookieSettings#{ secure => true};
_ -> CookieSettings
end),
Home = cowboy_req:uri(SetCookie, #{
path => rabbit_mgmt_util:get_path_prefix() ++ "/"
}),
Redirect = cowboy_req:reply(302, #{
<<"Location">> => iolist_to_binary(Home)
}, <<>>, SetCookie),
{ok, Redirect, State};
{false, ReqData1, Reason} ->
replyWithError(Reason, ReqData1, State)
case proplists:get_value(?OAUTH2_ACCESS_TOKEN, Body) of
undefined -> handleStrictOrPreferredAuthMechanism(Req0, Body, State);
AccessToken -> handleAccessToken(Req0, AccessToken, State)
end;

login(<<"GET">>, Req, State) ->
Auth = case rabbit_mgmt_util:qs_val(?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Req) of
undefined ->
case rabbit_mgmt_util:qs_val(?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Req) of
undefined -> undefined;
Val -> validate_auth_mechanism({?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Val})
end;
Val -> validate_auth_mechanism({?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Val})
end,
case Auth of
undefined ->
case rabbit_mgmt_util:qs_val(?OAUTH2_ACCESS_TOKEN, Req) of
undefined ->
{ok, cowboy_req:reply(302, #{<<"Location">> => iolist_to_binary(get_home_uri(Req))},
<<>>, Req), State};
_ -> {ok, cowboy_req:reply(405, Req), State}
end;
{Type, Value} ->
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH, Type, Value, Req, State)
end;

login(_, Req0, State) ->
%% Method not allowed.
{ok, cowboy_req:reply(405, Req0), State}.

handleStrictOrPreferredAuthMechanism(Req, Body, State) ->
case validate_auth_mechanism(get_auth_mechanism(Req, Body)) of
undefined ->
{ok, cowboy_req:reply(302, #{<<"Location">> => iolist_to_binary(get_home_uri(Req))},
<<>>, Req), State};
{Type, Value} ->
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH, Type, Value, Req, State)
end.

get_auth_mechanism(Req, Body) ->
case get_param_or_header(?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Req, Body) of
undefined ->
case get_param_or_header(?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Req, Body) of
undefined -> undefined;
Val -> {?MANAGEMENT_LOGIN_PREFERRED_AUTH_MECHANISM, Val}
end;
Val -> {?MANAGEMENT_LOGIN_STRICT_AUTH_MECHANISM, Val}
end.

validate_auth_mechanism({_, <<"oauth2:", _Id/binary>>} = Auth) -> Auth;
validate_auth_mechanism({_, <<"basic">>} = Auth) -> Auth;
validate_auth_mechanism({_, _}) -> undefined;
validate_auth_mechanism(_) -> undefined.

get_param_or_header(ParamName, Req, Body) ->
case proplists:get_value(ParamName, Body) of
undefined ->
case rabbit_mgmt_util:qs_val(ParamName, Req) of
undefined -> cowboy_req:header(<<"x-", ParamName/binary>>, Req);
Val -> Val
end;
Val -> Val
end.

handleAccessToken(Req0, AccessToken, State) ->
case rabbit_mgmt_util:is_authorized_user(Req0, #context{}, <<"">>, AccessToken, false) of
{true, Req1, _} ->
redirect_to_home_with_cookie(?OAUTH2_BOOTSTRAP_PATH,
?OAUTH2_ACCESS_TOKEN,
AccessToken,
Req1, State);
{false, ReqData1, Reason} ->
replyWithError(Reason, ReqData1, State)
end.

redirect_to_home_with_cookie(CookiePath, CookieName, CookieValue, Req=#{scheme := Scheme}, State) ->
CookieSettings0 = #{
http_only => true,
path => CookiePath,
max_age => 30,
same_site => strict
},
CookieSettings =
case Scheme of
<<"https">> -> CookieSettings0#{secure => true};
_ -> CookieSettings0
end,
SetCookie = cowboy_req:set_resp_cookie(CookieName, CookieValue, Req, CookieSettings),
Redirect = cowboy_req:reply(302, #{
<<"Location">> => iolist_to_binary(get_home_uri(SetCookie))
}, <<>>, SetCookie),
{ok, Redirect, State}.

get_home_uri(Req0) ->
cowboy_req:uri(Req0, #{path => rabbit_mgmt_util:get_path_prefix() ++ "/", qs => undefined}).


replyWithError(Reason, Req, State) ->
Home = cowboy_req:uri(Req, #{
path => rabbit_mgmt_util:get_path_prefix() ++ "/",
Expand Down
Loading
Loading