mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-28 04:10:44 -04:00
* add and update feature descriptions * add description doc links, improve spacing, remove dividers * update tests * Update ui/app/components/recovery/page/snapshots/load.hbs * update seal action and tests * use description argument for simple descriptions * clean up * update with finalized descriptions * updated policy descriptions * update client count description * fix typo and update tests * update tests * more test updates * Apply suggestions from code review --------- Co-authored-by: lane-wetmore <lane.wetmore@hashicorp.com> Co-authored-by: Kianna <30884335+kiannaquach@users.noreply.github.com>
This commit is contained in:
parent
7cd77ffaeb
commit
9932623861
59 changed files with 413 additions and 370 deletions
|
|
@ -7,9 +7,6 @@
|
|||
<div class="is-flex-column align-items-end">
|
||||
{{! Enterprise should always have a @billingStartTime but as a fallback allow the user to query dates manually }}
|
||||
{{#if (and @billingStartTime this.version.isEnterprise)}}
|
||||
<Hds::Text::Display @tag="p" @size="100" class="has-bottom-margin-xs" data-test-text-display="change-billing-data">
|
||||
{{if this.flags.isHvdManaged "Change data period" "Change billing period"}}
|
||||
</Hds::Text::Display>
|
||||
<Hds::Dropdown class="has-left-margin-xs" as |D|>
|
||||
<D.ToggleButton @text={{this.formatDate @startTimestamp}} @color="secondary" data-test-date-range-edit />
|
||||
<D.Description @text="Current period" />
|
||||
|
|
|
|||
|
|
@ -9,21 +9,6 @@
|
|||
@breadcrumbs={{array (hash label="Vault" route="vault.cluster.dashboard" icon="vault") (hash label="Client usage")}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:subtitle>
|
||||
{{#if @activityTimestamp}}
|
||||
Dashboard last updated:
|
||||
{{date-format @activityTimestamp "MMM d yyyy, h:mm:ss aaa" withTimeZone=true}}
|
||||
<Hds::Button
|
||||
@color="tertiary"
|
||||
@icon="reload"
|
||||
@isIconOnly={{true}}
|
||||
@size="small"
|
||||
@text="Refresh page"
|
||||
data-test-button="Refresh page"
|
||||
{{on "click" this.refreshRoute}}
|
||||
/>
|
||||
{{/if}}
|
||||
</:subtitle>
|
||||
<:description>
|
||||
{{#if (and this.version.isEnterprise @billingStartTime)}}
|
||||
{{! Enterprise should always have a @billingStartTime but as a fallback allow the user to query dates manually. }}
|
||||
|
|
@ -56,6 +41,27 @@
|
|||
</Hds::Text::Body>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
View the number and source of active Vault clients on the cluster to track license compliance.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/client-count"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
|
||||
{{#if @activityTimestamp}}
|
||||
<Hds::Text::Body @tag="p" @color="faint">
|
||||
Dashboard last updated:
|
||||
{{date-format @activityTimestamp "MMM d yyyy, h:mm:ss aaa" withTimeZone=true}}
|
||||
<Hds::Button
|
||||
@color="tertiary"
|
||||
@icon="reload"
|
||||
@isIconOnly={{true}}
|
||||
@size="small"
|
||||
@text="Refresh page"
|
||||
data-test-button="Refresh page"
|
||||
{{on "click" this.refreshRoute}}
|
||||
/>
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.showExportButton}}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
<div class="has-border-bottom-light">
|
||||
<div>
|
||||
<Clients::PageHeader
|
||||
@billingStartTime={{@config.billing_start_timestamp}}
|
||||
@retentionMonths={{@config.retention_months}}
|
||||
|
|
@ -16,13 +16,6 @@
|
|||
</div>
|
||||
|
||||
<div class="has-top-bottom-margin">
|
||||
<Hds::Text::Body @tag="p" @size="100" class="has-bottom-margin-s">
|
||||
This is the dashboard for your overall client count usage. Review Vault's
|
||||
<Hds::Link::Inline @href={{doc-link "/vault/docs/concepts/client-count"}} @isHrefExternal={{true}}>
|
||||
client counting documentation</Hds::Link::Inline>
|
||||
for more information.
|
||||
</Hds::Text::Body>
|
||||
|
||||
{{#if this.trackingDisabled}}
|
||||
<Hds::Alert @type="inline" @color="warning" class="has-bottom-margin-s" as |A|>
|
||||
<A.Title data-test-counts-disabled>Tracking is disabled</A.Title>
|
||||
|
|
|
|||
|
|
@ -3,12 +3,16 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Vault {{@version.versionDisplay}}">
|
||||
<Page::Header
|
||||
@title="Vault {{@version.versionDisplay}}"
|
||||
@description="Review the status of your Vault cluster, including: currently enabled secrets engines and authentication methods, and system configurations."
|
||||
>
|
||||
<:badges>
|
||||
{{#if @version.isEnterprise}}
|
||||
<Hds::Badge @text={{this.namespace.currentNamespace}} @icon="org" data-test-badge-namespace />
|
||||
{{/if}}
|
||||
</:badges>
|
||||
|
||||
</Page::Header>
|
||||
|
||||
<hr class="has-top-margin-xxs has-bottom-margin-l has-background-gray-200" />
|
||||
|
|
@ -3,12 +3,12 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title={{titleize (pluralize this.identityType)}}>
|
||||
<Page::Header @title={{titleize (pluralize @identityType)}} @description={{this.description}}>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs
|
||||
@breadcrumbs={{array
|
||||
(hash label="Vault" route="vault.cluster.dashboard" icon="vault")
|
||||
(hash label=(titleize (pluralize this.identityType)))
|
||||
(hash label=(titleize (pluralize @identityType)))
|
||||
}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
|
|
@ -17,12 +17,12 @@
|
|||
<nav class="tabs" aria-label="navigation for entities">
|
||||
<ul>
|
||||
<li>
|
||||
<LinkTo @route="vault.cluster.access.identity.index" @model={{pluralize this.identityType}}>
|
||||
{{capitalize (pluralize this.identityType)}}
|
||||
<LinkTo @route="vault.cluster.access.identity.index" @model={{pluralize @identityType}}>
|
||||
{{capitalize (pluralize @identityType)}}
|
||||
</LinkTo>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTo @route="vault.cluster.access.identity.aliases.index" @model={{pluralize this.identityType}}>
|
||||
<LinkTo @route="vault.cluster.access.identity.aliases.index" @model={{pluralize @identityType}}>
|
||||
Aliases
|
||||
</LinkTo>
|
||||
</li>
|
||||
|
|
@ -30,30 +30,30 @@
|
|||
</nav>
|
||||
|
||||
<Toolbar>
|
||||
{{#if this.model.meta.total}}
|
||||
{{#if @model.meta.total}}
|
||||
<ToolbarFilters>
|
||||
<Identity::LookupInput @type={{this.identityType}} />
|
||||
<Identity::LookupInput @type={{@identityType}} />
|
||||
</ToolbarFilters>
|
||||
{{/if}}
|
||||
<ToolbarActions>
|
||||
{{#if (eq this.identityType "entity")}}
|
||||
{{#if (eq @identityType "entity")}}
|
||||
<ToolbarLink
|
||||
@route={{"vault.cluster.access.identity.merge"}}
|
||||
@model={{pluralize this.identityType}}
|
||||
@model={{pluralize @identityType}}
|
||||
data-test-entity-merge-link={{true}}
|
||||
>
|
||||
Merge
|
||||
{{pluralize this.identityType}}
|
||||
{{pluralize @identityType}}
|
||||
</ToolbarLink>
|
||||
{{/if}}
|
||||
<ToolbarLink
|
||||
@route="vault.cluster.access.identity.create"
|
||||
@model={{pluralize this.identityType}}
|
||||
@model={{pluralize @identityType}}
|
||||
@type="add"
|
||||
data-test-entity-create-link={{true}}
|
||||
>
|
||||
Create
|
||||
{{this.identityType}}
|
||||
{{@identityType}}
|
||||
</ToolbarLink>
|
||||
</ToolbarActions>
|
||||
</Toolbar>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({});
|
||||
23
ui/app/components/identity/entity-nav.ts
Normal file
23
ui/app/components/identity/entity-nav.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
interface Args {
|
||||
identityType: 'entity' | 'group';
|
||||
model: {
|
||||
meta: {
|
||||
total: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default class EntityNavComponent extends Component<Args> {
|
||||
get description() {
|
||||
return this.args.identityType === 'entity'
|
||||
? 'Create and manage unique identities for human and non-human identities to serve as the canonical reference ID for policies and metadata.'
|
||||
: 'Create and name logical collections of entities to simplify policy management and permission scaling across your organization.';
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,12 @@
|
|||
@breadcrumbs={{array (hash label="Vault" route="vault.cluster.dashboard" icon="vault") (hash label="License")}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
View your Vault Enterprise license ID, status, expiration date, and feature entitlements.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/license"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<section class="box is-sideless is-marginless is-shadowless is-fullwidth">
|
||||
|
|
|
|||
|
|
@ -8,6 +8,12 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Configure authentication methods for accessing Vault.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/mfa"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.showIntroButton}}
|
||||
<Hds::Button
|
||||
|
|
|
|||
|
|
@ -14,6 +14,13 @@
|
|||
<:badges>
|
||||
<Hds::Badge @icon="org" @text={{this.namespacePath}} data-test-badge="namespace-path" />
|
||||
</:badges>
|
||||
<:description>
|
||||
Create logically separated, multi-tenant environments so teams can manage secrets, policies, and authentication
|
||||
methods independently.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/namespaces"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.showIntroButton}}
|
||||
<Hds::Button
|
||||
|
|
@ -48,7 +55,7 @@
|
|||
{{#if this.showContent}}
|
||||
{{! Show namespace list }}
|
||||
{{#if this.hasNamespaces}}
|
||||
<Toolbar>
|
||||
<Toolbar class="top-margin-16">
|
||||
<ToolbarFilters>
|
||||
<FilterInputExplicit
|
||||
@query={{@model.pageFilter}}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@
|
|||
}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
{{this.description}}
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/policies"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.showIntroButton}}
|
||||
<Hds::Button
|
||||
|
|
|
|||
|
|
@ -44,6 +44,18 @@ export default class PagePoliciesComponent extends Component<Args> {
|
|||
this.filter = this.args.filter || '';
|
||||
}
|
||||
|
||||
get description() {
|
||||
const policyType = this.args.policyType;
|
||||
if (policyType === PolicyTypes.ACL) {
|
||||
return 'Define fine-grained rules to explicitly grant or forbid access to specific paths and operations within your cluster. Because Vault is a “default deny” system, if a permission is not granted in a policy, an entity would not have permission.';
|
||||
} else if (policyType === PolicyTypes.EGP) {
|
||||
return 'Use Sentinel to specify policies as code that apply to discrete API paths and enforce organizational compliance standards.';
|
||||
} else if (policyType === PolicyTypes.RGP) {
|
||||
return 'Use Sentinel to specify policies as code that apply to tokens, entities, groups and enforce organizational compliance standards.';
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
// Check if the filter exactly matches a policy ID
|
||||
get filterMatchesKey(): boolean {
|
||||
const filter = this.filter;
|
||||
|
|
@ -94,7 +106,7 @@ export default class PagePoliciesComponent extends Component<Args> {
|
|||
|
||||
// Show when it is not in a dismissed state and there are no non-default policies and
|
||||
get showWizard() {
|
||||
if (this.args.policyType !== 'acl') return false;
|
||||
if (this.args.policyType !== PolicyTypes.ACL) return false;
|
||||
// Use total instead of filtered total to avoid flashing wizard when filtering with no results
|
||||
return !this.wizard.isDismissed(WIZARD_ID) && this.hasOnlyDefaultPolicies;
|
||||
}
|
||||
|
|
@ -106,9 +118,9 @@ export default class PagePoliciesComponent extends Component<Args> {
|
|||
const policyType = this.args.policyType;
|
||||
|
||||
// Use the appropriate sys endpoint based on policy type
|
||||
if (policyType === 'egp') {
|
||||
if (policyType === PolicyTypes.EGP) {
|
||||
await this.api.sys.systemDeletePoliciesEgpName(policyName);
|
||||
} else if (policyType === 'rgp') {
|
||||
} else if (policyType === PolicyTypes.RGP) {
|
||||
await this.api.sys.systemDeletePoliciesRgpName(policyName);
|
||||
} else {
|
||||
await this.api.sys.policiesDeleteAclPolicy(policyName);
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
{{!
|
||||
Copyright IBM Corp. 2016, 2025
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title={{@title}} @subtitle={{@subtitle}}>
|
||||
<:breadcrumbs>
|
||||
{{#if @breadcrumbs}}
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
{{/if}}
|
||||
</:breadcrumbs>
|
||||
<:badges>
|
||||
{{#if this.version.isCommunity}}
|
||||
<Hds::Badge @text="Enterprise" @color="highlight" data-test-badge="enterprise" />
|
||||
{{/if}}
|
||||
</:badges>
|
||||
<:actions>
|
||||
{{#if @action}}
|
||||
<Hds::Button
|
||||
@text={{@action.text}}
|
||||
@icon={{@action.icon}}
|
||||
@iconPosition={{@action.iconPosition}}
|
||||
@color={{@action.color}}
|
||||
@route={{@action.route}}
|
||||
@models={{@action.models}}
|
||||
/>
|
||||
{{/if}}
|
||||
</:actions>
|
||||
</Page::Header>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { service } from '@ember/service';
|
||||
import type VersionService from 'vault/services/version';
|
||||
|
||||
interface Args {
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
action?: unknown;
|
||||
}
|
||||
|
||||
export default class Header extends Component<Args> {
|
||||
@service declare readonly version: VersionService;
|
||||
}
|
||||
|
|
@ -3,11 +3,22 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Recovery::Page::Header
|
||||
@title="Secrets recovery"
|
||||
@subtitle="Recover lost or deleted data from a raft snapshot. Supported data includes KV v1 and Cubbyhole secrets or Database static roles."
|
||||
@breadcrumbs={{@breadcrumbs}}
|
||||
/>
|
||||
<Page::Header @title="Secrets recovery" class="bottom-margin-16">
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:badges>
|
||||
{{#if @model.showCommunityMessage}}
|
||||
<Hds::Badge @text="Enterprise" @color="highlight" data-test-badge="enterprise" />
|
||||
{{/if}}
|
||||
</:badges>
|
||||
<:description>
|
||||
Restore specific secrets from cluster snapshots without impacting the availability of the active Vault cluster.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/recover-secrets"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
{{#if @model.snapshots.showRaftStorageMessage}}
|
||||
<Hds::ApplicationState class="top-padding-32 is-marginless" as |A|>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Recovery::Page::Header @title="Upload snapshot" @breadcrumbs={{@breadcrumbs}} />
|
||||
<Page::Header @title="Upload snapshot">
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.bannerError}}
|
||||
<div class="has-top-padding-m has-bottom-padding-m">
|
||||
|
|
|
|||
|
|
@ -3,21 +3,29 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Recovery::Page::Header
|
||||
@title="Secrets recovery"
|
||||
@subtitle="Recover lost or deleted data from a raft snapshot. Supported data includes KV v1 and Cubbyhole secrets or Database static roles."
|
||||
@action={{(hash
|
||||
text="Recover secrets"
|
||||
icon="reload"
|
||||
iconPosition="leading"
|
||||
color="primary"
|
||||
route="vault.cluster.recovery.snapshots.snapshot.manage"
|
||||
models=(array @model.snapshot.snapshot_id)
|
||||
)}}
|
||||
@breadcrumbs={{@breadcrumbs}}
|
||||
/>
|
||||
<Page::Header @title="Secrets recovery">
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Restore specific secrets from cluster snapshots without impacting the availability of the active Vault cluster.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/recover-secrets"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
<Hds::Button
|
||||
@text="Recover secrets"
|
||||
@icon="reload"
|
||||
@iconPosition="leading"
|
||||
@color="primary"
|
||||
@route="vault.cluster.recovery.snapshots.snapshot.manage"
|
||||
@models={{(array @model.snapshot.snapshot_id)}}
|
||||
/>
|
||||
</:actions>
|
||||
</Page::Header>
|
||||
|
||||
<Hds::Table data-test-table="details">
|
||||
<Hds::Table class="top-margin-16" data-test-table="details">
|
||||
<:head as |H|>
|
||||
<H.Tr>
|
||||
{{#each this.tableColumns as |col|}}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Recovery::Page::Header
|
||||
@title="Secrets recovery"
|
||||
@subtitle="Recover lost or deleted data from a raft snapshot. Supported data includes KV v1 and Cubbyhole secrets or Database static roles."
|
||||
@breadcrumbs={{@breadcrumbs}}
|
||||
/>
|
||||
<Page::Header @title="Secrets recovery">
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Restore specific secrets from cluster snapshots without impacting the availability of the active Vault cluster.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/recover-secrets"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
{{#let @model.snapshot as |snapshot|}}
|
||||
<Hds::Card::Container
|
||||
|
|
@ -32,8 +38,6 @@
|
|||
</Hds::Card::Container>
|
||||
{{/let}}
|
||||
|
||||
<hr class="has-background-gray-300" />
|
||||
|
||||
<Hds::Text::Display @tag="h3" class="has-top-padding-m has-bottom-margin-l">Recover or read data</Hds::Text::Display>
|
||||
{{#if this.recoveryData}}
|
||||
<Hds::Alert @type="inline" @color="success" class="has-top-margin-m has-bottom-margin-m" data-test-inline-alert as |A|>
|
||||
|
|
|
|||
|
|
@ -3,28 +3,10 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#if this.error}}
|
||||
<Hds::Alert @type="inline" @color="critical" class="has-bottom-margin-m" data-test-seal-error as |A|>
|
||||
<A.Title>Error</A.Title>
|
||||
<A.Description>
|
||||
{{this.error}}
|
||||
</A.Description>
|
||||
</Hds::Alert>
|
||||
{{/if}}
|
||||
<p>
|
||||
Sealing a vault tells the Vault server to stop responding to any access operations until it is unsealed again. A sealed
|
||||
vault throws away its root key to unlock the data, so it physically is blocked from responding to operations again until
|
||||
the Vault is unsealed again with the "unseal" command or via the API.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||
<ConfirmAction
|
||||
@buttonText="Seal"
|
||||
@confirmTitle="Seal this cluster?"
|
||||
@confirmMessage="You will not be able to read or write any data until the cluster is unsealed again."
|
||||
@onConfirmAction={{this.handleSeal}}
|
||||
data-test-seal
|
||||
/>
|
||||
</div>
|
||||
<ConfirmAction
|
||||
@buttonText="Seal"
|
||||
@confirmTitle="Seal this cluster?"
|
||||
@confirmMessage="You will not be able to read or write any data until the cluster is unsealed again."
|
||||
@onConfirmAction={{this.handleSeal}}
|
||||
data-test-button="Seal"
|
||||
/>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { action } from '@ember/object';
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import errorMessage from 'vault/utils/error-message';
|
||||
|
||||
export default class SealActionComponent extends Component {
|
||||
@tracked error;
|
||||
|
||||
@action
|
||||
async handleSeal() {
|
||||
try {
|
||||
await this.args.onSeal();
|
||||
} catch (e) {
|
||||
this.error = errorMessage(e, 'Seal attempt failed. Check Vault logs for details.');
|
||||
}
|
||||
}
|
||||
}
|
||||
33
ui/app/components/seal-action.ts
Normal file
33
ui/app/components/seal-action.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Copyright IBM Corp. 2016, 2025
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { action } from '@ember/object';
|
||||
import { service } from '@ember/service';
|
||||
import Component from '@glimmer/component';
|
||||
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type FlashMessageService from 'vault/services/flash-messages';
|
||||
|
||||
interface Args {
|
||||
onSeal: CallableFunction;
|
||||
}
|
||||
|
||||
export default class SealActionComponent extends Component<Args> {
|
||||
@service declare readonly api: ApiService;
|
||||
@service declare readonly flashMessages: FlashMessageService;
|
||||
|
||||
@action
|
||||
async handleSeal() {
|
||||
try {
|
||||
await this.args.onSeal();
|
||||
} catch (error) {
|
||||
const message = await this.api.parseError(error, 'Check Vault logs for details.');
|
||||
|
||||
this.flashMessages.danger(message.message, {
|
||||
title: 'Seal attempt failed',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header
|
||||
@title="Secrets engines"
|
||||
@subtitle="Secrets engines available in the {{this.namespace.currentNamespace}} namespace of the {{this.version.clusterName}} cluster."
|
||||
@icon="key"
|
||||
>
|
||||
<Page::Header @title="Secrets engines" @icon="key">
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
View and manage your configured secrets engines in the current cluster, ranging from key value store (kv) to dynamic
|
||||
database credentials and more.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/secret-engines"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.showIntroButton}}
|
||||
<Hds::Button
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@ import type RouterService from '@ember/routing/router-service';
|
|||
import type SecretsEngineResource from 'vault/resources/secrets/engine';
|
||||
import type ApiService from 'vault/services/api';
|
||||
import type FlashMessageService from 'vault/services/flash-messages';
|
||||
import type NamespaceService from 'vault/services/namespace';
|
||||
import type VersionService from 'vault/services/version';
|
||||
import type WizardService from 'vault/services/wizard';
|
||||
|
||||
/**
|
||||
|
|
@ -38,9 +36,7 @@ interface Args {
|
|||
export default class SecretEngineList extends Component<Args> {
|
||||
@service declare readonly api: ApiService;
|
||||
@service declare readonly flashMessages: FlashMessageService;
|
||||
@service declare readonly namespace: NamespaceService;
|
||||
@service declare readonly router: RouterService;
|
||||
@service declare readonly version: VersionService;
|
||||
@service declare readonly wizard: WizardService;
|
||||
|
||||
@tracked engineTypeFilters: Array<string> = [];
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Hash data">
|
||||
<Page::Header
|
||||
@title="Hash data"
|
||||
@description="Generate secure cryptographic hashes of input data to verify integrity without storing the raw data."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.sum}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="sum" class="is-input">Sum</label>
|
||||
<Hds::Copy::Snippet
|
||||
|
|
@ -26,7 +29,7 @@
|
|||
</div>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<div class="field">
|
||||
<label for="hash-input" class="is-label">
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Lookup token">
|
||||
<Page::Header
|
||||
@title="Lookup token"
|
||||
@description="Inspect the metadata of a response-wrapping token, such as its creation time and TTL without revealing the underlying secret."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.lookupData}}
|
||||
<div class="box is-fullwidth is-sideless is-paddingless is-marginless">
|
||||
<div class="box is-fullwidth is-paddingless is-marginless">
|
||||
{{#each-in this.lookupData as |key value|}}
|
||||
{{#let (if (eq key "creation_ttl") "Creation TTL" (to-label key)) as |label|}}
|
||||
<InfoTableRow @label={{label}} @value={{value}} />
|
||||
|
|
@ -26,7 +29,7 @@
|
|||
</div>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="lookup" />
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<div class="field">
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Random bytes">
|
||||
<Page::Header
|
||||
@title="Random bytes"
|
||||
@description="Use Vault as a cryptographically secure entropy source to generate high-quality random bytes for external applications."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.randomBytes}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<label for="rand" class="is-label">Random bytes</label>
|
||||
<Hds::Copy::Snippet
|
||||
@textToCopy={{this.randomBytes}}
|
||||
|
|
@ -24,7 +27,7 @@
|
|||
</div>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-body">
|
||||
|
|
|
|||
|
|
@ -3,14 +3,17 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Rewrap token">
|
||||
<Page::Header
|
||||
@title="Rewrap token"
|
||||
@description="Migrate a wrapped secret to a new token to extend its lifetime without exposing the underlying secret data."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.rewrappedToken}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label class="is-label">Rewrapped token</label>
|
||||
<Hds::Copy::Snippet
|
||||
|
|
@ -26,7 +29,7 @@
|
|||
</div>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="rewrap" />
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<div class="field">
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Unwrap data">
|
||||
<Page::Header
|
||||
@title="Unwrap data"
|
||||
@description="Retrieve the original secret data from a wrapping token and automatically invalidate the token."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
|
|
@ -40,7 +43,7 @@
|
|||
</Hds::ButtonSet>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="unwrap" />
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<div class="field">
|
||||
|
|
|
|||
|
|
@ -7,10 +7,17 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Generate single-use tokens that encrypt sensitive data short-term to provide secure secret delivery and intercept
|
||||
detection.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/wrapping"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.token}}
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<div class="field">
|
||||
<label for="wrap-info" class="is-label">Wrapped token</label>
|
||||
<Hds::Copy::Snippet
|
||||
|
|
@ -35,7 +42,7 @@
|
|||
</div>
|
||||
{{else}}
|
||||
<form {{on "submit" this.handleSubmit}}>
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<NamespaceReminder @mode="perform" @noun="wrap" />
|
||||
<MessageError @errorMessage={{this.errorMessage}} />
|
||||
<Toolbar>
|
||||
|
|
|
|||
|
|
@ -179,6 +179,10 @@
|
|||
margin-bottom: size_variables.$spacing-12;
|
||||
}
|
||||
|
||||
.bottom-margin-16 {
|
||||
margin-bottom: size_variables.$spacing-16;
|
||||
}
|
||||
|
||||
.has-bottom-margin-m {
|
||||
margin-bottom: size_variables.$spacing-16;
|
||||
}
|
||||
|
|
@ -207,6 +211,10 @@
|
|||
margin-top: size_variables.$spacing-8;
|
||||
}
|
||||
|
||||
.top-margin-16 {
|
||||
margin-top: size_variables.$spacing-16;
|
||||
}
|
||||
|
||||
.has-top-margin-m {
|
||||
margin-top: size_variables.$spacing-16;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
}}
|
||||
|
||||
{{#if (has-feature "Control Groups")}}
|
||||
<Page::Header @title="Approval workflow">
|
||||
<Page::Header
|
||||
@title="Approval workflow"
|
||||
@description="Implement manual or automated checkpoints to require authorization before sensitive operations or accessing secrets."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs
|
||||
@breadcrumbs={{array
|
||||
|
|
@ -13,6 +16,7 @@
|
|||
}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
|
||||
</Page::Header>
|
||||
{{#if this.model.canConfigure}}
|
||||
<Toolbar>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Page::Header @title="Leases">
|
||||
<Page::Header
|
||||
@title="Leases"
|
||||
@description="Monitor and manage the lifetime of dynamic secrets, including tracking time to live (TTL), renewals, and manual revocations."
|
||||
>
|
||||
<:breadcrumbs>
|
||||
<KeyValueHeader
|
||||
@baseKey={{this.baseKey}}
|
||||
|
|
@ -13,6 +16,7 @@
|
|||
@showCurrent={{true}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
|
||||
<:actions>
|
||||
<Hds::Button
|
||||
@text="Back to Leases"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Enforce a secondary layer of identity verification (such as TOTP, Okta, or Duo) during the login process.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/login-mfa"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<Mfa::Nav />
|
||||
|
|
|
|||
|
|
@ -5,12 +5,10 @@
|
|||
|
||||
<Page::Header @title=" Multi-factor authentication">
|
||||
<:description>
|
||||
<Hds::Text::Body>
|
||||
Configure and enforce multi-factor authentication (MFA) for users logging into Vault, for any
|
||||
<br />
|
||||
authentication method.
|
||||
<Hds::Link::Inline @href={{doc-link "/vault/tutorials/auth-methods/multi-factor-authentication"}}>Learn more</Hds::Link::Inline>
|
||||
</Hds::Text::Body>
|
||||
Enforce a secondary layer of identity verification (such as TOTP, Okta, or Duo) during the login process.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/login-mfa"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
<Hds::Button @text="Configure MFA" @route="vault.cluster.access.mfa.methods.create" data-test-mfa-configure />
|
||||
|
|
|
|||
|
|
@ -13,14 +13,25 @@
|
|||
}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Enforce a secondary layer of identity verification (such as TOTP, Okta, or Duo) during the login process.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/login-mfa"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<div class="has-border-top-light has-top-padding-l">
|
||||
<div class="has-top-padding-l">
|
||||
{{#if this.showForms}}
|
||||
<h3 class="is-size-4 has-text-weight-semibold">Settings</h3>
|
||||
<p class="has-border-top-light has-top-padding-l">
|
||||
{{this.description}}
|
||||
<DocLink @path={{concat "/vault/api-docs/secret/identity/mfa/" this.type}}>Learn more.</DocLink>
|
||||
<Hds::Link::Inline
|
||||
@isHrefExternal={{true}}
|
||||
@href={{doc-link (concat "/vault/api-docs/secret/identity/mfa/" this.type)}}
|
||||
>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</p>
|
||||
<Mfa::MethodForm @model={{this.method}} @validations={{this.methodErrors}} class="is-shadowless" />
|
||||
<Mfa::MfaLoginEnforcementHeader
|
||||
|
|
@ -38,12 +49,7 @@
|
|||
/>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<p>
|
||||
Multi-factor authentication (MFA) allows you to set up another layer of security on top of existing authentication
|
||||
methods. Vault has four available methods.
|
||||
<DocLink @path="/vault/api-docs/secret/identity/mfa">Learn more.</DocLink>
|
||||
</p>
|
||||
<div class="is-flex-row has-top-margin-xl">
|
||||
<div class="is-flex-row">
|
||||
{{#each this.methods as |method|}}
|
||||
<RadioCard
|
||||
@value={{lowercase method.name}}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Enforce a secondary layer of identity verification (such as TOTP, Okta, or Duo) during the login process.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/login-mfa"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<Mfa::Nav />
|
||||
|
|
|
|||
|
|
@ -8,26 +8,22 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
</Page::Header>
|
||||
<div class="box is-fullwidth is-sideless is-flex-between is-shadowless is-marginless" data-test-oidc-header>
|
||||
<p>
|
||||
Configure Vault to act as an OIDC identity provider, and offer
|
||||
{{"Vault’s"}}
|
||||
various authentication
|
||||
<:description>
|
||||
Use Vault as an identity provider for external authentication methods that use the OpenID Connect (OIDC) protocol.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/oidc-provider"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.isCta}}
|
||||
<br />
|
||||
<Hds::Button
|
||||
@text="Create your first app"
|
||||
@route="vault.cluster.access.oidc.clients.create"
|
||||
data-test-oidc-configure
|
||||
/>
|
||||
{{/if}}
|
||||
methods and source of identity to any client applications.
|
||||
<Hds::Link::Inline @href={{doc-link "/vault/tutorials/auth-methods/oidc-identity-provider"}}>Learn more</Hds::Link::Inline>
|
||||
</p>
|
||||
{{#if this.isCta}}
|
||||
<Hds::Button
|
||||
@text="Create your first app"
|
||||
@route="vault.cluster.access.oidc.clients.create"
|
||||
data-test-oidc-configure
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</:actions>
|
||||
</Page::Header>
|
||||
{{#unless this.isCta}}
|
||||
{{! show tab links in list routes }}
|
||||
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless" data-test-oidc-tabs>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,21 @@
|
|||
@breadcrumbs={{array (hash label="Vault" route="vault.cluster.dashboard" icon="vault") (hash label="Seal Vault")}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Stop Vault from responding to all access operations by discarding its in-memory root key. You must use the CLI or API to
|
||||
unseal Vault and restore decryption capabilities.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/seal-vault"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{#if this.model.seal.canUpdate}}
|
||||
<SealAction @onSeal={{action "seal"}} />
|
||||
{{/if}}
|
||||
</:actions>
|
||||
</Page::Header>
|
||||
|
||||
{{#if this.model.seal.canUpdate}}
|
||||
<SealAction @onSeal={{action "seal"}} />
|
||||
{{else}}
|
||||
{{#unless this.model.seal.canUpdate}}
|
||||
<Hds::ApplicationState class="top-padding-32 is-marginless" as |A|>
|
||||
<A.Header
|
||||
@title="This token does not have sufficient capabilities to seal this vault"
|
||||
|
|
@ -22,4 +32,4 @@
|
|||
data-test-empty-state-title
|
||||
/>
|
||||
</Hds::ApplicationState>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
|
|
@ -7,9 +7,15 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Set a default and backup authentication methods to customize GUI login behavior.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/custom-login"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<Toolbar />
|
||||
<Toolbar class="top-margin-16" />
|
||||
|
||||
{{#if @loginRules}}
|
||||
{{#each @loginRules as |rule|}}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,15 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
{{#if @showTabs}}
|
||||
Configure global banners or modal notifications to display important system updates or compliance reminders to GUI
|
||||
users.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/custom-msg"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
{{/if}}
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
{{#if @showTabs}}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
<Hds::PageHeader class="page-header" as |PH|>
|
||||
<Hds::PageHeader class="page-header" ...attributes as |PH|>
|
||||
<PH.Title>{{@title}}</PH.Title>
|
||||
{{#if (has-block "breadcrumbs")}}
|
||||
<PH.Breadcrumb>
|
||||
|
|
|
|||
|
|
@ -7,9 +7,15 @@
|
|||
<:breadcrumbs>
|
||||
<Page::Breadcrumbs @breadcrumbs={{this.breadcrumbs}} />
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
Call Vault and plugin API endpoints directly from the browser to accelerate development and debugging.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/api-docs"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<NamespaceReminder as |R|>
|
||||
Requests use the header
|
||||
<code>X-Vault-Namespace: {{R.namespace.path}}</code>. You can also use
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<form {{on "submit" (fn this.onSubmit this.data)}} data-test-replication-enable-form>
|
||||
<MessageError @errorMessage={{this.error}} />
|
||||
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
<div class="box is-fullwidth is-marginless">
|
||||
<label for="replication-mode" class="is-label">
|
||||
Cluster mode
|
||||
</label>
|
||||
|
|
|
|||
|
|
@ -10,33 +10,14 @@
|
|||
@breadcrumbs={{array (hash label="Vault" route="vault" icon="vault" linkExternal=true) (hash label=this.title)}}
|
||||
/>
|
||||
</:breadcrumbs>
|
||||
<:description>
|
||||
{{this.description}}
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link this.docLink}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
</Page::Header>
|
||||
|
||||
<div class="box is-sideless is-fullwidth is-marginless">
|
||||
{{#if (eq @replicationMode "dr")}}
|
||||
<h2 class="title is-flex-center is-5 is-marginless">
|
||||
<Icon @size="24" @name="replication-direct" />
|
||||
Disaster recovery (DR) replication
|
||||
</h2>
|
||||
<p class="help has-text-grey-dark">
|
||||
{{replication-mode-description "dr"}}
|
||||
</p>
|
||||
{{else if (eq @replicationMode "performance")}}
|
||||
<h2 class="title is-flex-center is-5 is-marginless">
|
||||
<Icon @size="24" @name="replication-perf" />
|
||||
Performance replication
|
||||
</h2>
|
||||
{{#if (has-feature "Performance Replication")}}
|
||||
<p class="help has-text-grey-dark">
|
||||
{{replication-mode-description "performance"}}
|
||||
</p>
|
||||
{{else}}
|
||||
<p class="help has-text-grey-dark">
|
||||
Performance replication is a feature of Vault Enterprise Premium
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<EnableReplicationForm
|
||||
@replicationMode={{@replicationMode}}
|
||||
@canEnablePrimary={{this.canEnable "Primary"}}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,17 @@ export default class PageModeIndex extends Component {
|
|||
return 'Enable replication';
|
||||
}
|
||||
|
||||
get description() {
|
||||
if (this.args.replicationMode === 'dr') {
|
||||
return 'Maintain a synchronized standby cluster to enable business continuity and data preservation in the event of a primary site failure.';
|
||||
}
|
||||
return 'Pair geographically distributed clusters that share a common configuration to scale Vault.';
|
||||
}
|
||||
|
||||
get docLink() {
|
||||
return this.args.replicationMode === 'dr' ? '/vault/gui/dr' : '/vault/gui/pr';
|
||||
}
|
||||
|
||||
canEnable = (type) => {
|
||||
const { cluster, replicationMode } = this.args;
|
||||
let perm;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,13 @@
|
|||
<Hds::Badge @text="Plus feature" @color="highlight" @size="large" data-test-badge="Plus feature" />
|
||||
{{/if}}
|
||||
</:badges>
|
||||
<:description>
|
||||
Centralize your secret governance by synchronizing Vault-managed data to external secret managers in cloud services
|
||||
providers, GitHub, & Vercel.
|
||||
<Hds::Link::Inline @isHrefExternal={{true}} @href={{doc-link "/vault/gui/secret-sync"}}>
|
||||
Learn more
|
||||
</Hds::Link::Inline>
|
||||
</:description>
|
||||
<:actions>
|
||||
{{! Only allow users who have activated the feature to create a destination. }}
|
||||
{{#if @isActivated}}
|
||||
|
|
@ -20,35 +27,19 @@
|
|||
</:actions>
|
||||
</Page::Header>
|
||||
|
||||
<div class="box is-fullwidth is-sideless is-flex-between is-shadowless" data-test-cta-container>
|
||||
{{! One cta message regardless of OSS vs Enterprise with/without secrets sync or managed cluster. }}
|
||||
<p>
|
||||
This feature allows you to sync secrets to platforms and tools across your stack to get secrets when and where you need
|
||||
them.
|
||||
<Hds::Link::Standalone
|
||||
@text="Learn more about Secrets Sync"
|
||||
@icon="docs-link"
|
||||
@iconPosition="trailing"
|
||||
@isHrefExternal={{true}}
|
||||
@href={{doc-link "/vault/tutorials/enterprise/secrets-sync"}}
|
||||
data-test-cta-doc-link
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="is-flex-row gap-24 has-bottom-margin-m">
|
||||
<div>
|
||||
<Hds::Layout::Flex @gap="24" class="top-margin-16" as |LF|>
|
||||
<LF.Item @tag="div">
|
||||
<img src={{img-path "~/sync-landing-1.png"}} alt="Secrets sync destinations diagram" aria-describedby="sync-step-1" />
|
||||
<p id="sync-step-1" class="has-top-margin-m">
|
||||
<Hds::Text::Body @tag="p">
|
||||
<b>Step 1:</b>
|
||||
Create a destination, and set up the connection details to allow Vault access.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
</Hds::Text::Body>
|
||||
</LF.Item>
|
||||
<LF.Item @tag="div">
|
||||
<img src={{img-path "~/sync-landing-2.png"}} alt="Syncing secrets diagram" aria-describedby="sync-step-2" />
|
||||
<p id="sync-step-2" class="has-top-margin-m">
|
||||
<Hds::Text::Body @tag="p">
|
||||
<b>Step 2:</b>
|
||||
Select secrets from your KV v2 engine and sync secrets to your destination.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Hds::Text::Body>
|
||||
</LF.Item>
|
||||
</Hds::Layout::Flex>
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
@color="warning"
|
||||
@onDismiss={{if @canActivateSecretsSync (fn (mut this.hideOptIn) true) undefined}}
|
||||
data-test-secrets-sync-opt-in-banner
|
||||
class="bottom-margin-16"
|
||||
as |A|
|
||||
>
|
||||
<A.Title>Enable Secrets Sync feature</A.Title>
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ module('Acceptance | clients | counts', function (hooks) {
|
|||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await click(CLIENT_COUNT.dateRange.dropdownOption(1));
|
||||
assert
|
||||
.dom(GENERAL.hdsPageHeaderSubtitle)
|
||||
.dom(GENERAL.hdsPageHeaderDescription)
|
||||
.hasTextContaining(`Dashboard last updated: ${format(STATIC_NOW, 'MMM d yyyy')}`);
|
||||
// Save URL with query params before clicking refresh
|
||||
const url = currentURL();
|
||||
|
|
@ -166,7 +166,7 @@ module('Acceptance | clients | counts', function (hooks) {
|
|||
assert.true(this.refreshSpy.calledOnce, 'router.refresh() is called once');
|
||||
assert.strictEqual(currentURL(), url, 'url is the same after clicking refresh');
|
||||
assert
|
||||
.dom(GENERAL.hdsPageHeaderSubtitle)
|
||||
.dom(GENERAL.hdsPageHeaderDescription)
|
||||
.hasTextContaining(`Dashboard last updated: ${format(fakeUpdatedNow, 'MMM d yyyy')}`);
|
||||
});
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ module('Acceptance | clients | counts', function (hooks) {
|
|||
await click(CLIENT_COUNT.dateRange.edit);
|
||||
await click(CLIENT_COUNT.dateRange.dropdownOption(1));
|
||||
assert
|
||||
.dom(GENERAL.hdsPageHeaderSubtitle)
|
||||
.dom(GENERAL.hdsPageHeaderDescription)
|
||||
.hasTextContaining(`Dashboard last updated: ${format(STATIC_NOW, 'MMM d yyyy')}`);
|
||||
// Save URL with query params before clicking refresh
|
||||
const url = currentURL();
|
||||
|
|
@ -193,7 +193,7 @@ module('Acceptance | clients | counts', function (hooks) {
|
|||
assert.true(this.refreshSpy.calledOnce, 'router.refresh() is called once');
|
||||
assert.strictEqual(currentURL(), url, 'url is the same after clicking refresh');
|
||||
assert
|
||||
.dom(GENERAL.hdsPageHeaderSubtitle)
|
||||
.dom(GENERAL.hdsPageHeaderDescription)
|
||||
.hasTextContaining(`Dashboard last updated: ${format(fakeUpdatedNow, 'MMM d yyyy')}`);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -350,17 +350,12 @@ module('Acceptance | oidc-config clients', function (hooks) {
|
|||
});
|
||||
|
||||
test('it renders empty state when no clients are configured', async function (assert) {
|
||||
assert.expect(5);
|
||||
assert.expect(4);
|
||||
this.server.get('/identity/oidc/client', () => overrideResponse(404));
|
||||
|
||||
await visit(OIDC_BASE_URL);
|
||||
assert.strictEqual(currentURL(), '/vault/access/oidc');
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('OIDC provider');
|
||||
assert.dom(SELECTORS.oidcHeader).hasText(
|
||||
`Configure Vault to act as an OIDC identity provider, and offer Vault’s various authentication
|
||||
methods and source of identity to any client applications. Learn more Create your first app`,
|
||||
'renders call to action header when no clients are configured'
|
||||
);
|
||||
assert.dom('[data-test-oidc-landing]').exists('landing page renders when no clients are configured');
|
||||
assert
|
||||
.dom(SELECTORS.oidcLandingImg)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ module('Acceptance | reduced disclosure test', function (hooks) {
|
|||
assert.strictEqual(currentURL(), '/vault/settings/seal');
|
||||
|
||||
// seal
|
||||
await click('[data-test-seal]');
|
||||
await click(GENERAL.button('Seal'));
|
||||
await click(GENERAL.confirmButton);
|
||||
await pollCluster(this.owner);
|
||||
await settled();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ module('Acceptance | unseal', function (hooks) {
|
|||
assert.strictEqual(currentURL(), '/vault/settings/seal');
|
||||
|
||||
// seal
|
||||
await click('[data-test-seal]');
|
||||
await click(GENERAL.button('Seal'));
|
||||
await click(GENERAL.confirmButton);
|
||||
|
||||
await pollCluster(this.owner);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { debug } from '@ember/debug';
|
|||
export const OIDC_BASE_URL = `/vault/access/oidc`;
|
||||
|
||||
export const SELECTORS = {
|
||||
oidcHeader: '[data-test-oidc-header]',
|
||||
oidcClientCreateButton: '[data-test-oidc-configure]',
|
||||
oidcRouteTabs: '[data-test-oidc-tabs]',
|
||||
oidcLandingImg: '[data-test-oidc-img]',
|
||||
|
|
|
|||
|
|
@ -182,19 +182,6 @@ module('Integration | Component | clients/date-range', function (hooks) {
|
|||
.hasAttribute('aria-expanded', 'false', 'it closes dropdown after selection');
|
||||
});
|
||||
|
||||
test('it renders billing period text', async function (assert) {
|
||||
await this.renderComponent();
|
||||
assert
|
||||
.dom(this.element)
|
||||
.hasText('Change billing period January 2018', 'it renders billing related text');
|
||||
});
|
||||
|
||||
test('it renders data period text for HVD managed clusters', async function (assert) {
|
||||
this.owner.lookup('service:flags').featureFlags = ['VAULT_CLOUD_ADMIN_NAMESPACE'];
|
||||
await this.renderComponent();
|
||||
assert.dom(this.element).hasText('Change data period January 2018');
|
||||
});
|
||||
|
||||
test('it should send an empty string for start_time when selecting current period', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
|
|
|
|||
|
|
@ -276,9 +276,6 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
|||
.dom(GENERAL.hdsPageHeaderTitle)
|
||||
.hasTextContaining('Client usage', 'it renders page header title');
|
||||
assert.dom(GENERAL.breadcrumbs).hasTextContaining('Vault Client usage', 'it renders breadcrumbs');
|
||||
assert
|
||||
.dom(GENERAL.textDisplay('change-billing-data'))
|
||||
.hasTextContaining('Change billing period', 'it renders change billing period text');
|
||||
});
|
||||
|
||||
test('it renders data period text for HVD managed clusters', async function (assert) {
|
||||
|
|
@ -289,9 +286,6 @@ module('Integration | Component | clients/page-header', function (hooks) {
|
|||
.dom(GENERAL.hdsPageHeaderTitle)
|
||||
.hasTextContaining('Client usage', 'it renders page header title');
|
||||
assert.dom(GENERAL.breadcrumbs).hasTextContaining('Vault Client usage', 'it renders breadcrumbs');
|
||||
assert
|
||||
.dom(GENERAL.textDisplay('change-billing-data'))
|
||||
.hasTextContaining('Change data period', 'it renders change data period text');
|
||||
});
|
||||
|
||||
test('it allows date editing if no billing start time is provided', async function (assert) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
namespaces = [];
|
||||
}
|
||||
|
||||
this.breadcrumbs = [{ label: 'Secrets Recovery', route: 'vault.cluster.recovery.snapshots' }];
|
||||
|
||||
this.model = {
|
||||
snapshot,
|
||||
namespaces,
|
||||
|
|
@ -40,7 +42,9 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
this.version.type = 'enterprise';
|
||||
|
||||
this.renderComponent = () =>
|
||||
render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
render(
|
||||
hbs`<Recovery::Page::Snapshots::SnapshotManage @breadcrumbs={{this.breadcrumbs}} @model={{this.model}}/>`
|
||||
);
|
||||
});
|
||||
const scenarios = [
|
||||
{
|
||||
|
|
@ -119,7 +123,7 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
// have values if the request was successful, but handling just in case.
|
||||
data: { secret: {} },
|
||||
}));
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.inputByAttr('manual-mount-path')).exists();
|
||||
assert.dom(GENERAL.selectByAttr('mount')).doesNotExist();
|
||||
});
|
||||
|
|
@ -127,7 +131,7 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
test('it renders manual input if mounts request errors', async function (assert) {
|
||||
assert.expect(14);
|
||||
this.server.get('/sys/internal/ui/mounts', () => overrideResponse(403));
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
assert.dom(GENERAL.button('Type')).exists().hasText('Type');
|
||||
assert.dom(GENERAL.inputByAttr('manual-mount-path')).exists().isDisabled();
|
||||
assert.dom(GENERAL.selectByAttr('mount')).doesNotExist();
|
||||
|
|
@ -161,18 +165,19 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
});
|
||||
|
||||
test('it displays loaded snapshot card', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.badge('status')).hasText('Ready', 'status badge renders');
|
||||
});
|
||||
|
||||
test('it displays namespace selector for root namespace', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.selectByAttr('namespace')).exists('namespace selector is visible in root namespace');
|
||||
});
|
||||
|
||||
test('it validates form fields before read/recover operations', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
// Try to read without selecting mount or resource path
|
||||
await click(GENERAL.button('read'));
|
||||
|
||||
|
|
@ -181,14 +186,12 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
});
|
||||
|
||||
test('it clears form selections', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
|
||||
await this.renderComponent();
|
||||
await click(GENERAL.selectByAttr('namespace'));
|
||||
await click('[data-option-index="1"]');
|
||||
await click(GENERAL.selectByAttr('mount'));
|
||||
await click('[data-option-index]');
|
||||
await fillIn(GENERAL.inputByAttr('resourcePath'), 'test-path');
|
||||
|
||||
await click(GENERAL.button('clear'));
|
||||
|
||||
const nsSelect = find(GENERAL.selectByAttr('namespace'));
|
||||
|
|
@ -201,8 +204,7 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
});
|
||||
|
||||
test('it displays error alert when read operation fails', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
|
||||
await this.renderComponent();
|
||||
await fillIn(GENERAL.inputByAttr('resourcePath'), 'nonexistent-secret');
|
||||
await click(GENERAL.selectByAttr('mount'));
|
||||
await click('[data-option-index="1.0"]');
|
||||
|
|
@ -212,8 +214,7 @@ module('Integration | Component | recovery/snapshots/snapshot-manage', function
|
|||
});
|
||||
|
||||
test('it toggles JSON view in read modal', async function (assert) {
|
||||
await render(hbs`<Recovery::Page::Snapshots::SnapshotManage @model={{this.model}}/>`);
|
||||
|
||||
await this.renderComponent();
|
||||
await fillIn(GENERAL.inputByAttr('resourcePath'), 'test-secret');
|
||||
await click(GENERAL.selectByAttr('mount'));
|
||||
await click('[data-option-index]');
|
||||
|
|
|
|||
|
|
@ -17,17 +17,19 @@ module('Integration | Component | recovery/snapshots', function (hooks) {
|
|||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.breadcrumbs = [{ label: 'Secrets Recovery', route: 'vault.cluster.recovery.snapshots' }];
|
||||
|
||||
this.model = {
|
||||
snapshots: [],
|
||||
canLoadSnapshot: false,
|
||||
};
|
||||
this.renderComponent = () => render(hbs`<Recovery::Page::Snapshots @model={{this.model}}/>`);
|
||||
this.renderComponent = () =>
|
||||
render(hbs`<Recovery::Page::Snapshots @breadcrumbs={{this.breadcrumbs}} @model={{this.model}}/>`);
|
||||
});
|
||||
|
||||
test('it displays raft empty state if showRaftStorageMessage is passed into model', async function (assert) {
|
||||
this.model = { snapshots: { showRaftStorageMessage: true } };
|
||||
|
||||
await render(hbs`<Recovery::Page::Snapshots @model={{this.model}}/>`);
|
||||
await this.renderComponent();
|
||||
|
||||
assert
|
||||
.dom(GENERAL.emptyStateTitle)
|
||||
|
|
@ -43,8 +45,8 @@ module('Integration | Component | recovery/snapshots', function (hooks) {
|
|||
|
||||
test('it displays empty state in CE', async function (assert) {
|
||||
this.model = { snapshots: [], showCommunityMessage: true };
|
||||
await this.renderComponent();
|
||||
|
||||
await render(hbs`<Recovery::Page::Snapshots @model={{this.model}}/>`);
|
||||
assert
|
||||
.dom(GENERAL.emptyStateTitle)
|
||||
.hasText('Secrets Recovery is an enterprise feature', 'CE empty state title renders');
|
||||
|
|
@ -64,9 +66,7 @@ module('Integration | Component | recovery/snapshots', function (hooks) {
|
|||
test('it displays empty state in non root namespace', async function (assert) {
|
||||
const nsService = this.owner.lookup('service:namespace');
|
||||
nsService.setNamespace('test-ns');
|
||||
|
||||
await this.renderComponent();
|
||||
|
||||
assert
|
||||
.dom(GENERAL.emptyStateTitle)
|
||||
.hasText('Snapshot upload is restricted', 'non root namespace empty state title renders');
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ module('Integration | Component | seal-action', function (hooks) {
|
|||
hooks.beforeEach(function () {
|
||||
this.sealSuccess = sinon.spy(() => new Promise((resolve) => resolve({})));
|
||||
this.sealError = sinon.stub().throws({ message: SEAL_WHEN_STANDBY_MSG });
|
||||
this.flashMessages = this.owner.lookup('service:flash-messages');
|
||||
this.flashSpy = sinon.spy(this.flashMessages, 'danger');
|
||||
});
|
||||
|
||||
test('it handles success', async function (assert) {
|
||||
|
|
@ -25,11 +27,11 @@ module('Integration | Component | seal-action', function (hooks) {
|
|||
await render(hbs`<SealAction @onSeal={{action this.handleSeal}} />`);
|
||||
|
||||
// attempt seal
|
||||
await click('[data-test-seal]');
|
||||
await click(GENERAL.button('Seal'));
|
||||
await click(GENERAL.confirmButton);
|
||||
|
||||
assert.ok(this.sealSuccess.calledOnce, 'called onSeal action');
|
||||
assert.dom('[data-test-seal-error]').doesNotExist('Does not show error when successful');
|
||||
assert.false(this.flashSpy.calledWith(SEAL_WHEN_STANDBY_MSG), 'Does not show error when successful');
|
||||
});
|
||||
|
||||
test('it handles error', async function (assert) {
|
||||
|
|
@ -37,10 +39,10 @@ module('Integration | Component | seal-action', function (hooks) {
|
|||
await render(hbs`<SealAction @onSeal={{action this.handleSeal}} />`);
|
||||
|
||||
// attempt seal
|
||||
await click('[data-test-seal]');
|
||||
await click(GENERAL.button('Seal'));
|
||||
await click(GENERAL.confirmButton);
|
||||
|
||||
assert.ok(this.sealError.calledOnce, 'called onSeal action');
|
||||
assert.dom('[data-test-seal-error]').includesText(SEAL_WHEN_STANDBY_MSG, 'Shows error returned from API');
|
||||
assert.true(this.flashSpy.calledWith(SEAL_WHEN_STANDBY_MSG), 'Shows error returned from API');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -23,31 +23,11 @@ module('Integration | Component | sync | Secrets::LandingCta', function (hooks)
|
|||
this.transitionStub = sinon.stub(this.owner.lookup('service:router'), 'transitionTo');
|
||||
});
|
||||
|
||||
test('it should render promotional copy if feature is not activated', async function (assert) {
|
||||
await render(hbs`<Secrets::LandingCta @isActivated={{false}} /> `, {
|
||||
owner: this.engine,
|
||||
});
|
||||
|
||||
assert
|
||||
.dom(cta.summary)
|
||||
.hasText(
|
||||
'This feature allows you to sync secrets to platforms and tools across your stack to get secrets when and where you need them. Learn more about Secrets Sync'
|
||||
);
|
||||
assert.dom(cta.link).hasText('Learn more about Secrets Sync');
|
||||
assert.dom(cta.button).doesNotExist('does not render create destination button');
|
||||
});
|
||||
|
||||
test('it should render CTA copy and action if feature is activated', async function (assert) {
|
||||
test('it should render CTA if feature is activated', async function (assert) {
|
||||
await render(hbs`<Secrets::LandingCta @isActivated={{true}} /> `, {
|
||||
owner: this.engine,
|
||||
});
|
||||
|
||||
assert
|
||||
.dom(cta.summary)
|
||||
.hasText(
|
||||
'This feature allows you to sync secrets to platforms and tools across your stack to get secrets when and where you need them. Learn more about Secrets Sync'
|
||||
);
|
||||
assert.dom(cta.link).hasText('Learn more about Secrets Sync');
|
||||
assert.dom(cta.button).exists('it renders create destination button');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -83,7 +83,6 @@ module('Integration | Component | sync | Page::Overview', function (hooks) {
|
|||
this.setup();
|
||||
await this.renderComponent();
|
||||
assert.dom(overview.optInBanner.container).doesNotExist();
|
||||
assert.dom(cta.summary).exists();
|
||||
});
|
||||
|
||||
test('it should render header, tabs and toolbar for overview state if destinations exist', async function (assert) {
|
||||
|
|
@ -96,7 +95,6 @@ module('Integration | Component | sync | Page::Overview', function (hooks) {
|
|||
await this.renderComponent();
|
||||
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Secrets sync', 'Page title renders');
|
||||
assert.dom(cta.summary).doesNotExist('CTA does not render');
|
||||
assert.dom(tab('Overview')).hasText('Overview', 'Overview tab renders');
|
||||
assert.dom(tab('Destinations')).hasText('Destinations', 'Destinations tab renders');
|
||||
assert.dom(overview.createDestination).hasText('Create new destination', 'Toolbar action renders');
|
||||
|
|
@ -118,7 +116,6 @@ module('Integration | Component | sync | Page::Overview', function (hooks) {
|
|||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Secrets sync');
|
||||
assert.dom(GENERAL.badge('Plus feature')).hasText('Plus feature', 'Plus feature badge renders');
|
||||
assert.dom(cta.button).hasText('Create first destination', 'CTA action renders');
|
||||
assert.dom(cta.summary).exists();
|
||||
});
|
||||
|
||||
test('it should show activation error if cluster is not Plus tier', async function (assert) {
|
||||
|
|
@ -160,7 +157,6 @@ module('Integration | Component | sync | Page::Overview', function (hooks) {
|
|||
|
||||
assert.dom(GENERAL.hdsPageHeaderTitle).hasText('Secrets sync');
|
||||
assert.dom(cta.button).hasText('Create first destination', 'CTA action renders');
|
||||
assert.dom(cta.summary).exists();
|
||||
});
|
||||
|
||||
test('it should show the opt-in banner without permissions to activate', async function (assert) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue