blob: b774dadd6f4d7fefc23ef2497b4b787005e6791f [file] [log] [blame]
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import { module, test } from 'qunit';
import { visit, currentURL, click, fillIn, findAll, currentRouteName } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { setupMirage } from 'ember-cli-mirage/test-support';
import ENV from 'vault/config/environment';
import authPage from 'vault/tests/pages/auth';
import logout from 'vault/tests/pages/logout';
import { create } from 'ember-cli-page-object';
import { clickTrigger } from 'ember-power-select/test-support/helpers';
import ss from 'vault/tests/pages/components/search-select';
import fm from 'vault/tests/pages/components/flash-message';
import {
OIDC_BASE_URL, // -> '/vault/access/oidc'
SELECTORS,
clearRecord,
overrideCapabilities,
overrideMirageResponse,
ASSIGNMENT_LIST_RESPONSE,
ASSIGNMENT_DATA_RESPONSE,
} from 'vault/tests/helpers/oidc-config';
const searchSelect = create(ss);
const flashMessage = create(fm);
module('Acceptance | oidc-config clients and assignments', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.before(function () {
ENV['ember-cli-mirage'].handler = 'oidcConfig';
});
hooks.beforeEach(async function () {
this.store = await this.owner.lookup('service:store');
return authPage.login();
});
hooks.afterEach(function () {
return logout.visit();
});
hooks.after(function () {
ENV['ember-cli-mirage'].handler = null;
});
test('it renders only allow_all when no assignments are configured', async function (assert) {
assert.expect(3);
//* clear out test state
await clearRecord(this.store, 'oidc/assignment', 'test-assignment');
await visit(OIDC_BASE_URL + '/assignments');
assert.strictEqual(currentURL(), '/vault/access/oidc/assignments');
assert.dom('[data-test-tab="assignments"]').hasClass('active', 'assignments tab is active');
assert
.dom('[data-test-oidc-assignment-linked-block="allow_all"]')
.hasClass('is-disabled', 'renders default allow all assignment and is disabled.');
});
test('it renders empty state when no clients are configured', async function (assert) {
assert.expect(5);
this.server.get('/identity/oidc/client', () => overrideMirageResponse(404));
await visit(OIDC_BASE_URL);
assert.strictEqual(currentURL(), '/vault/access/oidc');
assert.dom('h1.title.is-3').hasText('OIDC Provider');
assert.dom(SELECTORS.oidcHeader).hasText(
`Configure Vault to act as an OIDC identity provider, and offer Vaults various authentication
methods and source of identity to any client applications. Learn more Create your first app`,
'renders call to action header when no clients are configured'
);
assert.dom('[data-test-oidc-landing]').exists('landing page renders when no clients are configured');
assert
.dom(SELECTORS.oidcLandingImg)
.hasAttribute('src', '/ui/images/oidc-landing.png', 'image renders image when no clients configured');
});
test('it creates an assignment inline, creates a client, updates client to limit access, deletes client', async function (assert) {
assert.expect(22);
//* clear out test state
await clearRecord(this.store, 'oidc/client', 'test-app');
await clearRecord(this.store, 'oidc/client', 'my-webapp'); // created by oidc-provider-test
await clearRecord(this.store, 'oidc/assignment', 'assignment-inline');
// create a client with allow all access
await visit(OIDC_BASE_URL + '/clients/create');
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.clients.create',
'navigates to create form'
);
await fillIn('[data-test-input="name"]', 'test-app');
await click('[data-test-toggle-group="More options"]');
// toggle ttls to false, testing it sets correct default duration
await click('[data-test-input="idTokenTtl"]');
await click('[data-test-input="accessTokenTtl"]');
await click(SELECTORS.clientSaveButton);
assert.strictEqual(
flashMessage.latestMessage,
'Successfully created the application test-app.',
'renders success flash upon client creation'
);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.clients.client.details',
'navigates to client details view after save'
);
// assert default values in details view are correct
assert.dom('[data-test-value-div="Assignment"]').hasText('allow_all', 'client allows all assignments');
assert.dom('[data-test-value-div="Type"]').hasText('confidential', 'type defaults to confidential');
assert
.dom('[data-test-value-div="Key"] a')
.hasText('default', 'client uses default key and renders a link');
assert
.dom('[data-test-value-div="Client ID"] [data-test-copy-button]')
.exists('client ID exists and has copy button');
assert
.dom('[data-test-value-div="Client Secret"] [data-test-copy-button]')
.exists('client secret exists and has copy button');
assert
.dom('[data-test-value-div="ID Token TTL"]')
.hasText('1 day', 'ID token ttl toggled off sets default of 24h');
assert
.dom('[data-test-value-div="Access Token TTL"]')
.hasText('1 day', 'access token ttl toggled off sets default of 24h');
// edit client
await click(SELECTORS.clientDetailsTab);
await click(SELECTORS.clientEditButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.clients.client.edit',
'navigates to edit page from details'
);
await fillIn('[data-test-input="redirectUris"] [data-test-string-list-input="0"]', 'some-url.com');
// limit access & create new assignment inline
await click('[data-test-oidc-radio="limited"]');
await clickTrigger();
await fillIn('.ember-power-select-search input', 'assignment-inline');
await searchSelect.options.objectAt(0).click();
await click('[data-test-search-select="entities"] .ember-basic-dropdown-trigger');
await searchSelect.options.objectAt(0).click();
await click('[data-test-search-select="groups"] .ember-basic-dropdown-trigger');
await searchSelect.options.objectAt(0).click();
await click(SELECTORS.assignmentSaveButton);
assert.strictEqual(
flashMessage.latestMessage,
'Successfully created the assignment assignment-inline.',
'renders success flash upon assignment creating'
);
await click(SELECTORS.clientSaveButton);
assert.strictEqual(
flashMessage.latestMessage,
'Successfully updated the application test-app.',
'renders success flash upon client updating'
);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.clients.client.details',
'navigates back to details on update'
);
assert.dom('[data-test-value-div="Redirect URI"]').hasText('some-url.com', 'shows updated attribute');
assert
.dom('[data-test-value-div="Assignment"]')
.hasText('assignment-inline', 'updated to limited assignment');
// edit back to allow_all
await click(SELECTORS.clientEditButton);
assert.dom(SELECTORS.clientSaveButton).hasText('Update', 'form button renders correct text');
await click('[data-test-oidc-radio="allow-all"]');
await click(SELECTORS.clientSaveButton);
assert
.dom('[data-test-value-div="Assignment"]')
.hasText('allow_all', 'client updated to allow all assignments');
// create another client
await visit(OIDC_BASE_URL + '/clients/create');
await fillIn('[data-test-input="name"]', 'app-to-delete');
await click(SELECTORS.clientSaveButton);
// immediately delete client, test transition
await click(SELECTORS.clientDeleteButton);
await click(SELECTORS.confirmActionButton);
assert.strictEqual(
flashMessage.latestMessage,
'Application deleted successfully',
'renders success flash upon deleting client'
);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.clients.index',
'navigates back to list view after delete'
);
// delete last client
await click('[data-test-oidc-client-linked-block]');
assert.strictEqual(currentRouteName(), 'vault.cluster.access.oidc.clients.client.details');
await click(SELECTORS.clientDeleteButton);
await click(SELECTORS.confirmActionButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.index',
'redirects to call to action if only existing client is deleted'
);
//* clean up test state
await clearRecord(this.store, 'oidc/assignment', 'assignment-inline');
});
test('it creates, updates, and deletes an assignment', async function (assert) {
assert.expect(14);
await visit(OIDC_BASE_URL + '/assignments');
//* ensure clean test state
await clearRecord(this.store, 'oidc/assignment', 'test-assignment');
// create a new assignment
await click(SELECTORS.assignmentCreateButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.create',
'navigates to create form'
);
assert.dom('[data-test-oidc-assignment-title]').hasText('Create assignment', 'Form title renders');
await fillIn('[data-test-input="name"]', 'test-assignment');
await click('[data-test-component="search-select"]#entities .ember-basic-dropdown-trigger');
await click('.ember-power-select-option');
await click(SELECTORS.assignmentSaveButton);
assert.strictEqual(
flashMessage.latestMessage,
'Successfully created the assignment test-assignment.',
'renders success flash upon creating the assignment'
);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.assignment.details',
'navigates to the assignments detail view after save'
);
// assert default values in assignment details view are correct
assert.dom('[data-test-value-div="Name"]').hasText('test-assignment');
assert.dom('[data-test-value-div="Entities"]').hasText('test-entity', 'shows the entity name.');
// edit assignment
await click(SELECTORS.assignmentEditButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.assignment.edit',
'navigates to the assignment edit page from details'
);
assert.dom('[data-test-oidc-assignment-title]').hasText('Edit assignment', 'Form title renders');
await click('[data-test-component="search-select"]#groups .ember-basic-dropdown-trigger');
await click('.ember-power-select-option');
assert.dom('[data-test-oidc-assignment-save]').hasText('Update');
await click(SELECTORS.assignmentSaveButton);
assert.strictEqual(
flashMessage.latestMessage,
'Successfully updated the assignment test-assignment.',
'renders success flash upon updating the assignment'
);
assert.dom('[data-test-value-div="Entities"]').hasText('test-entity', 'it still shows the entity name.');
assert.dom('[data-test-value-div="Groups"]').hasText('test-group', 'shows updated group name id.');
// delete the assignment
await click(SELECTORS.assignmentDeleteButton);
await click(SELECTORS.confirmActionButton);
assert.strictEqual(
flashMessage.latestMessage,
'Assignment deleted successfully',
'renders success flash upon deleting assignment'
);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.index',
'navigates back to assignment list view after delete'
);
});
test('it navigates to and from an assignment from the list view', async function (assert) {
assert.expect(6);
this.server.get('/identity/oidc/assignment', () =>
overrideMirageResponse(null, ASSIGNMENT_LIST_RESPONSE)
);
this.server.get('/identity/oidc/assignment/test-assignment', () =>
overrideMirageResponse(null, ASSIGNMENT_DATA_RESPONSE)
);
await visit(OIDC_BASE_URL + '/assignments');
assert
.dom('[data-test-oidc-assignment-linked-block="test-assignment"]')
.exists('displays linked block for test-assignment');
await click(SELECTORS.assignmentCreateButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.create',
'assignments index toolbar navigates to create form'
);
await click(SELECTORS.assignmentCancelButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.index',
'create form navigates back to assignment index on cancel'
);
await click('[data-test-popup-menu-trigger]');
await click('[data-test-oidc-assignment-menu-link="edit"]');
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.assignment.edit',
'linked block popup menu navigates to edit'
);
await click(SELECTORS.assignmentCancelButton);
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.assignment.details',
'edit form navigates back to assignment details on cancel'
);
// navigate to details from index page
await visit('/vault/access/oidc/assignments');
await click('[data-test-popup-menu-trigger]');
await click('[data-test-oidc-assignment-menu-link="details"]');
assert.strictEqual(
currentRouteName(),
'vault.cluster.access.oidc.assignments.assignment.details',
'popup menu navigates to assignment details'
);
});
test('it hides assignment delete and edit when no permission', async function (assert) {
assert.expect(5);
this.server.get('/identity/oidc/assignment', () =>
overrideMirageResponse(null, ASSIGNMENT_LIST_RESPONSE)
);
this.server.get('/identity/oidc/assignment/test-assignment', () =>
overrideMirageResponse(null, ASSIGNMENT_DATA_RESPONSE)
);
this.server.post('/sys/capabilities-self', () =>
overrideCapabilities(OIDC_BASE_URL + '/assignment/test-assignment', ['read'])
);
await visit(OIDC_BASE_URL + '/assignments');
await click('[data-test-oidc-assignment-linked-block="test-assignment"]');
assert
.dom('[data-test-oidc-assignment-title]')
.hasText('test-assignment', 'renders assignment name as title');
assert.dom(SELECTORS.assignmentDetailsTab).hasClass('active', 'details tab is active');
assert.dom(SELECTORS.assignmentDeleteButton).doesNotExist('delete option is hidden');
assert.dom(SELECTORS.assignmentEditButton).doesNotExist('edit button is hidden');
assert.strictEqual(
findAll('[data-test-component="info-table-row"]').length,
3,
'renders all assignment info rows'
);
});
});