vault/ui/tests/acceptance/saml-auth-method-test.js
2025-05-15 17:58:21 -04:00

158 lines
5.8 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { click, fillIn, find, visit, waitUntil } from '@ember/test-helpers';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { Response } from 'miragejs';
import { windowStub } from 'vault/tests/helpers/oidc-window-stub';
import { setupTotpMfaResponse } from 'vault/tests/helpers/mfa/mfa-helpers';
import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
import { MFA_SELECTORS } from 'vault/tests/helpers/mfa/mfa-selectors';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { logout } from 'vault/tests/helpers/auth/auth-helpers';
const DELAY_IN_MS = 500;
module('Acceptance | enterprise saml auth method', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.beforeEach(async function () {
this.openStub = windowStub();
this.server.put('/auth/saml/sso_service_url', () => ({
data: {
sso_service_url: 'test/fake/sso/route', // we aren't actually opening a popup so use a fake url
token_poll_id: '1234',
},
}));
this.server.put('/auth/saml/token', () => ({
auth: { client_token: 'root' },
}));
// ensure clean state
await logout(); // clears local storage
});
hooks.afterEach(function () {
this.openStub.restore();
});
test('it should login with saml when selected from auth methods dropdown', async function (assert) {
assert.expect(1);
this.server.get('/auth/token/lookup-self', (schema, req) => {
assert.true(true, 'request made to auth/token/lookup-self after saml callback');
req.passthrough();
});
// select from dropdown or click auth path tab
await waitUntil(() => find(AUTH_FORM.selectMethod), { timeout: DELAY_IN_MS });
await fillIn(AUTH_FORM.selectMethod, 'saml');
await click(AUTH_FORM.login);
});
test('it should login with saml from listed auth mount tab', async function (assert) {
assert.expect(4);
this.server.get('/sys/internal/ui/mounts', () => ({
data: {
auth: {
'test-path/': { description: '', options: {}, type: 'saml' },
},
},
}));
this.server.put('/auth/test-path/sso_service_url', () => {
assert.ok(true, 'role request made to correct non-standard mount path');
return {
data: {
sso_service_url: 'test/fake/sso/route',
token_poll_id: '1234',
},
};
});
this.server.put('/auth/test-path/token', () => {
assert.ok(true, 'login request made to correct non-standard mount path');
return {
auth: { client_token: 'root' },
};
});
this.server.get('/auth/token/lookup-self', (schema, req) => {
assert.ok(true, 'request made to auth/token/lookup-self after oidc callback');
assert.deepEqual(
req.requestHeaders,
{ 'X-Vault-Token': 'root' },
'calls lookup-self with returned client token after login'
);
req.passthrough();
});
await logout(); // clear local storage and refresh route so sys/internal/ui/mounts is reliably called
// click auth path tab
await waitUntil(() => find(AUTH_FORM.tabBtn('saml')), { timeout: DELAY_IN_MS });
await click(AUTH_FORM.login);
});
test('it should render API errors from sso_service_url', async function (assert) {
assert.expect(1);
this.server.put('/auth/saml/sso_service_url', () => {
return new Response(
400,
{ 'Content-Type': 'application/json' },
JSON.stringify({ errors: ["missing required 'role' parameter"] })
);
});
// select saml auth type
await waitUntil(() => find(AUTH_FORM.selectMethod), { timeout: DELAY_IN_MS });
await fillIn(AUTH_FORM.selectMethod, 'saml');
await click(AUTH_FORM.login);
assert
.dom('[data-test-message-error-description]')
.hasText("Authentication failed: missing required 'role' parameter", 'shows API error from role fetch');
});
test('it should render API errors from saml token login url', async function (assert) {
assert.expect(1);
this.server.put('/auth/saml/token', () => {
return new Response(
400,
{ 'Content-Type': 'application/json' },
JSON.stringify({ errors: ['something went wrong'] })
);
});
// select saml auth type
await waitUntil(() => find(AUTH_FORM.selectMethod), { timeout: DELAY_IN_MS });
await fillIn(AUTH_FORM.selectMethod, 'saml');
await click(AUTH_FORM.login);
assert
.dom('[data-test-message-error-description]')
.hasText('Authentication failed: something went wrong', 'shows API error from login attempt');
});
test('it should populate saml auth method on logout', async function (assert) {
await visit('/vault/logout');
// select from dropdown
await waitUntil(() => find(AUTH_FORM.selectMethod), { timeout: DELAY_IN_MS });
await fillIn(AUTH_FORM.selectMethod, 'saml');
await click(AUTH_FORM.login);
await waitUntil(() => find(GENERAL.testButton('user-menu-trigger')), { timeout: DELAY_IN_MS });
await click(GENERAL.testButton('user-menu-trigger'));
await click('#logout');
assert.dom(AUTH_FORM.selectMethod).hasValue('saml', 'Previous auth method selected on logout');
});
test('it prompts mfa if configured', async function (assert) {
assert.expect(1);
this.server.put('/auth/saml/token', () => setupTotpMfaResponse('saml'));
await waitUntil(() => find(AUTH_FORM.selectMethod), { timeout: DELAY_IN_MS });
await fillIn(AUTH_FORM.selectMethod, 'saml');
await click(AUTH_FORM.login);
await waitUntil(() => find(MFA_SELECTORS.mfaForm), { timeout: DELAY_IN_MS });
assert.dom(MFA_SELECTORS.mfaForm).exists('it renders TOTP MFA form');
});
});