Skip to content

Commit

Permalink
Implement SP-initiated login
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcialRosales committed Oct 25, 2022
1 parent f4a7d2f commit 5054b87
Show file tree
Hide file tree
Showing 45 changed files with 989 additions and 408 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-selenium.yaml
Expand Up @@ -91,7 +91,7 @@ jobs:
- name: Run Suites
run: |
${SELENIUM_DIR}/run-suites.sh
RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq ${SELENIUM_DIR}/run-suites.sh
- name: Upload Test Artifacts
if: always()
Expand Down
Expand Up @@ -474,6 +474,11 @@ end}.
{mapping, "management.oauth_metadata_url", "rabbitmq_management.oauth_metadata_url",
[{datatype, string}]}.

%% Configure the OAuth 2 type allowed for the end users to logon to the management UI
%% Default type is sp_initiated meaning the standard OAuth 2.0 mode where users come without any token
%% Other type is idp_initiated meaning users must come with a token
{mapping, "management.oauth_initiated_logon_type", "rabbitmq_management.oauth_initiated_logon_type",
[{datatype, {enum, [sp_initiated, idp_initiated]}}]}.

%% ===========================================================================

Expand Down
48 changes: 26 additions & 22 deletions deps/rabbitmq_management/priv/www/index.html
Expand Up @@ -26,29 +26,33 @@
var oauth = oauth_initialize_if_required();

