KMSE Capabilities & Phase 1 Cleanup (#15143)
* fixes issues in key-edit component * adds capabilities checks for keys and providers * adds distribute component to key and provider edit
This commit is contained in:
parent
cc531c793d
commit
d6933e9ef4
|
@ -149,4 +149,11 @@ export default class KeymgmtKeyAdapter extends ApplicationAdapter {
|
||||||
// TODO: re-fetch record data after
|
// TODO: re-fetch record data after
|
||||||
return this.ajax(this.url(backend, id, 'ROTATE'), 'PUT');
|
return this.ajax(this.url(backend, id, 'ROTATE'), 'PUT');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeFromProvider(model) {
|
||||||
|
const url = `${this.buildURL()}/${model.backend}/kms/${model.provider.name}/key/${model.name}`;
|
||||||
|
return this.ajax(url, 'DELETE').then(() => {
|
||||||
|
model.provider = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,7 +181,7 @@ export default class KeymgmtDistribute extends Component {
|
||||||
.distribute(backend, kms, key, data)
|
.distribute(backend, kms, key, data)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.flashMessages.success(`Successfully distributed key ${key} to ${kms}`);
|
this.flashMessages.success(`Successfully distributed key ${key} to ${kms}`);
|
||||||
this.router.transitionTo('vault.cluster.secrets.backend.show', key);
|
this.args.onClose();
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.flashMessages.danger(`Error distributing key: ${e.errors}`);
|
this.flashMessages.danger(`Error distributing key: ${e.errors}`);
|
||||||
|
|
|
@ -2,6 +2,8 @@ import Component from '@glimmer/component';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
import { task } from 'ember-concurrency';
|
||||||
|
import { waitFor } from '@ember/test-waiters';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module KeymgmtKeyEdit
|
* @module KeymgmtKeyEdit
|
||||||
|
@ -32,50 +34,50 @@ export default class KeymgmtKeyEdit extends Component {
|
||||||
return this.store.adapterFor('keymgmt/key');
|
return this.store.adapterFor('keymgmt/key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isMutable() {
|
||||||
|
return ['create', 'edit'].includes(this.args.mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
get isCreating() {
|
||||||
|
return this.args.mode === 'create';
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
toggleModal(bool) {
|
toggleModal(bool) {
|
||||||
this.isDeleteModalOpen = bool;
|
this.isDeleteModalOpen = bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@task
|
||||||
createKey(evt) {
|
@waitFor
|
||||||
|
*saveKey(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
this.args.model.save();
|
const { model } = this.args;
|
||||||
|
try {
|
||||||
|
yield model.save();
|
||||||
|
this.router.transitionTo(SHOW_ROUTE, model.name);
|
||||||
|
} catch (error) {
|
||||||
|
this.flashMessages.danger(error.errors.join('. '));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
updateKey(evt) {
|
async removeKey() {
|
||||||
evt.preventDefault();
|
try {
|
||||||
const name = this.args.model.name;
|
await this.keyAdapter.removeFromProvider(this.args.model);
|
||||||
this.args.model
|
this.flashMessages.success('Key has been successfully removed from provider');
|
||||||
.save()
|
} catch (error) {
|
||||||
.then(() => {
|
this.flashMessages.danger(error.errors?.join('. '));
|
||||||
this.router.transitionTo(SHOW_ROUTE, name);
|
}
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
this.flashMessages.danger(e.errors.join('. '));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
removeKey(id) {
|
|
||||||
// TODO: remove action
|
|
||||||
console.log('remove', id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
deleteKey() {
|
deleteKey() {
|
||||||
const secret = this.args.model;
|
const secret = this.args.model;
|
||||||
const backend = secret.backend;
|
const backend = secret.backend;
|
||||||
console.log({ secret });
|
|
||||||
secret
|
secret
|
||||||
.destroyRecord()
|
.destroyRecord()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
try {
|
this.router.transitionTo(LIST_ROOT_ROUTE, backend);
|
||||||
this.router.transitionTo(LIST_ROOT_ROUTE, backend, { queryParams: { tab: 'key' } });
|
|
||||||
} catch (e) {
|
|
||||||
console.debug(e);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
this.flashMessages.danger(e.errors?.join('. '));
|
this.flashMessages.danger(e.errors?.join('. '));
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import Model, { attr } from '@ember-data/model';
|
import Model, { attr } from '@ember-data/model';
|
||||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||||
|
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||||
|
|
||||||
export const KEY_TYPES = [
|
export const KEY_TYPES = [
|
||||||
'aes256-gcm96',
|
'aes256-gcm96',
|
||||||
|
@ -11,15 +12,23 @@ export const KEY_TYPES = [
|
||||||
'ecdsa-p521',
|
'ecdsa-p521',
|
||||||
];
|
];
|
||||||
export default class KeymgmtKeyModel extends Model {
|
export default class KeymgmtKeyModel extends Model {
|
||||||
@attr('string') name;
|
@attr('string', {
|
||||||
@attr('string') backend;
|
label: 'Key name',
|
||||||
|
subText: 'This is the name of the key that shows in Vault.',
|
||||||
|
})
|
||||||
|
name;
|
||||||
|
|
||||||
|
@attr('string')
|
||||||
|
backend;
|
||||||
|
|
||||||
@attr('string', {
|
@attr('string', {
|
||||||
|
subText: 'The type of cryptographic key that will be created.',
|
||||||
possibleValues: KEY_TYPES,
|
possibleValues: KEY_TYPES,
|
||||||
})
|
})
|
||||||
type;
|
type;
|
||||||
|
|
||||||
@attr('boolean', {
|
@attr('boolean', {
|
||||||
|
label: 'Allow deletion',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
})
|
})
|
||||||
deletionAllowed;
|
deletionAllowed;
|
||||||
|
@ -93,4 +102,27 @@ export default class KeymgmtKeyModel extends Model {
|
||||||
{ name: 'protection', type: 'string', subText: 'Where cryptographic operations are performed.' },
|
{ name: 'protection', type: 'string', subText: 'Where cryptographic operations are performed.' },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/key/${'id'}`, 'backend', 'id') keyPath;
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/key`, 'backend') keysPath;
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/key/${'id'}/kms`, 'backend', 'id') keyProvidersPath;
|
||||||
|
|
||||||
|
get canCreate() {
|
||||||
|
return this.keyPath.get('canCreate');
|
||||||
|
}
|
||||||
|
get canDelete() {
|
||||||
|
return this.keyPath.get('canDelete');
|
||||||
|
}
|
||||||
|
get canEdit() {
|
||||||
|
return this.keyPath.get('canUpdate');
|
||||||
|
}
|
||||||
|
get canRead() {
|
||||||
|
return this.keyPath.get('canRead');
|
||||||
|
}
|
||||||
|
get canList() {
|
||||||
|
return this.keysPath.get('canList');
|
||||||
|
}
|
||||||
|
get canListProviders() {
|
||||||
|
return this.keyProvidersPath.get('canList');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import Model, { attr } from '@ember-data/model';
|
||||||
import { tracked } from '@glimmer/tracking';
|
import { tracked } from '@glimmer/tracking';
|
||||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||||
import { withModelValidations } from 'vault/decorators/model-validations';
|
import { withModelValidations } from 'vault/decorators/model-validations';
|
||||||
|
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||||
|
|
||||||
const CRED_PROPS = {
|
const CRED_PROPS = {
|
||||||
azurekeyvault: ['client_id', 'client_secret', 'tenant_id'],
|
azurekeyvault: ['client_id', 'client_secret', 'tenant_id'],
|
||||||
|
@ -80,7 +81,11 @@ export default class KeymgmtProviderModel extends Model {
|
||||||
const attrs = expandAttributeMeta(this, ['name', 'created', 'keyCollection']);
|
const attrs = expandAttributeMeta(this, ['name', 'created', 'keyCollection']);
|
||||||
attrs.splice(1, 0, { hasBlock: true, label: 'Type', value: this.typeName, icon: this.icon });
|
attrs.splice(1, 0, { hasBlock: true, label: 'Type', value: this.typeName, icon: this.icon });
|
||||||
const l = this.keys.length;
|
const l = this.keys.length;
|
||||||
const value = l ? `${l} ${l > 1 ? 'keys' : 'key'}` : 'None';
|
const value = l
|
||||||
|
? `${l} ${l > 1 ? 'keys' : 'key'}`
|
||||||
|
: this.canListKeys
|
||||||
|
? 'None'
|
||||||
|
: 'You do not have permission to list keys';
|
||||||
attrs.push({ hasBlock: true, isLink: l, label: 'Keys', value });
|
attrs.push({ hasBlock: true, isLink: l, label: 'Keys', value });
|
||||||
return attrs;
|
return attrs;
|
||||||
}
|
}
|
||||||
|
@ -104,18 +109,48 @@ export default class KeymgmtProviderModel extends Model {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fetchKeys(page) {
|
async fetchKeys(page) {
|
||||||
try {
|
if (this.canListKeys) {
|
||||||
this.keys = await this.store.lazyPaginatedQuery('keymgmt/key', {
|
try {
|
||||||
backend: 'keymgmt',
|
this.keys = await this.store.lazyPaginatedQuery('keymgmt/key', {
|
||||||
provider: this.name,
|
backend: 'keymgmt',
|
||||||
responsePath: 'data.keys',
|
provider: this.name,
|
||||||
page,
|
responsePath: 'data.keys',
|
||||||
});
|
page,
|
||||||
} catch (error) {
|
});
|
||||||
this.keys = [];
|
} catch (error) {
|
||||||
if (error.httpStatus !== 404) {
|
this.keys = [];
|
||||||
throw error;
|
if (error.httpStatus !== 404) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.keys = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/kms/${'id'}`, 'backend', 'id') providerPath;
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/kms`, 'backend') providersPath;
|
||||||
|
@lazyCapabilities(apiPath`${'backend'}/kms/${'id'}/key`, 'backend', 'id') providerKeysPath;
|
||||||
|
|
||||||
|
get canCreate() {
|
||||||
|
return this.providerPath.get('canCreate');
|
||||||
|
}
|
||||||
|
get canDelete() {
|
||||||
|
return this.providerPath.get('canDelete');
|
||||||
|
}
|
||||||
|
get canEdit() {
|
||||||
|
return this.providerPath.get('canUpdate');
|
||||||
|
}
|
||||||
|
get canRead() {
|
||||||
|
return this.providerPath.get('canRead');
|
||||||
|
}
|
||||||
|
get canList() {
|
||||||
|
return this.providersPath.get('canList');
|
||||||
|
}
|
||||||
|
get canListKeys() {
|
||||||
|
return this.providerKeysPath.get('canList');
|
||||||
|
}
|
||||||
|
get canCreateKeys() {
|
||||||
|
return this.providerKeysPath.get('canCreate');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,12 @@
|
||||||
class="button is-primary"
|
class="button is-primary"
|
||||||
data-test-secret-save={{true}}
|
data-test-secret-save={{true}}
|
||||||
>
|
>
|
||||||
Save
|
{{if (or (not @key) this.isNewKey) "Add key" "Distribute key"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button type="button" class="button" {{on "click" @onClose}}>
|
||||||
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
</p.top>
|
</p.top>
|
||||||
<p.levelLeft>
|
<p.levelLeft>
|
||||||
<h1 class="title is-3" data-test-secret-header="true">
|
<h1 class="title is-3" data-test-secret-header="true">
|
||||||
{{#if (eq @mode "create")}}
|
{{#if this.isDistributing}}
|
||||||
|
Distribute key
|
||||||
|
{{else if (eq @mode "create")}}
|
||||||
Create key
|
Create key
|
||||||
{{else if (eq @mode "edit")}}
|
{{else if (eq @mode "edit")}}
|
||||||
Edit key
|
Edit key
|
||||||
|
@ -15,185 +17,218 @@
|
||||||
</p.levelLeft>
|
</p.levelLeft>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
{{#if (eq this.mode "show")}}
|
{{#if this.isDistributing}}
|
||||||
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless" data-test-keymgmt-key-toolbar>
|
<Keymgmt::Distribute @backend={{@model.backend}} @key={{@model}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||||
<nav class="tabs">
|
{{else}}
|
||||||
<ul>
|
{{#if (eq this.mode "show")}}
|
||||||
<li class={{if (not-eq @tab "versions") "is-active"}}>
|
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless" data-test-keymgmt-key-toolbar>
|
||||||
<LinkTo
|
<nav class="tabs">
|
||||||
@route="vault.cluster.secrets.backend.show"
|
<ul>
|
||||||
@model={{@model.id}}
|
<li class={{if (not-eq @tab "versions") "is-active"}}>
|
||||||
@query={{hash tab=""}}
|
<LinkTo
|
||||||
data-test-tab="Details"
|
@route="vault.cluster.secrets.backend.show"
|
||||||
|
@model={{@model.id}}
|
||||||
|
@query={{hash tab=""}}
|
||||||
|
data-test-tab="Details"
|
||||||
|
>
|
||||||
|
Details
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
<li class={{if (eq @tab "versions") "is-active"}}>
|
||||||
|
<LinkTo
|
||||||
|
@route="vault.cluster.secrets.backend.show"
|
||||||
|
@model={{@model.id}}
|
||||||
|
@query={{hash tab="versions"}}
|
||||||
|
data-test-tab="Versions"
|
||||||
|
>
|
||||||
|
Versions
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<Toolbar>
|
||||||
|
<ToolbarActions>
|
||||||
|
{{#if @model.canDelete}}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="toolbar-link"
|
||||||
|
disabled={{not @model.deletionAllowed}}
|
||||||
|
{{on "click" (fn (mut this.isDeleteModalOpen) true)}}
|
||||||
|
data-test-keymgmt-key-destroy
|
||||||
>
|
>
|
||||||
Details
|
Destroy key
|
||||||
</LinkTo>
|
</button>
|
||||||
</li>
|
{{/if}}
|
||||||
<li class={{if (eq @tab "versions") "is-active"}}>
|
{{#if @model.provider}}
|
||||||
<LinkTo
|
<ConfirmAction
|
||||||
@route="vault.cluster.secrets.backend.show"
|
@buttonClasses="toolbar-link"
|
||||||
@model={{@model.id}}
|
@onConfirmAction={{this.removeKey}}
|
||||||
@query={{hash tab="versions"}}
|
@confirmTitle="Remove this key?"
|
||||||
data-test-tab="Versions"
|
@confirmMessage="This will remove all versions of the key from the KMS provider. The key will stay in Vault."
|
||||||
|
@confirmButtonText="Remove"
|
||||||
|
data-test-keymgmt-key-remove
|
||||||
>
|
>
|
||||||
Versions
|
Remove key
|
||||||
</LinkTo>
|
</ConfirmAction>
|
||||||
</li>
|
{{/if}}
|
||||||
</ul>
|
{{#if (or @model.canDelete @model.provider)}}
|
||||||
</nav>
|
<div class="toolbar-separator"></div>
|
||||||
</div>
|
{{/if}}
|
||||||
<Toolbar>
|
<ConfirmAction
|
||||||
<ToolbarActions>
|
@buttonClasses="toolbar-link"
|
||||||
<button
|
@onConfirmAction={{fn this.rotateKey @model.id}}
|
||||||
type="button"
|
@confirmTitle="Rotate this key?"
|
||||||
class="toolbar-link"
|
@confirmMessage="After rotation, all key actions will default to using the newest version of the key."
|
||||||
disabled={{not @model.deletionAllowed}}
|
@confirmButtonText="Rotate"
|
||||||
{{on "click" (fn (mut this.isDeleteModalOpen) true)}}
|
data-test-keymgmt-key-rotate
|
||||||
data-test-keymgmt-key-destroy
|
>
|
||||||
>
|
Rotate key
|
||||||
Destroy key
|
</ConfirmAction>
|
||||||
</button>
|
{{#if @model.canEdit}}
|
||||||
<ConfirmAction
|
<ToolbarSecretLink
|
||||||
@buttonClasses="toolbar-link"
|
@secret={{@model.id}}
|
||||||
@onConfirmAction={{fn this.removeKey @model.id}}
|
@mode="edit"
|
||||||
@confirmTitle="Remove this key?"
|
@replace={{true}}
|
||||||
@confirmMessage="This will remove all versions of the key from the KMS provider. The key will stay in Vault."
|
@queryParams={{query-params itemType="key"}}
|
||||||
@confirmButtonText="Remove"
|
@data-test-edit-link={{true}}
|
||||||
data-test-keymgmt-key-remove
|
>
|
||||||
>
|
Edit key
|
||||||
Remove key
|
</ToolbarSecretLink>
|
||||||
</ConfirmAction>
|
{{/if}}
|
||||||
<div class="toolbar-separator"></div>
|
</ToolbarActions>
|
||||||
<ConfirmAction
|
</Toolbar>
|
||||||
@buttonClasses="toolbar-link"
|
{{/if}}
|
||||||
@onConfirmAction={{fn this.rotateKey @model.id}}
|
|
||||||
@confirmTitle="Rotate this key?"
|
|
||||||
@confirmMessage="After rotation, all key actions will default to using the newest version of the key."
|
|
||||||
@confirmButtonText="Rotate"
|
|
||||||
data-test-keymgmt-key-rotate
|
|
||||||
>
|
|
||||||
Rotate key
|
|
||||||
</ConfirmAction>
|
|
||||||
<ToolbarSecretLink
|
|
||||||
@secret={{@model.id}}
|
|
||||||
@mode="edit"
|
|
||||||
@replace={{true}}
|
|
||||||
@queryParams={{query-params itemType="key"}}
|
|
||||||
@data-test-edit-link={{true}}
|
|
||||||
>
|
|
||||||
Edit key
|
|
||||||
</ToolbarSecretLink>
|
|
||||||
</ToolbarActions>
|
|
||||||
</Toolbar>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if (eq this.mode "create")}}
|
{{#if this.isMutable}}
|
||||||
<form {{on "submit" this.createKey}}>
|
<form {{on "submit" (perform this.saveKey)}}>
|
||||||
{{#each @model.createFields as |attr|}}
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
<FormField @attr={{attr}} @model={{@model}} />
|
{{#let (if (eq @mode "create") "createFields" "updateFields") as |fieldsKey|}}
|
||||||
{{/each}}
|
{{#each (get @model fieldsKey) as |attr|}}
|
||||||
<input type="submit" value="Create key" />
|
<FormField data-test-field={{true}} @attr={{attr}} @model={{@model}} />
|
||||||
</form>
|
{{/each}}
|
||||||
{{else if (eq this.mode "edit")}}
|
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||||
<form {{on "submit" this.updateKey}}>
|
<div class="control">
|
||||||
{{#each @model.updateFields as |attr|}}
|
<button
|
||||||
<FormField data-test-field={{true}} @attr={{attr}} @model={{@model}} />
|
type="submit"
|
||||||
{{/each}}
|
disabled={{this.saveTask.isRunning}}
|
||||||
<input type="submit" value="Update" />
|
class="button is-primary {{if this.saveTask.isRunning 'is-loading'}}"
|
||||||
</form>
|
data-test-keymgmt-key-submit
|
||||||
{{else if (eq @tab "versions")}}
|
>
|
||||||
{{#each @model.versions as |version|}}
|
{{if this.isCreating "Create key" "Update"}}
|
||||||
<div class="list-item-row" data-test-keymgmt-key-version>
|
</button>
|
||||||
<div class="columns is-mobile">
|
</div>
|
||||||
<div class="column is-3 has-text-weight-bold">
|
<div class="control">
|
||||||
<Icon @name="history" class="has-text-grey-light" />
|
<LinkTo
|
||||||
<span>Version {{version.id}}</span>
|
@route={{if this.isCreating @root.path "vault.cluster.secrets.backend.show"}}
|
||||||
</div>
|
@model={{if this.isCreating @root.model @model.id}}
|
||||||
<div class="column is-3 has-text-grey">
|
@query={{unless this.isCreating (hash itemType="key")}}
|
||||||
{{date-from-now version.creation_time addSuffix=true}}
|
@disabled={{this.savekey.isRunning}}
|
||||||
</div>
|
class="button"
|
||||||
<div class="column is-6 is-flex-center">
|
data-test-keymgmt-key-cancel
|
||||||
{{#if (eq @model.minEnabledVersion version.id)}}
|
>
|
||||||
<Icon @name="check-circle-fill" class="has-text-success" />
|
Cancel
|
||||||
<span data-test-keymgmt-key-current-min>Current mininum enabled version</span>
|
</LinkTo>
|
||||||
{{/if}}
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/let}}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{else if (eq @tab "versions")}}
|
||||||
|
{{#each @model.versions as |version|}}
|
||||||
|
<div class="list-item-row" data-test-keymgmt-key-version>
|
||||||
|
<div class="columns is-mobile">
|
||||||
|
<div class="column is-3 has-text-weight-bold">
|
||||||
|
<Icon @name="history" class="has-text-grey-light" />
|
||||||
|
<span>Version {{version.id}}</span>
|
||||||
|
</div>
|
||||||
|
<div class="column is-3 has-text-grey">
|
||||||
|
{{date-from-now version.creation_time addSuffix=true}}
|
||||||
|
</div>
|
||||||
|
<div class="column is-6 is-flex-center">
|
||||||
|
{{#if (eq @model.minEnabledVersion version.id)}}
|
||||||
|
<Icon @name="check-circle-fill" class="has-text-success" />
|
||||||
|
<span data-test-keymgmt-key-current-min>Current mininum enabled version</span>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<div class="has-top-margin-xl has-bottom-margin-s">
|
|
||||||
<h2 class="title has-border-bottom-light is-5">Key Details</h2>
|
|
||||||
{{#each @model.showFields as |attr|}}
|
|
||||||
<InfoTableRow
|
|
||||||
@alwaysRender={{true}}
|
|
||||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
|
||||||
@value={{get @model attr.name}}
|
|
||||||
@defaultShown={{attr.options.defaultShown}}
|
|
||||||
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
{{else}}
|
||||||
<div class="has-top-margin-xl has-bottom-margin-s">
|
<div class="has-top-margin-xl has-bottom-margin-s">
|
||||||
<h2 class="title has-border-bottom-light is-5 {{if @model.provider.permissionsError 'is-borderless is-marginless'}}">
|
<h2 class="title has-border-bottom-light is-5">Key Details</h2>
|
||||||
Distribution Details
|
{{#each @model.showFields as |attr|}}
|
||||||
</h2>
|
<InfoTableRow
|
||||||
{{! TODO: Use capabilities to tell if it's not distributed vs no permissions }}
|
@alwaysRender={{true}}
|
||||||
{{#if @model.provider.permissionsError}}
|
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||||
<EmptyState
|
@value={{get @model attr.name}}
|
||||||
@title="You are not authorized"
|
@defaultShown={{attr.options.defaultShown}}
|
||||||
@subTitle="Error 403"
|
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
||||||
@message={{concat
|
/>
|
||||||
"You must be granted permissions to see whether this key is distributed. Ask your administrator if you think you should have access to LIST /"
|
{{/each}}
|
||||||
@model.backend
|
</div>
|
||||||
"/key/"
|
<div class="has-top-margin-xl has-bottom-margin-s">
|
||||||
@model.name
|
<h2 class="title has-border-bottom-light is-5 {{unless @model.provider.canListKeys 'is-borderless is-marginless'}}">
|
||||||
"/kms."
|
Distribution Details
|
||||||
}}
|
</h2>
|
||||||
@icon="minus-circle"
|
{{#if (not @model.provider)}}
|
||||||
/>
|
<EmptyState
|
||||||
{{else if @model.provider}}
|
@title="Key not distributed"
|
||||||
<InfoTableRow @label="Distributed" @value={{@model.provider}}>
|
@message="When this key is distributed to a destination, those details will appear here."
|
||||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{concat "kms/" @model.provider}}>
|
data-test-keymgmt-dist-empty-state
|
||||||
<Icon @name="check-circle-fill" class="has-text-success" />{{@model.provider}}
|
>
|
||||||
</LinkTo>
|
{{#if @model.canListProviders}}
|
||||||
</InfoTableRow>
|
<button type="button" class="link" {{on "click" (fn (mut this.isDistributing) true)}}>
|
||||||
{{#if @model.distribution}}
|
Distribute key
|
||||||
{{#each @model.distFields as |attr|}}
|
<Icon @name="chevron-right" />
|
||||||
<InfoTableRow
|
</button>
|
||||||
@alwaysRender={{true}}
|
{{/if}}
|
||||||
@label={{capitalize (or attr.label (humanize (dasherize attr.name)))}}
|
</EmptyState>
|
||||||
@value={{if
|
{{else if (not @model.provider.canListKeys)}}
|
||||||
(eq attr.name "protection")
|
|
||||||
(uppercase (get @model.distribution attr.name))
|
|
||||||
(get @model.distribution attr.name)
|
|
||||||
}}
|
|
||||||
@defaultShown={{attr.defaultShown}}
|
|
||||||
@helperText={{attr.subText}}
|
|
||||||
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
{{else}}
|
|
||||||
<EmptyState
|
<EmptyState
|
||||||
@title="You are not authorized"
|
@title="You are not authorized"
|
||||||
@subTitle="Error 403"
|
@subTitle="Error 403"
|
||||||
@message="You must be granted permissions to view distribution details for this key. Ask your administrator if you think you should have access to GET /keymgmt/keymgmt/key/example."
|
@message={{concat
|
||||||
|
"You must be granted permissions to see whether this key is distributed. Ask your administrator if you think you should have access to LIST /"
|
||||||
|
@model.backend
|
||||||
|
"/key/"
|
||||||
|
@model.name
|
||||||
|
"/kms."
|
||||||
|
}}
|
||||||
@icon="minus-circle"
|
@icon="minus-circle"
|
||||||
/>
|
/>
|
||||||
|
{{else}}
|
||||||
|
<InfoTableRow @label="Distributed" @value={{@model.provider}}>
|
||||||
|
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{concat "kms/" @model.provider}}>
|
||||||
|
<Icon @name="check-circle-fill" class="has-text-success" />{{@model.provider}}
|
||||||
|
</LinkTo>
|
||||||
|
</InfoTableRow>
|
||||||
|
{{#if @model.distribution}}
|
||||||
|
{{#each @model.distFields as |attr|}}
|
||||||
|
<InfoTableRow
|
||||||
|
@alwaysRender={{true}}
|
||||||
|
@label={{capitalize (or attr.label (humanize (dasherize attr.name)))}}
|
||||||
|
@value={{if
|
||||||
|
(eq attr.name "protection")
|
||||||
|
(uppercase (get @model.distribution attr.name))
|
||||||
|
(get @model.distribution attr.name)
|
||||||
|
}}
|
||||||
|
@defaultShown={{attr.defaultShown}}
|
||||||
|
@helperText={{attr.subText}}
|
||||||
|
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
{{else}}
|
||||||
|
<EmptyState
|
||||||
|
@title="You are not authorized"
|
||||||
|
@subTitle="Error 403"
|
||||||
|
@message="You must be granted permissions to view distribution details for this key. Ask your administrator if you think you should have access to GET /keymgmt/keymgmt/key/example."
|
||||||
|
@icon="minus-circle"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else}}
|
</div>
|
||||||
<EmptyState
|
{{/if}}
|
||||||
@title="Key not distributed"
|
|
||||||
@message="When this key is distributed to a destination, those details will appear here."
|
|
||||||
data-test-keymgmt-dist-empty-state
|
|
||||||
>
|
|
||||||
{{! TODO: Distribute link
|
|
||||||
<LinkTo @route="vault.cluster.secrets.backend.distribute">
|
|
||||||
Distribute
|
|
||||||
</LinkTo> }}
|
|
||||||
</EmptyState>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
</p.top>
|
</p.top>
|
||||||
<p.levelLeft>
|
<p.levelLeft>
|
||||||
<h1 class="title is-3" data-test-kms-provider-header>
|
<h1 class="title is-3" data-test-kms-provider-header>
|
||||||
{{#if this.isShowing}}
|
{{#if this.isDistributing}}
|
||||||
|
Destribute key to provider
|
||||||
|
{{else if this.isShowing}}
|
||||||
Provider
|
Provider
|
||||||
<span class="has-font-weight-normal">{{@model.id}}</span>
|
<span class="has-font-weight-normal">{{@model.id}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -14,177 +16,192 @@
|
||||||
</p.levelLeft>
|
</p.levelLeft>
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
{{#if this.isShowing}}
|
{{#if this.isDistributing}}
|
||||||
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
|
<Keymgmt::Distribute @backend={{@model.backend}} @provider={{@model}} @onClose={{fn (mut this.isDistributing) false}} />
|
||||||
<nav class="tabs">
|
|
||||||
<ul>
|
|
||||||
<li class={{unless this.viewingKeys "is-active"}} data-test-kms-provider-tab="details">
|
|
||||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab=""}}>
|
|
||||||
Details
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
<li class={{if this.viewingKeys "is-active"}} data-test-kms-provider-tab="keys">
|
|
||||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab="keys"}}>
|
|
||||||
Keys
|
|
||||||
</LinkTo>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
{{#unless this.viewingKeys}}
|
|
||||||
<Toolbar data-test-kms-provider-details-actions>
|
|
||||||
<ToolbarActions>
|
|
||||||
<ToolTip @verticalPosition="above" @horizontalPosition="center" as |T|>
|
|
||||||
<T.Trigger data-test-tooltip-trigger>
|
|
||||||
<ConfirmAction
|
|
||||||
@buttonClasses="toolbar-link"
|
|
||||||
@onConfirmAction={{this.onDelete}}
|
|
||||||
@disabled={{@model.keys.length}}
|
|
||||||
data-test-kms-provider-delete={{true}}
|
|
||||||
>
|
|
||||||
Delete provider
|
|
||||||
</ConfirmAction>
|
|
||||||
</T.Trigger>
|
|
||||||
{{#if @model.keys.length}}
|
|
||||||
<T.Content class="tool-tip">
|
|
||||||
<div class="box" data-test-kms-provider-delete-tooltip>
|
|
||||||
This provider cannot be deleted until all 20 keys distributed to it are revoked. This can be done from the
|
|
||||||
Keys tab.
|
|
||||||
</div>
|
|
||||||
</T.Content>
|
|
||||||
{{/if}}
|
|
||||||
</ToolTip>
|
|
||||||
<div class="toolbar-separator"></div>
|
|
||||||
{{! Update once distribute route has been created }}
|
|
||||||
{{! <LinkTo @route="vault.cluster.secrets.backend.kms-distribute">
|
|
||||||
Distribute key
|
|
||||||
<Icon @name="chevron-right" />
|
|
||||||
</LinkTo> }}
|
|
||||||
<ToolbarSecretLink
|
|
||||||
@secret={{@model.id}}
|
|
||||||
@mode="edit"
|
|
||||||
@replace={{true}}
|
|
||||||
@queryParams={{query-params itemType="provider"}}
|
|
||||||
>
|
|
||||||
Update credentials
|
|
||||||
</ToolbarSecretLink>
|
|
||||||
</ToolbarActions>
|
|
||||||
</Toolbar>
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
{{else}}
|
||||||
<form aria-label="update credentials" {{on "submit" this.onSave}}>
|
{{#if this.isShowing}}
|
||||||
<div class="box is-sideless is-fullwidth is-marginless">
|
<div class="tabs-container box is-sideless is-fullwidth is-paddingless is-marginless">
|
||||||
{{#if this.isCreating}}
|
<nav class="tabs">
|
||||||
{{#each @model.createFields as |attr index|}}
|
<ul>
|
||||||
{{#if (eq index 2)}}
|
<li class={{unless this.viewingKeys "is-active"}} data-test-kms-provider-tab="details">
|
||||||
<div class="has-border-top-light">
|
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab=""}}>
|
||||||
<h2 class="title is-5 has-top-margin-l has-bottom-margin-m" data-test-kms-provider-config-title>
|
Details
|
||||||
Provider configuration
|
</LinkTo>
|
||||||
</h2>
|
</li>
|
||||||
</div>
|
{{#if @model.canListKeys}}
|
||||||
|
<li class={{if this.viewingKeys "is-active"}} data-test-kms-provider-tab="keys">
|
||||||
|
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab="keys"}}>
|
||||||
|
Keys
|
||||||
|
</LinkTo>
|
||||||
|
</li>
|
||||||
|
{{/if}}
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
{{#unless this.viewingKeys}}
|
||||||
|
<Toolbar data-test-kms-provider-details-actions>
|
||||||
|
<ToolbarActions>
|
||||||
|
{{#if @model.canDelete}}
|
||||||
|
<ToolTip @verticalPosition="above" @horizontalPosition="center" as |T|>
|
||||||
|
<T.Trigger data-test-tooltip-trigger>
|
||||||
|
<ConfirmAction
|
||||||
|
@buttonClasses="toolbar-link"
|
||||||
|
@onConfirmAction={{this.onDelete}}
|
||||||
|
@disabled={{@model.keys.length}}
|
||||||
|
data-test-kms-provider-delete={{true}}
|
||||||
|
>
|
||||||
|
Delete provider
|
||||||
|
</ConfirmAction>
|
||||||
|
</T.Trigger>
|
||||||
|
{{#if @model.keys.length}}
|
||||||
|
<T.Content class="tool-tip">
|
||||||
|
<div class="box" data-test-kms-provider-delete-tooltip>
|
||||||
|
This provider cannot be deleted until all
|
||||||
|
{{@model.keys.length}}
|
||||||
|
key(s) distributed to it are revoked. This can be done from the Keys tab.
|
||||||
|
</div>
|
||||||
|
</T.Content>
|
||||||
|
{{/if}}
|
||||||
|
</ToolTip>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (and @model.canDelete (or @model.canListKeys @model.canEdit))}}
|
||||||
|
<div class="toolbar-separator"></div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (or @model.canListKeys @model.canCreateKeys)}}
|
||||||
|
<button type="button" class="toolbar-link" {{on "click" (fn (mut this.isDistributing) true)}}>
|
||||||
|
Distribute key
|
||||||
|
<Icon @name="chevron-right" />
|
||||||
|
</button>
|
||||||
|
{{/if}}
|
||||||
|
{{#if @model.canEdit}}
|
||||||
|
<ToolbarSecretLink
|
||||||
|
@secret={{@model.id}}
|
||||||
|
@mode="edit"
|
||||||
|
@replace={{true}}
|
||||||
|
@queryParams={{query-params itemType="provider"}}
|
||||||
|
disabled={{(not @model.canEdit)}}
|
||||||
|
>
|
||||||
|
Update credentials
|
||||||
|
</ToolbarSecretLink>
|
||||||
|
{{/if}}
|
||||||
|
</ToolbarActions>
|
||||||
|
</Toolbar>
|
||||||
|
{{/unless}}
|
||||||
|
{{else}}
|
||||||
|
<form aria-label="update credentials" {{on "submit" this.onSave}}>
|
||||||
|
<div class="box is-sideless is-fullwidth is-marginless">
|
||||||
|
{{#if this.isCreating}}
|
||||||
|
{{#each @model.createFields as |attr index|}}
|
||||||
|
{{#if (eq index 2)}}
|
||||||
|
<div class="has-border-top-light">
|
||||||
|
<h2 class="title is-5 has-top-margin-l has-bottom-margin-m" data-test-kms-provider-config-title>
|
||||||
|
Provider configuration
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<FormField @attr={{attr}} @model={{@model}} @modelValidations={{this.modelValidations}} />
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
{{#unless this.isCreating}}
|
||||||
|
<h2 class="title is-5" data-test-kms-provider-creds-title>
|
||||||
|
New credentials
|
||||||
|
</h2>
|
||||||
|
<p class="sub-text has-bottom-margin-m">
|
||||||
|
Old credentials cannot be read and will be lost as soon as new ones are added. Do this carefully.
|
||||||
|
</p>
|
||||||
|
{{/unless}}
|
||||||
|
{{#each @model.credentialFields as |cred|}}
|
||||||
|
<FormField @attr={{cred}} @model={{@model}} @modelValidations={{this.modelValidations}} />
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
<div class="field is-grouped box is-fullwidth is-bottomless">
|
||||||
|
<div class="control">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={{this.saveTask.isRunning}}
|
||||||
|
class="button is-primary {{if this.saveTask.isRunning 'is-loading'}}"
|
||||||
|
data-test-kms-provider-submit
|
||||||
|
>
|
||||||
|
{{if this.isCreating "Create provider" "Update"}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<LinkTo
|
||||||
|
@route={{if this.isCreating @root.path "vault.cluster.secrets.backend.show"}}
|
||||||
|
@model={{if this.isCreating @root.model @model.id}}
|
||||||
|
@query={{if this.isCreating (hash tab="provider") (hash itemType="provider")}}
|
||||||
|
@disabled={{this.saveTask.isRunning}}
|
||||||
|
class="button"
|
||||||
|
data-test-kms-provider-cancel
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</LinkTo>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if this.isShowing}}
|
||||||
|
<div class="has-bottom-margin-s">
|
||||||
|
{{#if this.viewingKeys}}
|
||||||
|
{{#let (options-for-backend "keymgmt" "key") as |options|}}
|
||||||
|
{{#if @model.keys.meta.total}}
|
||||||
|
{{#each @model.keys as |key|}}
|
||||||
|
<SecretList::Item
|
||||||
|
@item={{key}}
|
||||||
|
@backendModel={{@root}}
|
||||||
|
@backendType="keymgmt"
|
||||||
|
@delete={{fn this.onDeleteKey key}}
|
||||||
|
@itemPath={{concat options.modelPrefix key.id}}
|
||||||
|
@itemType={{options.item}}
|
||||||
|
@modelType={{@modelType}}
|
||||||
|
@options={{options}}
|
||||||
|
/>
|
||||||
|
{{/each}}
|
||||||
|
{{#if (gt @model.keys.meta.lastPage 1)}}
|
||||||
|
<PaginationControls
|
||||||
|
@total={{@model.keys.meta.total}}
|
||||||
|
@onChange={{perform this.fetchKeys}}
|
||||||
|
class="has-top-margin-xl has-bottom-margin-l"
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
<EmptyState
|
||||||
|
@title="No keys for this provider"
|
||||||
|
@message="Keys for this provider will be listed here. Add a key to get started."
|
||||||
|
>
|
||||||
|
<SecretLink @mode="create" @secret="" @queryParams={{query-params itemType="key"}} class="link">
|
||||||
|
Create key
|
||||||
|
</SecretLink>
|
||||||
|
</EmptyState>
|
||||||
|
{{/if}}
|
||||||
|
{{/let}}
|
||||||
|
{{else}}
|
||||||
|
{{#each @model.showFields as |attr|}}
|
||||||
|
{{#if attr.hasBlock}}
|
||||||
|
<InfoTableRow @label={{attr.label}} @value={{attr.value}} data-test-kms-provider-field={{attr.name}}>
|
||||||
|
{{#if attr.icon}}
|
||||||
|
<Icon @name={{attr.icon}} class="icon" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if attr.isLink}}
|
||||||
|
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab="keys"}}>
|
||||||
|
{{attr.value}}
|
||||||
|
</LinkTo>
|
||||||
|
{{else}}
|
||||||
|
{{attr.value}}
|
||||||
|
{{/if}}
|
||||||
|
</InfoTableRow>
|
||||||
|
{{else}}
|
||||||
|
<InfoTableRow
|
||||||
|
@alwaysRender={{true}}
|
||||||
|
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
||||||
|
@value={{get @model attr.name}}
|
||||||
|
@defaultShown={{attr.options.defaultShown}}
|
||||||
|
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
||||||
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<FormField @attr={{attr}} @model={{@model}} @modelValidations={{this.modelValidations}} />
|
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#unless this.isCreating}}
|
|
||||||
<h2 class="title is-5" data-test-kms-provider-creds-title>
|
|
||||||
New credentials
|
|
||||||
</h2>
|
|
||||||
<p class="sub-text has-bottom-margin-m">
|
|
||||||
Old credentials cannot be read and will be lost as soon as new ones are added. Do this carefully.
|
|
||||||
</p>
|
|
||||||
{{/unless}}
|
|
||||||
{{#each @model.credentialFields as |cred|}}
|
|
||||||
<FormField @attr={{cred}} @model={{@model}} @modelValidations={{this.modelValidations}} />
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field is-grouped box is-fullwidth is-bottomless">
|
{{/if}}
|
||||||
<div class="control">
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={{this.saveTask.isRunning}}
|
|
||||||
class="button is-primary {{if this.saveTask.isRunning 'is-loading'}}"
|
|
||||||
data-test-kms-provider-submit
|
|
||||||
>
|
|
||||||
{{if this.isCreating "Create provider" "Update"}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="control">
|
|
||||||
<LinkTo
|
|
||||||
@route={{if this.isCreating @root.path "vault.cluster.secrets.backend.show"}}
|
|
||||||
@model={{if this.isCreating @root.model @model.id}}
|
|
||||||
@query={{if this.isCreating (hash tab="provider") (hash itemType="provider")}}
|
|
||||||
@disabled={{this.saveTask.isRunning}}
|
|
||||||
class="button"
|
|
||||||
data-test-kms-provider-cancel
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</LinkTo>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if this.isShowing}}
|
|
||||||
<div class="has-bottom-margin-s">
|
|
||||||
{{#if this.viewingKeys}}
|
|
||||||
{{#let (options-for-backend "keymgmt" "key") as |options|}}
|
|
||||||
{{#if @model.keys.meta.total}}
|
|
||||||
{{#each @model.keys as |key|}}
|
|
||||||
<SecretList::Item
|
|
||||||
@item={{key}}
|
|
||||||
@backendModel={{@root}}
|
|
||||||
@backendType="keymgmt"
|
|
||||||
@delete={{fn this.onDeleteKey key}}
|
|
||||||
@itemPath={{concat options.modelPrefix key.id}}
|
|
||||||
@itemType={{options.item}}
|
|
||||||
@modelType={{@modelType}}
|
|
||||||
@options={{options}}
|
|
||||||
/>
|
|
||||||
{{/each}}
|
|
||||||
{{#if (gt @model.keys.meta.lastPage 1)}}
|
|
||||||
<PaginationControls
|
|
||||||
@total={{@model.keys.meta.total}}
|
|
||||||
@onChange={{perform this.fetchKeys}}
|
|
||||||
class="has-top-margin-xl has-bottom-margin-l"
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
<EmptyState
|
|
||||||
@title="No keys for this provider"
|
|
||||||
@message="Keys for this provider will be listed here. Add a key to get started."
|
|
||||||
>
|
|
||||||
<SecretLink @mode="create" @secret="" @queryParams={{query-params itemType="key"}} class="link">
|
|
||||||
Create key
|
|
||||||
</SecretLink>
|
|
||||||
</EmptyState>
|
|
||||||
{{/if}}
|
|
||||||
{{/let}}
|
|
||||||
{{else}}
|
|
||||||
{{#each @model.showFields as |attr|}}
|
|
||||||
{{#if attr.hasBlock}}
|
|
||||||
<InfoTableRow @label={{attr.label}} @value={{attr.value}} data-test-kms-provider-field={{attr.name}}>
|
|
||||||
{{#if attr.icon}}
|
|
||||||
<Icon @name={{attr.icon}} class="icon" />
|
|
||||||
{{/if}}
|
|
||||||
{{#if attr.isLink}}
|
|
||||||
<LinkTo @route="vault.cluster.secrets.backend.show" @model={{@model.id}} @query={{hash tab="keys"}}>
|
|
||||||
{{attr.value}}
|
|
||||||
</LinkTo>
|
|
||||||
{{else}}
|
|
||||||
{{attr.value}}
|
|
||||||
{{/if}}
|
|
||||||
</InfoTableRow>
|
|
||||||
{{else}}
|
|
||||||
<InfoTableRow
|
|
||||||
@alwaysRender={{true}}
|
|
||||||
@label={{capitalize (or attr.options.label (humanize (dasherize attr.name)))}}
|
|
||||||
@value={{get @model attr.name}}
|
|
||||||
@defaultShown={{attr.options.defaultShown}}
|
|
||||||
@formatDate={{if (eq attr.type "date") "MMM d yyyy, h:mm:ss aaa"}}
|
|
||||||
/>
|
|
||||||
{{/if}}
|
|
||||||
{{/each}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
|
@ -85,13 +85,10 @@ module('Integration | Component | keymgmt/distribute', function (hooks) {
|
||||||
this.server.shutdown();
|
this.server.shutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it does not render without @backend attr', async function (assert) {
|
|
||||||
await render(hbs`<Keymgmt::Distribute />`);
|
|
||||||
assert.dom(SELECTORS.form).doesNotExist('Form does not exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('it does not allow operation selection until valid key and provider selected', async function (assert) {
|
test('it does not allow operation selection until valid key and provider selected', async function (assert) {
|
||||||
await render(hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} />`);
|
await render(
|
||||||
|
hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} @onClose={{fn (mut this.onClose)}} />`
|
||||||
|
);
|
||||||
assert.dom(SELECTORS.operationsSection).hasAttribute('disabled');
|
assert.dom(SELECTORS.operationsSection).hasAttribute('disabled');
|
||||||
await clickTrigger();
|
await clickTrigger();
|
||||||
await settled();
|
await settled();
|
||||||
|
@ -108,7 +105,9 @@ module('Integration | Component | keymgmt/distribute', function (hooks) {
|
||||||
assert.dom(SELECTORS.errorProvider).exists('Shows key/provider match error on provider');
|
assert.dom(SELECTORS.errorProvider).exists('Shows key/provider match error on provider');
|
||||||
});
|
});
|
||||||
test('it shows key type select field if new key created', async function (assert) {
|
test('it shows key type select field if new key created', async function (assert) {
|
||||||
await render(hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} />`);
|
await render(
|
||||||
|
hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} @onClose={{fn (mut this.onClose)}} />`
|
||||||
|
);
|
||||||
assert.dom(SELECTORS.keyTypeSection).doesNotExist('Key Type section is not rendered by default');
|
assert.dom(SELECTORS.keyTypeSection).doesNotExist('Key Type section is not rendered by default');
|
||||||
// Add new item on search-select
|
// Add new item on search-select
|
||||||
await clickTrigger();
|
await clickTrigger();
|
||||||
|
@ -118,7 +117,9 @@ module('Integration | Component | keymgmt/distribute', function (hooks) {
|
||||||
assert.dom(SELECTORS.keyTypeSection).exists('Key Type selector is shown');
|
assert.dom(SELECTORS.keyTypeSection).exists('Key Type selector is shown');
|
||||||
});
|
});
|
||||||
test('it hides the provider field if passed from the parent', async function (assert) {
|
test('it hides the provider field if passed from the parent', async function (assert) {
|
||||||
await render(hbs`<Keymgmt::Distribute @backend="keymgmt" @provider="provider-azure" />`);
|
await render(
|
||||||
|
hbs`<Keymgmt::Distribute @backend="keymgmt" @provider="provider-azure" @onClose={{fn (mut this.onClose)}} />`
|
||||||
|
);
|
||||||
assert.dom(SELECTORS.providerInput).doesNotExist('Provider input is hidden');
|
assert.dom(SELECTORS.providerInput).doesNotExist('Provider input is hidden');
|
||||||
// Select existing key
|
// Select existing key
|
||||||
await clickTrigger();
|
await clickTrigger();
|
||||||
|
@ -140,7 +141,9 @@ module('Integration | Component | keymgmt/distribute', function (hooks) {
|
||||||
assert.dom(SELECTORS.errorNewKey).exists('Shows error on key type');
|
assert.dom(SELECTORS.errorNewKey).exists('Shows error on key type');
|
||||||
});
|
});
|
||||||
test('it hides the key field if passed from the parent', async function (assert) {
|
test('it hides the key field if passed from the parent', async function (assert) {
|
||||||
await render(hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} @key="example-1" />`);
|
await render(
|
||||||
|
hbs`<Keymgmt::Distribute @backend="keymgmt" @providers={{providers}} @key="example-1" @onClose={{fn (mut this.onClose)}} />`
|
||||||
|
);
|
||||||
assert.dom(SELECTORS.providerInput).exists('Provider input shown');
|
assert.dom(SELECTORS.providerInput).exists('Provider input shown');
|
||||||
assert.dom(SELECTORS.keySection).doesNotExist('Key input not shown');
|
assert.dom(SELECTORS.keySection).doesNotExist('Key input not shown');
|
||||||
await select(SELECTORS.providerInput, 'provider-azure');
|
await select(SELECTORS.providerInput, 'provider-azure');
|
||||||
|
|
|
@ -23,6 +23,7 @@ module('Integration | Component | keymgmt/key-edit', function (hooks) {
|
||||||
creation_time: now,
|
creation_time: now,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
canDelete: true,
|
||||||
});
|
});
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.tab = '';
|
this.tab = '';
|
||||||
|
|
|
@ -47,6 +47,12 @@ module('Integration | Component | keymgmt/provider-edit', function (hooks) {
|
||||||
test('it should render show view', async function (assert) {
|
test('it should render show view', async function (assert) {
|
||||||
assert.expect(13);
|
assert.expect(13);
|
||||||
|
|
||||||
|
// override capability getters
|
||||||
|
Object.defineProperties(this.model, {
|
||||||
|
canDelete: { value: true },
|
||||||
|
canListKeys: { value: true },
|
||||||
|
});
|
||||||
|
|
||||||
this.server.get('/keymgmt/kms/foo-bar/key', () => {
|
this.server.get('/keymgmt/kms/foo-bar/key', () => {
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
|
|
Loading…
Reference in New Issue