diff --git a/changelog/_13260.txt b/changelog/_13260.txt new file mode 100644 index 0000000000..1b90e6a242 --- /dev/null +++ b/changelog/_13260.txt @@ -0,0 +1,3 @@ +```release-note:change +secrets/openldap: Update plugin to [v0.18.0](https://github.com/hashicorp/vault-plugin-secrets-openldap/releases/tag/v0.18.0) +``` diff --git a/changelog/_13264.txt b/changelog/_13264.txt new file mode 100644 index 0000000000..08cfcbf9e3 --- /dev/null +++ b/changelog/_13264.txt @@ -0,0 +1,3 @@ +```release-note:change +secrets/kv: Update plugin to [v0.26.2](https://github.com/hashicorp/vault-plugin-secrets-kv/releases/tag/v0.26.2) +``` diff --git a/go.mod b/go.mod index 413e5c0cc3..ba6074c65b 100644 --- a/go.mod +++ b/go.mod @@ -159,13 +159,13 @@ require ( github.com/hashicorp/vault-plugin-database-snowflake v0.16.0 github.com/hashicorp/vault-plugin-secrets-ad v0.22.1 github.com/hashicorp/vault-plugin-secrets-alicloud v0.22.1 - github.com/hashicorp/vault-plugin-secrets-azure v0.23.0 + github.com/hashicorp/vault-plugin-secrets-azure v0.25.0 github.com/hashicorp/vault-plugin-secrets-gcp v0.23.0 github.com/hashicorp/vault-plugin-secrets-gcpkms v0.23.0 github.com/hashicorp/vault-plugin-secrets-kubernetes v0.13.1 - github.com/hashicorp/vault-plugin-secrets-kv v0.25.0 + github.com/hashicorp/vault-plugin-secrets-kv v0.26.2 github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.17.1 - github.com/hashicorp/vault-plugin-secrets-openldap v0.17.0 + github.com/hashicorp/vault-plugin-secrets-openldap v0.18.0 github.com/hashicorp/vault-plugin-secrets-terraform v0.14.1 github.com/hashicorp/vault-testing-stepwise v0.3.3 github.com/hashicorp/vault/api v1.22.0 diff --git a/go.sum b/go.sum index 699cdcbf4b..642ccd10b2 100644 --- a/go.sum +++ b/go.sum @@ -869,20 +869,20 @@ github.com/hashicorp/vault-plugin-secrets-ad v0.22.1 h1:Wqp6I0gSOI3kf3TK3JJeylDC github.com/hashicorp/vault-plugin-secrets-ad v0.22.1/go.mod h1:xERfc+dNwlLEefQDcjhhhHPTJLs9komReccRKMsDoAs= github.com/hashicorp/vault-plugin-secrets-alicloud v0.22.1 h1:6JvSoeYU+tRSnc0eGNz+1ttFK8gfIqw/sXHme4h6CRY= github.com/hashicorp/vault-plugin-secrets-alicloud v0.22.1/go.mod h1:2tiBfbs4TXU1mHg7oJUThSac4mzRdnBOmt6ZBmfkV8o= -github.com/hashicorp/vault-plugin-secrets-azure v0.23.0 h1:oetvEJqXP+2jGM9CfT/LjpJyuViNKdjPKFbDIkRdaPo= -github.com/hashicorp/vault-plugin-secrets-azure v0.23.0/go.mod h1:QkmR0rnexjc330oUzoiuXFsA6OnxmI56h4jnDb1PKbY= +github.com/hashicorp/vault-plugin-secrets-azure v0.25.0 h1:IYIGfFiw3ICLfVlF+YlPztthbc5pMaT3SwVr27VjERw= +github.com/hashicorp/vault-plugin-secrets-azure v0.25.0/go.mod h1:S4E5R3RVpGWnzR0bDGDwpV0HlyNsMAIcuYuNlgWv2zo= github.com/hashicorp/vault-plugin-secrets-gcp v0.23.0 h1:hqZlxS4Ya0DBt+FutW6z6tSEdnwHFusY49x7TLKVrAw= github.com/hashicorp/vault-plugin-secrets-gcp v0.23.0/go.mod h1:OmRHszxWAV9MTwFeQKDQmrpRw9q+RMp1vohCuhkkr1k= github.com/hashicorp/vault-plugin-secrets-gcpkms v0.23.0 h1:gXFfSYVpebgklMeLxWfMrBHOZJ5EdJtM80ir0NZNdMM= github.com/hashicorp/vault-plugin-secrets-gcpkms v0.23.0/go.mod h1:YdDoi8TIpbJ4lljL3fISJmZQxZqmJfHMdzxCS33pjBc= github.com/hashicorp/vault-plugin-secrets-kubernetes v0.13.1 h1:ug+5nibS3AulD3ElaQeD42N0VJsTUwTRVPgJSj0ovvM= github.com/hashicorp/vault-plugin-secrets-kubernetes v0.13.1/go.mod h1:t34JjbPLaLrhvwb7iKmvW9y72o7ZhxGfN0Q3yClsV8Y= -github.com/hashicorp/vault-plugin-secrets-kv v0.25.0 h1:5Xx8Hub0nAoFLIHZ4d9tMpPG+MACHXLzed5i7hViKTk= -github.com/hashicorp/vault-plugin-secrets-kv v0.25.0/go.mod h1:Xy3wQwAJxhVZweR2DXBrEgk1erkWKNrhYcuD4Gy4ACo= +github.com/hashicorp/vault-plugin-secrets-kv v0.26.2 h1:5ruO7aTfQqOKIuC+G6hXQbBKXZ6sPGDA3s2oCtyGtdU= +github.com/hashicorp/vault-plugin-secrets-kv v0.26.2/go.mod h1:VRZ9QtAibng01WsVj95vpF0oiEDuDTpBp2PSjxYyARI= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.17.1 h1:WezfLs6aH9MOWLux1XJ/8Z2kJncVQWRHJ1hKfXIjRKg= github.com/hashicorp/vault-plugin-secrets-mongodbatlas v0.17.1/go.mod h1:3mLTbgTz8GShAf81IFT0WdKAUjJoIMg/qmucHJfHiD8= -github.com/hashicorp/vault-plugin-secrets-openldap v0.17.0 h1:GpgYcuBL66zzFLkf143Q3zFr//BF/4jX/EXXy9wDUOM= -github.com/hashicorp/vault-plugin-secrets-openldap v0.17.0/go.mod h1:UPWSw0hYY37sC4MeeoG9G8jN88DYi3L4MeJjRauhaBI= +github.com/hashicorp/vault-plugin-secrets-openldap v0.18.0 h1:m2OlgzCKFlBP+/dpRMRsyu9gfwsWCWjJNRpU6UNgIE4= +github.com/hashicorp/vault-plugin-secrets-openldap v0.18.0/go.mod h1:HO9g8SO9blk3ayPBaHA0dIo4YgpASSkY7fy2DEY57bI= github.com/hashicorp/vault-plugin-secrets-terraform v0.14.1 h1:okWkXeDbTElc1tA2TzPsNV5yEa1fcttXZFMIKn2fVQk= github.com/hashicorp/vault-plugin-secrets-terraform v0.14.1/go.mod h1:FjgfSW1exgLCnc2ALi6CKwux2NMuqiAzKf/hMPm6WWU= github.com/hashicorp/vault-testing-stepwise v0.3.3 h1:/PQhXpJln7UQf3NJmG61ucAxsvttd+a0kb5NKW01iIA= diff --git a/ui/app/components/page/methods.ts b/ui/app/components/page/methods.ts index 19e4970fe6..5f3f267ebd 100644 --- a/ui/app/components/page/methods.ts +++ b/ui/app/components/page/methods.ts @@ -9,7 +9,7 @@ import { tracked } from '@glimmer/tracking'; import Component from '@glimmer/component'; import { dropTask } from 'ember-concurrency'; import sortObjects from 'vault/utils/sort-objects'; -import { WIZARD_ID } from '../wizard/methods/methods-wizard'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type ApiService from 'vault/services/api'; import type FlashMessageService from 'vault/services/flash-messages'; @@ -46,7 +46,7 @@ export default class PageAuthMethodsComponent extends Component { @tracked methodToDisable: AuthMethodResource | null = null; @tracked shouldRenderIntroModal = false; - wizardId = WIZARD_ID; + wizardId = WIZARD_ID_MAP.authMethods; // list returned by getter is sorted in template get authMethodList() { @@ -95,7 +95,7 @@ export default class PageAuthMethodsComponent extends Component { get showContent() { // Show when the 1) wizard is not shown OR 2) wizard intro modal is shown // This ensures the wizard intro modal is shown on top of the list view and the background content is not blank behind the modal - return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(WIZARD_ID)); + return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(this.wizardId)); } get showIntroButton() { diff --git a/ui/app/components/page/namespaces.ts b/ui/app/components/page/namespaces.ts index 3944e33b92..5a37d1ab81 100644 --- a/ui/app/components/page/namespaces.ts +++ b/ui/app/components/page/namespaces.ts @@ -8,7 +8,7 @@ import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import Component from '@glimmer/component'; import keys from 'core/utils/keys'; -import { WIZARD_ID } from 'vault/components/wizard/namespaces/namespace-wizard'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import errorMessage from 'vault/utils/error-message'; import type ApiService from 'vault/services/api'; @@ -62,6 +62,8 @@ export default class PageNamespacesComponent extends Component { @tracked showSetupAlert = false; @tracked shouldRenderIntroModal = false; + wizardId = WIZARD_ID_MAP.namespace; + constructor(owner: unknown, args: Args) { super(owner, args); this.query = this.args.model.pageFilter || ''; @@ -91,13 +93,13 @@ export default class PageNamespacesComponent extends Component { // Show header and breadcrumbs when viewing the intro page or during the list view. // Do not show during Guided Start as that has its own header get showPageHeader() { - return !this.showWizard || this.wizard.isIntroVisible(WIZARD_ID); + return !this.showWizard || this.wizard.isIntroVisible(this.wizardId); } get showContent() { // Show when the 1) wizard is not shown OR 2) wizard intro modal is shown // This ensures the wizard intro modal is shown on top of the list view and the background content is not blank behind the modal - return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(WIZARD_ID)); + return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(this.wizardId)); } get showIntroButton() { @@ -106,7 +108,7 @@ export default class PageNamespacesComponent extends Component { get showWizard() { // Show when there are no existing namespaces and it is not in a dismissed state - return !this.wizard.isDismissed(WIZARD_ID) && !this.hasNamespaces; + return !this.wizard.isDismissed(this.wizardId) && !this.hasNamespaces; } @action @@ -162,7 +164,7 @@ export default class PageNamespacesComponent extends Component { @action showIntroPage() { // Reset the wizard dismissal state to allow re-entering the wizard - this.wizard.reset(WIZARD_ID); + this.wizard.reset(this.wizardId); this.shouldRenderIntroModal = true; } diff --git a/ui/app/components/page/policies.ts b/ui/app/components/page/policies.ts index 63767e97e7..a7077de267 100644 --- a/ui/app/components/page/policies.ts +++ b/ui/app/components/page/policies.ts @@ -7,7 +7,7 @@ import { service } from '@ember/service'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; import Component from '@glimmer/component'; -import { WIZARD_ID } from 'vault/components/wizard/acl-policies/acl-wizard'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import errorMessage from 'vault/utils/error-message'; import { PolicyTypes } from 'core/utils/code-generators/policy'; @@ -39,6 +39,8 @@ export default class PagePoliciesComponent extends Component { @tracked policyToDelete = null; @tracked shouldRenderIntroModal = false; + wizardId = WIZARD_ID_MAP.aclPolicy; + constructor(owner: unknown, args: Args) { super(owner, args); this.filter = this.args.filter || ''; @@ -97,7 +99,7 @@ export default class PagePoliciesComponent extends Component { get showContent() { // Show when the 1) wizard is not shown OR 2) wizard intro modal is shown // This ensures the wizard intro modal is shown on top of the list view and the background content is not blank behind the modal - return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(WIZARD_ID)); + return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(this.wizardId)); } get showIntroButton() { @@ -108,7 +110,7 @@ export default class PagePoliciesComponent extends Component { get showWizard() { 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; + return !this.wizard.isDismissed(this.wizardId) && this.hasOnlyDefaultPolicies; } @action @@ -151,7 +153,7 @@ export default class PagePoliciesComponent extends Component { @action showIntroPage() { // Reset the wizard dismissal state to allow re-entering the wizard - this.wizard.reset(WIZARD_ID); + this.wizard.reset(this.wizardId); this.shouldRenderIntroModal = true; } diff --git a/ui/app/components/secret-engine/list.ts b/ui/app/components/secret-engine/list.ts index 43d4a253f2..35c5aa13d7 100644 --- a/ui/app/components/secret-engine/list.ts +++ b/ui/app/components/secret-engine/list.ts @@ -10,7 +10,7 @@ import { tracked } from '@glimmer/tracking'; import engineDisplayData from 'vault/helpers/engines-display-data'; import { ALL_ENGINES } from 'vault/utils/all-engines-metadata'; import { getEffectiveEngineType } from 'vault/utils/external-plugin-helpers'; -import { WIZARD_ID } from '../wizard/secret-engines/secret-engines-wizard'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type RouterService from '@ember/routing/router-service'; import type SecretsEngineResource from 'vault/resources/secrets/engine'; @@ -48,7 +48,8 @@ export default class SecretEngineList extends Component { @tracked versionSearchText = ''; @tracked shouldRenderIntroModal = false; - wizardId = WIZARD_ID; + + wizardId = WIZARD_ID_MAP.secretEngines; tableColumns = [ { @@ -198,7 +199,7 @@ export default class SecretEngineList extends Component { get showContent() { // Show when the 1) wizard is not shown OR 2) wizard intro modal is shown // This ensures the wizard intro modal is shown on top of the list view and the background content is not blank behind the modal - return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(WIZARD_ID)); + return !this.showWizard || (this.shouldRenderIntroModal && this.wizard.isIntroVisible(this.wizardId)); } get showIntroButton() { diff --git a/ui/app/components/wizard/acl-policies/acl-wizard.ts b/ui/app/components/wizard/acl-policies/acl-wizard.ts index fc10e8b194..59a9574705 100644 --- a/ui/app/components/wizard/acl-policies/acl-wizard.ts +++ b/ui/app/components/wizard/acl-policies/acl-wizard.ts @@ -6,6 +6,7 @@ import { service } from '@ember/service'; import { action } from '@ember/object'; import Component from '@glimmer/component'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type WizardService from 'vault/services/wizard'; @@ -14,12 +15,10 @@ interface Args { onRefresh: CallableFunction; } -export const WIZARD_ID = 'acl-policy'; - export default class WizardNamespacesWizardComponent extends Component { @service declare readonly wizard: WizardService; - wizardId = WIZARD_ID; + wizardId = WIZARD_ID_MAP.aclPolicy; @action async onDismiss() { diff --git a/ui/app/components/wizard/index.ts b/ui/app/components/wizard/index.ts index 11cb9f5c2d..31204eb680 100644 --- a/ui/app/components/wizard/index.ts +++ b/ui/app/components/wizard/index.ts @@ -5,13 +5,15 @@ import { service } from '@ember/service'; import Component from '@glimmer/component'; + import type WizardService from 'vault/services/wizard'; +import type { WizardId } from 'vault/app-types'; interface Args { /** * The unique identifier for the wizard used for handling wizard dismissal and intro visibility state */ - wizardId: string; + wizardId: WizardId; /** * Whether the intro page is in the default view or in modal view depending on how it is triggered */ diff --git a/ui/app/components/wizard/methods/methods-wizard.ts b/ui/app/components/wizard/methods/methods-wizard.ts index 6f87f0ce53..ee9627e8b3 100644 --- a/ui/app/components/wizard/methods/methods-wizard.ts +++ b/ui/app/components/wizard/methods/methods-wizard.ts @@ -6,6 +6,7 @@ import { service } from '@ember/service'; import { action } from '@ember/object'; import Component from '@glimmer/component'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type WizardService from 'vault/services/wizard'; @@ -14,11 +15,10 @@ interface Args { onRefresh: CallableFunction; } -export const WIZARD_ID = 'auth-methods'; export default class WizardMethodsWizardComponent extends Component { @service declare readonly wizard: WizardService; - wizardId = WIZARD_ID; + wizardId = WIZARD_ID_MAP.authMethods; @action async onDismiss() { diff --git a/ui/app/components/wizard/namespaces/namespace-wizard.ts b/ui/app/components/wizard/namespaces/namespace-wizard.ts index 576bf0f54f..b41dbe066e 100644 --- a/ui/app/components/wizard/namespaces/namespace-wizard.ts +++ b/ui/app/components/wizard/namespaces/namespace-wizard.ts @@ -9,6 +9,7 @@ import { tracked } from '@glimmer/tracking'; import Component from '@glimmer/component'; import { SecurityPolicy } from 'vault/components/wizard/namespaces/step-1'; import { CreationMethod } from 'vault/components/wizard/namespaces/step-3'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type ApiService from 'vault/services/api'; import type Block from 'vault/components/wizard/namespaces/step-2'; @@ -37,8 +38,6 @@ interface WizardState { codeSnippet: string | null; } -export const WIZARD_ID = 'namespace'; - export default class WizardNamespacesWizardComponent extends Component { @service declare readonly api: ApiService; @service declare readonly router: RouterService; @@ -59,7 +58,7 @@ export default class WizardNamespacesWizardComponent extends Component { methods = CreationMethod; policy = SecurityPolicy; - wizardId = WIZARD_ID; + wizardId = WIZARD_ID_MAP.namespace; // Whether the current step requirements have been met to proceed to the next step get canProceed() { diff --git a/ui/app/components/wizard/secret-engines/secret-engines-wizard.ts b/ui/app/components/wizard/secret-engines/secret-engines-wizard.ts index 58db7c3600..57a695e9d8 100644 --- a/ui/app/components/wizard/secret-engines/secret-engines-wizard.ts +++ b/ui/app/components/wizard/secret-engines/secret-engines-wizard.ts @@ -6,6 +6,7 @@ import Component from '@glimmer/component'; import { service } from '@ember/service'; import { action } from '@ember/object'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; import type ApiService from 'vault/services/api'; import type FlashMessageService from 'vault/services/flash-messages'; @@ -17,15 +18,13 @@ interface Args { onRefresh: CallableFunction; } -export const WIZARD_ID = 'secret-engines'; - export default class WizardSecretEnginesWizardComponent extends Component { @service declare readonly api: ApiService; @service declare readonly router: RouterService; @service declare readonly flashMessages: FlashMessageService; @service declare readonly wizard: WizardService; - wizardId = WIZARD_ID; + wizardId = WIZARD_ID_MAP.secretEngines; @action onDismiss() { diff --git a/ui/app/services/wizard.ts b/ui/app/services/wizard.ts index 7c2098fd93..7674eba6e1 100644 --- a/ui/app/services/wizard.ts +++ b/ui/app/services/wizard.ts @@ -6,8 +6,9 @@ import Service from '@ember/service'; import { tracked } from '@glimmer/tracking'; import localStorage from 'vault/lib/local-storage'; +import { DISMISSED_WIZARD_KEY } from 'vault/utils/constants/wizard'; -const DISMISSED_WIZARD_KEY = 'dismissed-wizards'; +import type { WizardId } from 'vault/app-types'; /** * WizardService manages the state of wizards across the application, @@ -31,7 +32,7 @@ export default class WizardService extends Service { * @param wizardId - The unique identifier for the wizard * @returns true if the wizard has been dismissed, false otherwise */ - isDismissed(wizardId: string): boolean { + isDismissed(wizardId: WizardId): boolean { return this.dismissedWizards.includes(wizardId); } @@ -39,7 +40,7 @@ export default class WizardService extends Service { * Mark a wizard as dismissed * @param wizardId - The unique identifier for the wizard to dismiss */ - dismiss(wizardId: string): void { + dismiss(wizardId: WizardId): void { // Only add if not already dismissed if (!this.dismissedWizards.includes(wizardId)) { this.dismissedWizards = [...this.dismissedWizards, wizardId]; @@ -51,7 +52,7 @@ export default class WizardService extends Service { * Clear the dismissed state for a specific wizard * @param wizardId - The unique identifier for the wizard to reset */ - reset(wizardId: string): void { + reset(wizardId: WizardId): void { this.dismissedWizards = this.dismissedWizards.filter((id: string) => id !== wizardId); localStorage.setItem(DISMISSED_WIZARD_KEY, this.dismissedWizards); // Reset intro visibility when wizard is reset @@ -72,7 +73,7 @@ export default class WizardService extends Service { * @param wizardId - The unique identifier for the wizard * @returns true if the intro is visible, false otherwise (defaults to true if wizard not dismissed, false if dismissed) */ - isIntroVisible(wizardId: string): boolean { + isIntroVisible(wizardId: WizardId): boolean { // If intro visibility has been explicitly set, use that value if (this.introVisibleState[wizardId] !== undefined) { return this.introVisibleState[wizardId]; @@ -87,7 +88,7 @@ export default class WizardService extends Service { * @param wizardId - The unique identifier for the wizard * @param visible - Whether the intro should be visible */ - setIntroVisible(wizardId: string, visible: boolean): void { + setIntroVisible(wizardId: WizardId, visible: boolean): void { this.introVisibleState = { ...this.introVisibleState, [wizardId]: visible, diff --git a/ui/app/utils/constants/wizard.ts b/ui/app/utils/constants/wizard.ts new file mode 100644 index 0000000000..166411f370 --- /dev/null +++ b/ui/app/utils/constants/wizard.ts @@ -0,0 +1,13 @@ +/** + * Copyright IBM Corp. 2016, 2025 + * SPDX-License-Identifier: BUSL-1.1 + */ + +export const DISMISSED_WIZARD_KEY = 'dismissed-wizards'; + +export const WIZARD_ID_MAP = { + aclPolicy: 'acl-policy', + authMethods: 'auth-methods', + secretEngines: 'secret-engines', + namespace: 'namespace', +} as const; diff --git a/ui/e2e/init.setup.ts b/ui/e2e/init.setup.ts index f80dd12f28..532d18ded0 100644 --- a/ui/e2e/init.setup.ts +++ b/ui/e2e/init.setup.ts @@ -7,6 +7,7 @@ import { test as base } from '@playwright/test'; import fs from 'fs'; import path from 'path'; import { USER_POLICY_MAP } from './policies'; +import { DISMISSED_WIZARD_KEY, WIZARD_ID_MAP } from '../app/utils/constants/wizard'; export type UserSetupOptions = { userType: string; @@ -21,6 +22,13 @@ export const setup = base.extend({ setup('initialize vault and setup user for testing', async ({ page, userType }) => { // on fresh app load navigating to the root will land us on the initialize page await page.goto('./'); + // manually update dismissed wizards so that they don't have to be skipped in tests before persisting storage state; + await page.evaluate( + ({ key, ids }) => { + localStorage.setItem(key, JSON.stringify(ids)); + }, + { key: DISMISSED_WIZARD_KEY, ids: Object.values(WIZARD_ID_MAP) } + ); // initialize vault await page.getByRole('spinbutton', { name: 'Key shares' }).fill('1'); await page.getByRole('spinbutton', { name: 'Key threshold' }).fill('1'); @@ -68,7 +76,7 @@ setup('initialize vault and setup user for testing', async ({ page, userType }) await page.getByRole('button', { name: 'Sign in' }).click(); // wait for the dashboard to load to ensure login was successful await page.waitForURL('**/dashboard'); - // save the authenticated state to file - // subsequent tests can then reuse this session data + // save the localStorage state to file which includes the auth token and dismissed wizards + // subsequent tests can then reuse the session data await page.context().storageState({ path: path.join(__dirname, `/tmp/${userType}-session.json`) }); }); diff --git a/ui/e2e/tests/superuser/filtering-engines.spec.ts b/ui/e2e/tests/superuser/filtering-engines.spec.ts index 67acb4eb63..21c9b765e0 100644 --- a/ui/e2e/tests/superuser/filtering-engines.spec.ts +++ b/ui/e2e/tests/superuser/filtering-engines.spec.ts @@ -12,11 +12,6 @@ test('filtering secrets engines workflow', async ({ page }) => { await page.goto('dashboard'); await page.getByRole('link', { name: 'Secrets', exact: true }).click(); - // skip intro page if it appears - if (await page.getByRole('button', { name: 'Skip' }).isVisible()) { - await page.getByRole('button', { name: 'Skip' }).click(); - } - // enable transit await page.getByRole('link', { name: 'Enable new engine' }).click(); await page.getByLabel('Transit - enabled engine type').click(); diff --git a/ui/e2e/tests/superuser/namespace.spec.ts b/ui/e2e/tests/superuser/namespace.spec.ts index 6fa043b476..f5474ae999 100644 --- a/ui/e2e/tests/superuser/namespace.spec.ts +++ b/ui/e2e/tests/superuser/namespace.spec.ts @@ -10,9 +10,6 @@ test('namespace workflow', async ({ page }) => { // nav to namespaces and create a new namespace await page.getByRole('link', { name: 'Access control' }).click(); await page.getByRole('link', { name: 'Namespaces' }).click(); - // skip guided tour if it appears - await page.getByRole('button', { name: 'Skip' }).click(); - await page.getByRole('link', { name: 'Create namespace' }).click(); await page.getByRole('textbox', { name: 'Path' }).fill('testNamespace'); await page.getByRole('button', { name: 'Save' }).click(); diff --git a/ui/e2e/tests/superuser/userpass.spec.ts b/ui/e2e/tests/superuser/userpass.spec.ts index efefe5f639..75d15cb307 100644 --- a/ui/e2e/tests/superuser/userpass.spec.ts +++ b/ui/e2e/tests/superuser/userpass.spec.ts @@ -10,9 +10,6 @@ test('userpass workflow', async ({ page }) => { await page.goto('dashboard'); await page.getByRole('link', { name: 'Access control' }).click(); await page.getByRole('link', { name: 'Authentication methods' }).click(); - - // dismiss intro page and click enable method in toolbar - await page.getByRole('button', { name: 'Skip' }).click(); await page.getByRole('link', { name: 'Enable new method' }).click(); // enable userpass auth method diff --git a/ui/tests/acceptance/access/methods-test.js b/ui/tests/acceptance/access/methods-test.js index fb0cc70318..8bf4d8cce8 100644 --- a/ui/tests/acceptance/access/methods-test.js +++ b/ui/tests/acceptance/access/methods-test.js @@ -13,7 +13,7 @@ import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { mountAuthCmd, runCmd } from 'vault/tests/helpers/commands'; import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { sanitizePath } from 'core/utils/sanitize-path'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; const { searchSelect } = GENERAL; @@ -25,7 +25,7 @@ module('Acceptance | auth-methods list view', function (hooks) { this.uid = uuidv4(); await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['auth-methods']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.authMethods); }); test('it navigates to auth method', async function (assert) { diff --git a/ui/tests/acceptance/access/namespaces/index-test.js b/ui/tests/acceptance/access/namespaces/index-test.js index 208081b715..f485cecb13 100644 --- a/ui/tests/acceptance/access/namespaces/index-test.js +++ b/ui/tests/acceptance/access/namespaces/index-test.js @@ -9,15 +9,15 @@ import { setupApplicationTest } from 'ember-qunit'; import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { createNS, deleteNS, runCmd } from 'vault/tests/helpers/commands'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; module('Acceptance | Enterprise | /access/namespaces', function (hooks) { setupApplicationTest(hooks); - hooks.beforeEach(async () => { + hooks.beforeEach(async function () { await login(); // dismiss the wizard - localStorage.setItem('dismissed-wizards', ['namespace']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.namespace); // Go to the manage namespaces page await visit('/vault/access/namespaces'); }); diff --git a/ui/tests/acceptance/auth/auth-list-test.js b/ui/tests/acceptance/auth/auth-list-test.js index 16e3540369..8d24294616 100644 --- a/ui/tests/acceptance/auth/auth-list-test.js +++ b/ui/tests/acceptance/auth/auth-list-test.js @@ -13,7 +13,7 @@ import { MANAGED_AUTH_BACKENDS } from 'vault/helpers/supported-managed-auth-back import { deleteAuthCmd, mountAuthCmd, runCmd, createNS } from 'vault/tests/helpers/commands'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { filterEnginesByMountCategory } from 'vault/utils/all-engines-metadata'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; const SELECTORS = { createUser: '[data-test-entity-create-link="user"]', @@ -27,7 +27,7 @@ module('Acceptance | auth backend list', function (hooks) { hooks.beforeEach(async function () { await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['auth-methods']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.authMethods); }); test('userpass secret backend', async function (assert) { @@ -158,7 +158,7 @@ module('Acceptance | auth backend list', function (hooks) { await runCmd(createNS(ns), false); await settled(); await loginNs(ns); - localStorage.setItem('dismissed-wizards', ['auth-methods']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.authMethods); // go directly to token configure route await visit(`/vault/settings/auth/configure/token/options?namespace=${ns}`); diff --git a/ui/tests/acceptance/enterprise-namespaces-test.js b/ui/tests/acceptance/enterprise-namespaces-test.js index 7c45a46b66..ac4bbdfefa 100644 --- a/ui/tests/acceptance/enterprise-namespaces-test.js +++ b/ui/tests/acceptance/enterprise-namespaces-test.js @@ -20,15 +20,15 @@ import { runCmd, createNSFromPaths, deleteNSFromPaths } from 'vault/tests/helper import { login, loginNs, logout } from 'vault/tests/helpers/auth/auth-helpers'; import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; module('Acceptance | Enterprise | namespaces', function (hooks) { setupApplicationTest(hooks); - hooks.beforeEach(async () => { + hooks.beforeEach(async function () { await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['namespace']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.namespace); }); test('it focuses the search input field when user toggles namespace picker', async function (assert) { diff --git a/ui/tests/acceptance/policy/index-test.js b/ui/tests/acceptance/policy/index-test.js index 9c57092d37..9bcdc46a6f 100644 --- a/ui/tests/acceptance/policy/index-test.js +++ b/ui/tests/acceptance/policy/index-test.js @@ -21,7 +21,7 @@ import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { runCmd } from 'vault/tests/helpers/commands'; import codemirror, { setCodeEditorValue } from 'vault/tests/helpers/codemirror'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; const SELECT = { policyByName: (name) => `[data-test-policy-link="${name}"]`, @@ -38,7 +38,7 @@ module('Acceptance | policies/acl', function (hooks) { this.uid = uuidv4(); await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['acl-policy']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.aclPolicy); }); test('it lists default and root acls', async function (assert) { diff --git a/ui/tests/acceptance/policy/policies-test.js b/ui/tests/acceptance/policy/policies-test.js index 701342555f..0928151672 100644 --- a/ui/tests/acceptance/policy/policies-test.js +++ b/ui/tests/acceptance/policy/policies-test.js @@ -8,7 +8,7 @@ import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { login } from 'vault/tests/helpers/auth/auth-helpers'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; module('Acceptance | policies', function (hooks) { setupApplicationTest(hooks); @@ -41,7 +41,7 @@ module('Acceptance | policies', function (hooks) { test('it navigates to and from policy show page from sidebar', async function (assert) { await visit('/vault/dashboard'); - localStorage.setItem('dismissed-wizards', ['acl-policy']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.aclPolicy); await click(GENERAL.navLink('Access control')); assert.strictEqual(currentURL(), '/vault/policies/acl', 'currentURL is /vault/policies/acl'); await waitFor('[data-test-component="navigate-input"]'); diff --git a/ui/tests/acceptance/secret-engine-list-view-test.js b/ui/tests/acceptance/secret-engine-list-view-test.js index f52fb03e21..5cafa26fc1 100644 --- a/ui/tests/acceptance/secret-engine-list-view-test.js +++ b/ui/tests/acceptance/secret-engine-list-view-test.js @@ -13,8 +13,8 @@ import { SECRET_ENGINE_SELECTORS as SES } from 'vault/tests/helpers/secret-engin import { deleteEngineCmd, mountEngineCmd, runCmd } from 'vault/tests/helpers/commands'; import { login, loginNs } from 'vault/tests/helpers/auth/auth-helpers'; import page from 'vault/tests/pages/settings/mount-secret-backend'; -import localStorage from 'vault/lib/local-storage'; import { setupMirage } from 'ember-cli-mirage/test-support'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; module('Acceptance | secret-engine list view', function (hooks) { setupApplicationTest(hooks); @@ -33,7 +33,7 @@ module('Acceptance | secret-engine list view', function (hooks) { this.uid = uuidv4(); await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['secret-engines']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.secretEngines); }); // the new API service camelizes response keys, so this tests is to assert that does NOT happen when we re-implement it @@ -210,7 +210,7 @@ module('Acceptance | secret-engine list view', function (hooks) { await runCmd([`write sys/namespaces/${this.namespace} -force`]); await loginNs(this.namespace); // log into namespace with root token // dismiss wizard - localStorage.setItem('dismissed-wizards', ['secret-engines']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.secretEngines); }); // Ember route models won't refresh within a namespace when this.router.transitionTo() is called diff --git a/ui/tests/acceptance/sidebar-nav-test.js b/ui/tests/acceptance/sidebar-nav-test.js index 590a10532d..873107dcf8 100644 --- a/ui/tests/acceptance/sidebar-nav-test.js +++ b/ui/tests/acceptance/sidebar-nav-test.js @@ -11,7 +11,7 @@ import { login } from 'vault/tests/helpers/auth/auth-helpers'; import modifyPassthroughResponse from 'vault/mirage/helpers/modify-passthrough-response'; import { setRunOptions } from 'ember-a11y-testing/test-support'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; -import localStorage from 'vault/lib/local-storage'; +import { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; const link = (label) => `[data-test-sidebar-nav-link="${label}"]`; const panel = (label) => `[data-test-sidebar-nav-panel="${label}"]`; @@ -34,7 +34,7 @@ module('Acceptance | sidebar navigation', function (hooks) { }); await login(); // dismiss wizard - localStorage.setItem('dismissed-wizards', ['auth-methods']); + this.owner.lookup('service:wizard').dismiss(WIZARD_ID_MAP.authMethods); }); test('it should navigate back to the dashboard when logo is clicked in app header', async function (assert) { diff --git a/ui/tests/unit/services/wizard-test.js b/ui/tests/unit/services/wizard-test.js index 204ef25961..0355cc375b 100644 --- a/ui/tests/unit/services/wizard-test.js +++ b/ui/tests/unit/services/wizard-test.js @@ -7,8 +7,7 @@ import { module, test } from 'qunit'; import { setupTest } from 'ember-qunit'; import sinon from 'sinon'; import localStorage from 'vault/lib/local-storage'; - -const DISMISSED_WIZARD_KEY = 'dismissed-wizards'; +import { DISMISSED_WIZARD_KEY } from 'vault/utils/constants/wizard'; module('Unit | Service | wizard', function (hooks) { setupTest(hooks); diff --git a/ui/types/vault/app-types.ts b/ui/types/vault/app-types.ts index aab8d63cda..59d1a807e3 100644 --- a/ui/types/vault/app-types.ts +++ b/ui/types/vault/app-types.ts @@ -4,6 +4,7 @@ */ import type EmberDataModel from 'ember-data/model'; // eslint-disable-line ember/use-ember-data-rfc-395-imports import type Owner from '@ember/owner'; +import type { WIZARD_ID_MAP } from 'vault/utils/constants/wizard'; // Type that comes back from expandAttributeMeta export interface FormField { @@ -153,3 +154,5 @@ export interface SearchSelectOption { export interface StringMap { [key: string]: string; } + +export type WizardId = (typeof WIZARD_ID_MAP)[keyof typeof WIZARD_ID_MAP];