mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
* UI: Implement overview page for KV v2 (#28162) * build json editor patch form * finish patch component and tests * add tab to each route * and path route * add overview tab to tests * update overview to use updated_time instead of created_time * redirect relevant secret.details to secret.index * compute secretState in component instead of pass as arg * add capabilities service * add error handling to fetchSubkeys adapter request * add overview tabs to test * add subtext to overview card * remaining redirects in secret edit * remove create new version from popup menu * fix breadcrumbs for overview * separate adding capabilities service * add service to kv engine * Revert "separate adding capabilities service" This reverts commit bb70b12ab7dbcde0fbd2d4d81768e5c8b1c420cc. * Revert "add service to kv engine" This reverts commit bfa880535ef7d529d7610936b2c1aae55673d23f. * update navigation test * consistently navigate to secret.index route to be explicit * finish overview navigation tests * add copyright header * update delete tests * fix nav testrs * cleanup secret edit redirects * remove redundant async/awaits * fix create test * edge case tests * secret acceptance tests * final component tests * rename kvSecretDetails external route to kvSecretOverview * add comment * UI: Add patch route and implement Page::Secret::Patch page component (sidebranch) (#28192) * add tab to each route * and path route * add overview tab to tests * update overview to use updated_time instead of created_time * redirect relevant secret.details to secret.index * compute secretState in component instead of pass as arg * add capabilities service * add error handling to fetchSubkeys adapter request * add patch route and put in page component * add patch secret action to subkeys card * fix component name * add patch capability * alphabetize computed capabilities * update links, cleanup selectors * fix more merge conflict stuff * add capabilities test * add models to patch link * add test for patch route * rename external route * add error templates * make notes about enterprise tests, filter one * remove errors, transition (redirect) instead * redirect patch routes * UI: Move fetching secret data to child route (#28198) * remove @secret from metadata details * use metadata model instead of secret in paths page * put delete back into kv/data adapter * grant access in control group test * update metadata route and permissions * remove secret from parent route, only fetch in details route * change more permissions to route perms, add tests * revert overview redirect from list view * wrap model in conditional for perms * remove redundant canReadCustomMetadata check * rename adapter method * handle overview 404 * remove comment * add customMetadata as an arg * update grantAccess in test * make version param easier to follow * VAULT-30494 handle 404 jira * refactor capabilities to return an object * update create tests * add test for default truthy capabilities * remove destroy-all-versions from kv/data adapter * UI: Add enterprise checks (#28215) * add enterprise check for subkey card * add max height and scroll to subkey card * only fetch subkeys if enterprise * remove check in overview * add test * Update ui/tests/integration/components/kv/page/kv-page-overview-test.js * fix test failures (#28222) * add assertion * add optional chaining * create/delete versioned secret in each module * wait for transition * add another waitUntil * UI: Add patch latest version to toolbar (#28223) * add patch latest version action to toolbar * make isPatchAllowed arg all encompassing * no longer need model check * use hash so both promises fire at the same time * add subkeys to policy * Update ui/lib/kv/addon/routes/secret.js * add changelog * small cleanup items! (#28229) * add conditional for enterprise checking tabs * cleanup fetchMultiplePaths method * add test * remove todo comment, ticket created and design wants to hold off * keep transition, update comments * cleanup tests, add index to breadcrumbs * add some test coverage * toggle so value is readable
1534 lines
65 KiB
JavaScript
1534 lines
65 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { module, test } from 'qunit';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { click, currentRouteName, currentURL, findAll, typeIn, visit, waitUntil } from '@ember/test-helpers';
|
|
import { setupApplicationTest } from 'vault/tests/helpers';
|
|
import authPage from 'vault/tests/pages/auth';
|
|
import {
|
|
createPolicyCmd,
|
|
deleteEngineCmd,
|
|
mountEngineCmd,
|
|
runCmd,
|
|
createTokenCmd,
|
|
tokenWithPolicyCmd,
|
|
} from 'vault/tests/helpers/commands';
|
|
import { personas } from 'vault/tests/helpers/kv/policy-generator';
|
|
import {
|
|
addSecretMetadataCmd,
|
|
clearRecords,
|
|
writeSecret,
|
|
writeVersionedSecret,
|
|
} from 'vault/tests/helpers/kv/kv-run-commands';
|
|
import { FORM, PAGE } from 'vault/tests/helpers/kv/kv-selectors';
|
|
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
|
import { setupControlGroup, grantAccess } from 'vault/tests/helpers/control-groups';
|
|
|
|
const secretPath = `my-#:$=?-secret`;
|
|
// This doesn't encode in a normal way, so hardcoding it here until we sort that out
|
|
const secretPathUrlEncoded = `my-%23:$=%3F-secret`;
|
|
// these are rendered individually by each page component, assigning a const here for consistency
|
|
const ALL_TABS = ['Overview', 'Secret', 'Metadata', 'Paths', 'Version History'];
|
|
const navToBackend = async (backend) => {
|
|
await visit(`/vault/secrets`);
|
|
return click(PAGE.backends.link(backend));
|
|
};
|
|
const assertCorrectBreadcrumbs = (assert, expected) => {
|
|
assert.dom(PAGE.breadcrumbs).hasText(expected.join(' '));
|
|
const breadcrumbs = findAll(PAGE.breadcrumb);
|
|
expected.forEach((text, idx) => {
|
|
assert.dom(breadcrumbs[idx]).hasText(text, `position ${idx} breadcrumb includes text ${text}`);
|
|
});
|
|
};
|
|
const assertDetailTabs = (assert, current, hidden = []) => {
|
|
ALL_TABS.forEach((tab) => {
|
|
if (hidden.includes(tab)) {
|
|
assert.dom(PAGE.secretTab(tab)).doesNotExist(`${tab} tab does not render`);
|
|
return;
|
|
}
|
|
assert.dom(PAGE.secretTab(tab)).hasText(tab);
|
|
if (current === tab) {
|
|
assert.dom(PAGE.secretTab(tab)).hasClass('active');
|
|
} else {
|
|
assert.dom(PAGE.secretTab(tab)).doesNotHaveClass('active');
|
|
}
|
|
});
|
|
};
|
|
// patchLatest is only available for enterprise so it's not included here
|
|
const DETAIL_TOOLBARS = ['delete', 'destroy', 'copy', 'versionDropdown', 'createNewVersion'];
|
|
const assertDetailsToolbar = (assert, expected = DETAIL_TOOLBARS) => {
|
|
assert
|
|
.dom(PAGE.toolbarAction)
|
|
.exists({ count: expected.length }, 'correct number of toolbar actions render');
|
|
expected.forEach((toolbar) => {
|
|
assert.dom(PAGE.detail[toolbar]).exists(`${toolbar} action exists`);
|
|
});
|
|
const unexpected = DETAIL_TOOLBARS.filter((t) => !expected.includes(t));
|
|
unexpected.forEach((toolbar) => {
|
|
assert.dom(PAGE.detail[toolbar]).doesNotExist(`${toolbar} action doesNotExist`);
|
|
});
|
|
};
|
|
|
|
const patchRedirectTest = (test, testCase) => {
|
|
// only run this test on enterprise so we are testing permissions specifically and not enterprise vs CE (which also redirects)
|
|
test(`enterprise: patch route redirects for users without permissions (${testCase})`, async function (assert) {
|
|
await visit(`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret`,
|
|
'redirects to index'
|
|
);
|
|
assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.kv.secret.index');
|
|
});
|
|
};
|
|
|
|
/**
|
|
* This test set is for testing the navigation, breadcrumbs, and tabs.
|
|
* Letter(s) in parenthesis at the end are shorthand for the persona,
|
|
* for ease of tracking down specific tests failures from CI
|
|
*/
|
|
module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|
setupApplicationTest(hooks);
|
|
|
|
hooks.beforeEach(async function () {
|
|
const uid = uuidv4();
|
|
this.store = this.owner.lookup('service:store');
|
|
this.version = this.owner.lookup('service:version');
|
|
this.emptyBackend = `kv-empty-${uid}`;
|
|
this.backend = `kv-nav-${uid}`;
|
|
await authPage.login();
|
|
await runCmd(mountEngineCmd('kv-v2', this.emptyBackend), false);
|
|
await runCmd(mountEngineCmd('kv-v2', this.backend), false);
|
|
await writeSecret(this.backend, 'app/nested/secret', 'foo', 'bar');
|
|
await writeVersionedSecret(this.backend, secretPath, 'foo', 'bar', 3);
|
|
await runCmd(addSecretMetadataCmd(this.backend, secretPath, { max_versions: 5, cas_required: true }));
|
|
return;
|
|
});
|
|
|
|
hooks.afterEach(async function () {
|
|
await authPage.login();
|
|
await runCmd(deleteEngineCmd(this.backend));
|
|
await runCmd(deleteEngineCmd(this.emptyBackend));
|
|
return;
|
|
});
|
|
|
|
module('admin persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd(
|
|
tokenWithPolicyCmd('admin', personas.admin(this.backend) + personas.admin(this.emptyBackend))
|
|
);
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('empty backend - breadcrumbs, title, tabs, emptyState (a)', async function (assert) {
|
|
assert.expect(23);
|
|
const backend = this.emptyBackend;
|
|
await navToBackend(backend);
|
|
|
|
// URL correct
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list`, 'lands on secrets list page');
|
|
// CONFIGURATION TAB
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.secretTab('Configuration')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).doesNotHaveClass('active');
|
|
// SECRETS TAB
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('active');
|
|
// Toolbar correct
|
|
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar renders');
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter does not show because no secrets exists.');
|
|
// Page content correct
|
|
assert.dom(PAGE.emptyStateTitle).hasText('No secrets yet');
|
|
assert.dom(PAGE.list.createSecret).hasText('Create secret');
|
|
|
|
// click toolbar CTA
|
|
await click(PAGE.list.createSecret);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/create`),
|
|
`url includes /vault/secrets/${backend}/kv/create`
|
|
);
|
|
|
|
// Click cancel btn
|
|
await click(FORM.cancelBtn);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list`),
|
|
`url includes /vault/secrets/${backend}/kv/list`
|
|
);
|
|
});
|
|
test('can access nested secret (a)', async function (assert) {
|
|
// enterprise has "Patch latest version" in the toolbar which adds an assertion
|
|
const count = this.version.isEnterprise ? 47 : 46;
|
|
assert.expect(count);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.list.filter).hasNoValue('List filter input is empty');
|
|
|
|
// Navigate through list items
|
|
await click(PAGE.list.item('app/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
|
|
|
await click(PAGE.list.item('nested/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/nested/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
|
|
|
await click(PAGE.list.item('secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
|
|
await click(PAGE.secretTab('Secret'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
const expectedToolbar = this.version.isEnterprise
|
|
? [...DETAIL_TOOLBARS, 'patchLatest']
|
|
: DETAIL_TOOLBARS;
|
|
assertDetailsToolbar(assert, expectedToolbar);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('it redirects from LIST, SHOW and EDIT views using old non-engine url to ember engine url (a)', async function (assert) {
|
|
assert.expect(4);
|
|
const backend = this.backend;
|
|
// create with initialKey
|
|
await visit(`/vault/secrets/${backend}/create/test`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/create?initialKey=test`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
// Reported bug, backported fix https://github.com/hashicorp/vault/pull/24281
|
|
// list for directory
|
|
await visit(`/vault/secrets/${backend}/list/app/`);
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list/app/`, `navigates to list`);
|
|
// show for secret
|
|
await visit(`/vault/secrets/${backend}/show/app/nested/secret`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
`navigates to overview`
|
|
);
|
|
// edit for secret
|
|
await visit(`/vault/secrets/${backend}/edit/app/nested/secret`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details/edit?version=1`,
|
|
`navigates to edit`
|
|
);
|
|
});
|
|
test('versioned secret nav, tabs (a)', async function (assert) {
|
|
assert.expect(27);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'navigates to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=3`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'title is correct on detail view');
|
|
assert.dom(PAGE.detail.versionDropdown).hasText('Version 3', 'Version dropdown shows current version');
|
|
assert.dom(PAGE.detail.createNewVersion).hasText('Create new version', 'Create version button shows');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 3 created');
|
|
assert.dom(PAGE.infoRowValue('foo')).exists('renders current data');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details/edit?version=3`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(FORM.versionAlert).doesNotExist('Does not show version alert for current version');
|
|
assert.dom(FORM.inputByAttr('path')).isDisabled();
|
|
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'Goes back to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
await click(PAGE.detail.versionDropdown);
|
|
await click(`${PAGE.detail.version(1)} a`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=1`,
|
|
'Goes to detail view for version 1'
|
|
);
|
|
assert.dom(PAGE.detail.versionDropdown).hasText('Version 1', 'Version dropdown shows selected version');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 1 created');
|
|
assert.dom(PAGE.infoRowValue('key-1')).exists('renders previous data');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details/edit?version=1`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(FORM.inputByAttr('path')).isDisabled();
|
|
assert.dom(FORM.keyInput()).hasValue('key-1', 'pre-populates form with selected version data');
|
|
assert.dom(FORM.maskedValueInput()).hasValue('val-1', 'pre-populates form with selected version data');
|
|
assert.dom(FORM.versionAlert).exists('Shows version alert');
|
|
await click(FORM.cancelBtn);
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`goes to metadata page`
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath);
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('No custom metadata');
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateActions}`)
|
|
.hasText('Add metadata', 'empty state has metadata CTA');
|
|
assert.dom(PAGE.metadata.editBtn).hasText('Edit metadata');
|
|
|
|
await click(PAGE.metadata.editBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata/edit`,
|
|
`goes to metadata edit page`
|
|
);
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`cancel btn goes back to metadata page`
|
|
);
|
|
});
|
|
test('breadcrumbs, tabs & page titles are correct (a)', async function (assert) {
|
|
assert.expect(123);
|
|
// only need to assert hrefs one test, no need for this function to be global
|
|
const assertTabHrefs = (assert, page) => {
|
|
ALL_TABS.forEach((tab) => {
|
|
const baseUrl = `/ui/vault/secrets/${backend}/kv`;
|
|
const hrefs = {
|
|
Overview: `${baseUrl}/${secretPathUrlEncoded}`,
|
|
Secret:
|
|
page === 'Secret'
|
|
? `${baseUrl}/${secretPathUrlEncoded}/details?version=3`
|
|
: `${baseUrl}/${secretPathUrlEncoded}/details`,
|
|
Metadata: `${baseUrl}/${secretPathUrlEncoded}/metadata`,
|
|
Paths: `${baseUrl}/${secretPathUrlEncoded}/paths`,
|
|
'Version History': `${baseUrl}/${secretPathUrlEncoded}/metadata/versions`,
|
|
};
|
|
assert
|
|
.dom(PAGE.secretTab(tab))
|
|
.hasAttribute('href', hrefs[tab], `${tab} tab for page: ${page} has expected href`);
|
|
});
|
|
};
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
|
|
// PAGE COMPONENTS RENDER THEIR OWN TABS, ASSERT EACH HREF ON EACH PAGE
|
|
// overview tab
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.index',
|
|
'navs to overview'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assertDetailTabs(assert, 'Overview');
|
|
assertTabHrefs(assert, 'Overview');
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret overview');
|
|
|
|
// secret tab
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.details.index',
|
|
'navs to details'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assertDetailTabs(assert, 'Secret');
|
|
assertTabHrefs(assert, 'Secret');
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret detail');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.details.edit',
|
|
'navs to create'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Edit']);
|
|
assert.dom(PAGE.title).hasText('Create New Version', 'correct page title for secret edit');
|
|
|
|
// metadata tab
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.metadata.index',
|
|
'navs to metadata'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assertDetailTabs(assert, 'Metadata');
|
|
assertTabHrefs(assert, 'Metadata');
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for metadata');
|
|
|
|
await click(PAGE.metadata.editBtn);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.metadata.edit',
|
|
'navs to metadata.edit'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata', 'Edit']);
|
|
assert.dom(PAGE.title).hasText('Edit Secret Metadata', 'correct page title for metadata edit');
|
|
|
|
// paths tab
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
await click(PAGE.secretTab('Paths'));
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.paths',
|
|
'navs to paths'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Paths']);
|
|
assertDetailTabs(assert, 'Paths');
|
|
assertTabHrefs(assert, 'Paths');
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for paths');
|
|
|
|
// version history tab
|
|
await click(PAGE.secretTab('Version History'));
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.metadata.versions',
|
|
'navs to version history'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Version History']);
|
|
assertDetailTabs(assert, 'Version History');
|
|
assertTabHrefs(assert, 'Version History');
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for version history');
|
|
});
|
|
// only run this test on enterprise so we are testing permissions specifically and not enterprise vs CE (which also redirects)
|
|
test('enterprise: patch route does not redirect for users with permissions (a)', async function (assert) {
|
|
await visit(`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`,
|
|
'redirects to index'
|
|
);
|
|
assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.kv.secret.patch');
|
|
});
|
|
});
|
|
|
|
module('data-reader persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd([
|
|
createPolicyCmd(
|
|
`data-reader-${this.backend}`,
|
|
personas.dataReader(this.backend) + personas.dataReader(this.emptyBackend)
|
|
),
|
|
createTokenCmd(`data-reader-${this.backend}`),
|
|
]);
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('empty backend - breadcrumbs, title, tabs, emptyState (dr)', async function (assert) {
|
|
assert.expect(16);
|
|
const backend = this.emptyBackend;
|
|
await navToBackend(backend);
|
|
|
|
// URL correct
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list`, 'lands on secrets list page');
|
|
// Breadcrumbs correct
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
// Title correct
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
// Tabs correct
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('active');
|
|
// Toolbar correct
|
|
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar renders');
|
|
assert
|
|
.dom(PAGE.list.filter)
|
|
.doesNotExist('list filter input does not render because no list capabilities');
|
|
// Page content correct
|
|
assert
|
|
.dom(PAGE.emptyStateTitle)
|
|
.doesNotExist('empty state does not render because no metadata access to list');
|
|
assert.dom(PAGE.list.overviewCard).exists('renders overview card');
|
|
|
|
await typeIn(PAGE.list.overviewInput, 'directory/');
|
|
await click(PAGE.list.overviewButton);
|
|
assert
|
|
.dom('[data-test-inline-error-message]')
|
|
.hasText('You do not have the required permissions or the directory does not exist.');
|
|
|
|
// click toolbar CTA
|
|
await visit(`/vault/secrets/${backend}/kv/list`);
|
|
await click(PAGE.list.createSecret);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/create`,
|
|
`url includes /vault/secrets/${backend}/kv/create`
|
|
);
|
|
|
|
// Click cancel btn
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list`,
|
|
`url includes /vault/secrets/${backend}/kv/list`
|
|
);
|
|
});
|
|
test('can access nested secret (dr)', async function (assert) {
|
|
assert.expect(23);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert
|
|
.dom(PAGE.list.filter)
|
|
.doesNotExist('List filter input does not render because no list capabilities');
|
|
|
|
await typeIn(PAGE.list.overviewInput, 'app/nested/secret');
|
|
await click(PAGE.list.overviewButton);
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
`navigated to secret overview ${currentURL()}`
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
assertDetailsToolbar(assert, ['copy']);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('versioned secret nav, tabs, breadcrumbs (dr)', async function (assert) {
|
|
assert.expect(31);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
|
|
// Navigate to secret
|
|
await typeIn(PAGE.list.overviewInput, secretPath);
|
|
await click(PAGE.list.overviewButton);
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'navigates to secret overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=3`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'Goes to secret detail view');
|
|
assertDetailTabs(assert, 'Secret', ['Version History']);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('Version dropdown hidden');
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('unable to create a new version');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 3 created');
|
|
assert.dom(PAGE.infoRowValue('foo')).exists('renders current data');
|
|
|
|
// data-reader can't navigate to older versions, but they can go to page directly
|
|
await visit(`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=1`);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('Version dropdown does not exist');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 1 created');
|
|
assert.dom(PAGE.infoRowValue('key-1')).exists('renders previous data');
|
|
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('cannot create new version');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`goes to metadata page`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath);
|
|
assert.dom(PAGE.toolbarAction).doesNotExist('no toolbar actions available on metadata');
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('No custom metadata');
|
|
assert
|
|
.dom(`${PAGE.metadata.secretMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('You do not have access to secret metadata');
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('edit button hidden');
|
|
});
|
|
test('breadcrumbs & page titles are correct (dr)', async function (assert) {
|
|
assert.expect(35);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title correct on config page');
|
|
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title correct on secrets list');
|
|
|
|
await typeIn(PAGE.list.overviewInput, 'app/nested/secret');
|
|
await click(PAGE.list.overviewButton);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title correct on secret detail');
|
|
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('cannot create new version');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret', 'Metadata']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title correct on metadata');
|
|
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('cannot edit metadata');
|
|
|
|
await click(PAGE.secretTab('Paths'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret', 'Paths']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'correct page title for paths');
|
|
|
|
assert.dom(PAGE.secretTab('Version History')).doesNotExist('Version History tab not shown');
|
|
});
|
|
patchRedirectTest(test, 'dr');
|
|
});
|
|
|
|
module('data-list-reader persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd([
|
|
createPolicyCmd(
|
|
`data-reader-list-${this.backend}`,
|
|
personas.dataListReader(this.backend) + personas.dataListReader(this.emptyBackend)
|
|
),
|
|
createTokenCmd(`data-reader-list-${this.backend}`),
|
|
]);
|
|
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('empty backend - breadcrumbs, title, tabs, emptyState (dlr)', async function (assert) {
|
|
assert.expect(15);
|
|
const backend = this.emptyBackend;
|
|
await navToBackend(backend);
|
|
|
|
// URL correct
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list`, 'lands on secrets list page');
|
|
// Breadcrumbs correct
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
// Title correct
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
// Tabs correct
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('active');
|
|
// Toolbar correct
|
|
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar renders');
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter does not show because no secrets exists.');
|
|
// Page content correct
|
|
assert.dom(PAGE.emptyStateTitle).hasText('No secrets yet');
|
|
assert.dom(PAGE.list.createSecret).hasText('Create secret');
|
|
|
|
// click toolbar CTA
|
|
await click(PAGE.list.createSecret);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/create`),
|
|
`url includes /vault/secrets/${backend}/kv/create`
|
|
);
|
|
|
|
// Click cancel btn
|
|
await click(FORM.cancelBtn);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list`),
|
|
`url includes /vault/secrets/${backend}/kv/list`
|
|
);
|
|
});
|
|
test('can access nested secret (dlr)', async function (assert) {
|
|
assert.expect(32);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.list.filter).hasNoValue('List filter input is empty');
|
|
|
|
// Navigate through list items
|
|
await click(PAGE.list.item('app/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter hidden since no nested list access');
|
|
|
|
assert
|
|
.dom(PAGE.list.overviewInput)
|
|
.hasValue('app/', 'overview card is pre-filled with directory param');
|
|
await typeIn(PAGE.list.overviewInput, 'nested/secret');
|
|
await click(PAGE.list.overviewButton);
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
`navigated to overview`
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details?version=1`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
assertDetailsToolbar(assert, ['delete', 'copy']);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('versioned secret nav, tabs, breadcrumbs (dlr)', async function (assert) {
|
|
assert.expect(31);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'navigates to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=3`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'Goes to secret detail view');
|
|
assertDetailTabs(assert, 'Secret', ['Version History']);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('does not show version dropdown');
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('unable to create a new version');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 3 created');
|
|
assert.dom(PAGE.infoRowValue('foo')).exists('renders current data');
|
|
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('cannot create new version');
|
|
|
|
// data-list-reader can't navigate to older versions, but they can go to page directly
|
|
await visit(`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=1`);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('no version dropdown');
|
|
assert.dom(PAGE.detail.versionTimestamp).containsText('Version 1 created');
|
|
assert.dom(PAGE.infoRowValue('key-1')).exists('renders previous data');
|
|
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('cannot create new version from old version');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`goes to metadata page`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath);
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('No custom metadata');
|
|
assert
|
|
.dom(`${PAGE.metadata.secretMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('You do not have access to secret metadata');
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('edit button hidden');
|
|
});
|
|
test('breadcrumbs & page titles are correct (dlr)', async function (assert) {
|
|
assert.expect(29);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
|
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
|
|
|
await click(PAGE.list.item(secretPath));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret detail');
|
|
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('cannot create new version');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for metadata');
|
|
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('cannot edit metadata');
|
|
|
|
await click(PAGE.secretTab('Paths'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Paths']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for paths');
|
|
|
|
assert.dom(PAGE.secretTab('Version History')).doesNotExist('Version History tab not shown');
|
|
});
|
|
patchRedirectTest(test, 'dlr');
|
|
});
|
|
|
|
module('metadata-maintainer persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd([
|
|
createPolicyCmd(
|
|
`metadata-maintainer-${this.backend}`,
|
|
personas.metadataMaintainer(this.backend) + personas.metadataMaintainer(this.emptyBackend)
|
|
),
|
|
createTokenCmd(`metadata-maintainer-${this.backend}`),
|
|
]);
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('empty backend - breadcrumbs, title, tabs, emptyState (mm)', async function (assert) {
|
|
assert.expect(15);
|
|
const backend = this.emptyBackend;
|
|
await navToBackend(backend);
|
|
// URL correct
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list`, 'lands on secrets list page');
|
|
// Breadcrumbs correct
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
// Title correct
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
// Tabs correct
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('active');
|
|
// Toolbar correct
|
|
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar only renders create secret action');
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter does not show because no secrets exists.');
|
|
// Page content correct
|
|
assert.dom(PAGE.emptyStateTitle).hasText('No secrets yet');
|
|
assert.dom(PAGE.list.createSecret).hasText('Create secret');
|
|
|
|
// click toolbar CTA
|
|
await click(PAGE.list.createSecret);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/create`),
|
|
`url includes /vault/secrets/${backend}/kv/create`
|
|
);
|
|
|
|
// Click cancel btn
|
|
await click(FORM.cancelBtn);
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list`),
|
|
`url includes /vault/secrets/${backend}/kv/list`
|
|
);
|
|
});
|
|
test('can access nested secret (mm)', async function (assert) {
|
|
assert.expect(42);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.list.filter).hasNoValue('List filter input is empty');
|
|
|
|
// Navigate through list items
|
|
await click(PAGE.list.item('app/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
|
|
|
await click(PAGE.list.item('nested/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/nested/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
|
|
|
await click(PAGE.list.item('secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
`goes to overview`
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details`,
|
|
`Goes to URL without version`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
assertDetailsToolbar(assert, ['delete', 'destroy', 'versionDropdown']);
|
|
assert.dom(PAGE.detail.versionDropdown).hasText('Version 1', 'Shows version timestamp');
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('versioned secret nav, tabs, breadcrumbs (mm)', async function (assert) {
|
|
assert.expect(40);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'navs to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details`,
|
|
'Url does not include version query param'
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'Goes to secret detail view');
|
|
assertDetailTabs(assert, 'Secret');
|
|
assert.dom(PAGE.detail.versionDropdown).hasText('Version 3', 'Version dropdown shows current version');
|
|
assert.dom(PAGE.detail.createNewVersion).doesNotExist('Create new version button not shown');
|
|
assert.dom(PAGE.detail.versionTimestamp).doesNotExist('Version created text not shown');
|
|
assert.dom(PAGE.infoRowValue('foo')).doesNotExist('does not render current data');
|
|
assert
|
|
.dom(PAGE.emptyStateTitle)
|
|
.hasText('You do not have permission to read this secret', 'Shows empty state on secret detail');
|
|
|
|
await click(PAGE.detail.versionDropdown);
|
|
await click(`${PAGE.detail.version(1)} a`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=1`,
|
|
'Goes to detail view for version 1'
|
|
);
|
|
assert.dom(PAGE.detail.versionDropdown).hasText('Version 1', 'Version dropdown shows selected version');
|
|
|
|
assert.dom(PAGE.infoRowValue('key-1')).doesNotExist('does not render previous data');
|
|
assert
|
|
.dom(PAGE.emptyStateTitle)
|
|
.hasText(
|
|
'You do not have permission to read this secret',
|
|
'Shows empty state on secret detail for older version'
|
|
);
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`goes to metadata page`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath);
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('No custom metadata');
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateActions}`)
|
|
.hasText('Add metadata', 'empty state has metadata CTA');
|
|
assert.dom(PAGE.metadata.editBtn).hasText('Edit metadata');
|
|
|
|
await click(PAGE.metadata.editBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata/edit`,
|
|
`goes to metadata edit page`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata', 'Edit']);
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`cancel btn goes back to metadata page`
|
|
);
|
|
});
|
|
test('breadcrumbs & page titles are correct (mm)', async function (assert) {
|
|
assert.expect(39);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
|
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
|
|
|
await click(PAGE.list.item(secretPath));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret detail');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for metadata');
|
|
|
|
await click(PAGE.metadata.editBtn);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata', 'Edit']);
|
|
assert.dom(PAGE.title).hasText('Edit Secret Metadata', 'correct page title for metadata edit');
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
await click(PAGE.secretTab('Paths'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Paths']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for paths');
|
|
|
|
await click(PAGE.secretTab('Version History'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Version History']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for version history');
|
|
});
|
|
patchRedirectTest(test, 'mm');
|
|
});
|
|
|
|
module('secret-creator persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd([
|
|
createPolicyCmd(
|
|
`secret-creator-${this.backend}`,
|
|
personas.secretCreator(this.backend) + personas.secretCreator(this.emptyBackend)
|
|
),
|
|
createTokenCmd(`secret-creator-${this.backend}`),
|
|
]);
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('empty backend - breadcrumbs, title, tabs, emptyState (sc)', async function (assert) {
|
|
assert.expect(15);
|
|
const backend = this.emptyBackend;
|
|
await navToBackend(backend);
|
|
|
|
// URL correct
|
|
assert.strictEqual(currentURL(), `/vault/secrets/${backend}/kv/list`, 'lands on secrets list page');
|
|
// Breadcrumbs correct
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
// Title correct
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
// Tabs correct
|
|
assert.dom(PAGE.secretTab('Secrets')).hasText('Secrets');
|
|
assert.dom(PAGE.secretTab('Secrets')).hasClass('active');
|
|
assert.dom(PAGE.secretTab('Configuration')).hasText('Configuration');
|
|
assert.dom(PAGE.secretTab('Configuration')).doesNotHaveClass('active');
|
|
// Toolbar correct
|
|
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar only renders create secret action');
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter input is not rendered');
|
|
// Page content correct
|
|
assert.dom(PAGE.list.overviewCard).exists('Overview card renders');
|
|
assert.dom(PAGE.list.createSecret).hasText('Create secret');
|
|
|
|
// click toolbar CTA
|
|
await click(PAGE.list.createSecret);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/create`,
|
|
`goes to /vault/secrets/${backend}/kv/create`
|
|
);
|
|
|
|
// Click cancel btn
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list`,
|
|
`url includes /vault/secrets/${backend}/kv/list`
|
|
);
|
|
});
|
|
test('can access nested secret (sc)', async function (assert) {
|
|
assert.expect(24);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.list.filter).doesNotExist('List filter input is not rendered');
|
|
|
|
// Navigate to secret
|
|
await typeIn(PAGE.list.overviewInput, 'app/nested/secret');
|
|
await click(PAGE.list.overviewButton);
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
'goes to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details`,
|
|
'goes to secret detail page'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
assertDetailsToolbar(assert, ['createNewVersion']);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('versioned secret nav, tabs, breadcrumbs (sc)', async function (assert) {
|
|
assert.expect(39);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
|
|
await typeIn(PAGE.list.overviewInput, secretPath);
|
|
await click(PAGE.list.overviewButton);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'Goes to overview'
|
|
);
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details`,
|
|
'Goes to detail view'
|
|
);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'Goes to secret detail view');
|
|
assertDetailTabs(assert, 'Secret', ['Version History']);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('Version dropdown does not render');
|
|
assert.dom(PAGE.detail.createNewVersion).hasText('Create new version', 'Create version button shows');
|
|
assert.dom(PAGE.detail.versionTimestamp).doesNotExist('Version created info is not rendered');
|
|
assert.dom(PAGE.infoRowValue('foo')).doesNotExist('current data not rendered');
|
|
assert
|
|
.dom(PAGE.emptyStateTitle)
|
|
.hasText('You do not have permission to read this secret', 'empty state shows');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details/edit`,
|
|
'Goes to edit page'
|
|
);
|
|
assert.dom(FORM.versionAlert).doesNotExist('Does not show version alert for current version');
|
|
assert
|
|
.dom(FORM.noReadAlert)
|
|
.hasText(
|
|
'Warning You do not have read permissions for this secret data. Saving will overwrite the existing secret.',
|
|
'Shows warning about no read permissions'
|
|
);
|
|
assert.dom(FORM.inputByAttr('path')).isDisabled();
|
|
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'Goes back to overview'
|
|
);
|
|
|
|
await visit(`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details?version=1`);
|
|
assert.dom(PAGE.detail.versionDropdown).doesNotExist('Version dropdown does not exist');
|
|
assert.dom(PAGE.detail.versionTimestamp).doesNotExist('version created data not rendered');
|
|
assert.dom(PAGE.infoRowValue('key-1')).doesNotExist('does not render previous data');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details/edit?version=1`,
|
|
'Url includes version query param'
|
|
);
|
|
assert.dom(FORM.inputByAttr('path')).isDisabled();
|
|
assert.dom(FORM.keyInput()).hasValue('', 'form does not pre-populate');
|
|
assert.dom(FORM.maskedValueInput()).hasValue('', 'form does not pre-populate');
|
|
assert.dom(FORM.noReadAlert).exists('Shows no read alert');
|
|
await click(FORM.cancelBtn);
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/metadata`,
|
|
`goes to metadata page`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath);
|
|
assert
|
|
.dom(`${PAGE.metadata.customMetadataSection} ${PAGE.emptyStateTitle}`)
|
|
.hasText('You do not have access to read custom metadata', 'shows correct empty state');
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('edit metadata button does not render');
|
|
});
|
|
test('breadcrumbs & page titles are correct (sc)', async function (assert) {
|
|
assert.expect(39);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
|
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
|
|
|
await typeIn(PAGE.list.overviewInput, secretPath);
|
|
await click(PAGE.list.overviewButton);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret detail');
|
|
|
|
await click(PAGE.secretTab('Secret'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret detail');
|
|
|
|
await click(PAGE.detail.createNewVersion);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Edit']);
|
|
assert.dom(PAGE.title).hasText('Create New Version', 'correct page title for secret edit');
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for metadata');
|
|
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('cannot edit metadata');
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
await click(PAGE.secretTab('Paths'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Paths']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for paths');
|
|
|
|
assert.dom(PAGE.secretTab('Version History')).doesNotExist('Version History tab not shown');
|
|
});
|
|
patchRedirectTest(test, 'sc');
|
|
});
|
|
|
|
module('enterprise controlled access persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
// Set up control group scenario
|
|
const userPolicy = `
|
|
path "${this.backend}/data/*" {
|
|
capabilities = ["create", "read", "update", "delete", "list"]
|
|
control_group = {
|
|
max_ttl = "24h"
|
|
factor "ops_manager" {
|
|
controlled_capabilities = ["read"]
|
|
identity {
|
|
group_names = ["managers"]
|
|
approvals = 1
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
path "${this.backend}/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
`;
|
|
const { userToken } = await setupControlGroup({ userPolicy, backend: this.backend });
|
|
this.userToken = userToken;
|
|
await authPage.login(userToken);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
test('can access nested secret (cg)', async function (assert) {
|
|
assert.expect(43);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
|
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.list.filter).hasNoValue('List filter input is empty');
|
|
|
|
// Navigate through list items
|
|
await click(PAGE.list.item('app/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
|
|
|
await click(PAGE.list.item('nested/'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/nested/`,
|
|
`navigated to ${currentURL()}`
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
|
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
|
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
|
|
|
// For some reason when we click on the item in tests it throws a global control group error
|
|
// But not when we visit the page directly
|
|
await visit(`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details`);
|
|
assert.true(
|
|
await waitUntil(() => currentRouteName() === 'vault.cluster.access.control-group-accessor'),
|
|
'redirects to access control group route'
|
|
);
|
|
await grantAccess({
|
|
apiPath: `${backend}/data/app/nested/secret`,
|
|
originUrl: `/vault/secrets/${backend}/kv/list/app/nested/`,
|
|
userToken: this.userToken,
|
|
backend: this.backend,
|
|
});
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list/app/nested/`,
|
|
'navigates to list url where secret is'
|
|
);
|
|
await click(PAGE.list.item('secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret`,
|
|
'goes to overview'
|
|
);
|
|
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/app%2Fnested%2Fsecret/details?version=1`,
|
|
'goes to secret details'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested', 'secret']);
|
|
assert.dom(PAGE.title).hasText('app/nested/secret', 'title is full secret path');
|
|
assertDetailsToolbar(assert, ['delete', 'copy', 'createNewVersion']);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(3));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/nested/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(2));
|
|
assert.true(
|
|
currentURL().startsWith(`/vault/secrets/${backend}/kv/list/app/`),
|
|
'links back to list directory'
|
|
);
|
|
|
|
await click(PAGE.breadcrumbAtIdx(1));
|
|
assert.true(currentURL().startsWith(`/vault/secrets/${backend}/kv/list`), 'links back to list root');
|
|
});
|
|
test('breadcrumbs & page titles are correct (cg)', async function (assert) {
|
|
assert.expect(43);
|
|
const backend = this.backend;
|
|
await navToBackend(backend);
|
|
await click(PAGE.secretTab('Configuration'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
|
|
|
await click(PAGE.secretTab('Secrets'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
|
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
|
|
|
await visit(`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/details`);
|
|
|
|
assert.true(
|
|
await waitUntil(() => currentRouteName() === 'vault.cluster.access.control-group-accessor'),
|
|
'redirects to access control group route'
|
|
);
|
|
await grantAccess({
|
|
apiPath: `${backend}/data/${encodeURIComponent(secretPath)}`,
|
|
originUrl: `/vault/secrets/${backend}/kv/list`,
|
|
userToken: this.userToken,
|
|
backend: this.backend,
|
|
});
|
|
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/list`,
|
|
'navigates back to list url after authorized'
|
|
);
|
|
await click(PAGE.list.item(secretPath));
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${backend}/kv/${secretPathUrlEncoded}`,
|
|
'Goes to overview'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret overview');
|
|
|
|
await click(PAGE.secretTab('Metadata'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Metadata']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for metadata');
|
|
assert.dom(PAGE.metadata.editBtn).doesNotExist('cannot edit metadata');
|
|
|
|
await click(PAGE.secretTab('Paths'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Paths']);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for paths');
|
|
|
|
assert.dom(PAGE.secretTab('Version History')).doesNotExist('Version History tab not shown');
|
|
|
|
await click(PAGE.secretTab('Secret'));
|
|
assert.true(
|
|
await waitUntil(() => currentRouteName() === 'vault.cluster.access.control-group-accessor'),
|
|
'redirects to access control group route'
|
|
);
|
|
await grantAccess({
|
|
apiPath: `${backend}/data/${encodeURIComponent(secretPath)}`,
|
|
originUrl: `/vault/secrets/${backend}/kv/${secretPathUrlEncoded}/paths`,
|
|
userToken: this.userToken,
|
|
backend: this.backend,
|
|
});
|
|
await click(PAGE.secretTab('Secret'));
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
|
assert.dom(PAGE.title).hasText(secretPath, 'correct page title for secret details');
|
|
await click(PAGE.detail.createNewVersion);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath, 'Edit']);
|
|
assert.dom(PAGE.title).hasText('Create New Version', 'correct page title for secret edit');
|
|
});
|
|
});
|
|
|
|
// patch is technically enterprise only but stubbing the version so they can run on both CE and enterprise
|
|
module('patch-persona', function (hooks) {
|
|
hooks.beforeEach(async function () {
|
|
const token = await runCmd([
|
|
createPolicyCmd(
|
|
`secret-patcher-${this.backend}`,
|
|
personas.secretPatcher(this.backend) + personas.secretPatcher(this.emptyBackend)
|
|
),
|
|
createTokenCmd(`secret-patcher-${this.backend}`),
|
|
]);
|
|
await authPage.login(token);
|
|
clearRecords(this.store);
|
|
return;
|
|
});
|
|
|
|
test('it navigates to patch a secret from overview', async function (assert) {
|
|
this.version.type = 'enterprise';
|
|
await navToBackend(this.backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
await click(GENERAL.overviewCard.actionText('Patch secret'));
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.patch',
|
|
'navs to patch'
|
|
);
|
|
assertCorrectBreadcrumbs(assert, ['Secrets', this.backend, secretPath, 'Patch']);
|
|
assert.dom(PAGE.title).hasText('Patch Secret to New Version');
|
|
await click(FORM.cancelBtn);
|
|
assert.strictEqual(
|
|
currentRouteName(),
|
|
'vault.cluster.secrets.backend.kv.secret.index',
|
|
'navs back to overview'
|
|
);
|
|
});
|
|
|
|
test('overview subkeys card is hidden for community edition', async function (assert) {
|
|
this.version.type = 'community';
|
|
await navToBackend(this.backend);
|
|
await click(PAGE.list.item(secretPath));
|
|
assert.dom(GENERAL.overviewCard.container('Subkeys')).doesNotExist();
|
|
});
|
|
|
|
test('it does not redirect for ent', async function (assert) {
|
|
this.version.type = 'enterprise';
|
|
await visit(`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`,
|
|
'redirects to index'
|
|
);
|
|
assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.kv.secret.patch');
|
|
});
|
|
|
|
test('it redirects for community edition', async function (assert) {
|
|
this.version.type = 'community';
|
|
await visit(`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret/patch`);
|
|
assert.strictEqual(
|
|
currentURL(),
|
|
`/vault/secrets/${this.backend}/kv/app%2Fnested%2Fsecret`,
|
|
'redirects to index'
|
|
);
|
|
assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.kv.secret.index');
|
|
});
|
|
});
|
|
});
|