if (oauth.enabled) {
oauth_is_logged_in().then( status => {
if (status.loggedIn && !has_auth_cookie_value()) {
oauth.logged_in = false;
oauth_initiateLogout();
}else {
if (!status.loggedIn) {
replace_content('outer', format('login_oauth', {}));
} else {
oauth.logged_in = true
oauth.access_token = status.user.access_token
oauth.expiryDate = new Date(status.user.expires_at * 1000) // it is epoch in seconds
let current = new Date();
_management_logger.debug("token expires in ",(oauth.expiryDate-current)/1000 ,
"secs at : ", oauth.expiryDate );
oauth.user_name = status.user.profile["user_name"]
if (!oauth.user_name || oauth.user_name == "") oauth.user_name = status.user.profile["sub"]
oauth.scopes = status.user.scope
if (!oauth.sp_initiated) {
oauth.logged_in = has_auth_cookie_value();
oauth.access_token = get_cookie_value('auth');
} else {
oauth_is_logged_in().then( status => {
if (status.loggedIn && !has_auth_cookie_value()) {
oauth.logged_in = false;
oauth_initiateLogout();
} else {
if (!status.loggedIn) {
replace_content('outer', format('login_oauth', {}));
} else {
oauth.logged_in = true;
oauth.access_token = status.user.access_token;
oauth.expiryDate = new Date(status.user.expires_at * 1000); // it is epoch in seconds
let current = new Date();
_management_logger.debug('token expires in ', (oauth.expiryDate-current)/1000,
'secs at : ', oauth.expiryDate );
oauth.user_name = status.user.profile['user_name'];
if (!oauth.user_name || oauth.user_name == '') {
oauth.user_name = status.user.profile['sub'];
}
oauth.scopes = status.user.scope;
}
}
}
});
}else {
replace_content('outer', format('login', {}));
start_app_login();
});
}
}

</script>
Expand Down
196 changes: 115 additions & 81 deletions deps/rabbitmq_management/priv/www/js/main.js
Expand Up @@ -5,31 +5,45 @@ $(document).ready(function() {
var error = url.searchParams.get("error");
if (error) {
renderWarningMessageInLoginStatus(error);
}else {
} else {
if (oauth.enabled) {
if (!oauth.logged_in ) {
get(oauth.readiness_url, "application/json", function(req) {
if (req.status !== 200) {
renderWarningMessageInLoginStatus(oauth.authority + " does not appear to be a running OAuth2.0 instance or may not have a trusted SSL certificate" );
} else {
replace_content('outer', format('login_oauth', {}));
}
});
}else {
start_app_login();
}
startWithOAuthLogin();
} else {
replace_content('outer', format('login', {}));
start_app_login();
startWithLoginPage();
}
}
});

function renderWarningMessageInLoginStatus(message) {
replace_content('outer', format('login_oauth', {}));
replace_content('login-status', '<p class="warning">' + message + '</p> <button id="loginWindow" onclick="oauth_initiateLogin()">Single Sign On</button>');
function startWithLoginPage() {
replace_content('outer', format('login', {}));
start_app_login();
}
function startWithOAuthLogin () {
if (!oauth.logged_in) {
if (oauth.sp_initiated) {
get(oauth.readiness_url, 'application/json', function (req) {
if (req.status !== 200) {
renderWarningMessageInLoginStatus(oauth.authority + ' does not appear to be a running OAuth2.0 instance or may not have a trusted SSL certificate')
} else {
replace_content('outer', format('login_oauth', {}))
start_app_login()
}
})
} else {
replace_content('outer', format('login_oauth', {}))
start_app_login()
}
} else {
start_app_login()
}
}

function renderWarningMessageInLoginStatus (message) {
replace_content('outer', format('login_oauth', {}))
replace_content('login-status', '<p class="warning">' + message + '</p> <button id="loginWindow" onclick="oauth_initiateLogin()">Click here to log in</button>')
}


function dispatcher_add(fun) {
dispatcher_modules.push(fun);
if (dispatcher_modules.length == extension_count) {
Expand Down Expand Up @@ -69,76 +83,89 @@ function getAccessToken() {
return getParameterByName('access_token');
}

function start_app_login() {
//console.log("start_app_login begin");
app = new Sammy.Application(function () {
this.get('#/', function() {});
this.put('#/login', function() {
username = this.params['username'];
password = this.params['password'];
set_auth_pref(username + ':' + password);
check_login();
});
});
if (oauth.enabled) {
var token = oauth.access_token;
if (token != null) {
set_auth_pref(oauth.user_name + ':' + oauth.access_token);
check_login();
} else if(has_auth_cookie_value()) {
check_login();
};
function start_app_login () {
app = new Sammy.Application(function () {
this.get('/', function () {})
this.get('#/', function () {})
if (oauth.enabled && !oauth.sp_initiated) {
this.get('#/login', function() {
oauth.access_token = this.params['access_token'];;
store_cookie_value("auth", oauth.access_token);
if (check_login()) go_to_home();
});
} else {
app.run();
if (get_cookie_value('auth') != null) {
check_login();
}
this.put('#/login', function() {
username = this.params['username'];
password = this.params['password'];
set_auth_pref(username + ':' + password);
check_login();
});
}
})
if (oauth.enabled) {
var token = oauth.access_token
if (token != null) {
if (oauth.sp_initiated) set_auth_pref(oauth.user_name + ':' + oauth.access_token)
else if (has_auth_cookie_value()) set_auth_pref(oauth.access_token)
check_login()
} else if (has_auth_cookie_value()) {
check_login()
} else {
app.run()
}
//console.log("start_app_login end");
} else {
app.run()
if (get_cookie_value('auth') != null) {
check_login()
}
}
}


function check_login() {
user = JSON.parse(sync_get('/whoami'));
if (user == false || user.error) {
// clear a local storage value used by earlier versions
clear_pref('auth');
clear_cookie_value('auth');
if (oauth.enabled) {
renderWarningMessageInLoginStatus("Not authorized");
} else {
replace_content('login-status', '<p>Login failed</p>');
}
}
else {
if (oauth.enabled) {
user.name = oauth.user_name;
// remove once we are able to configure which oauth2 claim can be used as identity
// for now we take the claim
}

hide_popup_warn();
replace_content('outer', format('layout', {}));
var user_login_session_timeout = parseInt(user.login_session_timeout);
// Update auth login_session_timeout if changed
if (has_auth_cookie_value() && !isNaN(user_login_session_timeout) &&
user_login_session_timeout !== get_login_session_timeout()) {
update_login_session_timeout(user_login_session_timeout);
}
setup_global_vars();
setup_constant_events();
update_vhosts();
update_interval();
setup_extensions();
}
}
function print_logging_session_info(user_login_session_timeout) {
function check_login () {
user = JSON.parse(sync_get('/whoami'))
if (user == false || user.error) {
clear_pref('auth')
clear_cookie_value('auth')
if (oauth.enabled) {
hide_popup_warn()
renderWarningMessageInLoginStatus('Not authorized')
} else {
replace_content('login-status', '<p>Login failed</p>')
}
return false
}

if (oauth.enabled) {
if (oauth.user_name) {
user.name = oauth.user_name
}
// remove once we are able to configure which oauth2 claim can be used as identity
// for now we take the claim
}

hide_popup_warn()
replace_content('outer', format('layout', {}))
var user_login_session_timeout = parseInt(user.login_session_timeout)
if (has_auth_cookie_value() && !isNaN(user_login_session_timeout) &&
user_login_session_timeout !== get_login_session_timeout()) {
update_login_session_timeout(user_login_session_timeout)
}
setup_global_vars()
setup_constant_events()
update_vhosts()
update_interval()
setup_extensions()
return true
}

function print_logging_session_info (user_login_session_timeout) {
let var_has_auth_cookie_value = has_auth_cookie_value()
let login_session_timeout = get_login_session_timeout()
console.log("user_login_session_timeout: " + user_login_session_timeout)
console.log("has_auth_cookie_value: " + var_has_auth_cookie_value)
console.log("login_session_timeout: " + login_session_timeout)
console.log("isNaN(user_login_session_timeout): " + isNaN(user_login_session_timeout))
console.log('user_login_session_timeout: ' + user_login_session_timeout)
console.log('has_auth_cookie_value: ' + var_has_auth_cookie_value)
console.log('login_session_timeout: ' + login_session_timeout)
console.log('isNaN(user_login_session_timeout): ' + isNaN(user_login_session_timeout))
}

function get_login_session_timeout() {
Expand Down Expand Up @@ -1324,7 +1351,11 @@ function sync_req(type, params0, path_template, options) {
return false;
}
}

function initiate_logout(error = "") {
clear_pref('auth');
clear_cookie_value('auth');
renderWarningMessageInLoginStatus(error);
}
function check_bad_response(req, full_page_404) {
// 1223 == 204 - see https://www.enhanceie.com/ie/bugs.asp
// MSIE7 and 8 appear to do this in response to HTTP 204.
Expand All @@ -1343,6 +1374,9 @@ function check_bad_response(req, full_page_404) {
if (typeof(error) != 'string') error = JSON.stringify(error);

if (error == 'bad_request' || error == 'not_found' || error == 'not_authorised' || error == 'not_authorized') {
if ((error == 'not_authorised' || error == 'not_authorized') && oauth.enabled) {
initiate_logout(reason);
}
show_popup('warn', fmt_escape_html(reason));
} else if (error == 'page_out_of_range') {
var seconds = 60;
Expand Down
23 changes: 20 additions & 3 deletions deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js
Expand Up @@ -58,6 +58,12 @@ function oauth_initialize(authSettings) {

if (!oauth.enabled) return oauth;

oauth.sp_initiated = true;
if (authSettings.oauth_initiated_logon_type == "idp_initiated") {
oauth.sp_initiated = false;
return oauth;
}

authSettings = auth_settings_apply_defaults(authSettings);

oidcSettings = {
Expand All @@ -77,7 +83,7 @@ function oauth_initialize(authSettings) {
audience: authSettings.oauth_resource_id, // required by oauth0
},
};
if (authSettings.oauth_metadata_url != "") oidcSettings.metadataUrl = authSettings.oauth_metadata_url
if (authSettings.oauth_metadata_url != "") oidcSettings.metadataUrl = authSettings.oauth_metadata_url;

if (authSettings.enable_uaa == true) {
// This is required for old versions of UAA because the newer ones do expose
Expand Down Expand Up @@ -105,7 +111,7 @@ function oauth_initialize(authSettings) {
});
mgr.events.addUserLoaded(function(user) {
oauth.access_token = user.access_token;
});
});

return oauth;
}
Expand Down Expand Up @@ -133,15 +139,22 @@ function oauth_is_logged_in() {
});
}
function oauth_initiateLogin() {
if (oauth.sp_initiated) {
mgr.signinRedirect({ state: { } }).then(function() {
_management_logger.debug("signinRedirect done");
}).catch(function(err) {
_management_logger.error(err);
});
})
} else {
location.href = oauth.authority;
}
}

function oauth_redirectToHome(oauth) {
set_auth_pref(oauth.user_name + ":" + oauth.access_token);
go_to_home();
}
function go_to_home() {
location.href = rabbit_path_prefix + "/"
}
function oauth_redirectToLogin(error) {
Expand All @@ -159,7 +172,11 @@ function oauth_completeLogin() {
}

function oauth_initiateLogout() {
if (oauth.sp_initiated) {
mgr.signoutRedirect();
} else {
go_to_home();
}
}
function oauth_completeLogout() {
mgr.signoutRedirectCallback().then(_ => oauth_redirectToLogin());
Expand Down
5 changes: 5 additions & 0 deletions deps/rabbitmq_management/selenium/README.md
Expand Up @@ -108,3 +108,8 @@ Or to run a single tests under the suite:
```
make test TEST=landing.js
```

**IMPORTANT**: `make start-rabbitmq` will always load `test/oauth/rabbitmq-localhost.config`.
Therefore, before you run the test make sure it has the appropriate settings for the
test case. When we run in headless mode, we do not have such a problem because every scenario
picks up its own dedicated rabbitmq configuration file which does not rely on localhost.

0 comments on commit 5054b87

Please sign in to comment.