diff --git a/ui/app/decorators/model-expanded-attributes.js b/ui/app/decorators/model-expanded-attributes.js index 8272505425..2eb40e56e6 100644 --- a/ui/app/decorators/model-expanded-attributes.js +++ b/ui/app/decorators/model-expanded-attributes.js @@ -5,7 +5,7 @@ import { expandAttributeMeta } from 'vault/utils/field-to-attrs'; import Model from '@ember-data/model'; -import { assert } from '@ember/debug'; +import { debug } from '@ember/debug'; /** * sets allByKey properties on model class. These are all the attributes on the model @@ -41,10 +41,9 @@ export function withExpandedAttributes() { const [key, stringArray] = Object.entries(obj)[0]; const expanded = stringArray.map((fieldName) => this.allByKey[fieldName]).filter((f) => !!f); // if this fails, it might mean there are missing fields in the model or the model must be hydrated via OpenAPI - assert( - `all model fields found in allByKey for group ${key}`, - expanded.length === stringArray.length - ); + if (expanded.length !== stringArray.length) { + debug(`not all model fields found in allByKey for group "${key}"`); + } return { [key]: expanded }; }); } diff --git a/ui/app/models/pki/role.js b/ui/app/models/pki/role.js index b7fe845fe6..25704a8719 100644 --- a/ui/app/models/pki/role.js +++ b/ui/app/models/pki/role.js @@ -4,76 +4,20 @@ */ import Model, { attr } from '@ember-data/model'; +import { service } from '@ember/service'; import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities'; import { withModelValidations } from 'vault/decorators/model-validations'; -import { withFormFields } from 'vault/decorators/model-form-fields'; +import { withExpandedAttributes } from 'vault/decorators/model-expanded-attributes'; 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': [ - 'allowedUserIds', - 'allowedSerialNumbers', - 'requireCn', - 'useCsrCommonName', - 'useCsrSans', - 'ou', - 'organization', - 'country', - 'locality', - 'province', - 'streetAddress', - 'postalCode', - ], - }, -]; - -@withFormFields(null, fieldGroups) +@withExpandedAttributes() @withModelValidations(validations) export default class PkiRoleModel extends Model { + @service version; // noStoreMetadata is enterprise-only, so we need this available + get useOpenAPI() { // must be a getter so it can be accessed in path-help.js return true; @@ -84,6 +28,73 @@ export default class PkiRoleModel extends Model { @attr('string', { readOnly: true }) backend; + get formFieldGroups() { + let defaultArray = [ + 'name', + 'issuerRef', + 'customTtl', + 'notBeforeDuration', + 'maxTtl', + 'generateLease', + 'noStore', + 'noStoreMetadata', + 'addBasicConstraints', + ]; + if (this.version.isCommunity) { + const entFields = ['noStoreMetadata']; + defaultArray = defaultArray.filter((field) => !entFields.includes(field)); + } + return this._expandGroups([ + { + default: defaultArray, + }, + { + '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': [ + 'allowedUserIds', + 'allowedSerialNumbers', + 'requireCn', + 'useCsrCommonName', + 'useCsrSans', + 'ou', + 'organization', + 'country', + 'locality', + 'province', + 'streetAddress', + 'postalCode', + ], + }, + ]); + } + /* Overriding OpenApi default options */ @attr('string', { label: 'Role name', @@ -146,6 +157,14 @@ export default class PkiRoleModel extends Model { }) noStore; + @attr('boolean', { + label: 'Do not store certificate metadata in storage backend', + detailsLabel: 'Store metadata in storage backend', // template reverses value + subText: + 'We don’t recommend storing metadata, since this information creates overhead in storage, and requires clean up.', + }) + noStoreMetadata; + @attr('boolean', { label: 'Basic constraints valid for non-CA', detailsLabel: 'Add basic constraints', 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 e06510b234..9ed5c90a89 100644 --- a/ui/lib/pki/addon/components/page/pki-role-details.hbs +++ b/ui/lib/pki/addon/components/page/pki-role-details.hbs @@ -72,7 +72,7 @@ @value={{val}} @alwaysRender={{true}} /> - {{else if (eq attr.name "noStore")}} + {{else if (includes attr.name (array "noStore" "noStoreMetadata"))}} @@ -44,9 +44,26 @@ module('Integration | Component | pki role details page', function (hooks) { assert .dom(PKI_ROLE_DETAILS.noStoreValue) .containsText('Yes', 'noStore shows opposite of what the value is'); + assert + .dom(PKI_ROLE_DETAILS.noStoreMetadataValue) + .doesNotExist('does not render value for enterprise-only field'); assert.dom(PKI_ROLE_DETAILS.customTtlValue).containsText('10 minutes', 'TTL shown as duration'); }); + test('it should render the enterprise-only values in enterprise edition', async function (assert) { + const version = this.owner.lookup('service:version'); + version.type = 'enterprise'; + await render( + hbs` + + `, + { owner: this.engine } + ); + assert + .dom(PKI_ROLE_DETAILS.noStoreMetadataValue) + .containsText('No', 'noStoreMetadata shows opposite of what the value is'); + }); + test('it should render the notAfter date if present', async function (assert) { assert.expect(1); this.model = this.store.createRecord('pki/role', { diff --git a/ui/tests/integration/components/pki/pki-role-form-test.js b/ui/tests/integration/components/pki/pki-role-form-test.js index c89b085380..107da6f8e1 100644 --- a/ui/tests/integration/components/pki/pki-role-form-test.js +++ b/ui/tests/integration/components/pki/pki-role-form-test.js @@ -37,7 +37,6 @@ module('Integration | Component | pki-role-form', function (hooks) { }); test('it should render default fields and toggle groups', async function (assert) { - assert.expect(13); await render( hbs` + `, + { owner: this.engine } + ); + assert.dom(GENERAL.fieldByAttr('noStoreMetadata')).exists(); + }); + test('it should save a new pki role with various options selected', async function (assert) { // Key usage, Key params and Not valid after options are tested in their respective component tests assert.expect(8);