mirror of
https://github.com/hashicorp/vault.git
synced 2026-02-03 20:40:45 -05:00
* override auth form with direct link * add changelog Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
178 lines
9.2 KiB
JavaScript
178 lines
9.2 KiB
JavaScript
/**
|
|
* Copyright IBM Corp. 2016, 2025
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
|
|
import { click, fillIn, waitFor } from '@ember/test-helpers';
|
|
import { fillInLoginFields, formatAuthMounts } from 'vault/tests/helpers/auth/auth-helpers';
|
|
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
|
import { module, test } from 'qunit';
|
|
import { setupMirage } from 'ember-cli-mirage/test-support';
|
|
import { setupRenderingTest } from 'ember-qunit';
|
|
import { setupTotpMfaResponse } from 'vault/tests/helpers/mfa/mfa-helpers';
|
|
import setupTestContext from './setup-test-context';
|
|
import sinon from 'sinon';
|
|
|
|
module('Integration | Component | auth | page | listing visibility', function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
setupMirage(hooks);
|
|
|
|
hooks.beforeEach(function () {
|
|
setupTestContext(this);
|
|
this.api = this.owner.lookup('service:api');
|
|
this.visibleAuthMounts = formatAuthMounts(this.api);
|
|
// extra setup for when the "oidc" is selected and the oidc-jwt component renders
|
|
this.routerStub = sinon.stub(this.owner.lookup('service:router'), 'urlFor').returns('123-example.com');
|
|
});
|
|
|
|
hooks.afterEach(function () {
|
|
this.routerStub.restore();
|
|
});
|
|
|
|
test('it formats and renders tabs if visible auth mounts exist', async function (assert) {
|
|
await this.renderComponent();
|
|
const expectedTabs = [
|
|
{ type: 'userpass', display: 'Userpass' },
|
|
{ type: 'oidc', display: 'OIDC' },
|
|
{ type: 'ldap', display: 'LDAP' },
|
|
];
|
|
|
|
assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist('dropdown does not render');
|
|
// there are 4 mount paths returned in visibleAuthMounts above,
|
|
// but two are of the same type so only expect 3 tabs
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'it groups mount paths by type and renders 3 tabs');
|
|
expectedTabs.forEach((m) => {
|
|
assert.dom(AUTH_FORM.tabBtn(m.type)).exists(`${m.type} renders as a tab`);
|
|
assert.dom(AUTH_FORM.tabBtn(m.type)).hasText(m.display, `${m.type} renders expected display name`);
|
|
});
|
|
assert
|
|
.dom(AUTH_FORM.tabBtn('userpass'))
|
|
.hasAttribute('aria-selected', 'true', 'it selects the first type by default');
|
|
});
|
|
|
|
test('it renders dropdown as alternate view', async function (assert) {
|
|
await this.renderComponent();
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'tabs render by default');
|
|
assert.dom(GENERAL.backButton).doesNotExist();
|
|
await click(GENERAL.button('Sign in with other methods'));
|
|
assert
|
|
.dom(GENERAL.button('Sign in with other methods'))
|
|
.doesNotExist('button disappears after it is clicked');
|
|
assert.dom(GENERAL.selectByAttr('auth type')).hasValue('userpass', 'dropdown has userpass selected');
|
|
assert.dom(AUTH_FORM.advancedSettings).exists('toggle renders even though userpass has visible mounts');
|
|
await click(AUTH_FORM.advancedSettings);
|
|
assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
|
|
assert.dom(GENERAL.inputByAttr('path')).hasValue('', 'it renders empty custom path input');
|
|
|
|
await fillIn(GENERAL.selectByAttr('auth type'), 'oidc');
|
|
assert.dom(AUTH_FORM.advancedSettings).exists('toggle renders even though oidc has a visible mount');
|
|
await click(AUTH_FORM.advancedSettings);
|
|
assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
|
|
assert.dom(GENERAL.inputByAttr('path')).hasValue('', 'it renders empty custom path input');
|
|
await click(GENERAL.backButton);
|
|
assert.dom(GENERAL.backButton).doesNotExist('"Back" button does not render after it is clicked');
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 3 }, 'clicking "Back" renders tabs again');
|
|
assert
|
|
.dom(GENERAL.button('Sign in with other methods'))
|
|
.exists('"Sign in with other methods" renders again');
|
|
});
|
|
|
|
test('it renders tabs for types prefixed with "ns_"', async function (assert) {
|
|
this.visibleAuthMounts = formatAuthMounts(this.api, { 'token/': { type: 'ns_token' } });
|
|
await this.renderComponent();
|
|
assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist('dropdown does not render');
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'it renders 1 tab');
|
|
assert.dom(AUTH_FORM.tabBtn('token')).exists(`token renders as a tab`);
|
|
assert
|
|
.dom(AUTH_FORM.tabBtn('token'))
|
|
.hasAttribute('aria-selected', 'true', 'it selects the first type by default');
|
|
});
|
|
|
|
// integration tests for ?with= query param
|
|
module('with a direct link', function (hooks) {
|
|
hooks.beforeEach(function () {
|
|
// if path exists, the mount has listing_visibility="unauth"
|
|
this.directLinkIsVisibleMount = { path: 'my_oidc/', type: 'oidc' };
|
|
this.directLinkIsJustType = { type: 'okta' };
|
|
});
|
|
|
|
test('it selects type in the dropdown if direct link is just type', async function (assert) {
|
|
this.directLinkData = this.directLinkIsJustType;
|
|
await this.renderComponent();
|
|
assert.dom(AUTH_FORM.tabBtn('okta')).doesNotExist('tab does not render');
|
|
assert.dom(GENERAL.selectByAttr('auth type')).hasValue('okta', 'dropdown has type selected');
|
|
assert.dom(AUTH_FORM.authForm('okta')).exists();
|
|
assert.dom(GENERAL.inputByAttr('username')).exists();
|
|
assert.dom(GENERAL.inputByAttr('password')).exists();
|
|
await click(AUTH_FORM.advancedSettings);
|
|
assert.dom(GENERAL.inputByAttr('path')).exists({ count: 1 });
|
|
assert
|
|
.dom(GENERAL.button('Sign in with other methods'))
|
|
.doesNotExist('"Sign in with other methods" does not render');
|
|
assert.dom(GENERAL.backButton).exists('back button renders because tabs exist for other methods');
|
|
await click(GENERAL.backButton);
|
|
assert
|
|
.dom(AUTH_FORM.tabBtn('userpass'))
|
|
.hasAttribute('aria-selected', 'true', 'first tab is selected on back');
|
|
});
|
|
|
|
test('it renders single method view instead of tabs if direct link includes path', async function (assert) {
|
|
this.directLinkData = this.directLinkIsVisibleMount;
|
|
await this.renderComponent();
|
|
assert.dom(AUTH_FORM.authForm('oidc')).exists;
|
|
assert.dom(AUTH_FORM.tabBtn('oidc')).hasText('OIDC', 'it renders tab for type');
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
|
|
assert.dom(GENERAL.inputByAttr('role')).exists();
|
|
assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
|
|
assert.dom(GENERAL.inputByAttr('path')).hasValue('my_oidc/');
|
|
assert.dom(GENERAL.button('Sign in with other methods')).exists('"Sign in with other methods" renders');
|
|
assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist();
|
|
assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
|
|
assert.dom(GENERAL.backButton).doesNotExist();
|
|
});
|
|
|
|
test('it treats direct link as only mount when multiple mounts are tuned with listing_visibility="unauth"', async function (assert) {
|
|
this.directLinkData = { path: 'userpass2/', type: 'userpass' };
|
|
await this.renderComponent();
|
|
assert.dom(AUTH_FORM.authForm('userpass')).exists;
|
|
assert.dom(AUTH_FORM.tabBtn('userpass')).hasText('Userpass', 'it renders tab for type');
|
|
assert.dom(AUTH_FORM.tabs).exists({ count: 1 }, 'only one tab renders');
|
|
assert.dom(GENERAL.inputByAttr('path')).hasAttribute('type', 'hidden');
|
|
assert.dom(GENERAL.inputByAttr('path')).hasValue('userpass2/');
|
|
assert.dom(GENERAL.button('Sign in with other methods')).exists('"Sign in with other methods" renders');
|
|
assert.dom(GENERAL.selectByAttr('auth type')).doesNotExist();
|
|
assert.dom(AUTH_FORM.advancedSettings).doesNotExist();
|
|
assert.dom(GENERAL.backButton).doesNotExist();
|
|
});
|
|
|
|
test('it prioritizes auth type from canceled mfa instead of direct link for path', async function (assert) {
|
|
assert.expect(1);
|
|
this.directLinkData = this.directLinkIsVisibleMount; // type is "oidc"
|
|
const authType = 'okta'; // set to a type that differs from direct link
|
|
this.server.post(`/auth/okta/login/matilda`, () => setupTotpMfaResponse(authType));
|
|
await this.renderComponent();
|
|
await click(GENERAL.button('Sign in with other methods'));
|
|
await fillIn(AUTH_FORM.selectMethod, authType);
|
|
await fillInLoginFields({ username: 'matilda', password: 'password' });
|
|
await click(GENERAL.submitButton);
|
|
await waitFor('[data-test-mfa-description]'); // wait until MFA validation renders
|
|
await click(GENERAL.cancelButton);
|
|
assert.dom(AUTH_FORM.selectMethod).hasValue(authType, 'Okta is selected in dropdown');
|
|
});
|
|
|
|
test('it prioritizes auth type from canceled mfa instead of direct link with type', async function (assert) {
|
|
assert.expect(1);
|
|
this.directLinkData = this.directLinkIsJustType; // type is "okta"
|
|
const authType = 'userpass'; // set to a type that differs from direct link
|
|
this.server.post(`/auth/userpass/login/matilda`, () => setupTotpMfaResponse(authType));
|
|
await this.renderComponent();
|
|
await fillIn(AUTH_FORM.selectMethod, authType);
|
|
await fillInLoginFields({ username: 'matilda', password: 'password' });
|
|
await click(GENERAL.submitButton);
|
|
await waitFor('[data-test-mfa-description]'); // wait until MFA validation renders
|
|
await click(GENERAL.cancelButton);
|
|
assert.dom(AUTH_FORM.tabBtn('userpass')).hasAttribute('aria-selected', 'true');
|
|
});
|
|
});
|
|
});
|