mirror of
https://github.com/hashicorp/vault.git
synced 2026-06-04 14:25:35 -04:00
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
This commit is contained in:
parent
bf2667bd2c
commit
6d1ddf36a8
16 changed files with 157 additions and 25 deletions
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
ui/app/components/wizard/secrets-keymgmt.js
Normal file
27
ui/app/components/wizard/secrets-keymgmt.js
Normal file
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export const FEATURE_MACHINE_STEPS = {
|
|||
secret: 5,
|
||||
},
|
||||
role: 7,
|
||||
provider: 8,
|
||||
},
|
||||
policies: 5,
|
||||
replication: 2,
|
||||
|
|
|
|||
|
|
@ -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' } },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {},
|
||||
|
|
|
|||
|
|
@ -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 });
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
</PageHeader>
|
||||
|
||||
{{#if this.isDistributing}}
|
||||
<Keymgmt::Distribute @backend={{@model.backend}} @key={{@model}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||
<Keymgmt::Distribute @backend={{@model.backend}} @key={{@model.id}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||
{{else}}
|
||||
{{#if (eq this.mode "show")}}
|
||||
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless" data-test-keymgmt-key-toolbar>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
</PageHeader>
|
||||
|
||||
{{#if this.isDistributing}}
|
||||
<Keymgmt::Distribute @backend={{@model.backend}} @provider={{@model}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||
<Keymgmt::Distribute @backend={{@model.backend}} @provider={{@model.id}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||
{{else}}
|
||||
{{#if this.isShowing}}
|
||||
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
|
||||
|
|
|
|||
11
ui/app/templates/components/wizard/keymgmt-engine.hbs
Normal file
11
ui/app/templates/components/wizard/keymgmt-engine.hbs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<WizardSection
|
||||
@headerText="Key Management"
|
||||
@headerIcon="key"
|
||||
@docText="Docs: Key Management Secrets Engine"
|
||||
@docPath="/docs/secrets/key-management"
|
||||
>
|
||||
<p>
|
||||
Key Management is a secrets engine that allows key generation, lifecycle management, and secure distribution of
|
||||
cryptographic keys into cloud key management services.
|
||||
</p>
|
||||
</WizardSection>
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
<WizardSection @headerText="Your Secrets Engine" @instructions="Click on the link to add a {{@nextStep}} in the page header">
|
||||
<p>
|
||||
{{#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}}
|
||||
</p>
|
||||
</WizardSection>
|
||||
|
|
@ -1,7 +1,16 @@
|
|||
{{#if @isSupported}}
|
||||
<WizardSection @headerText={{unless @actionText "All set!" "Generate Credential"}}>
|
||||
<WizardSection
|
||||
@headerText={{if
|
||||
(eq @mountSubtype "keymgmt")
|
||||
"Your key and provider"
|
||||
(unless @actionText "All set!" "Generate Credential")
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
{{#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 @@
|
|||
</p>
|
||||
</WizardSection>
|
||||
{{/if}}
|
||||
<WizardSection @headerText="Want to start again or move on?" @class="wizard-details">
|
||||
<WizardSection @headerText="Want to start again or move on?" @class="wizard-details has-bottom-margin-l">
|
||||
{{#if @isSupported}}
|
||||
<button type="button" class="button next-feature-step" {{action @onRepeat}}>
|
||||
Create another
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
@docText="Docs: Secrets Engines"
|
||||
@docPath="/docs/secrets/index.html"
|
||||
@instructions='Fill in the details for your engine and click "Enable Engine"'
|
||||
@class="has-bottom-margin-l"
|
||||
>
|
||||
<p>
|
||||
Good choice! Now you can customize your engine with a name and description that makes sense for your team, as well as
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
@docText="Docs: Secrets Engines"
|
||||
@docPath="/docs/secrets/index.html"
|
||||
@instructions='Select an engine and click "Next"'
|
||||
@class="has-bottom-margin-l"
|
||||
>
|
||||
<p>
|
||||
Vault is all about managing secrets, so let's set up your first Secrets Engine. You can use a static engine to store your
|
||||
|
|
|
|||
5
ui/app/templates/components/wizard/secrets-keymgmt.hbs
Normal file
5
ui/app/templates/components/wizard/secrets-keymgmt.hbs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<WizardSection @headerText={{this.headerText}} @instructions={{this.instructions}}>
|
||||
<p>
|
||||
{{this.body}}
|
||||
</p>
|
||||
</WizardSection>
|
||||
Loading…
Reference in a new issue