diff --git a/ui/app/adapters/pki/role.js b/ui/app/adapters/pki/role.js index addf1708a0..88dd17c321 100644 --- a/ui/app/adapters/pki/role.js +++ b/ui/app/adapters/pki/role.js @@ -55,4 +55,8 @@ export default class PkiRoleAdapter extends ApplicationAdapter { queryRecord(store, type, query) { return this.fetchByQuery(store, query); } + deleteRecord(store, type, snapshot) { + const { id, record } = snapshot; + return this.ajax(this._urlForRole(record.backend, id), 'DELETE'); + } } diff --git a/ui/app/models/pki/role.js b/ui/app/models/pki/role.js index 1c282d36e6..1a4e4b745c 100644 --- a/ui/app/models/pki/role.js +++ b/ui/app/models/pki/role.js @@ -1,13 +1,71 @@ import Model, { attr } from '@ember-data/model'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import { withModelValidations } from 'vault/decorators/model-validations'; - -import fieldToAttrs from 'vault/utils/field-to-attrs'; +import { withFormFields } from 'vault/decorators/model-form-fields'; const validations = { name: [{ type: 'presence', message: 'Name is required.' }], }; +const fieldGroups = [ + { + default: [ + 'name', + 'issuerRef', + 'customTtl', + 'notBeforeDuration', + 'maxTtl', + 'generateLease', + 'noStore', + 'addBasicConstraints', + ], + }, + { + 'Domain handling': [ + 'allowedDomains', + 'allowedDomainsTemplate', + 'allowBareDomains', + 'allowSubdomains', + 'allowGlobDomains', + 'allowWildcardCertificates', + 'allowLocalhost', // default: true (returned true by OpenApi) + 'allowAnyName', + 'enforceHostnames', // default: true (returned true by OpenApi) + ], + }, + { + 'Key parameters': ['keyType', 'keyBits', 'signatureBits'], + }, + { + 'Key usage': ['keyUsage', 'extKeyUsage', 'extKeyUsageOids'], + }, + { 'Policy identifiers': ['policyIdentifiers'] }, + { + 'Subject Alternative Name (SAN) Options': [ + 'allowIpSans', + 'allowedUriSans', + 'allowUriSansTemplate', + 'allowedOtherSans', + ], + }, + { + 'Additional subject fields': [ + 'allowedSerialNumbers', + 'requireCn', + 'useCsrCommonName', + 'useCsrSans', + 'ou', + 'organization', + 'country', + 'locality', + 'province', + 'streetAddress', + 'postalCode', + ], + }, +]; + +@withFormFields(null, fieldGroups) @withModelValidations(validations) export default class PkiRoleModel extends Model { get useOpenAPI() { @@ -242,34 +300,33 @@ export default class PkiRoleModel extends Model { @attr({ hideFormSection: true }) postalCode; /* End of overriding Additional subject field options */ - /* CAPABILITIES */ + /* CAPABILITIES + * Default to show UI elements unless we know they can't access the given path + */ @lazyCapabilities(apiPath`${'backend'}/roles/${'id'}`, 'backend', 'id') updatePath; get canDelete() { - return this.updatePath.get('canCreate'); + return this.updatePath.get('isLoading') || this.updatePath.get('canCreate') !== false; } get canEdit() { - return this.updatePath.get('canEdit'); + return this.updatePath.get('isLoading') || this.updatePath.get('canUpdate') !== false; } get canRead() { - return this.updatePath.get('canRead'); + return this.updatePath.get('isLoading') || this.updatePath.get('canRead') !== false; } @lazyCapabilities(apiPath`${'backend'}/issue/${'id'}`, 'backend', 'id') generatePath; - get canReadIssue() { - // ARG TODO was duplicate name, added Issue - return this.generatePath.get('canUpdate'); + get canGenerateCert() { + return this.generatePath.get('isLoading') || this.generatePath.get('canUpdate') !== false; } @lazyCapabilities(apiPath`${'backend'}/sign/${'id'}`, 'backend', 'id') signPath; get canSign() { - return this.signPath.get('canUpdate'); + return this.signPath.get('isLoading') || this.signPath.get('canUpdate') !== false; } @lazyCapabilities(apiPath`${'backend'}/sign-verbatim/${'id'}`, 'backend', 'id') signVerbatimPath; get canSignVerbatim() { - return this.signVerbatimPath.get('canUpdate'); + return this.signVerbatimPath.get('isLoading') || this.signVerbatimPath.get('canUpdate') !== false; } - _fieldToAttrsGroups = null; - // Gets header/footer copy for specific toggle groups. get fieldGroupsInfo() { return { @@ -297,67 +354,4 @@ export default class PkiRoleModel extends Model { }, }; } - - get fieldGroups() { - if (!this._fieldToAttrsGroups) { - this._fieldToAttrsGroups = fieldToAttrs(this, [ - { - default: [ - 'name', - 'issuerRef', - 'customTtl', - 'notBeforeDuration', - 'maxTtl', - 'generateLease', - 'noStore', - 'addBasicConstraints', - ], - }, - { - 'Domain handling': [ - 'allowedDomains', - 'allowedDomainsTemplate', - 'allowBareDomains', - 'allowSubdomains', - 'allowGlobDomains', - 'allowWildcardCertificates', - 'allowLocalhost', // default: true (returned true by OpenApi) - 'allowAnyName', - 'enforceHostnames', // default: true (returned true by OpenApi) - ], - }, - { - 'Key parameters': ['keyType', 'keyBits', 'signatureBits'], - }, - { - 'Key usage': ['keyUsage', 'extKeyUsage', 'extKeyUsageOids'], - }, - { 'Policy identifiers': ['policyIdentifiers'] }, - { - 'Subject Alternative Name (SAN) Options': [ - 'allowIpSans', - 'allowedUriSans', - 'allowUriSansTemplate', - 'allowedOtherSans', - ], - }, - { - 'Additional subject fields': [ - 'allowedSerialNumbers', - 'requireCn', - 'useCsrCommonName', - 'useCsrSans', - 'ou', - 'organization', - 'country', - 'locality', - 'province', - 'streetAddress', - 'postalCode', - ], - }, - ]); - } - return this._fieldToAttrsGroups; - } } diff --git a/ui/app/templates/components/pki/role-pki-edit.hbs b/ui/app/templates/components/pki/role-pki-edit.hbs index 97ca816cca..5c44acedbd 100644 --- a/ui/app/templates/components/pki/role-pki-edit.hbs +++ b/ui/app/templates/components/pki/role-pki-edit.hbs @@ -83,7 +83,7 @@ {{else}}
- {{#each this.model.fieldGroups as |fieldGroup|}} + {{#each this.model.formFieldGroups as |fieldGroup|}} {{#each-in fieldGroup as |group fields|}} {{#if (or (eq group "default") (eq group "Options"))}} {{#each fields as |attr|}} diff --git a/ui/lib/core/addon/components/secret-list-header-tab.hbs b/ui/lib/core/addon/components/secret-list-header-tab.hbs index b38e9904b3..5bda9ed305 100644 --- a/ui/lib/core/addon/components/secret-list-header-tab.hbs +++ b/ui/lib/core/addon/components/secret-list-header-tab.hbs @@ -1,6 +1,6 @@ {{#unless this.dontShowTab}} {{#if @isEngine}} - + {{@label}} {{else}} diff --git a/ui/lib/pki/addon/components/page/pki-role-details.hbs b/ui/lib/pki/addon/components/page/pki-role-details.hbs index 7e458c1482..815cc8724c 100644 --- a/ui/lib/pki/addon/components/page/pki-role-details.hbs +++ b/ui/lib/pki/addon/components/page/pki-role-details.hbs @@ -1,33 +1,38 @@ - - - - - -

- - PKI Role - {{@role.name}} -

-
-
- - Delete - -
- Generate Certificate - Sign Certificate - Edit + {{#if @role.canDelete}} + + Delete + +
+ {{/if}} + {{#if @role.canGenerateCert}} + + Generate Certificate + + + {{/if}} + {{#if @role.canSign}} + + Sign Certificate + + + {{/if}} + {{#if @role.canEdit}} + + Edit + + + {{/if}}
-{{#each @role.fieldGroups as |fg|}} +{{#each @role.formFieldGroups as |fg|}} {{#each-in fg as |group fields|}} {{#if (not-eq group "default")}}

{{group}}

diff --git a/ui/lib/pki/addon/components/page/pki-role-details.ts b/ui/lib/pki/addon/components/page/pki-role-details.ts index 174d335526..25bbcecbe8 100644 --- a/ui/lib/pki/addon/components/page/pki-role-details.ts +++ b/ui/lib/pki/addon/components/page/pki-role-details.ts @@ -1,22 +1,24 @@ import { action } from '@ember/object'; +import RouterService from '@ember/routing/router-service'; import Component from '@glimmer/component'; - -// interface Attribute { -// name: string; -// options?: { -// label?: string; -// }; -// } +import FlashMessageService from 'vault/services/flash-messages'; +import { inject as service } from '@ember/service'; +import errorMessage from 'vault/utils/error-message'; // TODO: pull this in from route model once it's TS interface Args { role: { backend: string; id: string; + rollbackAttributes: () => void; + destroyRecord: () => void; }; } export default class DetailsPage extends Component { + @service declare readonly router: RouterService; + @service declare readonly flashMessages: FlashMessageService; + get breadcrumbs() { return [ { label: 'secrets', route: 'secrets', linkExternal: true }, @@ -30,7 +32,15 @@ export default class DetailsPage extends Component { return ['keyUsage', 'extKeyUsage', 'extKeyUsageOids']; } - @action deleteRole() { - // TODO: delete role + @action + async deleteRole() { + try { + await this.args.role.destroyRecord(); + this.flashMessages.success('Role deleted successfully'); + this.router.transitionTo('vault.cluster.secrets.backend.pki.roles.index'); + } catch (error) { + this.args.role.rollbackAttributes(); + this.flashMessages.danger(errorMessage(error)); + } } } diff --git a/ui/lib/pki/addon/components/pki-role-form.hbs b/ui/lib/pki/addon/components/pki-role-form.hbs index 4be623d7e3..40629f790e 100644 --- a/ui/lib/pki/addon/components/pki-role-form.hbs +++ b/ui/lib/pki/addon/components/pki-role-form.hbs @@ -1,25 +1,9 @@ - - - - - -

- {{#if @model.isNew}} - Create a PKI role - {{else}} - Edit - {{@model.name}} - {{/if}} -

-
-
-
{{! ARG TODO write a test for namespace reminder }} - {{#each @model.fieldGroups as |fieldGroup|}} + {{#each @model.formFieldGroups as |fieldGroup|}} {{#each-in fieldGroup as |group fields|}} {{! DEFAULT VIEW }} {{#if (eq group "default")}} diff --git a/ui/lib/pki/addon/routes/certificates/index.js b/ui/lib/pki/addon/routes/certificates/index.js index 91d9a94c71..dfd49a9e49 100644 --- a/ui/lib/pki/addon/routes/certificates/index.js +++ b/ui/lib/pki/addon/routes/certificates/index.js @@ -8,7 +8,7 @@ export default class PkiCertificatesIndexRoute extends Route { beforeModel() { // Must call this promise before the model hook otherwise it doesn't add OpenApi to record. - return this.pathHelp.getNewModel('pki/certificate', 'pki'); + return this.pathHelp.getNewModel('pki/certificate', this.secretMountPath.currentPath); } model() { diff --git a/ui/lib/pki/addon/routes/issuers/index.js b/ui/lib/pki/addon/routes/issuers/index.js index 14667bdc31..5211ecc3f3 100644 --- a/ui/lib/pki/addon/routes/issuers/index.js +++ b/ui/lib/pki/addon/routes/issuers/index.js @@ -8,7 +8,7 @@ export default class PkiIssuersIndexRoute extends Route { beforeModel() { // Must call this promise before the model hook otherwise it doesn't add OpenApi to record. - return this.pathHelp.getNewModel('pki/issuer', 'pki'); + return this.pathHelp.getNewModel('pki/issuer', this.secretMountPath.currentPath); } model() { diff --git a/ui/lib/pki/addon/routes/keys/index.js b/ui/lib/pki/addon/routes/keys/index.js index 3234b3a0e1..ccde88b43e 100644 --- a/ui/lib/pki/addon/routes/keys/index.js +++ b/ui/lib/pki/addon/routes/keys/index.js @@ -8,7 +8,7 @@ export default class PkiKeysIndexRoute extends Route { beforeModel() { // Must call this promise before the model hook otherwise it doesn't add OpenApi to record. - return this.pathHelp.getNewModel('pki/key', 'pki'); + return this.pathHelp.getNewModel('pki/key', this.secretMountPath.currentPath); } model() { diff --git a/ui/lib/pki/addon/routes/roles/create.js b/ui/lib/pki/addon/routes/roles/create.js index a24a4b53cf..1f9b04424d 100644 --- a/ui/lib/pki/addon/routes/roles/create.js +++ b/ui/lib/pki/addon/routes/roles/create.js @@ -12,4 +12,15 @@ export default class PkiRolesCreateRoute extends PkiRolesIndexRoute { backend: this.secretMountPath.currentPath, }); } + + setupController(controller, resolvedModel) { + super.setupController(controller, resolvedModel); + const backend = this.secretMountPath.currentPath || 'pki'; + controller.breadcrumbs = [ + { label: 'secrets', route: 'secrets', linkExternal: true }, + { label: backend, route: 'overview' }, + { label: 'roles', route: 'roles.index' }, + { label: 'create' }, + ]; + } } diff --git a/ui/lib/pki/addon/routes/roles/index.js b/ui/lib/pki/addon/routes/roles/index.js index a0b0df80a9..abb5a61769 100644 --- a/ui/lib/pki/addon/routes/roles/index.js +++ b/ui/lib/pki/addon/routes/roles/index.js @@ -9,7 +9,7 @@ export default class PkiRolesIndexRoute extends Route { beforeModel() { // Must call this promise before the model hook otherwise // the model doesn't hydrate from OpenAPI correctly. - return this.pathHelp.getNewModel('pki/role', 'pki'); + return this.pathHelp.getNewModel('pki/role', this.secretMountPath.currentPath); } model() { diff --git a/ui/lib/pki/addon/routes/roles/role/details.js b/ui/lib/pki/addon/routes/roles/role/details.js index 8a9414d94f..66632b1164 100644 --- a/ui/lib/pki/addon/routes/roles/role/details.js +++ b/ui/lib/pki/addon/routes/roles/role/details.js @@ -8,4 +8,16 @@ export default class RolesRoleDetailsRoute extends PkiRolesIndexRoute { id: role, }); } + + setupController(controller, resolvedModel) { + super.setupController(controller, resolvedModel); + const { id } = resolvedModel; + const backend = this.secretMountPath.currentPath || 'pki'; + controller.breadcrumbs = [ + { label: 'secrets', route: 'secrets', linkExternal: true }, + { label: backend, route: 'overview' }, + { label: 'roles', route: 'roles.index' }, + { label: id }, + ]; + } } diff --git a/ui/lib/pki/addon/routes/roles/role/edit.js b/ui/lib/pki/addon/routes/roles/role/edit.js index 04eb231cc7..c915a23959 100644 --- a/ui/lib/pki/addon/routes/roles/role/edit.js +++ b/ui/lib/pki/addon/routes/roles/role/edit.js @@ -10,4 +10,17 @@ export default class PkiRoleEditRoute extends PkiRolesIndexRoute { id: role, }); } + + setupController(controller, resolvedModel) { + super.setupController(controller, resolvedModel); + const { id } = resolvedModel; + const backend = this.secretMountPath.currentPath || 'pki'; + controller.breadcrumbs = [ + { label: 'secrets', route: 'secrets', linkExternal: true }, + { label: backend, route: 'overview' }, + { label: 'roles', route: 'roles.index' }, + { label: id, route: 'roles.role.details' }, + { label: 'edit' }, + ]; + } } diff --git a/ui/lib/pki/addon/templates/roles/create.hbs b/ui/lib/pki/addon/templates/roles/create.hbs index 6f6978786e..437e6e7391 100644 --- a/ui/lib/pki/addon/templates/roles/create.hbs +++ b/ui/lib/pki/addon/templates/roles/create.hbs @@ -1,3 +1,14 @@ + + + + + +

+ Create a PKI role +

+
+
+ - + Create role diff --git a/ui/lib/pki/addon/templates/roles/role/details.hbs b/ui/lib/pki/addon/templates/roles/role/details.hbs index 4b5f7a632a..b7d370af33 100644 --- a/ui/lib/pki/addon/templates/roles/role/details.hbs +++ b/ui/lib/pki/addon/templates/roles/role/details.hbs @@ -1 +1,13 @@ + + + + + +

+ + PKI Role + {{this.model.name}} +

+
+
\ No newline at end of file diff --git a/ui/lib/pki/addon/templates/roles/role/edit.hbs b/ui/lib/pki/addon/templates/roles/role/edit.hbs index 494e252b78..d67accb7d0 100644 --- a/ui/lib/pki/addon/templates/roles/role/edit.hbs +++ b/ui/lib/pki/addon/templates/roles/role/edit.hbs @@ -1,3 +1,13 @@ + + + + + +

+ Edit role +

+
+