diff --git a/ui/app/models/pki/action.js b/ui/app/models/pki/action.js index e0e4577aac..f54a25d33e 100644 --- a/ui/app/models/pki/action.js +++ b/ui/app/models/pki/action.js @@ -169,8 +169,11 @@ export default class PkiActionModel extends Model { @attr('string', { readOnly: true }) issuerId; // returned from generate-root action // For generating and signing a CSR - @attr('string') csr; + @attr('string', { label: 'CSR', masked: true }) csr; @attr caChain; + @attr('string', { label: 'Key ID' }) keyId; + @attr('string', { masked: true }) privateKey; + @attr('string') privateKeyType; get backend() { return this.secretMountPath.currentPath; diff --git a/ui/lib/pki/addon/components/page/pki-key-details.hbs b/ui/lib/pki/addon/components/page/pki-key-details.hbs index 192b9ea95d..828253f82a 100644 --- a/ui/lib/pki/addon/components/page/pki-key-details.hbs +++ b/ui/lib/pki/addon/components/page/pki-key-details.hbs @@ -45,6 +45,7 @@ {{/each}} {{#if @key.privateKey}} diff --git a/ui/lib/pki/addon/components/pki-configure-form.hbs b/ui/lib/pki/addon/components/pki-configure-form.hbs index cacfebb9ad..7be3d3625f 100644 --- a/ui/lib/pki/addon/components/pki-configure-form.hbs +++ b/ui/lib/pki/addon/components/pki-configure-form.hbs @@ -44,8 +44,8 @@ {{else if (eq @config.actionType "generate-csr")}} {{else}} - - - - {{#each this.formFields as |field|}} - - {{/each}} - - - -
-
- - +{{#if @model.id}} + {{! Model only has ID once form has been submitted and saved }} + +
+
+ + Copy the CSR below for a parent issuer to sign and then import the signed certificate back into this mount. + {{#if @model.privateKey}} + The + private_key + is only available once. Make sure you copy and save it now. + {{/if}} + + {{#each this.showFields as |fieldName|}} + {{#let (find-by "name" fieldName @model.allFields) as |attr|}} + {{#let (get @model attr.name) as |value|}} + + {{#if (and attr.options.masked value)}} + + {{else if (eq attr.name "keyId")}} + + {{@model.keyId}} + + {{else}} + {{! this block only ever renders privateKey and privateKeyType }} + {{or value "internal"}} + {{/if}} + + {{/let}} + {{/let}} + {{/each}}
- {{#if this.alert}} +
+
+
- +
- {{/if}} -
- \ No newline at end of file +
+ +{{else}} +
+ + + + {{#each this.formFields as |field|}} + + {{/each}} + + + +
+
+ + +
+ {{#if this.alert}} +
+ +
+ {{/if}} +
+ +{{/if}} \ No newline at end of file diff --git a/ui/lib/pki/addon/components/pki-generate-csr.ts b/ui/lib/pki/addon/components/pki-generate-csr.ts index 736265f0b0..ef8795cbfd 100644 --- a/ui/lib/pki/addon/components/pki-generate-csr.ts +++ b/ui/lib/pki/addon/components/pki-generate-csr.ts @@ -12,11 +12,11 @@ import errorMessage from 'vault/utils/error-message'; interface Args { model: PkiActionModel; useIssuer: boolean; - onSave: CallableFunction; + onComplete: CallableFunction; onCancel: CallableFunction; } -export default class PkiGenerateIntermediateComponent extends Component { +export default class PkiGenerateCsrComponent extends Component { @service declare readonly flashMessages: FlashMessageService; @tracked modelValidations = null; @@ -24,6 +24,8 @@ export default class PkiGenerateIntermediateComponent extends Component { @tracked alert: string | null = null; formFields; + // fields rendered after CSR generation + showFields = ['csr', 'keyId', 'privateKey', 'privateKeyType']; constructor(owner: unknown, args: Args) { super(owner, args); @@ -57,13 +59,12 @@ export default class PkiGenerateIntermediateComponent extends Component { *save(event: Event): Generator> { event.preventDefault(); try { - const { model, onSave } = this.args; + const { model } = this.args; const { isValid, state, invalidFormMessage } = model.validate(); if (isValid) { const useIssuer = yield this.getCapability(); yield model.save({ adapterOptions: { actionType: 'generate-csr', useIssuer } }); this.flashMessages.success('Successfully generated CSR.'); - onSave(); } else { this.modelValidations = state; this.alert = invalidFormMessage; diff --git a/ui/lib/pki/addon/components/pki-sign-intermediate-form.hbs b/ui/lib/pki/addon/components/pki-sign-intermediate-form.hbs index 2e4c5301b2..6a2581e8d3 100644 --- a/ui/lib/pki/addon/components/pki-sign-intermediate-form.hbs +++ b/ui/lib/pki/addon/components/pki-sign-intermediate-form.hbs @@ -11,7 +11,7 @@ {{#each this.showFields as |fieldName|}} {{#let (find-by "name" fieldName @model.allFields) as |attr|}} - + {{#if (and attr.options.masked (get @model attr.name))}} {{else if (eq attr.name "serialNumber")}} diff --git a/ui/lib/pki/addon/templates/issuers/generate-intermediate.hbs b/ui/lib/pki/addon/templates/issuers/generate-intermediate.hbs index 3a497c4c3d..5b0096268d 100644 --- a/ui/lib/pki/addon/templates/issuers/generate-intermediate.hbs +++ b/ui/lib/pki/addon/templates/issuers/generate-intermediate.hbs @@ -12,5 +12,5 @@ \ No newline at end of file diff --git a/ui/lib/pki/addon/templates/keys/index.hbs b/ui/lib/pki/addon/templates/keys/index.hbs index e5a4193e81..d956232e9d 100644 --- a/ui/lib/pki/addon/templates/keys/index.hbs +++ b/ui/lib/pki/addon/templates/keys/index.hbs @@ -8,7 +8,7 @@ }} @isEngine={{true}} /> -{{#if this.model.hasConfig}} +{{#if (or this.model.hasConfig this.model.keyModels)}} assert.ok(true, 'onSave action fires'); - - await render(hbs``, { + await render(hbs``, { owner: this.engine, }); @@ -55,6 +54,9 @@ module('Integration | Component | PkiGenerateCsr', function (hooks) { await fillIn('[data-test-input="type"]', 'exported'); await fillIn('[data-test-input="commonName"]', 'foo'); await click('[data-test-save]'); + + const savedRecord = this.store.peekAll('pki/action').firstObject; + assert.false(savedRecord.isNew, 'record is saved'); }); test('it should display validation errors', async function (assert) { @@ -78,4 +80,65 @@ module('Integration | Component | PkiGenerateCsr', function (hooks) { await click('[data-test-cancel]'); }); + + test('it should show generated CSR for type=exported', async function (assert) { + assert.expect(6); + this.model.id = '1235-someId'; + this.model.csr = '-----BEGIN CERTIFICATE REQUEST-----...-----END CERTIFICATE REQUEST-----'; + this.model.keyId = '9179de78-1275-a1cf-ebb0-a4eb2e376636'; + this.model.privateKey = '-----BEGIN RSA PRIVATE KEY-----...-----END RSA PRIVATE KEY-----'; + this.model.privateKeyType = 'rsa'; + this.onComplete = () => assert.ok(true, 'onComplete action fires'); + + await render(hbs``, { + owner: this.engine, + }); + assert + .dom('[data-test-alert-banner="alert"]') + .hasText( + 'Next steps Copy the CSR below for a parent issuer to sign and then import the signed certificate back into this mount. The private_key is only available once. Make sure you copy and save it now.', + 'renders Next steps alert banner' + ); + assert + .dom('[data-test-value-div="CSR"] [data-test-masked-input] button') + .hasAttribute('data-clipboard-text', this.model.csr, 'it renders copyable csr'); + assert + .dom('[data-test-value-div="Key ID"] button') + .hasAttribute('data-clipboard-text', this.model.keyId, 'it renders copyable key_id'); + assert + .dom('[data-test-value-div="Private key"] [data-test-masked-input] button') + .hasAttribute('data-clipboard-text', this.model.privateKey, 'it renders copyable private_key'); + assert + .dom('[data-test-value-div="Private key type"]') + .hasText(this.model.privateKeyType, 'renders private_key_type'); + await click('[data-test-done]'); + }); + + test('it should show generated CSR for type=internal', async function (assert) { + assert.expect(5); + this.model.id = '1235-someId'; + this.model.csr = '-----BEGIN CERTIFICATE REQUEST-----...-----END CERTIFICATE REQUEST-----'; + this.model.keyId = '9179de78-1275-a1cf-ebb0-a4eb2e376636'; + this.onComplete = () => {}; + + await render(hbs``, { + owner: this.engine, + }); + assert + .dom('[data-test-alert-banner="alert"]') + .hasText( + 'Next steps Copy the CSR below for a parent issuer to sign and then import the signed certificate back into this mount.', + 'renders Next steps alert banner' + ); + assert + .dom('[data-test-value-div="CSR"] [data-test-masked-input] button') + .hasAttribute('data-clipboard-text', this.model.csr, 'it renders copyable csr'); + assert + .dom('[data-test-value-div="Key ID"] button') + .hasAttribute('data-clipboard-text', this.model.keyId, 'it renders copyable key_id'); + assert.dom('[data-test-value-div="Private key"]').hasText('internal', 'does not render private key'); + assert + .dom('[data-test-value-div="Private key type"]') + .hasText('internal', 'does not render private key type'); + }); }); diff --git a/ui/tests/integration/components/pki-generate-root-test.js b/ui/tests/integration/components/pki/pki-generate-root-test.js similarity index 100% rename from ui/tests/integration/components/pki-generate-root-test.js rename to ui/tests/integration/components/pki/pki-generate-root-test.js diff --git a/ui/tests/integration/components/pki-role-generate-test.js b/ui/tests/integration/components/pki/pki-role-generate-test.js similarity index 100% rename from ui/tests/integration/components/pki-role-generate-test.js rename to ui/tests/integration/components/pki/pki-role-generate-test.js diff --git a/ui/tests/integration/components/pki-sign-intermediate-form-test.js b/ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js similarity index 100% rename from ui/tests/integration/components/pki-sign-intermediate-form-test.js rename to ui/tests/integration/components/pki/pki-sign-intermediate-form-test.js