From 004d6da92c8640e1226b26d9eba12a4fcbfab5e1 Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Mon, 1 Dec 2025 15:59:50 -0500 Subject: [PATCH] [UI] Ember Data Migration: PKI Tidy (#11020) (#11049) * updates pki tidy auto route to use api service * updates pki tidy status view to use api service * updates pki tidy auto and manual workflows to use api service and form class Co-authored-by: Jordan Reimer --- ui/app/forms/secrets/pki/tidy.ts | 23 ++ .../page/pki-tidy-auto-configure.hbs | 4 +- .../page/pki-tidy-auto-settings.hbs | 28 +- .../addon/components/page/pki-tidy-manual.hbs | 2 +- .../addon/components/page/pki-tidy-status.hbs | 10 +- .../addon/components/page/pki-tidy-status.ts | 36 +- ui/lib/pki/addon/components/pki-tidy-form.hbs | 72 ++-- ui/lib/pki/addon/components/pki-tidy-form.ts | 59 +-- ui/lib/pki/addon/helpers/tidy-field-label.ts | 23 ++ ui/lib/pki/addon/helpers/tidy-groups.ts | 62 +++ ui/lib/pki/addon/routes/tidy.js | 13 +- .../pki/addon/routes/tidy/auto/configure.js | 18 +- ui/lib/pki/addon/routes/tidy/auto/index.js | 11 +- ui/lib/pki/addon/routes/tidy/index.js | 25 +- ui/lib/pki/addon/routes/tidy/manual.js | 23 +- .../addon/templates/tidy/auto/configure.hbs | 2 +- .../pki/addon/templates/tidy/auto/index.hbs | 2 +- ui/lib/pki/addon/templates/tidy/index.hbs | 2 +- ui/tests/acceptance/pki/pki-tidy-test.js | 12 +- .../pki/page/pki-tidy-auto-settings-test.js | 41 +- .../pki/page/pki-tidy-status-test.js | 51 +-- .../components/pki/pki-tidy-form-test.js | 356 +++++++----------- 22 files changed, 457 insertions(+), 418 deletions(-) create mode 100644 ui/app/forms/secrets/pki/tidy.ts create mode 100644 ui/lib/pki/addon/helpers/tidy-field-label.ts create mode 100644 ui/lib/pki/addon/helpers/tidy-groups.ts diff --git a/ui/app/forms/secrets/pki/tidy.ts b/ui/app/forms/secrets/pki/tidy.ts new file mode 100644 index 0000000000..815b04942f --- /dev/null +++ b/ui/app/forms/secrets/pki/tidy.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import OpenApiForm from 'vault/forms/open-api'; + +import type { PkiTidyRequest, PkiConfigureAutoTidyRequest } from '@hashicorp/vault-client-typescript'; + +type PkiTidyFormRequest = PkiTidyRequest | PkiConfigureAutoTidyRequest; + +export default class PkiTidyForm extends OpenApiForm { + constructor(...args: ConstructorParameters) { + super(...args); + + // use ttl picker for pause_duration + const pauseDuration = this.formFields.find((field) => field.name === 'pause_duration'); + if (pauseDuration) { + pauseDuration.options.editType = 'ttl'; + this.data.pause_duration = '0'; + } + } +} diff --git a/ui/lib/pki/addon/components/page/pki-tidy-auto-configure.hbs b/ui/lib/pki/addon/components/page/pki-tidy-auto-configure.hbs index fdd5fabd2f..d0a648e6e7 100644 --- a/ui/lib/pki/addon/components/page/pki-tidy-auto-configure.hbs +++ b/ui/lib/pki/addon/components/page/pki-tidy-auto-configure.hbs @@ -16,8 +16,8 @@ \ No newline at end of file diff --git a/ui/lib/pki/addon/components/page/pki-tidy-auto-settings.hbs b/ui/lib/pki/addon/components/page/pki-tidy-auto-settings.hbs index e9e20973bf..1a69f48090 100644 --- a/ui/lib/pki/addon/components/page/pki-tidy-auto-settings.hbs +++ b/ui/lib/pki/addon/components/page/pki-tidy-auto-settings.hbs @@ -16,7 +16,7 @@ - + Edit auto-tidy @@ -24,20 +24,32 @@
- {{#each @model.allGroups as |group|}} + {{#each (tidy-groups) as |group|}} {{#each-in group as |label fields|}} - {{#if (not-eq label "autoTidy")}} + {{#if (not-eq label "default")}}

{{label}}

{{/if}} - {{#each fields as |attr|}} + {{#each fields as |field|}} {{/each}} {{/each-in}} diff --git a/ui/lib/pki/addon/components/page/pki-tidy-manual.hbs b/ui/lib/pki/addon/components/page/pki-tidy-manual.hbs index 771a3bd73a..d886ead8f9 100644 --- a/ui/lib/pki/addon/components/page/pki-tidy-manual.hbs +++ b/ui/lib/pki/addon/components/page/pki-tidy-manual.hbs @@ -16,7 +16,7 @@
- {{#if @autoTidyConfig.enabled}} - + {{#if @enabled}} + Auto-tidy configuration - + Perform manual tidy {{else}} @@ -149,13 +149,13 @@ diff --git a/ui/lib/pki/addon/components/page/pki-tidy-status.ts b/ui/lib/pki/addon/components/page/pki-tidy-status.ts index 2846d721b0..6a8d42f593 100644 --- a/ui/lib/pki/addon/components/page/pki-tidy-status.ts +++ b/ui/lib/pki/addon/components/page/pki-tidy-status.ts @@ -8,17 +8,15 @@ import { tracked } from '@glimmer/tracking'; import { service } from '@ember/service'; import { task } from 'ember-concurrency'; import { waitFor } from '@ember/test-waiters'; -import errorMessage from 'vault/utils/error-message'; -import type Store from '@ember-data/store'; +import type ApiService from 'vault/services/api'; import type SecretMountPath from 'vault/services/secret-mount-path'; import type FlashMessageService from 'vault/services/flash-messages'; import type VersionService from 'vault/services/version'; -import type PkiTidyModel from 'vault/models/pki/tidy'; import type RouterService from '@ember/routing/router-service'; interface Args { - autoTidyConfig: PkiTidyModel; + enabled: boolean; tidyStatus: TidyStatusParams; } @@ -48,7 +46,7 @@ interface TidyStatusParams { } export default class PkiTidyStatusComponent extends Component { - @service declare readonly store: Store; + @service declare readonly api: ApiService; @service declare readonly secretMountPath: SecretMountPath; @service declare readonly flashMessages: FlashMessageService; @service declare readonly version: VersionService; @@ -92,7 +90,7 @@ export default class PkiTidyStatusComponent extends Component { get hasTidyConfig() { return !this.tidyStatusConfigFields.every( - (attr) => this.args.tidyStatus[attr as keyof TidyStatusParams] === null + (attr) => this.args.tidyStatus[attr as keyof TidyStatusParams] === undefined ); } @@ -147,17 +145,17 @@ export default class PkiTidyStatusComponent extends Component { } } - @task - @waitFor - *cancelTidy() { - try { - const tidyAdapter = this.store.adapterFor('pki/tidy'); - yield tidyAdapter.cancelTidy(this.secretMountPath.currentPath); - this.router.transitionTo('vault.cluster.secrets.backend.pki.tidy'); - } catch (error) { - this.flashMessages.danger(errorMessage(error)); - } finally { - this.confirmCancelTidy = false; - } - } + cancelTidy = task( + waitFor(async () => { + try { + await this.api.secrets.pkiTidyCancel(this.secretMountPath.currentPath); + this.router.transitionTo('vault.cluster.secrets.backend.pki.tidy'); + } catch (error) { + const { message } = await this.api.parseError(error); + this.flashMessages.danger(message); + } finally { + this.confirmCancelTidy = false; + } + }) + ); } diff --git a/ui/lib/pki/addon/components/pki-tidy-form.hbs b/ui/lib/pki/addon/components/pki-tidy-form.hbs index bc15414701..2ed570a8c4 100644 --- a/ui/lib/pki/addon/components/pki-tidy-form.hbs +++ b/ui/lib/pki/addon/components/pki-tidy-form.hbs @@ -5,8 +5,9 @@
-

Tidying cleans up the storage backend and/or CRL by removing certificates - that have expired and are past a certain buffer period beyond their expiration time. +

+ Tidying cleans up the storage backend and/or CRL by removing certificates that have expired and are past a certain buffer + period beyond their expiration time. Learn more

@@ -14,52 +15,55 @@
{{#if (eq @tidyType "auto")}} - {{#let (get @tidy.allByKey "enabled") as |enabledAttr|}} -
- - - - {{if @tidy.enabled enabledAttr.options.label enabledAttr.options.labelDisabled}} - - {{#unless @tidy.enabled}} -

{{enabledAttr.options.helperTextDisabled}}

- {{/unless}} -
-
-
- {{/let}} - {{#if @tidy.enabled}} +
+ + + + {{if @form.data.enabled "Automatic tidy enabled" "Automatic tidy disabled"}} + + {{#unless @form.data.enabled}} +

Automatic tidy operations will not run.

+ {{/unless}} +
+
+
+ + {{#if @form.data.enabled}} - {{#each @tidy.autoTidyConfigFields as |field|}} - + {{#each (tidy-groups "auto") as |fieldGroup|}} + {{#each-in fieldGroup as |group fields|}} + {{#each fields as |field|}} + + {{/each}} + {{/each-in}} {{/each}} {{/if}} {{/if}} - {{#if (or (eq @tidyType "manual") @tidy.enabled)}} - {{#each @tidy.formFieldGroups as |fieldGroup|}} + {{#if (or (eq @tidyType "manual") @form.data.enabled)}} + {{#each (tidy-groups "manual") as |fieldGroup|}} {{#each-in fieldGroup as |group fields|}} - {{#each fields as |attr|}} - {{#if (eq attr.name "acmeAccountSafetyBuffer")}} + {{#each fields as |field|}} + {{#if (eq field "acme_account_safety_buffer")}} {{else}} - {{! tidyAcme is handled by the ttl above }} - {{#if (not-eq attr.name "tidyAcme")}} - + {{! tidy_acme is set via the TtlPicker change event above }} + {{#if (not-eq field "tidy_acme")}} + {{/if}} {{/if}} {{/each}} diff --git a/ui/lib/pki/addon/components/pki-tidy-form.ts b/ui/lib/pki/addon/components/pki-tidy-form.ts index 3b168856a6..3a9947140e 100644 --- a/ui/lib/pki/addon/components/pki-tidy-form.ts +++ b/ui/lib/pki/addon/components/pki-tidy-form.ts @@ -4,7 +4,6 @@ */ import Component from '@glimmer/component'; -import errorMessage from 'vault/utils/error-message'; import { action } from '@ember/object'; import { service } from '@ember/service'; import { task } from 'ember-concurrency'; @@ -12,47 +11,51 @@ import { waitFor } from '@ember/test-waiters'; import { tracked } from '@glimmer/tracking'; import type RouterService from '@ember/routing/router-service'; -import type PkiTidyModel from 'vault/models/pki/tidy'; -import type { FormField, TtlEvent } from 'vault/app-types'; +import type PkiTidyForm from 'vault/forms/secrets/pki/tidy'; +import type { TtlEvent } from 'vault/app-types'; +import type ApiService from 'vault/services/api'; +import type SecretMountPathService from 'vault/services/secret-mount-path'; interface Args { - tidy: PkiTidyModel; + form: PkiTidyForm; tidyType: string; onSave: CallableFunction; onCancel: CallableFunction; } -interface PkiTidyTtls { - acmeAccountSafetyBuffer: string; -} -interface PkiTidyBooleans { - tidyAcme: boolean; -} - -export default class PkiTidyForm extends Component { +export default class PkiTidyFormComponent extends Component { @service('app-router') declare readonly router: RouterService; + @service declare readonly api: ApiService; + @service declare readonly secretMountPath: SecretMountPathService; @tracked errorBanner = ''; @tracked invalidFormAlert = ''; - @task - @waitFor - *save(event: Event) { - event.preventDefault(); - try { - yield this.args.tidy.save({ adapterOptions: { tidyType: this.args.tidyType } }); - this.args.onSave(); - } catch (e) { - this.errorBanner = errorMessage(e); - this.invalidFormAlert = 'There was an error submitting this form.'; - } - } + save = task( + waitFor(async (event: Event) => { + event.preventDefault(); + try { + const { currentPath } = this.secretMountPath; + const { data } = this.args.form.toJSON(); + + if (this.args.tidyType === 'auto') { + await this.api.secrets.pkiConfigureAutoTidy(currentPath, data); + } else { + await this.api.secrets.pkiTidy(currentPath, data); + } + this.args.onSave(); + } catch (e) { + const { message } = await this.api.parseError(e); + this.errorBanner = message; + this.invalidFormAlert = 'There was an error submitting this form.'; + } + }) + ); @action - handleTtl(attr: FormField, e: TtlEvent) { + handleAcmeTtl(e: TtlEvent) { const { enabled, goSafeTimeString } = e; - const ttlAttr = attr.name; - this.args.tidy[ttlAttr as keyof PkiTidyTtls] = goSafeTimeString; - this.args.tidy[attr.options.mapToBoolean as keyof PkiTidyBooleans] = enabled; + this.args.form.data.acme_account_safety_buffer = goSafeTimeString; + this.args.form.data.tidy_acme = enabled; } } diff --git a/ui/lib/pki/addon/helpers/tidy-field-label.ts b/ui/lib/pki/addon/helpers/tidy-field-label.ts new file mode 100644 index 0000000000..2d5708fdda --- /dev/null +++ b/ui/lib/pki/addon/helpers/tidy-field-label.ts @@ -0,0 +1,23 @@ +/** + * Copyright IBM Corp. 2016, 2025 + * SPDX-License-Identifier: BUSL-1.1 + */ +import { toLabel } from 'core/helpers/to-label'; + +export default function tidyFieldLabel(field: string) { + const label = toLabel([field]); + return ( + { + acme_account_safety_buffer: 'ACME account safety buffer', + tidy_acme: 'Tidy ACME', + enabled: 'Automatic tidy enabled', + tidy_cert_store: 'Tidy the certificate store', + tidy_cross_cluster_revoked_certs: 'Tidy cross-cluster revoked certificates', + tidy_cmpv2_nonce_store: 'Tidy CMPv2 nonce store', + tidy_move_legacy_ca_bundle: 'Tidy legacy CA bundle', + tidy_revocation_queue: 'Tidy cross-cluster revocation requests', + tidy_revoked_cert_issuer_associations: 'Tidy revoked certificate issuer associations', + tidy_revoked_certs: 'Tidy revoked certificates', + }[field] || label + ); +} diff --git a/ui/lib/pki/addon/helpers/tidy-groups.ts b/ui/lib/pki/addon/helpers/tidy-groups.ts new file mode 100644 index 0000000000..f62fa527b0 --- /dev/null +++ b/ui/lib/pki/addon/helpers/tidy-groups.ts @@ -0,0 +1,62 @@ +/** + * Copyright IBM Corp. 2016, 2025 + * SPDX-License-Identifier: BUSL-1.1 + */ + +import Helper from '@ember/component/helper'; +import { service } from '@ember/service'; + +import type VersionService from 'vault/services/version'; + +export default class TidyGroups extends Helper { + @service declare readonly version: VersionService; + + compute([group]: [group?: 'auto' | 'manual']) { + // groups that are shared between auto and manual tidy types + const shared: Array>> = [ + { + 'Universal operations': [ + 'tidy_cert_store', + 'tidy_cert_metadata', + 'tidy_revoked_certs', + 'tidy_revoked_cert_issuer_associations', + 'safety_buffer', + 'pause_duration', + ], + }, + { + 'ACME operations': ['tidy_acme', 'acme_account_safety_buffer'], + }, + { + 'Issuer operations': ['tidy_expired_issuers', 'tidy_move_legacy_ca_bundle', 'issuer_safety_buffer'], + }, + ]; + // cross cluster operations are only available in enterprise + if (this.version.isEnterprise) { + shared.push({ + 'Cross-cluster operations': [ + 'tidy_revocation_queue', + 'tidy_cross_cluster_revoked_certs', + 'tidy_cmpv2_nonce_store', + 'revocation_queue_safety_buffer', + ], + }); + } + // auto tidy specific fields + const auto = { + default: ['interval_duration', 'min_startup_backoff_duration', 'max_startup_backoff_duration'], + }; + + if (group === 'auto') { + return [auto]; + } + if (group === 'manual') { + return shared; + } + // if group is not specified, return combined groups + // add enabled field to the top of auto tidy fields for details view + auto.default.unshift('enabled'); + shared.unshift(auto); + return shared; + } +} diff --git a/ui/lib/pki/addon/routes/tidy.js b/ui/lib/pki/addon/routes/tidy.js index 55a9bf8052..6926f1a21c 100644 --- a/ui/lib/pki/addon/routes/tidy.js +++ b/ui/lib/pki/addon/routes/tidy.js @@ -6,18 +6,19 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; import { withConfig } from 'pki/decorators/check-issuers'; -import { hash } from 'rsvp'; @withConfig() export default class PkiTidyRoute extends Route { - @service store; + @service api; - model() { + async model() { const engine = this.modelFor('application'); - return hash({ + const autoTidyConfig = await this.api.secrets.pkiReadAutoTidyConfiguration(engine.id); + + return { hasConfig: this.pkiMountHasConfig, engine, - autoTidyConfig: this.store.findRecord('pki/tidy', engine.id), - }); + autoTidyConfig, + }; } } diff --git a/ui/lib/pki/addon/routes/tidy/auto/configure.js b/ui/lib/pki/addon/routes/tidy/auto/configure.js index cb3b955ed9..7ec539e892 100644 --- a/ui/lib/pki/addon/routes/tidy/auto/configure.js +++ b/ui/lib/pki/addon/routes/tidy/auto/configure.js @@ -5,25 +5,27 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { withConfirmLeave } from 'core/decorators/confirm-leave'; +import PkiTidyForm from 'vault/forms/secrets/pki/tidy'; -@withConfirmLeave() export default class PkiTidyAutoConfigureRoute extends Route { @service store; @service secretMountPath; - // inherits model from tidy/auto + model() { + const { autoTidyConfig } = this.modelFor('tidy'); + return new PkiTidyForm('PkiConfigureAutoTidyRequest', autoTidyConfig); + } setupController(controller, resolvedModel) { // autoTidyConfig id is the backend path - const { id: backend } = resolvedModel; + const { currentPath } = this.secretMountPath; super.setupController(controller, resolvedModel); controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, - { label: this.secretMountPath.currentPath, route: 'overview', model: backend }, - { label: 'Configuration', route: 'configuration.index', model: backend }, - { label: 'Tidy', route: 'tidy', model: backend }, - { label: 'Auto', route: 'tidy.auto', model: backend }, + { label: currentPath, route: 'overview', model: currentPath }, + { label: 'Configuration', route: 'configuration.index', model: currentPath }, + { label: 'Tidy', route: 'tidy', model: currentPath }, + { label: 'Auto', route: 'tidy.auto', model: currentPath }, { label: 'Configure' }, ]; } diff --git a/ui/lib/pki/addon/routes/tidy/auto/index.js b/ui/lib/pki/addon/routes/tidy/auto/index.js index 94d1e12097..ccbc944702 100644 --- a/ui/lib/pki/addon/routes/tidy/auto/index.js +++ b/ui/lib/pki/addon/routes/tidy/auto/index.js @@ -13,15 +13,14 @@ export default class TidyAutoIndexRoute extends Route { // inherits model from tidy/auto setupController(controller, resolvedModel) { - // autoTidyConfig id is the backend path - const { id: backend } = resolvedModel; - super.setupController(...arguments); + const { currentPath } = this.secretMountPath; + super.setupController(controller, resolvedModel); controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, - { label: this.secretMountPath.currentPath, route: 'overview', model: backend }, - { label: 'Tidy', route: 'tidy.index', model: backend }, + { label: currentPath, route: 'overview', model: currentPath }, + { label: 'Tidy', route: 'tidy.index', model: currentPath }, { label: 'Auto' }, ]; - controller.title = this.secretMountPath.currentPath; + controller.backend = currentPath; } } diff --git a/ui/lib/pki/addon/routes/tidy/index.js b/ui/lib/pki/addon/routes/tidy/index.js index 980ee0a2e4..27b222ba35 100644 --- a/ui/lib/pki/addon/routes/tidy/index.js +++ b/ui/lib/pki/addon/routes/tidy/index.js @@ -4,35 +4,28 @@ */ import Route from '@ember/routing/route'; -import { PKI_DEFAULT_EMPTY_STATE_MSG } from '../overview'; -import { hash } from 'rsvp'; import { service } from '@ember/service'; +import { PKI_DEFAULT_EMPTY_STATE_MSG } from '../overview'; import timestamp from 'core/utils/timestamp'; export default class PkiTidyIndexRoute extends Route { - @service store; + @service api; @service secretMountPath; async fetchTidyStatus() { - const adapter = this.store.adapterFor('application'); - const tidyStatusResponse = await adapter.ajax( - `/v1/${this.secretMountPath.currentPath}/tidy-status`, - 'GET' - ); - const responseTimestamp = timestamp.now(); - tidyStatusResponse.data.responseTimestamp = responseTimestamp; - return tidyStatusResponse.data; + const status = await this.api.secrets.pkiTidyStatus(this.secretMountPath.currentPath); + return { ...status, responseTimestamp: timestamp.now() }; } - - model() { + async model() { const { hasConfig, autoTidyConfig, engine } = this.modelFor('tidy'); + const tidyStatus = await this.fetchTidyStatus(); - return hash({ - tidyStatus: this.fetchTidyStatus(), + return { + tidyStatus, hasConfig, autoTidyConfig, engine, - }); + }; } setupController(controller, resolvedModel) { diff --git a/ui/lib/pki/addon/routes/tidy/manual.js b/ui/lib/pki/addon/routes/tidy/manual.js index 1ef4943678..174bcc413f 100644 --- a/ui/lib/pki/addon/routes/tidy/manual.js +++ b/ui/lib/pki/addon/routes/tidy/manual.js @@ -5,24 +5,35 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import { withConfirmLeave } from 'core/decorators/confirm-leave'; +import PkiTidyForm from 'vault/forms/secrets/pki/tidy'; -@withConfirmLeave() export default class PkiTidyManualRoute extends Route { @service store; @service secretMountPath; model() { - return this.store.createRecord('pki/tidy', { backend: this.secretMountPath.currentPath }); + return new PkiTidyForm( + 'PkiTidyRequest', + { + acme_account_safety_buffer: 2592000, + issuer_safety_buffer: 31536000, + revocation_queue_safety_buffer: 172800, + safety_buffer: 259200, + tidy_acme: false, + tidy_revocation_queue: false, + }, + { isNew: true } + ); } setupController(controller, resolvedModel) { super.setupController(controller, resolvedModel); + const { currentPath } = this.secretMountPath; controller.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, - { label: this.secretMountPath.currentPath, route: 'overview', model: resolvedModel.backend }, - { label: 'Configuration', route: 'configuration.index', model: resolvedModel.backend }, - { label: 'Tidy', route: 'tidy', model: resolvedModel.backend }, + { label: currentPath, route: 'overview', model: currentPath }, + { label: 'Configuration', route: 'configuration.index', model: currentPath }, + { label: 'Tidy', route: 'tidy', model: currentPath }, { label: 'Manual' }, ]; } diff --git a/ui/lib/pki/addon/templates/tidy/auto/configure.hbs b/ui/lib/pki/addon/templates/tidy/auto/configure.hbs index 7c5cfd43d2..dcb04a6ae9 100644 --- a/ui/lib/pki/addon/templates/tidy/auto/configure.hbs +++ b/ui/lib/pki/addon/templates/tidy/auto/configure.hbs @@ -3,4 +3,4 @@ SPDX-License-Identifier: BUSL-1.1 }} - \ No newline at end of file + \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/tidy/auto/index.hbs b/ui/lib/pki/addon/templates/tidy/auto/index.hbs index 8006fc49f6..8e45c60744 100644 --- a/ui/lib/pki/addon/templates/tidy/auto/index.hbs +++ b/ui/lib/pki/addon/templates/tidy/auto/index.hbs @@ -3,4 +3,4 @@ SPDX-License-Identifier: BUSL-1.1 }} - \ No newline at end of file + \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/tidy/index.hbs b/ui/lib/pki/addon/templates/tidy/index.hbs index 706f953186..ca98c21e15 100644 --- a/ui/lib/pki/addon/templates/tidy/index.hbs +++ b/ui/lib/pki/addon/templates/tidy/index.hbs @@ -6,7 +6,7 @@ {{#if this.model.hasConfig}} - + {{else}} diff --git a/ui/tests/acceptance/pki/pki-tidy-test.js b/ui/tests/acceptance/pki/pki-tidy-test.js index b8affba13b..08ac995d2b 100644 --- a/ui/tests/acceptance/pki/pki-tidy-test.js +++ b/ui/tests/acceptance/pki/pki-tidy-test.js @@ -48,7 +48,7 @@ module('Acceptance | pki tidy', function (hooks) { .exists('Configure manual tidy button exists'); await click(PKI_TIDY.tidyConfigureModal.tidyModalManualButton); assert.dom(PKI_TIDY_FORM.tidyFormName('manual')).exists('Manual tidy form exists'); - await click(PKI_TIDY_FORM.inputByAttr('tidyCertStore')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_cert_store')); await fillIn(PKI_TIDY_FORM.tidyPauseDuration, '10'); await click(PKI_TIDY_FORM.tidySave); await click(PKI_TIDY.cancelTidyAction); @@ -130,12 +130,12 @@ module('Acceptance | pki tidy', function (hooks) { assert .dom(PKI_TIDY_FORM.tidySectionHeader('ACME operations')) .exists('Auto tidy form enabled shows ACME operations field'); - await click(PKI_TIDY_FORM.inputByAttr('tidyCertStore')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_cert_store')); await click(PKI_TIDY_FORM.tidySave); assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.pki.tidy.auto.index'); await click(PKI_TIDY_FORM.editAutoTidyButton); assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.pki.tidy.auto.configure'); - await click(PKI_TIDY_FORM.inputByAttr('tidyRevokedCerts')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_revoked_certs')); await click(PKI_TIDY_FORM.tidySave); assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.pki.tidy.auto.index'); }); @@ -151,7 +151,7 @@ module('Acceptance | pki tidy', function (hooks) { await click(PKI_TIDY.tidyConfigureModal.tidyModalManualButton); assert.dom(PKI_TIDY_FORM.tidyFormName('manual')).exists(); - await click(PKI_TIDY_FORM.inputByAttr('tidyCertStore')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_cert_store')); await click(GENERAL.ttl.toggle('Tidy ACME disabled')); assert @@ -190,8 +190,8 @@ module('Acceptance | pki tidy', function (hooks) { assert.dom(PKI_TIDY.tidyConfigureModal.configureTidyModal).exists('Configure tidy modal exists'); await click(PKI_TIDY.tidyConfigureModal.tidyModalAutoButton); await click(GENERAL.ttl.toggle('enabled')); - await click(PKI_TIDY_FORM.inputByAttr('tidyCertStore')); - await click(PKI_TIDY_FORM.inputByAttr('tidyRevokedCerts')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_cert_store')); + await click(PKI_TIDY_FORM.inputByAttr('tidy_revoked_certs')); await click(PKI_TIDY_FORM.tidySave); await visit(`/vault/secrets-engines/${this.mountPath}/pki/tidy`); assert diff --git a/ui/tests/integration/components/pki/page/pki-tidy-auto-settings-test.js b/ui/tests/integration/components/pki/page/pki-tidy-auto-settings-test.js index a1f1daff21..2c13a0f08f 100644 --- a/ui/tests/integration/components/pki/page/pki-tidy-auto-settings-test.js +++ b/ui/tests/integration/components/pki/page/pki-tidy-auto-settings-test.js @@ -15,35 +15,32 @@ module('Integration | Component | page/pki-tidy-auto-settings', function (hooks) setupEngine(hooks, 'pki'); hooks.beforeEach(function () { - const backend = 'pki-auto-tidy'; - this.backend = backend; - - this.context = { owner: this.engine }; - this.store = this.owner.lookup('service:store'); - + this.backend = 'pki-auto-tidy'; this.breadcrumbs = [ { label: 'Secrets', route: 'secrets', linkExternal: true }, - { label: backend, route: 'overview', model: backend }, - { label: 'Tidy', route: 'tidy.index', model: backend }, + { label: this.backend, route: 'overview', model: this.backend }, + { label: 'Tidy', route: 'tidy.index', model: this.backend }, { label: 'Auto' }, ]; + + this.model = { + enabled: false, + interval_duration: '2d', + tidy_cert_store: false, + tidy_expired_issuers: true, + }; + + this.renderComponent = () => + render( + hbs``, + { + owner: this.engine, + } + ); }); test('it renders', async function (assert) { - const model = this.store.createRecord('pki/tidy', { - backend: this.backend, - tidyType: 'auto', - enabled: false, - intervalDuration: '2d', - tidyCertStore: false, - tidyExpiredIssuers: true, - }); - this.set('model', model); - - await render( - hbs``, - this.context - ); + await this.renderComponent(); assert.dom('[data-test-breadcrumbs] li').exists({ count: 4 }, 'an item exists for each breadcrumb'); assert.dom('[data-test-header-title]').hasText('Automatic Tidy Configuration', 'title is correct'); diff --git a/ui/tests/integration/components/pki/page/pki-tidy-status-test.js b/ui/tests/integration/components/pki/page/pki-tidy-status-test.js index 001d2df0b2..c4b1126803 100644 --- a/ui/tests/integration/components/pki/page/pki-tidy-status-test.js +++ b/ui/tests/integration/components/pki/page/pki-tidy-status-test.js @@ -17,14 +17,10 @@ module('Integration | Component | Page::PkiTidyStatus', function (hooks) { setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); - this.secretMountPath = this.owner.lookup('service:secret-mount-path'); - this.secretMountPath.currentPath = 'pki-test'; + this.backend = 'pki-test'; + this.secretMountPath = this.owner.lookup('service:secret-mount-path').update(); - this.store.createRecord('pki/issuer', { issuerId: 'abcd-efgh' }); - this.store.createRecord('pki/tidy', { backend: this.secretMountPath.currentPath, tidyType: 'auto' }); - - this.autoTidyConfig = this.store.peekAll('pki/tidy'); + this.enabled = true; this.tidyStatus = { acme_account_deleted_count: 0, acme_account_revoked_count: 0, @@ -54,60 +50,45 @@ module('Integration | Component | Page::PkiTidyStatus', function (hooks) { time_started: '2023-05-18T13:27:36.390959-07:00', }; this.engineId = 'pki'; + + this.renderComponent = () => + render(hbs``, { + owner: this.engine, + }); }); test('shows the correct titles for the alert banner based on states', async function (assert) { - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); // running state assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy in progress'); assert.dom(PKI_TIDY.cancelTidyAction).exists(); assert.dom(PKI_TIDY.hdsAlertButtonText).hasText('Cancel tidy'); // inactive state this.tidyStatus.state = 'Inactive'; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy is inactive'); // finished state this.tidyStatus.state = 'Finished'; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy operation finished'); // error state this.tidyStatus.state = 'Error'; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy operation failed'); // cancelling state this.tidyStatus.state = 'Cancelling'; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy operation cancelling'); // cancelled state this.tidyStatus.state = 'Cancelled'; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.hdsAlertTitle).hasText('Tidy operation cancelled'); }); + test('shows the fields even if the data returns null values', async function (assert) { this.tidyStatus.time_started = null; this.tidyStatus.time_finished = null; - await render( - hbs``, - { owner: this.engine } - ); + await this.renderComponent(); assert.dom(PKI_TIDY.timeStartedRow).exists(); assert.dom(PKI_TIDY.timeFinishedRow).exists(); }); diff --git a/ui/tests/integration/components/pki/pki-tidy-form-test.js b/ui/tests/integration/components/pki/pki-tidy-form-test.js index a6b8529bc5..274419ba83 100644 --- a/ui/tests/integration/components/pki/pki-tidy-form-test.js +++ b/ui/tests/integration/components/pki/pki-tidy-form-test.js @@ -8,41 +8,60 @@ import { setupRenderingTest } from 'ember-qunit'; import { click, render, fillIn } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; import { setupEngine } from 'ember-engines/test-support'; -import { setupMirage } from 'ember-cli-mirage/test-support'; import { PKI_TIDY_FORM } from 'vault/tests/helpers/pki/pki-selectors'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { convertToSeconds } from 'core/utils/duration-utils'; +import PkiTidyForm from 'vault/forms/secrets/pki/tidy'; +import TidyGroupsHelper from 'pki/helpers/tidy-groups'; +import tidyFieldLabel from 'pki/helpers/tidy-field-label'; +import sinon from 'sinon'; module('Integration | Component | pki tidy form', function (hooks) { setupRenderingTest(hooks); setupEngine(hooks, 'pki'); - setupMirage(hooks); hooks.beforeEach(function () { - this.store = this.owner.lookup('service:store'); + this.backend = 'pki-test'; + this.owner.lookup('service:secret-mount-path').update(this.backend); + this.version = this.owner.lookup('service:version'); this.version.type = 'enterprise'; - this.server.post('/sys/capabilities-self', () => {}); - this.onSave = () => {}; - this.onCancel = () => {}; - this.manualTidy = this.store.createRecord('pki/tidy', { backend: 'pki-manual-tidy' }); + + this.tidyGroupsHelper = new TidyGroupsHelper(this.owner); + const groupsReducer = (fields, group) => [...fields, ...Object.values(group)[0]]; + this.allFields = this.tidyGroupsHelper.compute([]).reduce(groupsReducer, []); + this.autoFields = this.tidyGroupsHelper.compute(['auto']).reduce(groupsReducer, []); + + this.onSave = sinon.spy(); + this.onCancel = sinon.spy(); + + const { secrets } = this.owner.lookup('service:api'); + this.tidyStub = sinon.stub(secrets, 'pkiTidy').resolves(); + this.autoTidyStub = sinon.stub(secrets, 'pkiConfigureAutoTidy').resolves(); + // setting defaults here to simulate how this form works in the app. + // on init, we retrieve these from the server and pre-populate form this.autoTidyServerDefaults = { enabled: false, + acme_account_safety_buffer: '48h', interval_duration: '12h', safety_buffer: '3d', issuer_safety_buffer: '365d', min_startup_backoff_duration: '5m', max_startup_backoff_duration: '15m', + revocation_queue_safety_buffer: '10s', + pause_duration: '0', + }; + + this.renderComponent = (type = 'auto') => { + this.tidyType = type; + const formData = type === 'auto' ? this.autoTidyServerDefaults : {}; + const schemaKey = type === 'auto' ? 'PkiConfigureAutoTidyRequest' : 'PkiTidyRequest'; + this.form = new PkiTidyForm(schemaKey, formData, { isNew: type === 'manual' }); + return render( + hbs``, + { owner: this.engine } + ); }; - this.store.pushPayload('pki/tidy', { - modelName: 'pki/tidy', - id: 'pki-auto-tidy', - // setting defaults here to simulate how this form works in the app. - // on init, we retrieve these from the server and pre-populate form (instead of explicitly set on the model) - ...this.autoTidyServerDefaults, - }); - this.autoTidy = this.store.peekRecord('pki/tidy', 'pki-auto-tidy'); - this.numTidyAttrs = Object.keys(this.autoTidy.allByKey).length; }); test('it hides or shows fields depending on auto-tidy toggle', async function (assert) { @@ -53,31 +72,24 @@ module('Integration | Component | pki tidy form', function (hooks) { 'Issuer operations', 'Cross-cluster operations', ]; - const loopAssertCount = this.numTidyAttrs * 2 - 3; // loop skips 3 params const headerAssertCount = sectionHeaders.length * 2; + + await this.renderComponent(); + // the form isn't created until we render so expect assertions need to be after render + const loopAssertCount = this.allFields.length * 2 - 3; // loop skips 3 params assert.expect(loopAssertCount + headerAssertCount + 4); - await render( - hbs` - - `, - { owner: this.engine } - ); assert.dom(GENERAL.toggleInput('enabled')).isNotChecked(); assert .dom(GENERAL.ttl.toggle('enabled')) .hasText('Automatic tidy disabled Automatic tidy operations will not run.'); - this.autoTidy.eachAttribute((attr) => { - if (attr === 'enabled') return; - assert - .dom(PKI_TIDY_FORM.inputByAttr(attr)) - .doesNotExist(`does not render ${attr} when auto tidy disabled`); + this.allFields.forEach((field) => { + if (field !== 'enabled') { + assert + .dom(PKI_TIDY_FORM.inputByAttr(field)) + .doesNotExist(`does not render ${field} when auto tidy disabled`); + } }); sectionHeaders.forEach((group) => { @@ -89,10 +101,12 @@ module('Integration | Component | pki tidy form', function (hooks) { assert.dom(GENERAL.toggleInput('enabled')).isChecked(); assert.dom(GENERAL.ttl.toggle('enabled')).hasText('Automatic tidy enabled'); - this.autoTidy.eachAttribute((attr) => { - const skipFields = ['enabled', 'tidyAcme']; - if (skipFields.includes(attr)) return; // combined with duration ttl or asserted elsewhere - assert.dom(PKI_TIDY_FORM.inputByAttr(attr)).exists(`renders ${attr} when auto tidy enabled`); + this.allFields.forEach((field) => { + const skipFields = ['enabled', 'tidy_acme']; + // combined with duration ttl or asserted elsewhere + if (!skipFields.includes(field)) { + assert.dom(PKI_TIDY_FORM.inputByAttr(field)).exists(`renders ${field} when auto tidy enabled`); + } }); sectionHeaders.forEach((group) => { @@ -102,74 +116,49 @@ module('Integration | Component | pki tidy form', function (hooks) { test('it renders all attribute fields, including enterprise', async function (assert) { assert.expect(35); - this.autoTidy.enabled = true; - const skipFields = ['enabled', 'tidyAcme']; // combined with duration ttl or asserted separately - await render( - hbs` - - `, - { owner: this.engine } - ); - this.autoTidy.eachAttribute((attr) => { - if (skipFields.includes(attr)) return; - assert.dom(PKI_TIDY_FORM.inputByAttr(attr)).exists(`renders ${attr} for auto tidyType`); + await this.renderComponent(); + await click(GENERAL.toggleInput('enabled')); + + const skipFields = ['enabled', 'tidy_acme']; // combined with duration ttl or asserted separately + this.allFields.forEach((field) => { + if (!skipFields.includes(field)) { + assert.dom(PKI_TIDY_FORM.inputByAttr(field)).exists(`renders ${field} for auto tidyType`); + } }); // MANUAL TIDY - await render( - hbs` - - `, - { owner: this.engine } - ); + await this.renderComponent('manual'); + assert.dom(GENERAL.toggleInput('enabled')).doesNotExist('hides automatic tidy toggle'); - this.manualTidy.eachAttribute((attr) => { - if (skipFields.includes(attr)) return; - // auto tidy fields we shouldn't see in the manual tidy form - if (this.manualTidy.autoTidyConfigFields.includes(attr)) { - assert - .dom(PKI_TIDY_FORM.inputByAttr(attr)) - .doesNotExist(`${attr} should not appear on manual tidyType`); - } else { - assert.dom(PKI_TIDY_FORM.inputByAttr(attr)).exists(`renders ${attr} for manual tidyType`); + this.allFields.forEach((field) => { + if (!skipFields.includes(field)) { + // auto tidy fields we shouldn't see in the manual tidy form + if (this.autoFields.includes(field)) { + assert + .dom(PKI_TIDY_FORM.inputByAttr(field)) + .doesNotExist(`${field} should not appear on manual tidyType`); + } else { + assert.dom(PKI_TIDY_FORM.inputByAttr(field)).exists(`renders ${field} for manual tidyType`); + } } }); }); test('it hides enterprise fields for CE', async function (assert) { this.version.type = 'community'; - this.autoTidy.enabled = true; const enterpriseFields = [ - 'tidyRevocationQueue', - 'tidyCrossClusterRevokedCerts', - 'revocationQueueSafetyBuffer', + 'tidy_revocation_queue', + 'tidy_cross_cluster_revoked_certs', + 'tidy_cmpv2_nonce_store', + 'revocation_queue_safety_buffer', ]; // tidyType = auto - await render( - hbs` - - `, - { owner: this.engine } - ); + await this.renderComponent(); + await click(GENERAL.toggleInput('enabled')); assert .dom(PKI_TIDY_FORM.tidySectionHeader('Cross-cluster operations')) @@ -182,17 +171,7 @@ module('Integration | Component | pki tidy form', function (hooks) { }); // tidyType = manual - await render( - hbs` - - `, - { owner: this.engine } - ); + await this.renderComponent('manual'); enterpriseFields.forEach((entAttr) => { assert @@ -201,160 +180,111 @@ module('Integration | Component | pki tidy form', function (hooks) { }); }); - test('it should change the attributes on the model', async function (assert) { - assert.expect(12); + test('it should update form values', async function (assert) { + assert.expect(11); // ttl picker defaults to seconds, unless unit is set by default value (set in beforeEach hook) // on submit, any user inputted values should be converted to seconds for the payload const fillInValues = { - acmeAccountSafetyBuffer: { time: 680, unit: 'h' }, - intervalDuration: { time: 10, unit: 'h' }, - issuerSafetyBuffer: { time: 20, unit: 'd' }, - maxStartupBackoffDuration: { time: 30, unit: 'm' }, - minStartupBackoffDuration: { time: 10, unit: 'm' }, - pauseDuration: { time: 30, unit: 's' }, - revocationQueueSafetyBuffer: { time: 40, unit: 's' }, - safetyBuffer: { time: 50, unit: 'd' }, + acme_account_safety_buffer: { time: 680, unit: 'h' }, + interval_duration: { time: 10, unit: 'h' }, + issuer_safety_buffer: { time: 20, unit: 'd' }, + max_startup_backoff_duration: { time: 30, unit: 'm' }, + min_startup_backoff_duration: { time: 10, unit: 'm' }, + pause_duration: { time: 30, unit: 's' }, + revocation_queue_safety_buffer: { time: 40, unit: 's' }, + safety_buffer: { time: 50, unit: 'd' }, }; const calcValue = (param) => { const { time, unit } = fillInValues[param]; return `${convertToSeconds(time, unit)}s`; }; - this.server.post('/pki-auto-tidy/config/auto-tidy', (schema, req) => { - assert.propEqual( - JSON.parse(req.requestBody), - { - acme_account_safety_buffer: '48h', - enabled: true, - min_startup_backoff_duration: calcValue('minStartupBackoffDuration'), - max_startup_backoff_duration: calcValue('maxStartupBackoffDuration'), - interval_duration: calcValue('intervalDuration'), - issuer_safety_buffer: calcValue('issuerSafetyBuffer'), - pause_duration: calcValue('pauseDuration'), - revocation_queue_safety_buffer: calcValue('revocationQueueSafetyBuffer'), - safety_buffer: calcValue('safetyBuffer'), - tidy_acme: true, - tidy_cert_metadata: true, - tidy_cert_store: true, - tidy_cmpv2_nonce_store: true, - tidy_cross_cluster_revoked_certs: true, - tidy_expired_issuers: true, - tidy_move_legacy_ca_bundle: true, - tidy_revocation_queue: true, - tidy_revoked_cert_issuer_associations: true, - tidy_revoked_certs: true, - }, - 'response contains updated model values' - ); - }); - await render( - hbs` - - `, - { owner: this.engine } - ); + + await this.renderComponent(); assert.dom(GENERAL.toggleInput('enabled')).isNotChecked(); assert.dom(GENERAL.ttl.toggle('enabled')).hasTextContaining('Automatic tidy disabled'); - assert.false(this.autoTidy.enabled, 'enabled is false on model'); + assert.false(this.form.data.enabled, 'enabled is false on form'); // enable auto-tidy await click(GENERAL.toggleInput('enabled')); assert.dom(GENERAL.toggleInput('enabled')).isChecked(); assert.dom(GENERAL.ttl.toggle('enabled')).hasText('Automatic tidy enabled'); - assert.dom(PKI_TIDY_FORM.toggleInput('acmeAccountSafetyBuffer')).isNotChecked('ACME tidy is disabled'); + assert.dom(PKI_TIDY_FORM.toggleInput('acme_account_safety_buffer')).isNotChecked('ACME tidy is disabled'); assert .dom(PKI_TIDY_FORM.toggleLabel('Tidy ACME disabled')) .exists('ACME label has correct disabled text'); - assert.false(this.autoTidy.tidyAcme, 'tidyAcme is false on model'); - await click(PKI_TIDY_FORM.toggleInput('acmeAccountSafetyBuffer')); + await click(PKI_TIDY_FORM.toggleInput('acme_account_safety_buffer')); await fillIn(PKI_TIDY_FORM.acmeAccountSafetyBuffer, 2); // units are days based on defaultValue - assert.dom(PKI_TIDY_FORM.toggleInput('acmeAccountSafetyBuffer')).isChecked('ACME tidy is enabled'); + assert.dom(PKI_TIDY_FORM.toggleInput('acme_account_safety_buffer')).isChecked('ACME tidy is enabled'); assert.dom(PKI_TIDY_FORM.toggleLabel('Tidy ACME enabled')).exists('ACME label has correct enabled text'); - assert.true(this.autoTidy.tidyAcme, 'tidyAcme toggles to true'); + assert.true(this.form.data.tidy_acme, 'tidy_acme toggles to true'); - this.autoTidy.eachAttribute(async (attr, { type }) => { - const skipFields = ['enabled', 'tidyAcme', 'acmeAccountSafetyBuffer']; // combined with duration ttl or asserted separately - if (skipFields.includes(attr)) return; - // all params right now are either a boolean or TTL, this if/else will need to be updated if that changes - if (type === 'boolean') { - await click(PKI_TIDY_FORM.inputByAttr(attr)); - } else { - const { time } = fillInValues[attr]; - await fillIn(PKI_TIDY_FORM.toggleInput(attr), `${time}`); + for (const field of this.allFields) { + const skipFields = ['enabled', 'tidy_acme', 'acme_account_safety_buffer']; // combined with duration ttl or asserted separately + if (!skipFields.includes(field)) { + // all params right now are either a boolean or TTL, this if/else will need to be updated if that changes + if (Object.keys(fillInValues).includes(field)) { + const { time } = fillInValues[field]; + await fillIn(GENERAL.ttl.input(tidyFieldLabel(field)), `${time}`); + } else { + await click(PKI_TIDY_FORM.inputByAttr(field)); + } } - }); + } await click(PKI_TIDY_FORM.tidySave); + const payload = { + acme_account_safety_buffer: '48h', + enabled: true, + min_startup_backoff_duration: calcValue('min_startup_backoff_duration'), + max_startup_backoff_duration: calcValue('max_startup_backoff_duration'), + interval_duration: calcValue('interval_duration'), + issuer_safety_buffer: calcValue('issuer_safety_buffer'), + pause_duration: calcValue('pause_duration'), + revocation_queue_safety_buffer: calcValue('revocation_queue_safety_buffer'), + safety_buffer: calcValue('safety_buffer'), + tidy_acme: true, + tidy_cert_metadata: true, + tidy_cert_store: true, + tidy_cmpv2_nonce_store: true, + tidy_cross_cluster_revoked_certs: true, + tidy_expired_issuers: true, + tidy_move_legacy_ca_bundle: true, + tidy_revocation_queue: true, + tidy_revoked_cert_issuer_associations: true, + tidy_revoked_certs: true, + }; + assert.true(this.autoTidyStub.calledWith(this.backend, payload), 'API called with correct payload'); }); test('it updates auto-tidy config', async function (assert) { - assert.expect(4); - this.server.post('/pki-auto-tidy/config/auto-tidy', (schema, req) => { - assert.ok(true, 'Request made to update auto-tidy'); - assert.propEqual( - JSON.parse(req.requestBody), - { - ...this.autoTidyServerDefaults, - acme_account_safety_buffer: '720h', - tidy_acme: false, - }, - 'response contains default auto-tidy params' - ); - }); - this.onSave = () => assert.ok(true, 'onSave callback fires on save success'); - this.onCancel = () => assert.ok(true, 'onCancel callback fires on save success'); + assert.expect(3); - await render( - hbs` - - `, - { owner: this.engine } - ); + await this.renderComponent(); await click(PKI_TIDY_FORM.tidySave); + assert.true( + this.autoTidyStub.calledWith(this.backend, this.autoTidyServerDefaults), + 'API called with correct payload' + ); + assert.true(this.onSave.called, 'onSave called on save success'); + await click(PKI_TIDY_FORM.tidyCancel); + assert.true(this.onCancel.called, 'onCancel called on click'); }); test('it saves and performs manual tidy', async function (assert) { - assert.expect(4); + assert.expect(3); - this.server.post('/pki-manual-tidy/tidy', (schema, req) => { - assert.ok(true, 'Request made to perform manual tidy'); - assert.propEqual( - JSON.parse(req.requestBody), - { acme_account_safety_buffer: '720h', tidy_acme: false }, - 'response contains manual tidy params' - ); - return { id: 'pki-manual-tidy' }; - }); - this.onSave = () => assert.ok(true, 'onSave callback fires on save success'); - this.onCancel = () => assert.ok(true, 'onCancel callback fires on save success'); - - await render( - hbs` - - `, - { owner: this.engine } - ); + await this.renderComponent('manual'); await click(PKI_TIDY_FORM.tidySave); + assert.true(this.tidyStub.calledWith(this.backend), 'API called with correct payload'); + assert.true(this.onSave.called, 'onSave called on save success'); + await click(PKI_TIDY_FORM.tidyCancel); + assert.true(this.onCancel.called, 'onCancel called on click'); }); });