Skip to content

Commit

Permalink
Add private key, issuing ca, ca chain to generated cert display
Browse files Browse the repository at this point in the history
  • Loading branch information
hashishaw committed Jul 6, 2023
1 parent d01a3c1 commit 81e1599
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 7 deletions.
4 changes: 2 additions & 2 deletions ui/app/models/pki/certificate/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ export default class PkiCertificateBaseModel extends Model {
@attr('string', { masked: true }) certificate;
@attr('number') expiration;
@attr('string', { label: 'Issuing CA', masked: true }) issuingCa;
@attr('string') privateKey; // only returned for type=exported
@attr('string') privateKeyType; // only returned for type=exported
@attr('string', { masked: true }) privateKey; // only returned for type=exported and /issue
@attr('string') privateKeyType; // only returned for type=exported and /issue
@attr('number', { formatDate: true }) revocationTime;
@attr('string') serialNumber;

Expand Down
13 changes: 12 additions & 1 deletion ui/app/models/pki/certificate/generate.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ const generateFromRole = [
],
},
];
@withFormFields(null, generateFromRole)
// Extra fields returned on the /issue endpoint
const certDisplayFields = [
'certificate',
'commonName',
'revocationTime',
'serialNumber',
'caChain',
'issuingCa',
'privateKey',
'privateKeyType',
];
@withFormFields(certDisplayFields, generateFromRole)
export default class PkiCertificateGenerateModel extends PkiCertificateBaseModel {
getHelpUrl(backend) {
return `/v1/${backend}/issue/example?help=1`;
Expand Down
24 changes: 21 additions & 3 deletions ui/lib/pki/addon/components/page/pki-certificate-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,28 @@
</ToolbarActions>
</Toolbar>

{{#if @model.privateKey}}
<div class="has-top-margin-m">
<Hds::Alert data-test-cert-detail-next-steps @type="inline" @color="highlight" class="has-bottom-margin-s" as |A|>
<A.Title>Next steps</A.Title>
<A.Description>
The
<code>private_key</code>
is only available once. Make sure you copy and save it now.
</A.Description>
</Hds::Alert>
</div>
{{/if}}

{{#each @model.formFields as |field|}}
{{#if (eq field.name "certificate")}}
<InfoTableRow @label="Certificate">
<MaskedInput @value={{@model.certificate}} @displayOnly={{true}} @allowCopy={{true}} />
{{#if field.options.masked}}
<InfoTableRow @label={{or field.options.label (capitalize (humanize (dasherize field.name)))}}>
<MaskedInput
@value={{or (get @model field.name) null}}
@displayOnly={{true}}
@allowCopy={{true}}
@allowDownload={{true}}
/>
</InfoTableRow>
{{else if (eq field.name "serialNumber")}}
<InfoTableRow @label="Serial number">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,25 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function (
},
},
});
store.pushPayload('pki/certificate/generate', {
modelName: 'pki/certificate/generate',
data: {
certificate: '-----BEGIN CERTIFICATE-----',
ca_chain: '-----BEGIN CERTIFICATE-----',
issuer_ca: '-----BEGIN CERTIFICATE-----',
private_key: '-----BEGIN PRIVATE KEY-----',
private_key_type: 'rsa',
common_name: 'example.com Intermediate Authority',
issue_date: 1673540867000,
serial_number: id,
parsed_certificate: {
not_valid_after: 1831220897000,
not_valid_before: 1673540867000,
},
},
});
this.model = store.peekRecord('pki/certificate/base', id);
this.generatedModel = store.peekRecord('pki/certificate/generate', id);

this.server.post('/sys/capabilities-self', () => ({
data: {
Expand All @@ -50,7 +68,7 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function (
}));
});

test('it should render actions and fields', async function (assert) {
test('it should render actions and fields for base cert', async function (assert) {
assert.expect(6);

this.server.post('/pki/revoke', (schema, req) => {
Expand Down Expand Up @@ -90,6 +108,56 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function (
assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed');
});

test('it should render actions and fields for generated cert', async function (assert) {
assert.expect(10);

this.server.post('/pki/revoke', (schema, req) => {
const data = JSON.parse(req.requestBody);
assert.strictEqual(
data.serial_number,
this.model.serialNumber,
'Revoke request made with serial number'
);
return {
data: {
revocation_time: 1673972804,
revocation_time_rfc3339: '2023-01-17T16:26:44.960933411Z',
},
};
});

await render(hbs`<Page::PkiCertificateDetails @model={{this.generatedModel}} />`, { owner: this.engine });
assert.dom('[data-test-cert-detail-next-steps]').exists('Private key next steps warning shows');
assert
.dom('[data-test-component="info-table-row"]')
.exists({ count: 9 }, 'Correct number of fields render when certificate has not been revoked');
assert
.dom('[data-test-value-div="Certificate"] [data-test-masked-input]')
.exists('Masked input renders for certificate');
assert.dom('[data-test-value-div="Serial number"] code').exists('Serial number renders as monospace');
assert
.dom('[data-test-value-div="CA Chain"] [data-test-masked-input]')
.exists('CA Chain shows with masked value');
assert
.dom('[data-test-value-div="Issuing CA"] [data-test-masked-input]')
.exists('Issuing CA shows with masked value');
assert
.dom('[data-test-value-div="Private key"] [data-test-masked-input]')
.exists('Private key shows with masked value');

await click('[data-test-pki-cert-download-button]');
const { serialNumber, certificate } = this.model;
assert.ok(
this.downloadSpy.calledWith(serialNumber.replace(/(\s|:)+/g, '-'), certificate),
'Download pem method called with correct args'
);

await click('[data-test-confirm-action-trigger]');
await click('[data-test-confirm-button]');

assert.dom('[data-test-value-div="Revocation time"]').exists('Revocation time is displayed');
});

test('it should render back button', async function (assert) {
assert.expect(1);

Expand Down

0 comments on commit 81e1599

Please sign in to comment.