mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-28 04:10:44 -04:00
* separate header comp * replacing header * redirect to general settings * moving kv configure under plugin settings * add exit button * removing all use of old header with new, updated logic * reuse secretPath, add button to badge * test updates pt1 * test updates pt2, refactors * test fixes * testing * removing extendedConfig * put tabs out of header * adding new config edit page & updates * adding page test * pr comments * replace type with effectiveType * test fixes * adding badges, cleanup test Co-authored-by: Dan Rivera <dan.rivera@hashicorp.com>
This commit is contained in:
parent
05c153c70d
commit
5013a5e764
42 changed files with 701 additions and 268 deletions
|
|
@ -111,6 +111,7 @@ export default class App extends Application {
|
|||
externalRoutes: {
|
||||
secrets: 'vault.cluster.secrets.backends',
|
||||
syncDestination: 'vault.cluster.sync.secrets.destinations.destination',
|
||||
secretsGeneralSettingsConfiguration: 'vault.cluster.secrets.backend.configuration.general-settings',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
</:actions>
|
||||
</Page::Header>
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{if engineDisplayData.isConfigurable (or engineDisplayData.configRoute "configuration.plugin-settings")}}
|
||||
@configRoute={{this.configRoute}}
|
||||
@displayName={{engineDisplayData.displayName}}
|
||||
@path={{@model.secretsEngine.id}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import type FlashMessageService from 'vault/services/flash-messages';
|
|||
import type ApiService from 'vault/services/api';
|
||||
import type SecretsEngineResource from 'vault/resources/secrets/engine';
|
||||
import type UnsavedChangesService from 'vault/services/unsaved-changes';
|
||||
import engineDisplayData from 'vault/helpers/engines-display-data';
|
||||
|
||||
const CHARACTER_LIMIT = 500;
|
||||
|
||||
|
|
@ -65,6 +66,19 @@ export default class GeneralSettingsComponent extends Component<Args> {
|
|||
return changedFieldsCopy;
|
||||
}
|
||||
|
||||
get configRoute() {
|
||||
const engine = this.args.model.secretsEngine;
|
||||
const isKvv2 = engine.version === 2 && engine.effectiveEngineType === 'kv';
|
||||
const engineMetadata = engineDisplayData(engine.effectiveEngineType);
|
||||
|
||||
// Kvv2 is configurable but shares metadata with Kvv1 so isConfigurable is left unset
|
||||
if (engineMetadata.isConfigurable || isKvv2) {
|
||||
return engineMetadata.configRoute || 'configuration.plugin-settings';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
validateTtl(ttlValue: FormDataEntryValue | number | null) {
|
||||
if (isNaN(Number(ttlValue))) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
</:actions>
|
||||
</Page::Header>
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{or engineDisplayData.configRoute "configuration.plugin-settings"}}
|
||||
@configRoute={{this.configRoute}}
|
||||
@displayName={{engineDisplayData.displayName}}
|
||||
@path={{@model.secretsEngine.id}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -79,6 +79,19 @@ export default class PluginSettingsComponent extends Component<Args> {
|
|||
}
|
||||
}
|
||||
|
||||
get configRoute() {
|
||||
const engine = this.args.model.secretsEngine;
|
||||
const isKvv2 = engine.version === 2 && engine.effectiveEngineType === 'kv';
|
||||
const engineMetadata = engineDisplayData(engine.effectiveEngineType);
|
||||
|
||||
// Kvv2 is configurable but shares metadata with Kvv1 so isConfigurable is left unset
|
||||
if (engineMetadata.isConfigurable || isKvv2) {
|
||||
return engineMetadata.configRoute || 'configuration.plugin-settings';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
label = (field: string) => {
|
||||
const label = toLabel([field]);
|
||||
// convert words like id and ttl to uppercase
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ export const ALL_ENGINES: EngineDisplayData[] = [
|
|||
pluginCategory: 'generic',
|
||||
displayName: 'KV',
|
||||
engineRoute: 'kv.list',
|
||||
configRoute: 'kv.configuration', // only utilized to display config data for kvv2, not in conjunction with isConfigurable as templates determine whether engine is kv v1 or v2
|
||||
glyph: 'key-values',
|
||||
mountCategory: ['secret'],
|
||||
type: 'kv',
|
||||
|
|
|
|||
62
ui/lib/kv/addon/components/kv-header.hbs
Normal file
62
ui/lib/kv/addon/components/kv-header.hbs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title={{@pageTitle}} @description={{if @backend "KV" ""}} @icon={{@backend.icon}}>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:actions>
|
||||
{{#if (has-block "actions")}}
|
||||
{{yield to="actions"}}
|
||||
{{/if}}
|
||||
</:actions>
|
||||
<:badges>
|
||||
{{#if (has-block "badges")}}
|
||||
{{yield to="badges"}}
|
||||
{{/if}}
|
||||
{{#if @secretPath}}
|
||||
<Hds::Copy::Button
|
||||
@isIconOnly={{true}}
|
||||
@text="Copy your secret path"
|
||||
@textToCopy={{@secretPath}}
|
||||
data-test-copy-button
|
||||
/>
|
||||
{{/if}}
|
||||
</:badges>
|
||||
</Page::Header>
|
||||
|
||||
{{#if @configRoute}}
|
||||
<Mount::ConfigureTabs
|
||||
@configRoute={{@configRoute}}
|
||||
@displayName="KV"
|
||||
@path={{@backend.id}}
|
||||
@externalRoute="secretsGeneralSettingsConfiguration"
|
||||
/>
|
||||
{{else}}
|
||||
{{#if (has-block "tabs")}}
|
||||
<div class="tabs-container box is-marginless is-fullwidth is-paddingless">
|
||||
<nav class="tabs" aria-label="kv tabs">
|
||||
<ul>
|
||||
{{yield to="tabs"}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (has-block "syncDetails")}}
|
||||
{{yield to="syncDetails"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (or (has-block "toolbarFilters") (has-block "toolbarActions"))}}
|
||||
<Toolbar aria-label="menu items for managing {{or @mountName @secretPath @pageTitle}}">
|
||||
<ToolbarFilters aria-label="filters for secrets list">
|
||||
{{yield to="toolbarFilters"}}
|
||||
</ToolbarFilters>
|
||||
<ToolbarActions aria-label="actions for secrets">
|
||||
{{yield to="toolbarActions"}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<PageHeader as |p|>
|
||||
<p.top>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</p.top>
|
||||
<p.levelLeft>
|
||||
<h1 class="title is-3" data-test-header-title>
|
||||
{{#if @mountName}}
|
||||
<Icon @name="key-values" @size="24" class="has-text-grey-light" />
|
||||
{{@mountName}}
|
||||
<Hds::Badge @text="version 2" />
|
||||
{{else if @secretPath}}
|
||||
{{@secretPath}}
|
||||
<Hds::Copy::Button @isIconOnly={{true}} @text="Copy your secret path" @textToCopy={{@secretPath}} />
|
||||
{{else}}
|
||||
{{@pageTitle}}
|
||||
{{/if}}
|
||||
</h1>
|
||||
</p.levelLeft>
|
||||
</PageHeader>
|
||||
|
||||
{{#if (has-block "syncDetails")}}
|
||||
{{yield to="syncDetails"}}
|
||||
{{/if}}
|
||||
|
||||
{{#if (has-block "tabLinks")}}
|
||||
<div class="tabs-container box is-bottomless is-marginless is-paddingless">
|
||||
<nav class="tabs" aria-label="kv tabs">
|
||||
<ul>
|
||||
{{yield to="tabLinks"}}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if (or (has-block "toolbarFilters") (has-block "toolbarActions"))}}
|
||||
<Toolbar aria-label="menu items for managing {{or @mountName @secretPath @pageTitle}}">
|
||||
<ToolbarFilters aria-label="filters for secrets list">
|
||||
{{yield to="toolbarFilters"}}
|
||||
</ToolbarFilters>
|
||||
<ToolbarActions aria-label="actions for secrets">
|
||||
{{yield to="toolbarActions"}}
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
{{/if}}
|
||||
|
|
@ -3,12 +3,25 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @mountName={{@backend}}>
|
||||
<:tabLinks>
|
||||
<li><LinkTo @route="list" data-test-secrets-tab="Secrets">Secrets</LinkTo></li>
|
||||
<li><LinkTo @route="configuration" data-test-secrets-tab="Configuration">Configuration</LinkTo></li>
|
||||
</:tabLinks>
|
||||
</KvPageHeader>
|
||||
<KvHeader
|
||||
@pageTitle="{{@backend.id}} configuration"
|
||||
@backend={{@backend}}
|
||||
@breadcrumbs={{@breadcrumbs}}
|
||||
@configRoute="configuration"
|
||||
>
|
||||
<:badges>
|
||||
<Hds::Badge @text="version 2" data-test-badge />
|
||||
</:badges>
|
||||
<:actions>
|
||||
<Hds::Button @color="secondary" @route="list" @text="Exit configuration" data-test-button="Exit configuration" />
|
||||
</:actions>
|
||||
|
||||
<:toolbarActions>
|
||||
<ToolbarLink @route="configure" data-test-secret-backend-configure>
|
||||
Edit configuration
|
||||
</ToolbarLink>
|
||||
</:toolbarActions>
|
||||
</KvHeader>
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
{{#each-in @config as |key value|}}
|
||||
|
|
|
|||
63
ui/lib/kv/addon/components/page/configure.hbs
Normal file
63
ui/lib/kv/addon/components/page/configure.hbs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvHeader
|
||||
@pageTitle="{{@backend.id}} configuration"
|
||||
@backend={{@backend}}
|
||||
@breadcrumbs={{@breadcrumbs}}
|
||||
@configRoute="configure"
|
||||
>
|
||||
<:badges>
|
||||
<Hds::Badge @text="version 2" data-test-badge />
|
||||
</:badges>
|
||||
<:actions>
|
||||
<Hds::Button @color="secondary" @route="list" @text="Exit configuration" data-test-button="Exit configuration" />
|
||||
</:actions>
|
||||
|
||||
<:toolbarActions>
|
||||
</:toolbarActions>
|
||||
</KvHeader>
|
||||
|
||||
<form {{on "submit" (perform this.save)}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="update" @noun="KV secret metadata" />
|
||||
<MessageError @errorMessage={{this.errorBanner}} class="has-top-margin-s" />
|
||||
{{#each @form.metadataFields as |field|}}
|
||||
{{#unless (eq field.name "custom_metadata")}}
|
||||
<FormField @attr={{field}} @model={{@form}} @modelValidations={{this.modelValidations}} />
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped is-grouped-split is-fullwidth box is-bottomless">
|
||||
<div class="has-top-padding-s">
|
||||
<Hds::Button
|
||||
@text="Save"
|
||||
@icon={{if this.save.isRunning "loading"}}
|
||||
type="submit"
|
||||
disabled={{this.save.isRunning}}
|
||||
data-test-kv-save
|
||||
/>
|
||||
<Hds::Button
|
||||
@text="Cancel"
|
||||
@color="secondary"
|
||||
class="has-left-margin-s"
|
||||
disabled={{this.save.isRunning}}
|
||||
{{on "click" this.navigateToConfiguration}}
|
||||
data-test-kv-cancel
|
||||
/>
|
||||
{{#if this.invalidFormAlert}}
|
||||
<div class="control">
|
||||
<AlertInline
|
||||
data-test-invalid-form-alert
|
||||
@type="danger"
|
||||
class="has-top-padding-s"
|
||||
@message={{this.invalidFormAlert}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
67
ui/lib/kv/addon/components/page/configure.ts
Normal file
67
ui/lib/kv/addon/components/page/configure.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { task } from 'ember-concurrency';
|
||||
import type KvForm from 'vault/app/forms/secrets/kv';
|
||||
import type { Breadcrumb } from 'vault/vault/app-types';
|
||||
import type FlashMessageService from 'vault/services/flash-messages';
|
||||
import type RouterService from '@ember/routing/router-service';
|
||||
import type ApiService from 'vault/services/api';
|
||||
import SecretsEngineResource from 'vault/app/resources/secrets/engine';
|
||||
|
||||
interface Args {
|
||||
form: KvForm;
|
||||
backend: SecretsEngineResource;
|
||||
breadcrumbs: Array<Breadcrumb>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @module KvConfigurePageComponent
|
||||
* KvConfigurePageComponent is a component to show secrets mount and engine configuration data
|
||||
*
|
||||
* @param {object} form - config form data for mount and engine
|
||||
* @param {string} backend - The kv secrets engine data
|
||||
* @param {array} breadcrumbs - Breadcrumbs as an array of objects that contain label, route, and modelId. They are updated via the util kv-breadcrumbs to handle dynamic *pathToSecret on the list-directory route.
|
||||
*/
|
||||
|
||||
export default class KvConfigurePageComponent extends Component<Args> {
|
||||
@service declare readonly flashMessages: FlashMessageService;
|
||||
@service('app-router') declare readonly router: RouterService;
|
||||
@service declare readonly api: ApiService;
|
||||
|
||||
@tracked errorBanner = '';
|
||||
@tracked invalidFormAlert = '';
|
||||
@tracked modelValidations = null;
|
||||
|
||||
@action
|
||||
navigateToConfiguration() {
|
||||
this.router.transitionTo(`vault.cluster.secrets.backend.kv.configuration`);
|
||||
}
|
||||
|
||||
@task
|
||||
*save(event: Event | null) {
|
||||
event.preventDefault();
|
||||
try {
|
||||
const { isValid, state, invalidFormMessage, data } = this.args.form.toJSON();
|
||||
this.modelValidations = isValid ? null : state;
|
||||
this.invalidFormAlert = invalidFormMessage;
|
||||
|
||||
if (isValid) {
|
||||
yield this.api.secrets.kvV2Configure(data.path, data);
|
||||
this.flashMessages.success(`Successfully updated ${data.path}'s configuration.`);
|
||||
this.navigateToConfiguration();
|
||||
}
|
||||
} catch (error) {
|
||||
const { message } = yield this.api.parseError(error);
|
||||
this.errorBanner = message;
|
||||
this.invalidFormAlert = 'There was an error submitting this form.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,39 +3,47 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @mountName={{@backend}}>
|
||||
<:tabLinks>
|
||||
<li>
|
||||
<LinkTo
|
||||
@route={{this.router.currentRoute.localName}}
|
||||
@models={{@currentRouteParams}}
|
||||
data-test-secrets-tab="Secrets"
|
||||
@current-when={{true}}
|
||||
>Secrets</LinkTo>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTo @route="configuration" @model={{@backend}} data-test-secrets-tab="Configuration">Configuration</LinkTo>
|
||||
</li>
|
||||
</:tabLinks>
|
||||
|
||||
<KvHeader @pageTitle={{@backendModel.id}} @backend={{@backendModel}} @breadcrumbs={{@breadcrumbs}} @filter={{@filterValue}}>
|
||||
<:toolbarFilters>
|
||||
{{#if (and (not-eq @secrets 403) (or @secrets @filterValue))}}
|
||||
<KvListFilter @mountPoint={{this.mountPoint}} @filterValue={{@filterValue}} />
|
||||
{{/if}}
|
||||
</:toolbarFilters>
|
||||
|
||||
<:toolbarActions>
|
||||
<ToolbarLink
|
||||
data-test-toolbar-create-secret
|
||||
<:tabs>
|
||||
<li><LinkTo @route="list" data-test-tab="Secrets" @model={{@backend}}>Secrets</LinkTo></li>
|
||||
</:tabs>
|
||||
<:badges>
|
||||
<Hds::Badge @text="version 2" data-test-badge />
|
||||
</:badges>
|
||||
|
||||
<:actions>
|
||||
<Hds::Dropdown as |D|>
|
||||
<D.ToggleButton @text="Manage" @color="secondary" data-test-dropdown="Manage" />
|
||||
<D.Interactive
|
||||
@icon="settings"
|
||||
@route="configuration"
|
||||
@model={{@backendModel.id}}
|
||||
data-test-popup-menu="Configure"
|
||||
>Configure</D.Interactive>
|
||||
<D.Interactive
|
||||
{{on "click" (fn (mut this.engineToDisable) @backendModel)}}
|
||||
@color="critical"
|
||||
@icon="trash"
|
||||
data-test-popup-menu="Delete"
|
||||
>Delete</D.Interactive>
|
||||
</Hds::Dropdown>
|
||||
|
||||
<Hds::Button
|
||||
@text="Create secret"
|
||||
@icon="plus"
|
||||
@route="create"
|
||||
@model={{@backend}}
|
||||
@model={{@backendModel}}
|
||||
@query={{hash initialKey=@filterValue}}
|
||||
@type="add"
|
||||
>
|
||||
Create secret
|
||||
</ToolbarLink>
|
||||
</:toolbarActions>
|
||||
</KvPageHeader>
|
||||
data-test-button="create secret"
|
||||
/>
|
||||
</:actions>
|
||||
</KvHeader>
|
||||
|
||||
{{#if (eq @secrets 403)}}
|
||||
<div class="box is-fullwidth is-shadowless has-tall-padding">
|
||||
|
|
@ -177,4 +185,14 @@
|
|||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{#if this.engineToDisable}}
|
||||
<ConfirmModal
|
||||
@color="critical"
|
||||
@confirmMessage="Any data in this engine will be permanently deleted."
|
||||
@confirmTitle="Disable engine?"
|
||||
@onClose={{fn (mut this.engineToDisable) null}}
|
||||
@onConfirm={{perform this.disableEngine this.engineToDisable}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
|
@ -10,6 +10,7 @@ import { tracked } from '@glimmer/tracking';
|
|||
import { getOwner } from '@ember/owner';
|
||||
import { ancestorKeysForKey } from 'core/utils/key-utils';
|
||||
import { pathIsDirectory } from 'kv/utils/kv-breadcrumbs';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
/**
|
||||
* @module List
|
||||
|
|
@ -31,6 +32,7 @@ export default class KvListPageComponent extends Component {
|
|||
|
||||
@tracked secretPath;
|
||||
@tracked metadataToDelete = null; // set to the metadata intended to delete
|
||||
@tracked engineToDisable = undefined;
|
||||
|
||||
// used for KV list and list-directory view
|
||||
// ex: beep/
|
||||
|
|
@ -57,6 +59,24 @@ export default class KvListPageComponent extends Component {
|
|||
};
|
||||
}
|
||||
|
||||
@task
|
||||
*disableEngine(engine) {
|
||||
const { engineType, id, path } = engine;
|
||||
|
||||
try {
|
||||
yield this.api.sys.mountsDisableSecretsEngine(id);
|
||||
this.flashMessages.success(`The ${engineType} Secrets Engine at ${path} has been disabled.`);
|
||||
this.router.transitionTo('vault.cluster.secrets.backends');
|
||||
} catch (err) {
|
||||
const { message } = yield this.api.parseError(err);
|
||||
this.flashMessages.danger(
|
||||
`There was an error disabling the ${engineType} Secrets Engine at ${path}: ${message}.`
|
||||
);
|
||||
} finally {
|
||||
this.engineToDisable = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
async onDelete(secretPath) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @secretPath={{@path}}>
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}} @secretPath={{@path}}>
|
||||
<:syncDetails>
|
||||
{{#if this.syncStatus}}
|
||||
<Hds::Alert data-test-sync-alert @type="inline" class="has-top-margin-s has-bottom-margin-m" @color="neutral" as |A|>
|
||||
|
|
@ -45,7 +45,7 @@
|
|||
{{/if}}
|
||||
</:syncDetails>
|
||||
|
||||
<:tabLinks>
|
||||
<:tabs>
|
||||
<li>
|
||||
<LinkTo @route="secret.index" @models={{array @backend @path}} data-test-secrets-tab="Overview">Overview</LinkTo>
|
||||
</li>
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
>Version History</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</:tabLinks>
|
||||
</:tabs>
|
||||
|
||||
<:toolbarFilters>
|
||||
{{#unless this.emptyState}}
|
||||
|
|
@ -132,7 +132,7 @@
|
|||
</ToolbarLink>
|
||||
{{/if}}
|
||||
</:toolbarActions>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
{{#if (or this.isSecretDeleted (not this.emptyState))}}
|
||||
<div class="info-table-row-header">
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Create New Version">
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Create New Version">
|
||||
<:toolbarFilters>
|
||||
<Toggle @name="json" @checked={{this.showJsonView}} @onChange={{fn (mut this.showJsonView)}}>
|
||||
<span class="has-text-grey">JSON</span>
|
||||
</Toggle>
|
||||
</:toolbarFilters>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
{{#if this.showOldVersionAlert}}
|
||||
<Hds::Alert data-test-secret-version-alert @type="inline" @color="warning" class="has-top-bottom-margin" as |A|>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @secretPath={{@path}}>
|
||||
<:tabLinks>
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}} @secretPath={{@path}}>
|
||||
<:tabs>
|
||||
<li>
|
||||
<LinkTo @route="secret.index" @models={{array @backend @path}} data-test-secrets-tab="Overview">Overview</LinkTo>
|
||||
</li>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
>Version History</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</:tabLinks>
|
||||
</:tabs>
|
||||
|
||||
<:toolbarActions>
|
||||
{{#if @capabilities.canDeleteMetadata}}
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
</ToolbarLink>
|
||||
{{/if}}
|
||||
</:toolbarActions>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
<h2 class="title is-5 has-bottom-padding-s has-top-margin-l">
|
||||
Custom metadata
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Edit Secret Metadata" />
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Edit Secret Metadata" />
|
||||
|
||||
{{#if @capabilities.canUpdateMetadata}}
|
||||
<hr class="is-marginless has-background-gray-200" />
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Version Diff">
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Version Diff">
|
||||
<:toolbarFilters>
|
||||
<span class="has-text-grey has-text-weight-semibold is-size-8">FROM:</span>
|
||||
<KvVersionDropdown
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
</:toolbarFilters>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
{{#if this.deactivatedState}}
|
||||
<EmptyState
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @secretPath={{@path}}>
|
||||
<:tabLinks>
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}} @secretPath={{@path}}>
|
||||
<:tabs>
|
||||
<li>
|
||||
<LinkTo @route="secret.index" @models={{array @backend @path}} data-test-secrets-tab="Overview">Overview</LinkTo>
|
||||
</li>
|
||||
|
|
@ -29,14 +29,14 @@
|
|||
Version History
|
||||
</LinkTo>
|
||||
</li>
|
||||
</:tabLinks>
|
||||
</:tabs>
|
||||
|
||||
<:toolbarActions>
|
||||
{{#if @capabilities.canReadMetadata}}
|
||||
<ToolbarLink @route="secret.metadata.diff" @models={{array @backend @path}}>Version diff</ToolbarLink>
|
||||
{{/if}}
|
||||
</:toolbarActions>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
{{#if @capabilities.canReadMetadata}}
|
||||
<div class="sub-text has-text-weight-semibold is-flex-end has-short-padding">
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @secretPath={{@path}}>
|
||||
<:tabLinks>
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}} @secretPath={{@path}}>
|
||||
<:tabs>
|
||||
<li>
|
||||
<LinkTo @route="secret.index" @models={{array @backend @path}} data-test-secrets-tab="Overview">Overview</LinkTo>
|
||||
</li>
|
||||
|
|
@ -30,10 +30,10 @@
|
|||
>Version History</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</:tabLinks>
|
||||
</:tabs>
|
||||
<:toolbarActions>
|
||||
</:toolbarActions>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
{{#if (or @metadata @subkeys.metadata)}}
|
||||
<div class="flex row-wrap gap-24 has-top-margin-l">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Patch Secret to New Version" />
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Patch Secret to New Version" />
|
||||
{{#if this.controlGroupError}}
|
||||
<ControlGroupInlineError @error={{this.controlGroupError}} class="has-top-margin-s has-bottom-margin-s">
|
||||
<:customMessage>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @secretPath={{@path}}>
|
||||
<:tabLinks>
|
||||
<KvHeader @breadcrumbs={{@breadcrumbs}} @pageTitle={{@path}} @secretPath={{@path}}>
|
||||
<:tabs>
|
||||
<li>
|
||||
<LinkTo @route="secret.index" @models={{array @backend @path}} data-test-secrets-tab="Overview">Overview</LinkTo>
|
||||
</li>
|
||||
|
|
@ -36,7 +36,7 @@
|
|||
</LinkTo>
|
||||
</li>
|
||||
{{/if}}
|
||||
</:tabLinks>
|
||||
</KvPageHeader>
|
||||
</:tabs>
|
||||
</KvHeader>
|
||||
|
||||
<KvPathsCard @backend={{@backend}} @path={{@path}} class="has-top-margin-xl" />
|
||||
|
|
@ -3,13 +3,13 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{@breadcrumbs}} @pageTitle="Create Secret">
|
||||
<KvHeader @backend={{@backend}} @breadcrumbs={{@breadcrumbs}} @pageTitle="Create Secret">
|
||||
<:toolbarFilters>
|
||||
<Toggle @name="json" @checked={{this.showJsonView}} @onChange={{fn (mut this.showJsonView)}}>
|
||||
<span class="has-text-grey">JSON</span>
|
||||
</Toggle>
|
||||
</:toolbarFilters>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
|
||||
<KvCreateEditForm
|
||||
@form={{@form}}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default class KvEngine extends Engine {
|
|||
'secret-mount-path',
|
||||
'version',
|
||||
],
|
||||
externalRoutes: ['secrets', 'syncDestination'],
|
||||
externalRoutes: ['secrets', 'syncDestination', 'secretsGeneralSettingsConfiguration'],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,4 +25,5 @@ export default buildRoutes(function () {
|
|||
});
|
||||
});
|
||||
this.route('configuration');
|
||||
this.route('configure');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,40 +11,21 @@ export default class KvConfigurationRoute extends Route {
|
|||
|
||||
async model() {
|
||||
const backend = this.modelFor('application');
|
||||
const {
|
||||
type,
|
||||
path,
|
||||
accessor,
|
||||
running_plugin_version,
|
||||
local,
|
||||
seal_wrap,
|
||||
config: { default_lease_ttl, max_lease_ttl },
|
||||
options: { version },
|
||||
} = await this.api.sys.internalUiReadMountInformation(backend.id);
|
||||
// display mount config if engine config request fails
|
||||
const engineConfig = await this.api.secrets.kvV2ReadConfiguration(backend.id).catch(() => {});
|
||||
|
||||
return {
|
||||
...engineConfig,
|
||||
type,
|
||||
path,
|
||||
accessor,
|
||||
running_plugin_version,
|
||||
local,
|
||||
seal_wrap,
|
||||
default_lease_ttl,
|
||||
max_lease_ttl,
|
||||
version,
|
||||
};
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
const { id } = this.modelFor('application');
|
||||
controller.backend = id;
|
||||
const backend = this.modelFor('application');
|
||||
controller.backend = backend;
|
||||
controller.breadcrumbs = [
|
||||
{ label: 'Secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: id, route: 'list', model: id },
|
||||
{ label: backend.id, route: 'list', model: backend.id },
|
||||
{ label: 'Configuration' },
|
||||
];
|
||||
}
|
||||
|
|
|
|||
35
ui/lib/kv/addon/routes/configure.js
Normal file
35
ui/lib/kv/addon/routes/configure.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import KvForm from 'vault/forms/secrets/kv';
|
||||
import { service } from '@ember/service';
|
||||
|
||||
export default class KvConfigureRoute extends Route {
|
||||
@service api;
|
||||
@service('app-router') router;
|
||||
|
||||
async model() {
|
||||
const backend = this.modelFor('application');
|
||||
const engineConfig = await this.api.secrets.kvV2ReadConfiguration(backend.id).catch(() => {});
|
||||
const { max_versions, cas_required, delete_version_after } = engineConfig;
|
||||
|
||||
return {
|
||||
form: new KvForm({ path: backend.id, max_versions, cas_required, delete_version_after }),
|
||||
};
|
||||
}
|
||||
|
||||
setupController(controller, resolvedModel) {
|
||||
super.setupController(controller, resolvedModel);
|
||||
const backend = this.modelFor('application');
|
||||
controller.backend = backend;
|
||||
controller.breadcrumbs = [
|
||||
{ label: 'Secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: backend.id, route: 'list', model: backend.id },
|
||||
{ label: 'Configuration', route: 'configuration', model: backend },
|
||||
{ label: 'Edit' },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -58,8 +58,10 @@ export default class KvSecretsListRoute extends Route {
|
|||
const filterValue = pathToSecret ? (pageFilter ? pathToSecret + pageFilter : pathToSecret) : pageFilter;
|
||||
const secrets = await this.fetchMetadata(backend, pathToSecret, params);
|
||||
const capabilities = await this.capabilities.for('kvMetadata', { backend, path: path_to_secret });
|
||||
const backendModel = this.modelFor('application');
|
||||
|
||||
return {
|
||||
backendModel,
|
||||
secrets,
|
||||
backend,
|
||||
pathToSecret,
|
||||
|
|
|
|||
6
ui/lib/kv/addon/templates/configure.hbs
Normal file
6
ui/lib/kv/addon/templates/configure.hbs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Configure @form={{this.model.form}} @backend={{this.backend}} @breadcrumbs={{this.breadcrumbs}} />
|
||||
|
|
@ -3,15 +3,20 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<KvPageHeader @breadcrumbs={{this.breadcrumbs}} @mountName={{this.mountName}}>
|
||||
<:tabLinks>
|
||||
<KvHeader
|
||||
@backend={{this.backend}}
|
||||
@pageTitle={{this.backend}}
|
||||
@breadcrumbs={{this.breadcrumbs}}
|
||||
@mountName={{this.mountName}}
|
||||
>
|
||||
<:tabs>
|
||||
<li><LinkTo @route="list" @model={{this.mountName}} data-test-secrets-tab="Secrets">Secrets</LinkTo></li>
|
||||
<li><LinkTo
|
||||
@route="configuration"
|
||||
@model={{this.mountName}}
|
||||
data-test-secrets-tab="Configuration"
|
||||
>Configuration</LinkTo></li>
|
||||
</:tabLinks>
|
||||
</KvPageHeader>
|
||||
</:tabs>
|
||||
</KvHeader>
|
||||
|
||||
<Page::Error @error={{this.model}} />
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
<Page::List
|
||||
@secrets={{this.model.secrets}}
|
||||
@backend={{this.model.backend}}
|
||||
@backendModel={{this.model.backendModel}}
|
||||
@pathToSecret={{this.model.pathToSecret}}
|
||||
@filterValue={{this.model.filterValue}}
|
||||
@failedDirectoryQuery={{this.model.failedDirectoryQuery}}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<Page::List
|
||||
@secrets={{this.model.secrets}}
|
||||
@backend={{this.model.backend}}
|
||||
@backendModel={{this.model.backendModel}}
|
||||
@pathToSecret={{this.model.pathToSecret}}
|
||||
@filterValue={{this.model.filterValue}}
|
||||
@failedDirectoryQuery={{this.model.failedDirectoryQuery}}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
"ember-concurrency": "*",
|
||||
"@ember/test-waiters": "*",
|
||||
"ember-inflector": "*",
|
||||
"ember-template-lint": "*"
|
||||
"ember-template-lint": "*",
|
||||
"ember-cli-typescript": "*"
|
||||
},
|
||||
"ember-addon": {
|
||||
"paths": [
|
||||
|
|
|
|||
|
|
@ -85,7 +85,6 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) {
|
|||
});
|
||||
|
||||
test('it can navigate to secrets within a secret directory', async function (assert) {
|
||||
assert.expect(23);
|
||||
const backend = this.backend;
|
||||
const [root, subdirectory, secret] = this.fullSecretPath.split('/');
|
||||
|
||||
|
|
@ -115,14 +114,16 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) {
|
|||
);
|
||||
|
||||
// Title correct
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText(`${backend}`);
|
||||
// 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');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
// Create button correct
|
||||
assert.dom(GENERAL.button('create secret')).exists('renders create secret button');
|
||||
// Toolbar correct
|
||||
assert.dom(PAGE.toolbarAction).exists({ count: 1 }, 'toolbar only renders create secret action');
|
||||
assert.dom(PAGE.list.filter).hasValue(`${root}/`);
|
||||
// List content correct
|
||||
assert.dom(GENERAL.listItem(`${subdirectory}/`)).exists('renders linked block for subdirectory');
|
||||
|
|
@ -134,7 +135,7 @@ module('Acceptance | kv-v2 workflow | edge cases', function (hooks) {
|
|||
.hasText(`Current version The current version of this secret. 1`);
|
||||
// Secret details visible
|
||||
await click(PAGE.secretTab('Secret'));
|
||||
assert.dom(PAGE.title).hasText(this.fullSecretPath);
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText(this.fullSecretPath);
|
||||
assert.dom(PAGE.secretTab('Secret')).hasText('Secret');
|
||||
assert.dom(PAGE.secretTab('Secret')).hasClass('active');
|
||||
assert.dom(PAGE.secretTab('Metadata')).hasText('Metadata');
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
return login(token);
|
||||
});
|
||||
test('empty backend - breadcrumbs, title, tabs, emptyState (a)', async function (assert) {
|
||||
assert.expect(23);
|
||||
assert.expect(19);
|
||||
const backend = this.emptyBackend;
|
||||
await navToBackend(backend);
|
||||
|
||||
|
|
@ -254,20 +254,18 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
'lands on secrets list page'
|
||||
);
|
||||
// CONFIGURATION TAB
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
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');
|
||||
assert.dom(GENERAL.tabLink('plugin-settings')).hasClass('active');
|
||||
// SECRETS TAB
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
await click(GENERAL.tab('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');
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasClass('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.');
|
||||
|
|
@ -295,7 +293,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.expect(count);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, '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');
|
||||
|
|
@ -308,7 +306,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
||||
|
||||
|
|
@ -319,7 +317,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
||||
|
||||
|
|
@ -618,12 +616,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
// Breadcrumbs correct
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
// Title correct
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
// 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');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasClass('active');
|
||||
// Dropdown correct
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
// Toolbar correct
|
||||
assert.dom(PAGE.toolbar).exists({ count: 1 }, 'toolbar renders');
|
||||
assert
|
||||
|
|
@ -662,7 +662,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.expect(23);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, 'title text correct');
|
||||
assert.dom(PAGE.emptyStateTitle).doesNotExist('No empty state');
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert
|
||||
|
|
@ -757,16 +757,18 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.dom(PAGE.metadata.editBtn).doesNotExist('edit button hidden');
|
||||
});
|
||||
test('breadcrumbs & page titles are correct (dr)', async function (assert) {
|
||||
assert.expect(35);
|
||||
assert.expect(36);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title correct on config page');
|
||||
assert.dom(PAGE.title).hasText(`${backend} configuration`, 'title correct on config page');
|
||||
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title correct on secrets list');
|
||||
assert.dom(PAGE.title).hasText(backend, 'title correct on secrets list');
|
||||
|
||||
await typeIn(PAGE.list.overviewInput, 'app/nested/secret');
|
||||
await click(GENERAL.submitButton);
|
||||
|
|
@ -816,12 +818,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
// Breadcrumbs correct
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
// Title correct
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
// 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');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasClass('active');
|
||||
// Dropdown correct
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
// 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.');
|
||||
|
|
@ -847,7 +851,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.expect(32);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, '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');
|
||||
|
|
@ -860,7 +864,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).doesNotExist('List filter hidden since no nested list access');
|
||||
|
||||
assert
|
||||
|
|
@ -956,17 +960,20 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.dom(PAGE.metadata.editBtn).doesNotExist('edit button hidden');
|
||||
});
|
||||
test('breadcrumbs & page titles are correct (dlr)', async function (assert) {
|
||||
assert.expect(29);
|
||||
assert.expect(30);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
||||
assert.dom(PAGE.title).hasText(`${backend} configuration`, 'correct page title for configuration');
|
||||
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
await click(GENERAL.tab('Secrets'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
||||
assert.dom(PAGE.title).hasText(backend, 'correct page title for secret list');
|
||||
|
||||
await click(PAGE.list.item(secretPath));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
||||
|
|
@ -1013,12 +1020,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
// Breadcrumbs correct
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
// Title correct
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
// 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');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasClass('active');
|
||||
// Dropdown correct
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
// 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.');
|
||||
|
|
@ -1044,7 +1053,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.expect(42);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, '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');
|
||||
|
|
@ -1057,7 +1066,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
||||
|
||||
|
|
@ -1068,7 +1077,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
||||
|
||||
|
|
@ -1182,16 +1191,19 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
);
|
||||
});
|
||||
test('breadcrumbs & page titles are correct (mm)', async function (assert) {
|
||||
assert.expect(39);
|
||||
assert.expect(40);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
||||
assert.dom(PAGE.title).hasText(`${backend} configuration`, 'correct page title for configuration');
|
||||
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
await click(GENERAL.tab('Secrets'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
||||
assert.dom(PAGE.title).hasText(backend, 'correct page title for secret list');
|
||||
|
||||
await click(PAGE.list.item(secretPath));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, secretPath]);
|
||||
|
|
@ -1242,12 +1254,14 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
// Breadcrumbs correct
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
// Title correct
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
// 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');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasText('Secrets');
|
||||
assert.dom(GENERAL.tab('Secrets')).hasClass('active');
|
||||
// Dropdown correct
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
// 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');
|
||||
|
|
@ -1275,7 +1289,7 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
assert.expect(24);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, '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');
|
||||
|
|
@ -1398,16 +1412,19 @@ module('Acceptance | kv-v2 workflow | navigation', function (hooks) {
|
|||
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);
|
||||
assert.expect(40);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for configuration');
|
||||
assert.dom(PAGE.title).hasText(`${backend} configuration`, 'correct page title for configuration');
|
||||
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
await click(GENERAL.tab('Secrets'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
||||
assert.dom(PAGE.title).hasText(backend, 'correct page title for secret list');
|
||||
|
||||
await typeIn(PAGE.list.overviewInput, secretPath);
|
||||
await click(GENERAL.submitButton);
|
||||
|
|
@ -1473,7 +1490,7 @@ path "${this.backend}/subkeys/*" {
|
|||
assert.expect(44);
|
||||
const backend = this.backend;
|
||||
await navToBackend(backend);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'title text correct');
|
||||
assert.dom(PAGE.title).hasText(backend, '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');
|
||||
|
|
@ -1486,7 +1503,7 @@ path "${this.backend}/subkeys/*" {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('nested/')).exists('Shows nested secret');
|
||||
|
||||
|
|
@ -1497,7 +1514,7 @@ path "${this.backend}/subkeys/*" {
|
|||
`navigated to ${currentURL()}`
|
||||
);
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'app', 'nested']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`);
|
||||
assert.dom(PAGE.title).hasText(backend);
|
||||
assert.dom(PAGE.list.filter).hasValue('app/nested/', 'List filter input is prefilled');
|
||||
assert.dom(PAGE.list.item('secret')).exists('Shows deeply nested secret');
|
||||
|
||||
|
|
@ -1555,16 +1572,20 @@ path "${this.backend}/subkeys/*" {
|
|||
);
|
||||
});
|
||||
test('breadcrumbs & page titles are correct (cg)', async function (assert) {
|
||||
assert.expect(42);
|
||||
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');
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
|
||||
await click(PAGE.secretTab('Secrets'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend, 'Configuration']);
|
||||
assert.dom(PAGE.title).hasText(`${backend} configuration`, 'correct page title for configuration');
|
||||
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/list`);
|
||||
await click(GENERAL.tab('Secrets'));
|
||||
assertCorrectBreadcrumbs(assert, ['Secrets', backend]);
|
||||
assert.dom(PAGE.title).hasText(`${backend} version 2`, 'correct page title for secret list');
|
||||
assert.dom(PAGE.title).hasText(backend, 'correct page title for secret list');
|
||||
|
||||
await visit(`/vault/secrets-engines/${backend}/kv/${secretPathUrlEncoded}/details`);
|
||||
|
||||
|
|
|
|||
|
|
@ -58,7 +58,9 @@ module('Acceptance | secrets/secret/create, read, delete', function (hooks) {
|
|||
|
||||
await click(GENERAL.submitButton);
|
||||
|
||||
await click(PAGE.secretTab('Configuration'));
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
await click(GENERAL.tabLink('plugin-settings'));
|
||||
|
||||
assert
|
||||
.dom(PAGE.infoRowValue('Maximum number of versions'))
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import { runCmd, tokenWithPolicyCmd } from 'vault/tests/helpers/commands';
|
|||
|
||||
import { create } from 'ember-cli-page-object';
|
||||
import page from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
import configPage from 'vault/tests/pages/secrets/backend/configuration';
|
||||
import { login } from 'vault/tests/helpers/auth/auth-helpers';
|
||||
import consoleClass from 'vault/tests/pages/components/console/ui-panel';
|
||||
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
|
||||
|
|
@ -69,10 +68,17 @@ module('Acceptance | secrets-engines/enable', function (hooks) {
|
|||
await page.maxTTLUnit('h').maxTTLVal(maxTTLHours);
|
||||
await click(GENERAL.submitButton);
|
||||
|
||||
// TODO: Update this when KVV2 is migrated to new configuration flow
|
||||
await click('[data-test-secrets-tab="Configuration"]');
|
||||
assert.strictEqual(configPage.defaultTTL, `${this.calcDays(defaultTTLHours)}`, 'shows the proper TTL');
|
||||
assert.strictEqual(configPage.maxTTL, `${this.calcDays(maxTTLHours)}`, 'shows the proper max TTL');
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
await click(GENERAL.tabLink('general-settings'));
|
||||
assert
|
||||
.dom(GENERAL.inputByAttr('default_lease_ttl'))
|
||||
.hasValue(`${defaultTTLHours}`, 'shows the proper TTL');
|
||||
assert.dom(GENERAL.selectByAttr('default_lease_ttl')).hasValue('h', 'shows the proper TTL unit');
|
||||
|
||||
assert.dom(GENERAL.inputByAttr('max_lease_ttl')).hasValue(`${maxTTLHours}`, 'shows the proper max TTL');
|
||||
assert.dom(GENERAL.selectByAttr('max_lease_ttl')).hasValue('h', 'shows the proper max TTL unit');
|
||||
});
|
||||
|
||||
test('it sets the ttl when enabled then disabled', async function (assert) {
|
||||
|
|
@ -91,10 +97,14 @@ module('Acceptance | secrets-engines/enable', function (hooks) {
|
|||
await page.maxTTLUnit('h').maxTTLVal(maxTTLHours);
|
||||
await click(GENERAL.submitButton);
|
||||
|
||||
// TODO: Update this when KVV2 is migrated to new configuration flow
|
||||
await click('[data-test-secrets-tab="Configuration"]');
|
||||
assert.strictEqual(configPage.defaultTTL, '1 month 1 day', 'shows system default TTL');
|
||||
assert.strictEqual(configPage.maxTTL, `${this.calcDays(maxTTLHours)}`, 'shows the proper max TTL');
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
await click(GENERAL.menuItem('Configure'));
|
||||
await click(GENERAL.tabLink('general-settings'));
|
||||
|
||||
assert.dom(GENERAL.inputByAttr('default_lease_ttl')).hasValue('32', 'shows system default TTL');
|
||||
assert.dom(GENERAL.inputByAttr('max_lease_ttl')).hasValue(`${maxTTLHours}`, 'shows the proper max TTL');
|
||||
assert.dom(GENERAL.selectByAttr('max_lease_ttl')).hasValue('h', 'shows the proper max TTL unit');
|
||||
});
|
||||
|
||||
test('it sets the max ttl after pki chosen, resets after', async function (assert) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
export const PAGE = {
|
||||
// General selectors that are common between pages
|
||||
title: '[data-test-header-title]',
|
||||
title: '.hds-page-header__title',
|
||||
breadcrumbs: '[data-test-breadcrumbs]',
|
||||
breadcrumb: '[data-test-breadcrumbs] li',
|
||||
breadcrumbAtIdx: (idx) => `[data-test-breadcrumbs] li:nth-child(${idx + 1}) a`,
|
||||
|
|
@ -56,7 +56,7 @@ export const PAGE = {
|
|||
toggleDiffDescription: '[data-test-diff-description]',
|
||||
},
|
||||
list: {
|
||||
createSecret: '[data-test-toolbar-create-secret]',
|
||||
createSecret: '[data-test-button="create secret"]',
|
||||
item: (secret) => (!secret ? '[data-test-list-item]' : `[data-test-list-item="${secret}"]`),
|
||||
menuItem: (label) => `[data-test-list-menu-item="${label}"]`,
|
||||
filter: `[data-test-kv-list-filter]`,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@ import { setupRenderingTest } from 'vault/tests/helpers';
|
|||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
|
||||
module('Integration | Component | kv | kv-page-header', function (hooks) {
|
||||
module('Integration | Component | kv | kv-header', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'kv');
|
||||
|
||||
|
|
@ -27,16 +28,20 @@ module('Integration | Component | kv | kv-page-header', function (hooks) {
|
|||
this.renderComponent = () =>
|
||||
render(
|
||||
hbs`
|
||||
<KvPageHeader
|
||||
<KvHeader
|
||||
@backend={{this.backend}}
|
||||
@breadcrumbs={{this.breadcrumbs}}
|
||||
@pageTitle={{this.pageTitle}}
|
||||
@mountName={{this.mountName}}
|
||||
@secretPath={{this.secretPath}}
|
||||
>
|
||||
<:tabLinks>
|
||||
<:tabs>
|
||||
<li><LinkTo @route="list" data-test-secrets-tab="Secrets">Secrets</LinkTo></li>
|
||||
<li><LinkTo @route="configuration" data-test-secrets-tab="Configuration">Configuration</LinkTo></li>
|
||||
</:tabLinks>
|
||||
</:tabs>
|
||||
|
||||
<:badges>
|
||||
<Hds::Badge @text="version 2" data-test-badge />
|
||||
</:badges>
|
||||
|
||||
<:toolbarActions>
|
||||
<ToolbarLink @route="secrets.create" @type="add">Create secret</ToolbarLink>
|
||||
|
|
@ -45,7 +50,7 @@ module('Integration | Component | kv | kv-page-header', function (hooks) {
|
|||
<:toolbarFilters>
|
||||
<p>stuff here</p>
|
||||
</:toolbarFilters>
|
||||
</KvPageHeader>
|
||||
</KvHeader>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
|
@ -66,38 +71,38 @@ module('Integration | Component | kv | kv-page-header', function (hooks) {
|
|||
assert.expect(2);
|
||||
this.pageTitle = 'Create new version';
|
||||
await this.renderComponent();
|
||||
assert.dom('[data-test-header-title]').hasText('Create new version', 'displays custom title.');
|
||||
assert.dom('[data-test-header-title] svg').doesNotExist('Does not show icon if not at engine level.');
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Create new version', 'displays custom title.');
|
||||
assert.dom(GENERAL.icon('key-values')).doesNotExist('Does not show icon if not at engine level.');
|
||||
});
|
||||
|
||||
test('it renders a title and copy button for @secretPath', async function (assert) {
|
||||
assert.expect(3);
|
||||
this.secretPath = 'my/secret/path';
|
||||
this.pageTitle = this.secretPath;
|
||||
await this.renderComponent();
|
||||
assert.dom('[data-test-header-title]').hasText('my/secret/path', 'displays path');
|
||||
assert.dom('[data-test-header-title] button').exists('renders copy button for path');
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('my/secret/path', 'displays path');
|
||||
assert.dom(GENERAL.copyButton).exists('renders copy button for path');
|
||||
assert.dom('[data-test-icon="clipboard-copy"]').exists('renders copy icon');
|
||||
});
|
||||
|
||||
test('it renders a title, icon and tag if engine view', async function (assert) {
|
||||
assert.expect(2);
|
||||
this.mountName = this.backend;
|
||||
assert.expect(3);
|
||||
this.breadcrumbs = [
|
||||
{ label: 'Secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: this.backend, route: 'secrets' },
|
||||
];
|
||||
this.backend = { id: this.backend, icon: 'key-values' };
|
||||
this.pageTitle = this.backend.id;
|
||||
await this.renderComponent();
|
||||
assert
|
||||
.dom('[data-test-header-title]')
|
||||
.hasText(`${this.backend} version 2`, 'Mount path and version tag render for title.');
|
||||
assert
|
||||
.dom('[data-test-header-title] [data-test-icon="key-values"]')
|
||||
.exists('An icon renders next to title.');
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText(`${this.pageTitle}`, 'Mount path renders for title.');
|
||||
assert.dom(GENERAL.badge()).hasText('version 2', 'version badge renders in header.');
|
||||
assert.dom(GENERAL.icon('key-values')).exists('An icon renders next to title.');
|
||||
});
|
||||
|
||||
test('it renders tabs', async function (assert) {
|
||||
assert.expect(2);
|
||||
assert.expect(1);
|
||||
await this.renderComponent();
|
||||
assert.dom('[data-test-secrets-tab="Secrets"]').hasText('Secrets', 'Secrets tab renders');
|
||||
assert
|
||||
.dom('[data-test-secrets-tab="Configuration"]')
|
||||
.hasText('Configuration', 'Configuration tab renders');
|
||||
});
|
||||
|
||||
test('it should yield block for toolbar actions', async function (assert) {
|
||||
|
|
@ -6,9 +6,10 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { render, click } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { PAGE } from 'vault/tests/helpers/kv/kv-selectors';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
|
||||
module('Integration | Component | kv-v2 | Page::Configuration', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
|
@ -29,7 +30,29 @@ module('Integration | Component | kv-v2 | Page::Configuration', function (hooks)
|
|||
max_lease_ttl: '123h',
|
||||
version: '2',
|
||||
};
|
||||
this.backend = 'my-kv';
|
||||
this.backend = {
|
||||
accessor: 'kv_05319fa9',
|
||||
config: {
|
||||
default_lease_ttl: 2764800,
|
||||
force_no_cache: false,
|
||||
listing_visibility: 'hidden',
|
||||
max_lease_ttl: 2764800,
|
||||
},
|
||||
description: '',
|
||||
external_entropy_access: false,
|
||||
local: false,
|
||||
options: {
|
||||
version: '2',
|
||||
},
|
||||
path: 'my-kv/',
|
||||
plugin_version: '',
|
||||
running_plugin_version: 'v0.25.0+builtin',
|
||||
running_sha256: '',
|
||||
seal_wrap: false,
|
||||
type: 'kv',
|
||||
uuid: '0cd6346f-c93a-ecfa-b01d-6b690a745c8e',
|
||||
id: 'my-kv',
|
||||
};
|
||||
this.breadcrumbs = [
|
||||
{ label: 'Secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: 'my-kv', route: 'list' },
|
||||
|
|
@ -38,7 +61,7 @@ module('Integration | Component | kv-v2 | Page::Configuration', function (hooks)
|
|||
});
|
||||
|
||||
test('it renders kv configuration details', async function (assert) {
|
||||
assert.expect(15);
|
||||
assert.expect(6);
|
||||
|
||||
await render(
|
||||
hbs`
|
||||
|
|
@ -51,21 +74,13 @@ module('Integration | Component | kv-v2 | Page::Configuration', function (hooks)
|
|||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(PAGE.title).includesText('my-kv', 'renders engine path as page title');
|
||||
assert.dom(PAGE.secretTab('Secrets')).exists('renders Secrets tab');
|
||||
assert.dom(PAGE.secretTab('Configuration')).exists('renders Configuration tab');
|
||||
assert.dom(PAGE.title).includesText('my-kv configuration', 'renders engine path as page title');
|
||||
assert.dom(GENERAL.tab('general-settings')).exists('renders general settings tab');
|
||||
assert.dom(GENERAL.tab('plugin-settings')).exists('renders kv settings tab');
|
||||
|
||||
await click(GENERAL.tab('plugin-settings'));
|
||||
assert.dom(PAGE.infoRowValue('Require check and set')).hasText('Yes');
|
||||
assert.dom(PAGE.infoRowValue('Automate secret deletion')).hasText('Never delete');
|
||||
assert.dom(PAGE.infoRowValue('Maximum number of versions')).hasText('0');
|
||||
assert.dom(PAGE.infoRowValue('Type')).hasText('kv');
|
||||
assert.dom(PAGE.infoRowValue('Path')).hasText('my-kv');
|
||||
assert.dom(PAGE.infoRowValue('Accessor')).hasText('kv_80616825');
|
||||
assert.dom(PAGE.infoRowValue('Running plugin version')).hasText('2.7.0');
|
||||
assert.dom(PAGE.infoRowValue('Local')).hasText('No');
|
||||
assert.dom(PAGE.infoRowValue('Seal wrap')).hasText('No');
|
||||
assert.dom(PAGE.infoRowValue('Default Lease TTL')).hasText('3 days');
|
||||
assert.dom(PAGE.infoRowValue('Max Lease TTL')).hasText('5 days 3 hours');
|
||||
assert.dom(PAGE.infoRowValue('Version')).hasText('2');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { GENERAL } from 'vault/tests/helpers/general-selectors';
|
||||
import KvForm from 'vault/forms/secrets/kv';
|
||||
|
||||
module('Integration | Component | kv-v2 | Page::Configure', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'kv');
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
this.config = {
|
||||
cas_required: true,
|
||||
max_versions: 5,
|
||||
delete_version_after: '10000s',
|
||||
};
|
||||
this.form = new KvForm({});
|
||||
this.editForm = new KvForm(this.config);
|
||||
this.backend = {
|
||||
accessor: 'kv_05319fa9',
|
||||
config: {
|
||||
default_lease_ttl: 2764800,
|
||||
force_no_cache: false,
|
||||
listing_visibility: 'hidden',
|
||||
max_lease_ttl: 2764800,
|
||||
},
|
||||
description: '',
|
||||
external_entropy_access: false,
|
||||
local: false,
|
||||
options: {
|
||||
version: '2',
|
||||
},
|
||||
path: 'my-kv/',
|
||||
plugin_version: '',
|
||||
running_plugin_version: 'v0.25.0+builtin',
|
||||
running_sha256: '',
|
||||
seal_wrap: false,
|
||||
type: 'kv',
|
||||
uuid: '0cd6346f-c93a-ecfa-b01d-6b690a745c8e',
|
||||
id: 'my-kv',
|
||||
};
|
||||
this.breadcrumbs = [
|
||||
{ label: 'Secrets', route: 'secrets', linkExternal: true },
|
||||
{ label: 'my-kv', route: 'list' },
|
||||
{ label: 'Configuration', route: 'configuration', model: this.backend },
|
||||
{ label: 'Edit' },
|
||||
];
|
||||
});
|
||||
|
||||
test('it renders kv configure form', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await render(
|
||||
hbs`
|
||||
<Page::Configure
|
||||
@form={{this.form}}
|
||||
@backend={{this.backend}}
|
||||
@breadcrumbs={{this.breadcrumbs}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(GENERAL.inputByAttr('max_versions')).exists();
|
||||
assert.dom(GENERAL.inputByAttr('cas_required')).exists();
|
||||
assert.dom(GENERAL.toggleInput('Automate secret deletion')).exists();
|
||||
});
|
||||
|
||||
test('it renders kv configure form with existing config', async function (assert) {
|
||||
assert.expect(5);
|
||||
this.form = this.editForm;
|
||||
|
||||
await render(
|
||||
hbs`
|
||||
<Page::Configure
|
||||
@form={{this.form}}
|
||||
@backend={{this.backend}}
|
||||
@breadcrumbs={{this.breadcrumbs}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom(GENERAL.inputByAttr('max_versions')).hasValue('5');
|
||||
assert.dom(GENERAL.inputByAttr('cas_required')).hasValue('on');
|
||||
assert.dom(GENERAL.toggleInput('Automate secret deletion')).isChecked();
|
||||
assert.dom(GENERAL.ttl.input('Automate secret deletion')).hasValue('10000');
|
||||
assert.dom(GENERAL.selectByAttr('ttl-unit')).hasValue('s');
|
||||
});
|
||||
});
|
||||
|
|
@ -38,6 +38,29 @@ module('Integration | Component | kv-v2 | Page::List', function (hooks) {
|
|||
{ label: this.backend, route: 'list' },
|
||||
];
|
||||
this.capabilities = { canRead: true, canDelete: true };
|
||||
this.backendModel = {
|
||||
accessor: 'kv_05319fa9',
|
||||
config: {
|
||||
default_lease_ttl: 2764800,
|
||||
force_no_cache: false,
|
||||
listing_visibility: 'hidden',
|
||||
max_lease_ttl: 2764800,
|
||||
},
|
||||
description: '',
|
||||
external_entropy_access: false,
|
||||
local: false,
|
||||
options: {
|
||||
version: '2',
|
||||
},
|
||||
path: 'kv-engine/',
|
||||
plugin_version: '',
|
||||
running_plugin_version: 'v0.25.0+builtin',
|
||||
running_sha256: '',
|
||||
seal_wrap: false,
|
||||
type: 'kv',
|
||||
uuid: '0cd6346f-c93a-ecfa-b01d-6b690a745c8e',
|
||||
id: 'kv-engine',
|
||||
};
|
||||
|
||||
this.renderComponent = () =>
|
||||
render(
|
||||
|
|
@ -45,6 +68,7 @@ module('Integration | Component | kv-v2 | Page::List', function (hooks) {
|
|||
<Page::List
|
||||
@secrets={{this.secrets}}
|
||||
@backend={{this.backend}}
|
||||
@backendModel={{this.backendModel}}
|
||||
@pathToSecret={{this.pathToSecret}}
|
||||
@filterValue={{this.filterValue}}
|
||||
@failedDirectoryQuery={{this.failedDirectoryQuery}}
|
||||
|
|
@ -69,8 +93,10 @@ module('Integration | Component | kv-v2 | Page::List', function (hooks) {
|
|||
await this.renderComponent();
|
||||
|
||||
assert.dom(PAGE.title).includesText(this.backend, 'renders mount path as page title');
|
||||
assert.dom(PAGE.secretTab('Secrets')).exists('renders Secrets tab');
|
||||
assert.dom(PAGE.secretTab('Configuration')).exists('renders Configuration tab');
|
||||
assert.dom(GENERAL.tab('Secrets')).exists('renders Secrets tab');
|
||||
assert.dom(GENERAL.dropdownToggle('Manage')).exists('renders manage dropdown');
|
||||
await click(GENERAL.dropdownToggle('Manage'));
|
||||
assert.dom(GENERAL.menuItem('Configure')).exists('renders configure option');
|
||||
assert.dom(PAGE.list.filter).exists('renders filter input');
|
||||
assert.dom(PAGE.list.createSecret).exists('renders create secret action');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue