From 6d1ddf36a803c25d3d476d77cdb35f3c02bcf12d Mon Sep 17 00:00:00 2001 From: Jordan Reimer Date: Tue, 26 Apr 2022 13:17:42 -0600 Subject: [PATCH] KMSE Wizard Steps (#15171) * fixes issues in key-edit component * adds capabilities checks for keys and providers * adds distribute component to key and provider edit * adds wizard steps for kmse --- ui/app/components/keymgmt/distribute.js | 36 ++++++++++------ ui/app/components/wizard/secrets-keymgmt.js | 27 ++++++++++++ .../cluster/settings/mount-secret-backend.js | 4 ++ ui/app/helpers/wizard-constants.js | 1 + ui/app/machines/secrets-machine.js | 42 +++++++++++++++++++ .../cluster/secrets/backend/create-root.js | 5 +++ .../cluster/secrets/backend/secret-edit.js | 10 +++++ .../templates/components/keymgmt/key-edit.hbs | 2 +- .../components/keymgmt/provider-edit.hbs | 2 +- .../components/wizard/keymgmt-engine.hbs | 11 +++++ .../components/wizard/mounts-wizard.hbs | 1 + .../components/wizard/secrets-details.hbs | 19 +++++---- .../components/wizard/secrets-display.hbs | 15 +++++-- .../components/wizard/secrets-enable.hbs | 1 + .../components/wizard/secrets-idle.hbs | 1 + .../components/wizard/secrets-keymgmt.hbs | 5 +++ 16 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 ui/app/components/wizard/secrets-keymgmt.js create mode 100644 ui/app/templates/components/wizard/keymgmt-engine.hbs create mode 100644 ui/app/templates/components/wizard/secrets-keymgmt.hbs diff --git a/ui/app/components/keymgmt/distribute.js b/ui/app/components/keymgmt/distribute.js index 8400670b13..1eaeaccf80 100644 --- a/ui/app/components/keymgmt/distribute.js +++ b/ui/app/components/keymgmt/distribute.js @@ -33,6 +33,7 @@ export default class KeymgmtDistribute extends Component { @service store; @service flashMessages; @service router; + @service wizard; @tracked keyModel; @tracked isNewKey = false; @@ -53,6 +54,14 @@ export default class KeymgmtDistribute extends Component { this.getKeyInfo(this.args.key); } this.formData.operations = []; + this.updateWizard('nextStep'); + } + + updateWizard(key) { + // wizard will pause unless we manually continue it -- verify that keymgmt tutorial is in progress + if (this.wizard[key] === 'distribute') { + this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'keymgmt'); + } } get keyTypes() { @@ -175,12 +184,15 @@ export default class KeymgmtDistribute extends Component { return { key, provider, purpose: operations.join(','), protection }; } - distributeKey(backend, kms, key, data) { - let adapter = this.store.adapterFor('keymgmt/key'); + distributeKey(backend, data) { + const adapter = this.store.adapterFor('keymgmt/key'); + const { key, provider, purpose, protection } = data; return adapter - .distribute(backend, kms, key, data) + .distribute(backend, provider, key, { purpose, protection }) .then(() => { - this.flashMessages.success(`Successfully distributed key ${key} to ${kms}`); + this.flashMessages.success(`Successfully distributed key ${key} to ${provider}`); + // move wizard forward if tutorial is in progress + this.updateWizard('featureState'); this.args.onClose(); }) .catch((e) => { @@ -233,15 +245,13 @@ export default class KeymgmtDistribute extends Component { return; } if (this.isNewKey) { - this.keyModel - .save() - .then(() => { - this.flashMessages.success(`Successfully created key ${this.keyModel.name}`); - }) - .catch((e) => { - this.flashMessages.danger(`Error creating new key ${this.keyModel.name}: ${e.errors}`); - }); + try { + await this.keyModel.save(); + this.flashMessages.success(`Successfully created key ${this.keyModel.name}`); + } catch (e) { + this.flashMessages.danger(`Error creating new key ${this.keyModel.name}: ${e.errors}`); + } } - this.distributeKey(backend, 'example-kms', 'example-key', this.formatData(this.formData)); + this.distributeKey(backend, data); } } diff --git a/ui/app/components/wizard/secrets-keymgmt.js b/ui/app/components/wizard/secrets-keymgmt.js new file mode 100644 index 0000000000..7ea5db24bf --- /dev/null +++ b/ui/app/components/wizard/secrets-keymgmt.js @@ -0,0 +1,27 @@ +import Component from '@glimmer/component'; + +export default class WizardSecretsKeymgmtComponent extends Component { + get headerText() { + return { + provider: 'Creating a provider', + displayProvider: 'Distributing a key', + distribute: 'Creating a key', + }[this.args.featureState]; + } + + get body() { + return { + provider: 'This process connects an external provider to Vault. You will need its credentials.', + displayProvider: 'A key can now be created and distributed to this destination.', + distribute: 'This process creates a key and distributes it to your provider.', + }[this.args.featureState]; + } + + get instructions() { + return { + provider: 'Enter your provider details and click “Create provider“.', + displayProvider: 'Click “Distribute key” in the toolbar.', + distribute: 'Enter your key details and click “Distribute key”.', + }[this.args.featureState]; + } +} diff --git a/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js b/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js index a2d7562337..cf18cd432b 100644 --- a/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js +++ b/ui/app/controllers/vault/cluster/settings/mount-secret-backend.js @@ -12,6 +12,10 @@ export default Controller.extend({ if (SUPPORTED_BACKENDS.includes(type)) { if (type === 'kmip') { transition = this.transitionToRoute('vault.cluster.secrets.backend.kmip.scopes', path); + } else if (type === 'keymgmt') { + transition = this.transitionToRoute('vault.cluster.secrets.backend.index', path, { + queryParams: { tab: 'provider' }, + }); } else { transition = this.transitionToRoute('vault.cluster.secrets.backend.index', path); } diff --git a/ui/app/helpers/wizard-constants.js b/ui/app/helpers/wizard-constants.js index 93b070887f..5ac0568978 100644 --- a/ui/app/helpers/wizard-constants.js +++ b/ui/app/helpers/wizard-constants.js @@ -48,6 +48,7 @@ export const FEATURE_MACHINE_STEPS = { secret: 5, }, role: 7, + provider: 8, }, policies: 5, replication: 2, diff --git a/ui/app/machines/secrets-machine.js b/ui/app/machines/secrets-machine.js index 4a40da87b2..8e399112bf 100644 --- a/ui/app/machines/secrets-machine.js +++ b/ui/app/machines/secrets-machine.js @@ -51,6 +51,9 @@ export default { encryption: { cond: (type) => type === 'transit', }, + provider: { + cond: (type) => type === 'keymgmt', + }, }, }, }, @@ -126,6 +129,33 @@ export default { CONTINUE: 'display', }, }, + provider: { + onEntry: [ + { type: 'render', level: 'step', component: 'wizard/secrets-keymgmt' }, + { type: 'render', level: 'feature', component: 'wizard/mounts-wizard' }, + ], + on: { + CONTINUE: 'displayProvider', + }, + }, + displayProvider: { + onEntry: [ + { type: 'render', level: 'step', component: 'wizard/secrets-keymgmt' }, + { type: 'render', level: 'feature', component: 'wizard/mounts-wizard' }, + ], + on: { + CONTINUE: 'distribute', + }, + }, + distribute: { + onEntry: [ + { type: 'render', level: 'step', component: 'wizard/secrets-keymgmt' }, + { type: 'render', level: 'feature', component: 'wizard/mounts-wizard' }, + ], + on: { + CONTINUE: 'display', + }, + }, display: { onEntry: [ { type: 'render', level: 'step', component: 'wizard/secrets-display' }, @@ -149,6 +179,18 @@ export default { cond: (type) => type === 'transit', actions: [{ type: 'routeTransition', params: ['vault.cluster.secrets.backend.create-root'] }], }, + provider: { + cond: (type) => type === 'keymgmt', + actions: [ + { + type: 'routeTransition', + params: [ + 'vault.cluster.secrets.backend.create-root', + { queryParams: { itemType: 'provider' } }, + ], + }, + ], + }, }, }, }, diff --git a/ui/app/routes/vault/cluster/secrets/backend/create-root.js b/ui/app/routes/vault/cluster/secrets/backend/create-root.js index 6362d30cc2..368eb359e4 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/create-root.js +++ b/ui/app/routes/vault/cluster/secrets/backend/create-root.js @@ -29,6 +29,7 @@ const transformModel = (queryParams) => { export default EditBase.extend({ wizard: service(), + createModel(transition) { const { backend } = this.paramsFor('vault.cluster.secrets.backend'); let modelType = this.modelType(backend, null, { queryParams: transition.to.queryParams }); @@ -57,6 +58,10 @@ export default EditBase.extend({ }, model(params, transition) { + // wizard will pause unless we manually continue it -- verify that keymgmt tutorial is in progress + if (params.itemType === 'provider' && this.wizard.nextStep === 'provider') { + this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'keymgmt'); + } return hash({ secret: this.createModel(transition), capabilities: {}, diff --git a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js index 10f0d9995f..1ccc5bfdbd 100644 --- a/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js +++ b/ui/app/routes/vault/cluster/secrets/backend/secret-edit.js @@ -9,6 +9,8 @@ import { encodePath, normalizePath } from 'vault/utils/path-encoding-helpers'; export default Route.extend(UnloadModelRoute, { pathHelp: service('path-help'), + wizard: service(), + secretParam() { let { secret } = this.paramsFor(this.routeName); return secret ? normalizePath(secret) : ''; @@ -211,8 +213,16 @@ export default Route.extend(UnloadModelRoute, { let secretModel = this.store.peekRecord(modelType, secretId); return secretModel; }, + // wizard will pause unless we manually continue it + updateWizard(params) { + // verify that keymgmt tutorial is in progress + if (params.itemType === 'provider' && this.wizard.nextStep === 'displayProvider') { + this.wizard.transitionFeatureMachine(this.wizard.featureState, 'CONTINUE', 'keymgmt'); + } + }, async model(params, { to: { queryParams } }) { + this.updateWizard(params); let secret = this.secretParam(); let backend = this.enginePathParam(); let modelType = this.modelType(backend, secret, { queryParams }); diff --git a/ui/app/templates/components/keymgmt/key-edit.hbs b/ui/app/templates/components/keymgmt/key-edit.hbs index f25579855e..e190b3ec13 100644 --- a/ui/app/templates/components/keymgmt/key-edit.hbs +++ b/ui/app/templates/components/keymgmt/key-edit.hbs @@ -18,7 +18,7 @@ {{#if this.isDistributing}} - + {{else}} {{#if (eq this.mode "show")}}
diff --git a/ui/app/templates/components/keymgmt/provider-edit.hbs b/ui/app/templates/components/keymgmt/provider-edit.hbs index 1791a63481..33938f95ed 100644 --- a/ui/app/templates/components/keymgmt/provider-edit.hbs +++ b/ui/app/templates/components/keymgmt/provider-edit.hbs @@ -17,7 +17,7 @@ {{#if this.isDistributing}} - + {{else}} {{#if this.isShowing}}
diff --git a/ui/app/templates/components/wizard/keymgmt-engine.hbs b/ui/app/templates/components/wizard/keymgmt-engine.hbs new file mode 100644 index 0000000000..55fab3ecab --- /dev/null +++ b/ui/app/templates/components/wizard/keymgmt-engine.hbs @@ -0,0 +1,11 @@ + +

+ Key Management is a secrets engine that allows key generation, lifecycle management, and secure distribution of + cryptographic keys into cloud key management services. +

+
\ No newline at end of file diff --git a/ui/app/templates/components/wizard/mounts-wizard.hbs b/ui/app/templates/components/wizard/mounts-wizard.hbs index c80f2f13bc..ab04ce0165 100644 --- a/ui/app/templates/components/wizard/mounts-wizard.hbs +++ b/ui/app/templates/components/wizard/mounts-wizard.hbs @@ -6,6 +6,7 @@ actionText=this.actionText nextFeature=this.nextFeature nextStep=this.nextStep + featureState=this.wizard.featureState needsConnection=this.needsConnection needsEncryption=this.needsEncryption isSupported=this.isSupported diff --git a/ui/app/templates/components/wizard/secrets-details.hbs b/ui/app/templates/components/wizard/secrets-details.hbs index be75257cb0..44bb424ed3 100644 --- a/ui/app/templates/components/wizard/secrets-details.hbs +++ b/ui/app/templates/components/wizard/secrets-details.hbs @@ -1,12 +1,17 @@

- {{#if @needsEncryption}} - The Transit Secrets Engine uses encryption keys to provide "encryption as a service". Click on "Create Encryption Key" - at the top to create one. - {{/if}} - {{#if @needsConnection}} - Now that the engine has been mounted, let’s connect a - {{@mountSubtype}}. + {{#if (eq @mountSubtype "keymgmt")}} + This secrets engine manages keys and distributes them to external destinations. We recommend that you create a provider + to which you can distribute keys. + {{else}} + {{#if @needsEncryption}} + The Transit Secrets Engine uses encryption keys to provide "encryption as a service". Click on "Create Encryption + Key" at the top to create one. + {{/if}} + {{#if @needsConnection}} + Now that the engine has been mounted, let’s connect a + {{@mountSubtype}}. + {{/if}} {{/if}}

\ No newline at end of file diff --git a/ui/app/templates/components/wizard/secrets-display.hbs b/ui/app/templates/components/wizard/secrets-display.hbs index bc92c751d0..f12d4179d9 100644 --- a/ui/app/templates/components/wizard/secrets-display.hbs +++ b/ui/app/templates/components/wizard/secrets-display.hbs @@ -1,7 +1,16 @@ {{#if @isSupported}} - +

- {{#if @actionText}} + {{#if (eq @mountSubtype "keymgmt")}} + Your key and your provider have been created and connected. From here, you can click the key name to view the key + You’re now ready to start using the secrets engine. + {{else if @actionText}} Here is your generated credential. As you can see, we can only show the credential once, so you'll want to be sure to save it. If you need another credential in the future, just come back and generate a new one. {{else}} @@ -20,7 +29,7 @@

{{/if}} - + {{#if @isSupported}}