27bb03bbc0
* adding copyright header * fix fmt and a test
411 lines
16 KiB
JavaScript
411 lines
16 KiB
JavaScript
/**
|
|
* 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, selectChoose } 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,
|
|
SELECTORS,
|
|
CLIENT_LIST_RESPONSE,
|
|
SCOPE_LIST_RESPONSE,
|
|
SCOPE_DATA_RESPONSE,
|
|
PROVIDER_LIST_RESPONSE,
|
|
PROVIDER_DATA_RESPONSE,
|
|
clearRecord,
|
|
overrideCapabilities,
|
|
overrideMirageResponse,
|
|
} from 'vault/tests/helpers/oidc-config';
|
|
const searchSelect = create(ss);
|
|
const flashMessage = create(fm);
|
|
|
|
// OIDC_BASE_URL = '/vault/access/oidc'
|
|
|
|
module('Acceptance | oidc-config providers and scopes', 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');
|
|
// mock client list so OIDC BASE URL does not redirect to landing call-to-action image
|
|
this.server.get('/identity/oidc/client', () => overrideMirageResponse(null, CLIENT_LIST_RESPONSE));
|
|
return authPage.login();
|
|
});
|
|
|
|
hooks.afterEach(function () {
|
|
return logout.visit();
|
|
});
|
|
|
|
hooks.after(function () {
|
|
ENV['ember-cli-mirage'].handler = null;
|
|
});
|
|
|
|
// LIST SCOPES EMPTY
|
|
test('it navigates to scopes list view and renders empty state when no scopes are configured', async function (assert) {
|
|
assert.expect(4);
|
|
this.server.get('/identity/oidc/scope', () => overrideMirageResponse(404));
|
|
await visit(OIDC_BASE_URL);
|
|
await click('[data-test-tab="scopes"]');
|
|
assert.strictEqual(currentURL(), '/vault/access/oidc/scopes');
|
|
assert.dom('[data-test-tab="scopes"]').hasClass('active', 'scopes tab is active');
|
|
assert
|
|
.dom(SELECTORS.scopeEmptyState)
|
|
.hasText(
|
|
`No scopes yet Use scope to define identity information about the authenticated user. Learn more. Create scope`,
|
|
'renders empty state no scopes are configured'
|
|
);
|
|
assert
|
|
.dom(SELECTORS.scopeCreateButtonEmptyState)
|
|
.hasAttribute('href', '/ui/vault/access/oidc/scopes/create', 'empty state renders create scope link');
|
|
});
|
|
|
|
// LIST SCOPE EXIST
|
|
test('it renders scope list when scopes exist', async function (assert) {
|
|
assert.expect(11);
|
|
this.server.get('/identity/oidc/scope', () => overrideMirageResponse(null, SCOPE_LIST_RESPONSE));
|
|
this.server.get('/identity/oidc/scope/test-scope', () =>
|
|
overrideMirageResponse(null, SCOPE_DATA_RESPONSE)
|
|
);
|
|
await visit(OIDC_BASE_URL + '/scopes');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.index',
|
|
'redirects to scopes index route when scopes exist'
|
|
);
|
|
assert
|
|
.dom('[data-test-oidc-scope-linked-block="test-scope"]')
|
|
.exists('displays linked block for test scope');
|
|
|
|
// navigates to/from create, edit, detail views from list view
|
|
await click(SELECTORS.scopeCreateButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.create',
|
|
'scope index toolbar navigates to create form'
|
|
);
|
|
await click(SELECTORS.scopeCancelButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.index',
|
|
'create form navigates back to index on cancel'
|
|
);
|
|
|
|
await click('[data-test-popup-menu-trigger]');
|
|
await click('[data-test-oidc-scope-menu-link="edit"]');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.edit',
|
|
'linked block popup menu navigates to edit'
|
|
);
|
|
await click(SELECTORS.scopeCancelButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.details',
|
|
'scope edit form navigates back to details on cancel'
|
|
);
|
|
|
|
// navigate to details from index page
|
|
await click('[data-test-breadcrumb-link="oidc-scopes"]');
|
|
await click('[data-test-popup-menu-trigger]');
|
|
await click('[data-test-oidc-scope-menu-link="details"]');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.details',
|
|
'popup menu navigates to details'
|
|
);
|
|
// check that details tab has all the information
|
|
assert.dom(SELECTORS.scopeDetailsTab).hasClass('active', 'details tab is active');
|
|
assert.dom(SELECTORS.scopeDeleteButton).exists('toolbar renders delete option');
|
|
assert.dom(SELECTORS.scopeEditButton).exists('toolbar renders edit button');
|
|
assert.strictEqual(findAll('[data-test-component="info-table-row"]').length, 2, 'renders all info rows');
|
|
});
|
|
|
|
// ERROR DELETING SCOPE
|
|
test('it throws error when trying to delete when scope is currently being associated with any provider', async function (assert) {
|
|
assert.expect(3);
|
|
this.server.get('/identity/oidc/scope', () => overrideMirageResponse(null, SCOPE_LIST_RESPONSE));
|
|
this.server.get('/identity/oidc/scope/test-scope', () =>
|
|
overrideMirageResponse(null, SCOPE_DATA_RESPONSE)
|
|
);
|
|
this.server.get('/identity/oidc/provider', () => overrideMirageResponse(null, PROVIDER_LIST_RESPONSE));
|
|
this.server.get('/identity/oidc/provider/test-provider', () => {
|
|
overrideMirageResponse(null, PROVIDER_DATA_RESPONSE);
|
|
});
|
|
// throw error when trying to delete test-scope since it is associated to test-provider
|
|
this.server.delete(
|
|
'/identity/oidc/scope/test-scope',
|
|
() => ({
|
|
errors: [
|
|
'unable to delete scope "test-scope" because it is currently referenced by these providers: test-provider',
|
|
],
|
|
}),
|
|
400
|
|
);
|
|
await visit(OIDC_BASE_URL + '/scopes');
|
|
await click('[data-test-oidc-scope-linked-block="test-scope"]');
|
|
assert.dom('[data-test-oidc-scope-header]').hasText('test-scope', 'renders scope name');
|
|
assert.dom(SELECTORS.scopeDetailsTab).hasClass('active', 'details tab is active');
|
|
|
|
// try to delete scope
|
|
await click(SELECTORS.scopeDeleteButton);
|
|
await click(SELECTORS.confirmActionButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'unable to delete scope "test-scope" because it is currently referenced by these providers: test-provider',
|
|
'renders error flash upon scope deletion'
|
|
);
|
|
});
|
|
|
|
// CRUD SCOPE + CRUD PROVIDER
|
|
test('it creates a scope, and creates a provider with that scope', async function (assert) {
|
|
assert.expect(28);
|
|
|
|
//* clear out test state
|
|
await clearRecord(this.store, 'oidc/scope', 'test-scope');
|
|
await clearRecord(this.store, 'oidc/provider', 'test-provider');
|
|
|
|
// create a new scope
|
|
await visit(OIDC_BASE_URL + '/scopes/create');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.create',
|
|
'navigates to create form'
|
|
);
|
|
await fillIn('[data-test-input="name"]', 'test-scope');
|
|
await fillIn('[data-test-input="description"]', 'this is a test');
|
|
await fillIn('[data-test-component="code-mirror-modifier"] textarea', SCOPE_DATA_RESPONSE.template);
|
|
await click(SELECTORS.scopeSaveButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Successfully created the scope test-scope.',
|
|
'renders success flash upon scope creation'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.details',
|
|
'navigates to scope detail view after save'
|
|
);
|
|
assert.dom(SELECTORS.scopeDetailsTab).hasClass('active', 'scope details tab is active');
|
|
assert.dom('[data-test-value-div="Name"]').hasText('test-scope', 'has correct created name');
|
|
assert
|
|
.dom('[data-test-value-div="Description"]')
|
|
.hasText('this is a test', 'has correct created description');
|
|
|
|
// edit scope
|
|
await click(SELECTORS.scopeEditButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.edit',
|
|
'navigates to edit page from details'
|
|
);
|
|
await fillIn('[data-test-input="description"]', 'this is an edit test');
|
|
await click(SELECTORS.scopeSaveButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Successfully updated the scope test-scope.',
|
|
'renders success flash upon scope updating'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.scope.details',
|
|
'navigates back to scope details on update'
|
|
);
|
|
assert
|
|
.dom('[data-test-value-div="Description"]')
|
|
.hasText('this is an edit test', 'has correct edited description');
|
|
|
|
// create a provider using test-scope
|
|
await click('[data-test-breadcrumb-link="oidc-scopes"]');
|
|
await click('[data-test-tab="providers"]');
|
|
assert.dom('[data-test-tab="providers"]').hasClass('active', 'providers tab is active');
|
|
await click('[data-test-oidc-provider-create]');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.create',
|
|
'navigates to provider create form'
|
|
);
|
|
await fillIn('[data-test-input="name"]', 'test-provider');
|
|
await clickTrigger('#scopesSupported');
|
|
await selectChoose('#scopesSupported', 'test-scope');
|
|
await click(SELECTORS.providerSaveButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Successfully created the OIDC provider test-provider.',
|
|
'renders success flash upon provider creation'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.provider.details',
|
|
'navigates to provider detail view after save'
|
|
);
|
|
|
|
// assert default values in details view are correct
|
|
assert.dom('[data-test-value-div="Issuer URL"]').hasTextContaining('http://', 'issuer includes scheme');
|
|
assert
|
|
.dom('[data-test-value-div="Issuer URL"]')
|
|
.hasTextContaining('identity/oidc/provider/test', 'issuer path populates correctly');
|
|
assert
|
|
.dom('[data-test-value-div="Scopes"] a')
|
|
.hasAttribute('href', '/ui/vault/access/oidc/scopes/test-scope/details', 'lists scopes as links');
|
|
|
|
// check provider's application list view
|
|
await click(SELECTORS.providerClientsTab);
|
|
assert.strictEqual(
|
|
findAll('[data-test-oidc-client-linked-block]').length,
|
|
2,
|
|
'all applications appear in provider applications tab'
|
|
);
|
|
|
|
// edit and limit applications
|
|
await click(SELECTORS.providerDetailsTab);
|
|
await click(SELECTORS.providerEditButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.provider.edit',
|
|
'navigates to provider edit page from details'
|
|
);
|
|
await click('[data-test-oidc-radio="limited"]');
|
|
await click('[data-test-component="search-select"]#allowedClientIds .ember-basic-dropdown-trigger');
|
|
await fillIn('.ember-power-select-search input', 'test-app');
|
|
await searchSelect.options.objectAt(0).click();
|
|
await click(SELECTORS.providerSaveButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Successfully updated the OIDC provider test-provider.',
|
|
'renders success flash upon provider updating'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.provider.details',
|
|
'navigates back to provider details after updating'
|
|
);
|
|
const providerModel = this.store.peekRecord('oidc/provider', 'test-provider');
|
|
assert.propEqual(
|
|
providerModel.allowedClientIds,
|
|
['whaT7KB0C3iBH1l3rXhd5HPf0n6vXU0s'],
|
|
'provider saves client_id (not id or name) in allowed_client_ids param'
|
|
);
|
|
await click(SELECTORS.providerClientsTab);
|
|
assert
|
|
.dom('[data-test-oidc-client-linked-block]')
|
|
.hasTextContaining('test-app', 'list of applications is just test-app');
|
|
|
|
// edit back to allow all
|
|
await click(SELECTORS.providerDetailsTab);
|
|
await click(SELECTORS.providerEditButton);
|
|
await click('[data-test-oidc-radio="allow-all"]');
|
|
await click(SELECTORS.providerSaveButton);
|
|
await click(SELECTORS.providerClientsTab);
|
|
assert.strictEqual(
|
|
findAll('[data-test-oidc-client-linked-block]').length,
|
|
2,
|
|
'all applications appear in provider applications tab'
|
|
);
|
|
|
|
// delete
|
|
await click(SELECTORS.providerDetailsTab);
|
|
await click(SELECTORS.providerDeleteButton);
|
|
await click(SELECTORS.confirmActionButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Provider deleted successfully',
|
|
'success flash message renders after deleting provider'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.index',
|
|
'navigates back to list view after delete'
|
|
);
|
|
|
|
// delete scope
|
|
await visit(OIDC_BASE_URL + '/scopes/test-scope/details');
|
|
await click(SELECTORS.scopeDeleteButton);
|
|
await click(SELECTORS.confirmActionButton);
|
|
assert.strictEqual(
|
|
flashMessage.latestMessage,
|
|
'Scope deleted successfully',
|
|
'renders success flash upon deleting scope'
|
|
);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.scopes.index',
|
|
'navigates back to list view after delete'
|
|
);
|
|
});
|
|
|
|
// LIST PROVIDERS
|
|
test('it lists default provider and navigates to details', async function (assert) {
|
|
assert.expect(7);
|
|
await visit(OIDC_BASE_URL);
|
|
await click('[data-test-tab="providers"]');
|
|
assert.dom('[data-test-tab="providers"]').hasClass('active', 'providers tab is active');
|
|
assert.strictEqual(currentURL(), '/vault/access/oidc/providers');
|
|
assert
|
|
.dom('[data-test-oidc-provider-linked-block="default"]')
|
|
.exists('index page lists default provider');
|
|
await click('[data-test-popup-menu-trigger]');
|
|
|
|
await click('[data-test-oidc-provider-menu-link="edit"]');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.provider.edit',
|
|
'provider linked block popup menu navigates to edit'
|
|
);
|
|
await click(SELECTORS.providerCancelButton);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.provider.details',
|
|
'provider edit form navigates back to details on cancel'
|
|
);
|
|
|
|
// navigate to details from index page
|
|
await click('[data-test-breadcrumb-link="oidc-providers"]');
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.access.oidc.providers.index',
|
|
'providers breadcrumb navigates back to list view'
|
|
);
|
|
await click('[data-test-oidc-provider-linked-block="default"] [data-test-popup-menu-trigger]');
|
|
await click('[data-test-oidc-provider-menu-link="details"]');
|
|
assert.dom(SELECTORS.providerDeleteButton).isDisabled('delete button is disabled for default provider');
|
|
});
|
|
|
|
// PROVIDER DELETE + EDIT PERMISSIONS
|
|
test('it hides delete and edit for a provider when no permission', async function (assert) {
|
|
assert.expect(3);
|
|
this.server.get('/identity/oidc/providers', () =>
|
|
overrideMirageResponse(null, { providers: ['test-provider'] })
|
|
);
|
|
this.server.get('/identity/oidc/provider/test-provider', () =>
|
|
overrideMirageResponse(null, {
|
|
allowed_client_ids: ['*'],
|
|
issuer: 'http://127.0.0.1:8200/v1/identity/oidc/provider/test-provider',
|
|
scopes_supported: ['test-scope'],
|
|
})
|
|
);
|
|
this.server.post('/sys/capabilities-self', () =>
|
|
overrideCapabilities(OIDC_BASE_URL + '/provider/test-provider', ['read'])
|
|
);
|
|
|
|
await visit(OIDC_BASE_URL + '/providers');
|
|
await click('[data-test-oidc-provider-linked-block]');
|
|
assert.dom(SELECTORS.providerDetailsTab).hasClass('active', 'details tab is active');
|
|
assert.dom(SELECTORS.providerDeleteButton).doesNotExist('delete option is hidden');
|
|
assert.dom(SELECTORS.providerEditButton).doesNotExist('edit button is hidden');
|
|
});
|
|
});
|