UI/OIDC provider fix (#12871)

* Add cluster name to oidc-provider path

* Move oidc-provider route up on router

* Return base url for changelog if no version

* OIDC Provider check on targetRouteName instead of transitionToTargetRoute

* restore dynamic provider segment on route

* Fix redirect after auth issue

* handle permission denied
This commit is contained in:
Chelsea Shaw 2021-10-20 10:38:29 -04:00 committed by GitHub
parent eb6df00992
commit b76d2cd09c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 27 additions and 20 deletions

View File

@ -21,9 +21,6 @@ export default Mixin.create({
transitionToTargetRoute(transition = {}) { transitionToTargetRoute(transition = {}) {
const targetRoute = this.targetRouteName(transition); const targetRoute = this.targetRouteName(transition);
if (OIDC_PROVIDER === this.router.currentRouteName || OIDC_PROVIDER === transition?.to?.name) {
return RSVP.resolve();
}
if ( if (
targetRoute && targetRoute &&
targetRoute !== this.routeName && targetRoute !== this.routeName &&
@ -83,6 +80,9 @@ export default Mixin.create({
return DR_REPLICATION_SECONDARY; return DR_REPLICATION_SECONDARY;
} }
if ((transition && transition.targetName === OIDC_PROVIDER) || this.routeName === OIDC_PROVIDER) {
return OIDC_PROVIDER;
}
if (!isAuthed) { if (!isAuthed) {
if ((transition && transition.targetName === OIDC_CALLBACK) || this.routeName === OIDC_CALLBACK) { if ((transition && transition.targetName === OIDC_CALLBACK) || this.routeName === OIDC_CALLBACK) {
return OIDC_CALLBACK; return OIDC_CALLBACK;

View File

@ -9,6 +9,9 @@ export default class Router extends EmberRouter {
Router.map(function() { Router.map(function() {
this.route('vault', { path: '/' }, function() { this.route('vault', { path: '/' }, function() {
this.route('cluster', { path: '/:cluster_name' }, function() { this.route('cluster', { path: '/:cluster_name' }, function() {
this.route('identity', function() {
this.route('oidc-provider', { path: '/oidc/provider/:provider_name/authorize' });
});
this.route('oidc-callback', { path: '/auth/*auth_path/oidc/callback' }); this.route('oidc-callback', { path: '/auth/*auth_path/oidc/callback' });
this.route('auth'); this.route('auth');
this.route('init'); this.route('init');
@ -139,10 +142,6 @@ Router.map(function() {
} }
this.route('not-found', { path: '/*path' }); this.route('not-found', { path: '/*path' });
this.route('identity', function() {
this.route('oidc-provider', { path: '/oidc/provider/:oidc_name/authorize' });
});
}); });
this.route('not-found', { path: '/*path' }); this.route('not-found', { path: '/*path' });
}); });

View File

@ -13,6 +13,7 @@ export default class VaultClusterIdentityOidcProviderRoute extends Route {
} }
_redirect(url, params) { _redirect(url, params) {
if (!url) return;
let redir = this._buildUrl(url, params); let redir = this._buildUrl(url, params);
this.win.location.replace(redir); this.win.location.replace(redir);
} }
@ -27,23 +28,27 @@ export default class VaultClusterIdentityOidcProviderRoute extends Route {
error: 'login_required', error: 'login_required',
}); });
} else if (!currentToken || 'login' === qp.prompt?.toLowerCase()) { } else if (!currentToken || 'login' === qp.prompt?.toLowerCase()) {
let shouldLogout = !!currentToken;
if ('login' === qp.prompt?.toLowerCase()) { if ('login' === qp.prompt?.toLowerCase()) {
this.auth.deleteCurrentToken(); // need to remove before redirect to avoid infinite loop
qp.prompt = null; qp.prompt = null;
} }
let { cluster_name } = this.paramsFor('vault.cluster'); return this._redirectToAuth(transition.to.params?.provider_name, qp, shouldLogout);
let url = this.router.urlFor(transition.to.name, transition.to.params, { queryParams: qp });
return this.transitionTo(AUTH, cluster_name, { queryParams: { redirect_to: url } });
} }
} }
_redirectToAuth(oidcName, queryParams, logout = false) { _redirectToAuth(provider_name, queryParams, logout = false) {
let { cluster_name } = this.paramsFor('vault.cluster'); let { cluster_name } = this.paramsFor('vault.cluster');
let currentRoute = this.router.urlFor(PROVIDER, oidcName, { queryParams }); let url = this.router.urlFor(PROVIDER, cluster_name, provider_name, { queryParams });
// This is terrible, I'm sorry
// Need to do this because transitionTo (as used in auth-form) expects url without
// rootURL /ui/ at the beginning, but urlFor builds it in. We can't use currentRoute
// because it hasn't transitioned yet
url = url.replace(/^(\/?ui)/, '');
if (logout) { if (logout) {
this.auth.deleteCurrentToken(); this.auth.deleteCurrentToken();
} }
return this.transitionTo(AUTH, cluster_name, { queryParams: { redirect_to: currentRoute } }); return this.transitionTo(AUTH, cluster_name, { queryParams: { redirect_to: url } });
} }
_buildUrl(urlString, params) { _buildUrl(urlString, params) {
@ -72,11 +77,14 @@ export default class VaultClusterIdentityOidcProviderRoute extends Route {
} }
async model(params) { async model(params) {
let { oidc_name, ...qp } = params; let { provider_name, ...qp } = params;
let decodedRedirect = decodeURI(qp.redirect_uri); let decodedRedirect = decodeURI(qp.redirect_uri);
let url = this._buildUrl(`${this.win.origin}/v1/identity/oidc/provider/${oidc_name}/authorize`, qp); let endpoint = this._buildUrl(
`${this.win.origin}/v1/identity/oidc/provider/${provider_name}/authorize`,
qp
);
try { try {
const response = await this.auth.ajax(url, 'GET', {}); const response = await this.auth.ajax(endpoint, 'GET', {});
if ('consent' === qp.prompt?.toLowerCase()) { if ('consent' === qp.prompt?.toLowerCase()) {
return { return {
consent: { consent: {
@ -90,8 +98,8 @@ export default class VaultClusterIdentityOidcProviderRoute extends Route {
} catch (errorRes) { } catch (errorRes) {
let resp = await errorRes.json(); let resp = await errorRes.json();
let code = resp.error; let code = resp.error;
if (code === 'max_age_violation') { if (code === 'max_age_violation' || resp?.errors?.includes('permission denied')) {
this._redirectToAuth(oidc_name, qp, true); this._redirectToAuth(provider_name, qp, true);
} else if (code === 'invalid_redirect_uri') { } else if (code === 'invalid_redirect_uri') {
return { return {
error: { error: {

View File

@ -15,7 +15,7 @@ etc.
export function changelogUrlFor([version]) { export function changelogUrlFor([version]) {
let url = 'https://www.github.com/hashicorp/vault/blob/main/CHANGELOG.md#'; let url = 'https://www.github.com/hashicorp/vault/blob/main/CHANGELOG.md#';
if (!version) return url;
try { try {
// strip the '+prem' from enterprise versions and remove periods // strip the '+prem' from enterprise versions and remove periods
let versionNumber = version let versionNumber = version