From 91d88c3de1ec55c0e3d250686f96c5af177f1c29 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Thu, 2 Jan 2025 11:50:49 -0600 Subject: [PATCH] Add UI support name constraints (#29263) * add open api params * support pki name constraints * fix conditional * revert helptextwsubtext * fix typo * add name constraints to sign intermediate form * add changelog * update test --- builtin/logical/pki/fields.go | 2 +- changelog/29263.txt | 3 ++ ui/app/models/pki/sign-intermediate.js | 51 ++++++++++++++++--- ui/lib/core/addon/components/form-field.js | 6 +-- .../components/pki-generate-toggle-groups.hbs | 7 +++ .../components/pki-sign-intermediate-form.ts | 10 ++++ .../pki/pki-sign-intermediate-form-test.js | 21 ++++---- 7 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 changelog/29263.txt diff --git a/builtin/logical/pki/fields.go b/builtin/logical/pki/fields.go index 24d030576f..e9da69c065 100644 --- a/builtin/logical/pki/fields.go +++ b/builtin/logical/pki/fields.go @@ -419,7 +419,7 @@ Ranges must be specified in the notation of IP address and prefix length, like " Type: framework.TypeCommaStringSlice, Description: `Email addresses for which this certificate is allowed to sign or issue child certificates (see https://tools.ietf.org/html/rfc5280#section-4.2.1.10).`, DisplayAttrs: &framework.DisplayAttributes{ - Name: "Permitted email adresses", + Name: "Permitted email addresses", }, } fields["excluded_email_addresses"] = &framework.FieldSchema{ diff --git a/changelog/29263.txt b/changelog/29263.txt new file mode 100644 index 0000000000..f6df0717fd --- /dev/null +++ b/changelog/29263.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Add support for the name constraints extension to be fully specified when creating root and intermediate CA certificates. +``` \ No newline at end of file diff --git a/ui/app/models/pki/sign-intermediate.js b/ui/app/models/pki/sign-intermediate.js index 2dfbb7bf6c..c5aeff91eb 100644 --- a/ui/app/models/pki/sign-intermediate.js +++ b/ui/app/models/pki/sign-intermediate.js @@ -21,7 +21,6 @@ const validations = { 'notBeforeDuration', 'enforceLeafNotAfterBehavior', 'format', - 'permittedDnsDomains', 'maxPathLength', ]) export default class PkiSignIntermediateModel extends PkiCertificateBaseModel { @@ -58,19 +57,55 @@ export default class PkiSignIntermediateModel extends PkiCertificateBaseModel { }) enforceLeafNotAfterBehavior; - @attr({ - label: 'Permitted DNS domains', - subText: - 'DNS domains for which certificates are allowed to be issued or signed by this CA certificate. Enter each value as a new input.', - }) - permittedDnsDomains; - @attr({ subText: 'Specifies the maximum path length to encode in the generated certificate. -1 means no limit', defaultValue: '-1', }) maxPathLength; + /* Name constraint overrides */ + @attr({ + subText: 'DNS domains for which certificates are allowed to be issued or signed by this CA certificate.', + }) + permittedDnsDomains; + + @attr({ + subText: 'Domains for which this certificate is not allowed to sign or issue child certificates.', + }) + excludedDnsDomains; + + @attr({ + subText: 'Email addresses for which this certificate is not allowed to sign or issue child certificates.', + }) + excludedEmailAddresses; + + @attr({ + subText: + 'IP ranges for which this certificate is not allowed to sign or issue child certificates. Ranges must be specified in the notation of IP address and prefix length, such as "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291.', + }) + excludedIpRanges; + + @attr({ + subText: 'URI domains for which this certificate is not allowed to sign or issue child certificates.', + }) + excludedUriDomains; + + @attr({ + subText: 'Email addresses for which this certificate is allowed to sign or issue child certificates.', + }) + permittedEmailAddresses; + + @attr({ + subText: + 'IP ranges for which this certificate is allowed to sign or issue child certificates. Ranges must be specified in the notation of IP address and prefix length, such as "192.0.2.0/24" or "2001:db8::/32", as defined in RFC 4632 and RFC 4291.', + }) + permittedIpRanges; + + @attr({ + subText: 'URI domains for which this certificate is allowed to sign or issue child certificates.', + }) + permittedUriDomains; + /* Signing Options overrides */ @attr({ label: 'Use PSS', diff --git a/ui/lib/core/addon/components/form-field.js b/ui/lib/core/addon/components/form-field.js index 59dba55784..2fb40e00c9 100644 --- a/ui/lib/core/addon/components/form-field.js +++ b/ui/lib/core/addon/components/form-field.js @@ -43,7 +43,6 @@ import { removeFromArray } from 'vault/helpers/remove-from-array'; * @param {Model} model - Ember Data model that `attr` is defined on * @param {boolean} [disabled=false] - whether the field is disabled * @param {boolean} [showHelpText=true] - whether to show the tooltip with help text from OpenAPI - * @param {string} [subText] - text to be displayed below the label * @param {string} [mode] - used when editType is 'kv' * @param {object} [modelValidations] - Object of errors. If attr.name is in object and has error message display in AlertInline. * @param {function} [onChange] - called whenever a value on the model changes via the component @@ -96,12 +95,11 @@ export default class FormFieldComponent extends Component { get disabled() { return this.args.disabled || false; } + get showHelpText() { return this.args.showHelpText === false ? false : true; } - get subText() { - return this.args.subText || ''; - } + // used in the label element next to the form element get labelString() { const label = this.args.attr.options?.label || ''; diff --git a/ui/lib/pki/addon/components/pki-generate-toggle-groups.hbs b/ui/lib/pki/addon/components/pki-generate-toggle-groups.hbs index 210e9101b0..a2f1b07585 100644 --- a/ui/lib/pki/addon/components/pki-generate-toggle-groups.hbs +++ b/ui/lib/pki/addon/components/pki-generate-toggle-groups.hbs @@ -43,6 +43,13 @@ etc.) to be protected by a single certificate. {{else if (eq group "Additional subject fields")}} These fields provide more information about the client to which the certificate belongs. + {{else if (eq group "Name constraints")}} + These fields create the name constraints extension when generating CA certificates. Specifying any combination of + these parameters will trigger the creation of the name constraints extension as per + RFC 5280 section 4.2.1.10. {{/if}}

{{#each fields as |fieldName|}} diff --git a/ui/lib/pki/addon/components/pki-sign-intermediate-form.ts b/ui/lib/pki/addon/components/pki-sign-intermediate-form.ts index 2fb1c660ab..335e9b5fed 100644 --- a/ui/lib/pki/addon/components/pki-sign-intermediate-form.ts +++ b/ui/lib/pki/addon/components/pki-sign-intermediate-form.ts @@ -49,6 +49,16 @@ export default class PkiSignIntermediateFormComponent extends Component { get groups() { return { + 'Name constraints': [ + 'permittedDnsDomains', + 'permittedEmailAddresses', + 'permittedIpRanges', + 'permittedUriDomains', + 'excludedDnsDomains', + 'excludedEmailAddresses', + 'excludedIpRanges', + 'excludedUriDomains', + ], 'Signing options': ['usePss', 'skid', 'signatureBits'], 'Subject Alternative Name (SAN) Options': ['altNames', 'ipSans', 'uriSans', 'otherSans'], 'Additional subject fields': [ diff --git a/ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js b/ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js index 0b49812901..3dca52d491 100644 --- a/ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js +++ b/ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js @@ -14,9 +14,7 @@ import { setupMirage } from 'ember-cli-mirage/test-support'; const selectors = { form: '[data-test-sign-intermediate-form]', csrInput: '[data-test-input="csr"]', - toggleSigningOptions: '[data-test-toggle-group="Signing options"]', - toggleSANOptions: '[data-test-toggle-group="Subject Alternative Name (SAN) Options"]', - toggleAdditionalFields: '[data-test-toggle-group="Additional subject fields"]', + toggleGroup: (group) => `[data-test-toggle-group="${group}"]`, fieldByName: (name) => `[data-test-field="${name}"]`, saveButton: '[data-test-pki-sign-intermediate-save]', cancelButton: '[data-test-pki-sign-intermediate-cancel]', @@ -40,19 +38,24 @@ module('Integration | Component | pki-sign-intermediate-form', function (hooks) }); test('renders correctly on load', async function (assert) { - assert.expect(9); + assert.expect(10); await render(hbs``, { owner: this.engine, }); assert.dom(selectors.form).exists('Form is rendered'); assert.dom(selectors.resultsContainer).doesNotExist('Results display not rendered'); - assert.dom('[data-test-field]').exists({ count: 10 }, '10 default fields shown'); - assert.dom(selectors.toggleSigningOptions).exists(); - assert.dom(selectors.toggleSANOptions).exists(); - assert.dom(selectors.toggleAdditionalFields).exists(); + assert.dom('[data-test-field]').exists({ count: 9 }, '9 default fields shown'); + [ + 'Name constraints', + 'Signing options', + 'Subject Alternative Name (SAN) Options', + 'Additional subject fields', + ].forEach((group) => { + assert.dom(selectors.toggleGroup(group)).exists(`${group} renders`); + }); - await click(selectors.toggleSigningOptions); + await click(selectors.toggleGroup('Signing options')); ['usePss', 'skid', 'signatureBits'].forEach((name) => { assert.dom(selectors.fieldByName(name)).exists(); });