open-vault/ui/tests/acceptance/oidc-config/providers-scopes-test.js

406 lines
16 KiB
JavaScript

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');
});
});