mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-01 19:17:58 +00:00
UI: Add PKI role attribute no_store_metadata (#27251)
* Add noStoreMetadata attribute to pki/role model and details component * update tests * Make noStoreMetadata enterprise only * update tests * Cleanup tests * Address PR comments
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
import { expandAttributeMeta } from 'vault/utils/field-to-attrs';
|
||||||
import Model from '@ember-data/model';
|
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
|
* 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 [key, stringArray] = Object.entries(obj)[0];
|
||||||
const expanded = stringArray.map((fieldName) => this.allByKey[fieldName]).filter((f) => !!f);
|
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
|
// if this fails, it might mean there are missing fields in the model or the model must be hydrated via OpenAPI
|
||||||
assert(
|
if (expanded.length !== stringArray.length) {
|
||||||
`all model fields found in allByKey for group ${key}`,
|
debug(`not all model fields found in allByKey for group "${key}"`);
|
||||||
expanded.length === stringArray.length
|
}
|
||||||
);
|
|
||||||
return { [key]: expanded };
|
return { [key]: expanded };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,76 +4,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import Model, { attr } from '@ember-data/model';
|
import Model, { attr } from '@ember-data/model';
|
||||||
|
import { service } from '@ember/service';
|
||||||
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
|
||||||
import { withModelValidations } from 'vault/decorators/model-validations';
|
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 = {
|
const validations = {
|
||||||
name: [{ type: 'presence', message: 'Name is required.' }],
|
name: [{ type: 'presence', message: 'Name is required.' }],
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldGroups = [
|
@withExpandedAttributes()
|
||||||
{
|
|
||||||
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)
|
|
||||||
@withModelValidations(validations)
|
@withModelValidations(validations)
|
||||||
export default class PkiRoleModel extends Model {
|
export default class PkiRoleModel extends Model {
|
||||||
|
@service version; // noStoreMetadata is enterprise-only, so we need this available
|
||||||
|
|
||||||
get useOpenAPI() {
|
get useOpenAPI() {
|
||||||
// must be a getter so it can be accessed in path-help.js
|
// must be a getter so it can be accessed in path-help.js
|
||||||
return true;
|
return true;
|
||||||
@@ -84,6 +28,73 @@ export default class PkiRoleModel extends Model {
|
|||||||
|
|
||||||
@attr('string', { readOnly: true }) backend;
|
@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 */
|
/* Overriding OpenApi default options */
|
||||||
@attr('string', {
|
@attr('string', {
|
||||||
label: 'Role name',
|
label: 'Role name',
|
||||||
@@ -146,6 +157,14 @@ export default class PkiRoleModel extends Model {
|
|||||||
})
|
})
|
||||||
noStore;
|
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', {
|
@attr('boolean', {
|
||||||
label: 'Basic constraints valid for non-CA',
|
label: 'Basic constraints valid for non-CA',
|
||||||
detailsLabel: 'Add basic constraints',
|
detailsLabel: 'Add basic constraints',
|
||||||
|
|||||||
@@ -72,7 +72,7 @@
|
|||||||
@value={{val}}
|
@value={{val}}
|
||||||
@alwaysRender={{true}}
|
@alwaysRender={{true}}
|
||||||
/>
|
/>
|
||||||
{{else if (eq attr.name "noStore")}}
|
{{else if (includes attr.name (array "noStore" "noStoreMetadata"))}}
|
||||||
<InfoTableRow
|
<InfoTableRow
|
||||||
@label={{capitalize (or attr.options.detailsLabel attr.options.label (humanize (dasherize attr.name)))}}
|
@label={{capitalize (or attr.options.detailsLabel attr.options.label (humanize (dasherize attr.name)))}}
|
||||||
@value={{not val}}
|
@value={{not val}}
|
||||||
|
|||||||
@@ -191,6 +191,7 @@ export const PKI_KEYS = {
|
|||||||
export const PKI_ROLE_DETAILS = {
|
export const PKI_ROLE_DETAILS = {
|
||||||
issuerLabel: '[data-test-row-label="Issuer"]',
|
issuerLabel: '[data-test-row-label="Issuer"]',
|
||||||
noStoreValue: '[data-test-value-div="Store in storage backend"]',
|
noStoreValue: '[data-test-value-div="Store in storage backend"]',
|
||||||
|
noStoreMetadataValue: '[data-test-value-div="Store metadata in storage backend"]',
|
||||||
keyUsageValue: '[data-test-value-div="Key usage"]',
|
keyUsageValue: '[data-test-value-div="Key usage"]',
|
||||||
extKeyUsageValue: '[data-test-value-div="Ext key usage"]',
|
extKeyUsageValue: '[data-test-value-div="Ext key usage"]',
|
||||||
customTtlValue: '[data-test-value-div="Issued certificates expire after"]',
|
customTtlValue: '[data-test-value-div="Issued certificates expire after"]',
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ module('Integration | Component | pki role details page', function (hooks) {
|
|||||||
name: 'Foobar',
|
name: 'Foobar',
|
||||||
backend: 'pki',
|
backend: 'pki',
|
||||||
noStore: false,
|
noStore: false,
|
||||||
|
noStoreMetadata: true,
|
||||||
keyUsage: [],
|
keyUsage: [],
|
||||||
extKeyUsage: ['bar', 'baz'],
|
extKeyUsage: ['bar', 'baz'],
|
||||||
ttl: 600,
|
ttl: 600,
|
||||||
@@ -27,7 +28,6 @@ module('Integration | Component | pki role details page', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should render the page component', async function (assert) {
|
test('it should render the page component', async function (assert) {
|
||||||
assert.expect(5);
|
|
||||||
await render(
|
await render(
|
||||||
hbs`
|
hbs`
|
||||||
<Page::PkiRoleDetails @role={{this.model}} />
|
<Page::PkiRoleDetails @role={{this.model}} />
|
||||||
@@ -44,9 +44,26 @@ module('Integration | Component | pki role details page', function (hooks) {
|
|||||||
assert
|
assert
|
||||||
.dom(PKI_ROLE_DETAILS.noStoreValue)
|
.dom(PKI_ROLE_DETAILS.noStoreValue)
|
||||||
.containsText('Yes', 'noStore shows opposite of what the value is');
|
.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');
|
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`
|
||||||
|
<Page::PkiRoleDetails @role={{this.model}} />
|
||||||
|
`,
|
||||||
|
{ 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) {
|
test('it should render the notAfter date if present', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
this.model = this.store.createRecord('pki/role', {
|
this.model = this.store.createRecord('pki/role', {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ module('Integration | Component | pki-role-form', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('it should render default fields and toggle groups', async function (assert) {
|
test('it should render default fields and toggle groups', async function (assert) {
|
||||||
assert.expect(13);
|
|
||||||
await render(
|
await render(
|
||||||
hbs`
|
hbs`
|
||||||
<PkiRoleForm
|
<PkiRoleForm
|
||||||
@@ -55,6 +54,9 @@ module('Integration | Component | pki-role-form', function (hooks) {
|
|||||||
assert.dom(GENERAL.ttl.toggle('Max TTL')).exists();
|
assert.dom(GENERAL.ttl.toggle('Max TTL')).exists();
|
||||||
assert.dom(GENERAL.fieldByAttr('generateLease')).exists();
|
assert.dom(GENERAL.fieldByAttr('generateLease')).exists();
|
||||||
assert.dom(GENERAL.fieldByAttr('noStore')).exists();
|
assert.dom(GENERAL.fieldByAttr('noStore')).exists();
|
||||||
|
assert
|
||||||
|
.dom(GENERAL.fieldByAttr('noStoreMetadata'))
|
||||||
|
.doesNotExist('noStoreMetadata is not shown b/c not enterprise');
|
||||||
assert.dom(GENERAL.inputByAttr('addBasicConstraints')).exists();
|
assert.dom(GENERAL.inputByAttr('addBasicConstraints')).exists();
|
||||||
assert.dom(PKI_ROLE_FORM.domainHandling).exists('shows form-field group add domain handling');
|
assert.dom(PKI_ROLE_FORM.domainHandling).exists('shows form-field group add domain handling');
|
||||||
assert.dom(PKI_ROLE_FORM.keyParams).exists('shows form-field group key params');
|
assert.dom(PKI_ROLE_FORM.keyParams).exists('shows form-field group key params');
|
||||||
@@ -66,6 +68,23 @@ module('Integration | Component | pki-role-form', function (hooks) {
|
|||||||
.exists('shows form-field group additional subject fields');
|
.exists('shows form-field group additional subject fields');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it renders enterprise-only values in enterprise edition', async function (assert) {
|
||||||
|
const version = this.owner.lookup('service:version');
|
||||||
|
version.type = 'enterprise';
|
||||||
|
await render(
|
||||||
|
hbs`
|
||||||
|
<PkiRoleForm
|
||||||
|
@role={{this.role}}
|
||||||
|
@issuers={{this.issuers}}
|
||||||
|
@onCancel={{this.onCancel}}
|
||||||
|
@onSave={{this.onSave}}
|
||||||
|
/>
|
||||||
|
`,
|
||||||
|
{ owner: this.engine }
|
||||||
|
);
|
||||||
|
assert.dom(GENERAL.fieldByAttr('noStoreMetadata')).exists();
|
||||||
|
});
|
||||||
|
|
||||||
test('it should save a new pki role with various options selected', async function (assert) {
|
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
|
// Key usage, Key params and Not valid after options are tested in their respective component tests
|
||||||
assert.expect(8);
|
assert.expect(8);
|
||||||
|
|||||||
Reference in New Issue
Block a user