diff --git a/ui/app/adapters/keymgmt/key.js b/ui/app/adapters/keymgmt/key.js index 2046d096a..8b3fcaad2 100644 --- a/ui/app/adapters/keymgmt/key.js +++ b/ui/app/adapters/keymgmt/key.js @@ -149,4 +149,11 @@ export default class KeymgmtKeyAdapter extends ApplicationAdapter { // TODO: re-fetch record data after 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; + }); + } } diff --git a/ui/app/components/keymgmt/distribute.js b/ui/app/components/keymgmt/distribute.js index a138ad31d..8400670b1 100644 --- a/ui/app/components/keymgmt/distribute.js +++ b/ui/app/components/keymgmt/distribute.js @@ -181,7 +181,7 @@ export default class KeymgmtDistribute extends Component { .distribute(backend, kms, key, data) .then(() => { this.flashMessages.success(`Successfully distributed key ${key} to ${kms}`); - this.router.transitionTo('vault.cluster.secrets.backend.show', key); + this.args.onClose(); }) .catch((e) => { this.flashMessages.danger(`Error distributing key: ${e.errors}`); diff --git a/ui/app/components/keymgmt/key-edit.js b/ui/app/components/keymgmt/key-edit.js index 74531ba0e..2c46b20ae 100644 --- a/ui/app/components/keymgmt/key-edit.js +++ b/ui/app/components/keymgmt/key-edit.js @@ -2,6 +2,8 @@ import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import { tracked } from '@glimmer/tracking'; +import { task } from 'ember-concurrency'; +import { waitFor } from '@ember/test-waiters'; /** * @module KeymgmtKeyEdit @@ -32,50 +34,50 @@ export default class KeymgmtKeyEdit extends Component { return this.store.adapterFor('keymgmt/key'); } + get isMutable() { + return ['create', 'edit'].includes(this.args.mode); + } + + get isCreating() { + return this.args.mode === 'create'; + } + @action toggleModal(bool) { this.isDeleteModalOpen = bool; } - @action - createKey(evt) { + @task + @waitFor + *saveKey(evt) { 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 - updateKey(evt) { - evt.preventDefault(); - const name = this.args.model.name; - this.args.model - .save() - .then(() => { - this.router.transitionTo(SHOW_ROUTE, name); - }) - .catch((e) => { - this.flashMessages.danger(e.errors.join('. ')); - }); - } - - @action - removeKey(id) { - // TODO: remove action - console.log('remove', id); + async removeKey() { + try { + await this.keyAdapter.removeFromProvider(this.args.model); + this.flashMessages.success('Key has been successfully removed from provider'); + } catch (error) { + this.flashMessages.danger(error.errors?.join('. ')); + } } @action deleteKey() { const secret = this.args.model; const backend = secret.backend; - console.log({ secret }); secret .destroyRecord() .then(() => { - try { - this.router.transitionTo(LIST_ROOT_ROUTE, backend, { queryParams: { tab: 'key' } }); - } catch (e) { - console.debug(e); - } + this.router.transitionTo(LIST_ROOT_ROUTE, backend); }) .catch((e) => { this.flashMessages.danger(e.errors?.join('. ')); diff --git a/ui/app/models/keymgmt/key.js b/ui/app/models/keymgmt/key.js index 2c7ce6354..e88c5ff70 100644 --- a/ui/app/models/keymgmt/key.js +++ b/ui/app/models/keymgmt/key.js @@ -1,5 +1,6 @@ import Model, { attr } from '@ember-data/model'; import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; +import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; export const KEY_TYPES = [ 'aes256-gcm96', @@ -11,15 +12,23 @@ export const KEY_TYPES = [ 'ecdsa-p521', ]; export default class KeymgmtKeyModel extends Model { - @attr('string') name; - @attr('string') backend; + @attr('string', { + label: 'Key name', + subText: 'This is the name of the key that shows in Vault.', + }) + name; + + @attr('string') + backend; @attr('string', { + subText: 'The type of cryptographic key that will be created.', possibleValues: KEY_TYPES, }) type; @attr('boolean', { + label: 'Allow deletion', defaultValue: false, }) deletionAllowed; @@ -93,4 +102,27 @@ export default class KeymgmtKeyModel extends Model { { 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'); + } } diff --git a/ui/app/models/keymgmt/provider.js b/ui/app/models/keymgmt/provider.js index 1dbdda642..58f26087e 100644 --- a/ui/app/models/keymgmt/provider.js +++ b/ui/app/models/keymgmt/provider.js @@ -2,6 +2,7 @@ import Model, { attr } from '@ember-data/model'; import { tracked } from '@glimmer/tracking'; import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; import { withModelValidations } from 'vault/decorators/model-validations'; +import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; const CRED_PROPS = { azurekeyvault: ['client_id', 'client_secret', 'tenant_id'], @@ -80,7 +81,11 @@ export default class KeymgmtProviderModel extends Model { const attrs = expandAttributeMeta(this, ['name', 'created', 'keyCollection']); attrs.splice(1, 0, { hasBlock: true, label: 'Type', value: this.typeName, icon: this.icon }); 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 }); return attrs; } @@ -104,18 +109,48 @@ export default class KeymgmtProviderModel extends Model { } async fetchKeys(page) { - try { - this.keys = await this.store.lazyPaginatedQuery('keymgmt/key', { - backend: 'keymgmt', - provider: this.name, - responsePath: 'data.keys', - page, - }); - } catch (error) { - this.keys = []; - if (error.httpStatus !== 404) { - throw error; + if (this.canListKeys) { + try { + this.keys = await this.store.lazyPaginatedQuery('keymgmt/key', { + backend: 'keymgmt', + provider: this.name, + responsePath: 'data.keys', + page, + }); + } catch (error) { + this.keys = []; + 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'); + } } diff --git a/ui/app/templates/components/keymgmt/distribute.hbs b/ui/app/templates/components/keymgmt/distribute.hbs index 16716045a..696995ed1 100644 --- a/ui/app/templates/components/keymgmt/distribute.hbs +++ b/ui/app/templates/components/keymgmt/distribute.hbs @@ -147,7 +147,12 @@ class="button is-primary" data-test-secret-save={{true}} > - Save + {{if (or (not @key) this.isNewKey) "Add key" "Distribute key"}} + + +