Skip to content

Commit

Permalink
Display CertificateCard instead of MaskedInput for certificates in PKI (
Browse files Browse the repository at this point in the history
#22160)

* replaced each instance of MaskedInput in PKI with CertificateCard

* modify tests for pki-generate-csr

* add test for pki-issuer-details. modify test for pki-certificate-details

* added test for pki-key-details. modified test for pki-sign-intermediate-form

* update 2 test helper files and modify test for pki-issuer-rotate-root

* update test for certificate-card-test.js, update test for the kubernetes configuration-test.js

* modify pki-action-forms-test.js to no longer look for masked input. expand test for pki-issuer-details-test.js to check for all issuer details

* change CertificateCard to show different format types (PEM, DER, nothing) depending on the value provided. update 2 test files to account for this.

* change CertificateCard arg name from @certficateValue to @DaTa to be more inclusive of different uses of CertificateCard (i.e when used for a private key, not a certificate). add description to certificate-card.js

* change naming for attr.options.masked to attr.options.displayCard to reflect the change from MaskedInput to CertificateCard

* add changelog

* change attribute to isCertificate to better fit the title of the component CertificateCard. edit pki-certificate-details.hbs to get rid of extraneous code
  • Loading branch information
malinac02 authored and hellobontempo committed Aug 18, 2023
1 parent c2283ee commit f38f806
Show file tree
Hide file tree
Showing 27 changed files with 216 additions and 106 deletions.
3 changes: 3 additions & 0 deletions changelog/22160.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
ui: display CertificateCard instead of MaskedInput for certificates in PKI
```
8 changes: 4 additions & 4 deletions ui/app/models/pki/action.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ export default class PkiActionModel extends Model {
@attr importedIssuers;
@attr importedKeys;
@attr mapping;
@attr('string', { readOnly: true, masked: true }) certificate;
@attr('string', { readOnly: true, isCertificate: true }) certificate;

/* actionType generate-root */

// readonly attrs returned right after root generation
@attr serialNumber;
@attr('string', { label: 'Issuing CA', readOnly: true, masked: true }) issuingCa;
@attr('string', { label: 'Issuing CA', readOnly: true, isCertificate: true }) issuingCa;
// end of readonly

@attr('string', {
Expand Down Expand Up @@ -212,10 +212,10 @@ export default class PkiActionModel extends Model {
@attr('string', { label: 'Issuer ID', readOnly: true, detailLinkTo: 'issuers.issuer.details' }) issuerId; // returned from generate-root action

// For generating and signing a CSR
@attr('string', { label: 'CSR', masked: true }) csr;
@attr('string', { label: 'CSR', isCertificate: true }) csr;
@attr caChain;
@attr('string', { label: 'Key ID', detailLinkTo: 'keys.key.details' }) keyId;
@attr('string', { masked: true }) privateKey;
@attr('string', { isCertificate: true }) privateKey;
@attr('string') privateKeyType;

get backend() {
Expand Down
8 changes: 4 additions & 4 deletions ui/app/models/pki/certificate/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ export default class PkiCertificateBaseModel extends Model {
otherSans;

// Attrs that come back from API POST request
@attr({ label: 'CA Chain', masked: true }) caChain;
@attr('string', { masked: true }) certificate;
@attr({ label: 'CA Chain', isCertificate: true }) caChain;
@attr('string', { isCertificate: true }) certificate;
@attr('number') expiration;
@attr('string', { label: 'Issuing CA', masked: true }) issuingCa;
@attr('string', { masked: true }) privateKey; // only returned for type=exported and /issue
@attr('string', { label: 'Issuing CA', isCertificate: true }) issuingCa;
@attr('string', { isCertificate: 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
4 changes: 2 additions & 2 deletions ui/app/models/pki/issuer.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export default class PkiIssuerModel extends Model {
@attr isDefault;
@attr('string', { label: 'Issuer ID', detailLinkTo: 'issuers.issuer.details' }) issuerId;
@attr('string', { label: 'Default key ID', detailLinkTo: 'keys.key.details' }) keyId;
@attr({ label: 'CA Chain', masked: true }) caChain;
@attr({ masked: true }) certificate;
@attr({ label: 'CA Chain', isCertificate: true }) caChain;
@attr({ isCertificate: true }) certificate;
@attr('string') serialNumber;

// parsed from certificate contents in serializer (see parse-pki-cert.js)
Expand Down
9 changes: 5 additions & 4 deletions ui/lib/core/addon/components/certificate-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@
@level="mid"
@hasBorder={{true}}
class="is-flex-row has-top-padding-m has-bottom-padding-m is-medium-width"
data-test-certificate-card
>
<span class="has-left-margin-s">
<Icon @name="certificate" @size="24" data-test-certificate-icon />
</span>
<div class="has-left-margin-m is-min-width-0 is-flex-1">
<p class="has-text-weight-bold" data-test-certificate-label>
PEM Format
{{this.format}}
</p>
<code class="is-size-8 truncate-second-line has-text-grey" data-test-certificate-value>
{{@certificateValue}}
{{@data}}
</code>
</div>
<div class="is-flex has-background-white-bis has-side-padding-s has-top-bottom-margin-negative-m">
<CopyButton
data-test-certificate-copy
data-test-copy-button
class="button is-transparent is-flex-v-centered"
@clipboardText={{@certificateValue}}
@clipboardText={{@data}}
@buttonType="button"
@success={{action (set-flash-message "Certificate copied")}}
>
Expand Down
40 changes: 40 additions & 0 deletions ui/lib/core/addon/components/certificate-card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import Component from '@glimmer/component';

/**
* @module CertificateCard
* The CertificateCard component receives data and optionally receives a boolean declaring if that data is meant to be in PEM
* Format. It renders using the <HDS::Card::Container>. To the left there is a certificate icon. In the center there is a label
* which says which format (PEM or DER) the data is in. Below the label is the truncated data. To the right there is a copy
* button to copy the data.
*
* @example
* ```js
* <CertificateCard @data={{value}} @isPem={{true}} />
* ```
* @param {string} data - the data to be displayed in the component (usually in PEM or DER format)
* @param {boolean} isPem - optional argument for if the data is required to be in PEM format (and should thus have the PEM Format label)
*/

export default class CertificateCardComponent extends Component {
// Returns the format the data is in: PEM, DER, or no format if no data is provided
get format() {
if (!this.args.data) return '';

let value;
if (typeof this.args.data === 'object') {
value = this.args.data[0];
} else {
value = this.args.data;
}

if (value.substring(0, 11) === '-----BEGIN ' || this.args.isPem === true) {
return 'PEM Format';
}
return 'DER Format';
}
}
2 changes: 1 addition & 1 deletion ui/lib/kubernetes/addon/components/page/configuration.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<InfoTableRow @label="Kubernetes host" @value={{@config.kubernetesHost}} />
{{#if @config.kubernetesCaCert}}
<InfoTableRow @label="Certificate">
<CertificateCard @certificateValue={{@config.kubernetesCaCert}} />
<CertificateCard @data={{@config.kubernetesCaCert}} @isPem={{true}} />
</InfoTableRow>
{{/if}}
{{else}}
Expand Down
4 changes: 2 additions & 2 deletions ui/lib/pki/addon/components/page/pki-certificate-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
{{/if}}

{{#each @model.formFields as |field|}}
{{#if field.options.masked}}
{{#if field.options.isCertificate}}
<InfoTableRow @label={{or field.options.label (capitalize (humanize (dasherize field.name)))}}>
<MaskedInput @value={{or (get @model field.name) null}} @displayOnly={{true}} @allowCopy={{true}} />
<CertificateCard @data={{get @model field.name}} />
</InfoTableRow>
{{else if (eq field.name "serialNumber")}}
<InfoTableRow @label="Serial number">
Expand Down
9 changes: 2 additions & 7 deletions ui/lib/pki/addon/components/page/pki-issuer-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,9 @@
</h2>
{{/if}}
{{#each fields as |attr|}}
{{#if attr.options.masked}}
{{#if attr.options.isCertificate}}
<InfoTableRow @label={{or attr.options.label (humanize (dasherize attr.name))}} @value={{get @issuer attr.name}}>
<MaskedInput
@name={{or attr.options.label (humanize (dasherize attr.name))}}
@value={{get @issuer attr.name}}
@displayOnly={{true}}
@allowCopy={{true}}
/>
<CertificateCard @data={{get @issuer attr.name}} />
</InfoTableRow>
{{else if (eq attr.name "keyId")}}
<InfoTableRow @label={{or attr.options.label (humanize (dasherize attr.name))}} @value={{get @issuer attr.name}}>
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/pki/addon/components/page/pki-key-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
{{/each}}
{{#if @key.privateKey}}
<InfoTableRow @label="Private key">
<MaskedInput @value={{@key.privateKey}} @name="Private key" @displayOnly={{true}} @allowCopy={{true}} />
<CertificateCard @data={{@key.privateKey}} />
</InfoTableRow>
{{/if}}
</div>
4 changes: 2 additions & 2 deletions ui/lib/pki/addon/components/pki-generate-csr.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
@value={{value}}
@addCopyButton={{eq attr.name "keyId"}}
>
{{#if (and attr.options.masked value)}}
<MaskedInput @value={{value}} @displayOnly={{true}} @allowCopy={{true}} />
{{#if (and attr.options.isCertificate value)}}
<CertificateCard @data={{value}} />
{{else if (eq attr.name "keyId")}}
<LinkTo @route="keys.key.details" @model={{@model.keyId}}>
{{@model.keyId}}
Expand Down
6 changes: 3 additions & 3 deletions ui/lib/pki/addon/components/pki-generate-root.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
>
<LinkTo @route={{attr.options.detailLinkTo}} @model={{get @model attr.name}}>{{get @model attr.name}}</LinkTo>
</InfoTableRow>
{{else if attr.options.masked}}
{{else if attr.options.isCertificate}}
<InfoTableRow
@label={{capitalize (or attr.options.detailsLabel attr.options.label (humanize (dasherize attr.name)))}}
>
<MaskedInput @value={{get @model attr.name}} @displayOnly={{true}} @allowCopy={{true}} />
<CertificateCard @data={{get @model attr.name}} />
</InfoTableRow>
{{else}}
<InfoTableRow
Expand All @@ -27,7 +27,7 @@
{{/each}}
<InfoTableRow @label="Private key">
{{#if @model.privateKey}}
<MaskedInput @value={{@model.privateKey}} @displayOnly={{true}} @allowCopy={{true}} />
<CertificateCard @data={{@model.privateKey}} />
{{else}}
<span class="tag">internal</span>
{{/if}}
Expand Down
4 changes: 2 additions & 2 deletions ui/lib/pki/addon/components/pki-info-table-rows.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
@value={{value}}
@addCopyButton={{or (eq attr.name "issuerId") (eq attr.name "keyId")}}
>
{{#if (and attr.options.masked value)}}
<MaskedInput @value={{value}} @displayOnly={{true}} @allowCopy={{true}} />
{{#if (and attr.options.isCertificate value)}}
<CertificateCard @data={{value}} />
{{else if attr.options.detailLinkTo}}
<LinkTo @route={{attr.options.detailLinkTo}} @model={{value}}>{{value}}</LinkTo>
{{else if (or (eq attr.name "privateKey") (eq attr.name "privateKeyType"))}}
Expand Down
4 changes: 2 additions & 2 deletions ui/lib/pki/addon/components/pki-sign-intermediate-form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
{{#each this.showFields as |fieldName|}}
{{#let (find-by "name" fieldName @model.allFields) as |attr|}}
<InfoTableRow @label={{or attr.options.label (humanize (dasherize attr.name))}} @value={{get @model attr.name}}>
{{#if (and attr.options.masked (get @model attr.name))}}
<MaskedInput @value={{get @model attr.name}} @displayOnly={{true}} @allowCopy={{true}} />
{{#if (and attr.options.isCertificate (get @model attr.name))}}
<CertificateCard @data={{get @model attr.name}} />
{{else if (eq attr.name "serialNumber")}}
<LinkTo
@route="certificates.certificate.details"
Expand Down
14 changes: 4 additions & 10 deletions ui/tests/acceptance/pki/pki-action-forms-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ module('Acceptance | pki action forms test', function (hooks) {
assert.dom(S.configuration.title).hasText('View Root Certificate');
assert.dom(S.configuration.nextStepsBanner).doesNotExist('no private key warning');
assert.dom(S.configuration.title).hasText('View Root Certificate', 'Updates title on page');
assert.dom(S.configuration.saved.certificate).hasClass('allow-copy', 'copyable certificate is masked');
assert.dom(S.configuration.saved.certificate).exists('Copyable certificate exists');
assert.dom(S.configuration.saved.issuerName).hasText(issuerName);
assert.dom(S.configuration.saved.issuerLink).exists('Issuer link exists');
assert.dom(S.configuration.saved.keyLink).exists('Key link exists');
Expand Down Expand Up @@ -230,17 +230,13 @@ module('Acceptance | pki action forms test', function (hooks) {
.dom(S.configuration.nextStepsBanner)
.hasText('Next steps The private_key is only available once. Make sure you copy and save it now.');
assert.dom(S.configuration.title).hasText('View Root Certificate', 'Updates title on page');
assert
.dom(S.configuration.saved.certificate)
.hasClass('allow-copy', 'copyable masked certificate exists');
assert.dom(S.configuration.saved.certificate).exists('Copyable certificate exists');
assert
.dom(S.configuration.saved.issuerName)
.doesNotExist('Issuer name not shown because it was not named');
assert.dom(S.configuration.saved.issuerLink).exists('Issuer link exists');
assert.dom(S.configuration.saved.keyLink).exists('Key link exists');
assert
.dom(S.configuration.saved.privateKey)
.hasClass('allow-copy', 'copyable masked private key exists');
assert.dom(S.configuration.saved.privateKey).exists('Copyable private key exists');
assert.dom(S.configuration.saved.keyName).doesNotExist('Key name not shown because it was not named');
assert.dom('[data-test-done]').exists('Done button exists');
// Check that linked issuer has correct common name
Expand Down Expand Up @@ -283,9 +279,7 @@ module('Acceptance | pki action forms test', function (hooks) {
.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.'
);
assert
.dom(S.configuration.saved.privateKey)
.hasClass('allow-copy', 'copyable masked private key exists');
assert.dom(S.configuration.saved.privateKey).exists('Copyable private key exists');
await click('[data-test-done]');
assert.strictEqual(
currentURL(),
Expand Down
10 changes: 5 additions & 5 deletions ui/tests/helpers/pki.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
*/

export const SELECTORS = {
caChain: '[data-test-value-div="CA chain"] [data-test-masked-input]',
certificate: '[data-test-value-div="Certificate"] [data-test-masked-input]',
caChain: '[data-test-value-div="CA chain"] [data-test-certificate-card]',
certificate: '[data-test-value-div="Certificate"] [data-test-certificate-card]',
commonName: '[data-test-row-value="Common name"]',
csr: '[data-test-value-div="CSR"] [data-test-masked-input]',
csr: '[data-test-value-div="CSR"] [data-test-certificate-card]',
expiryDate: '[data-test-row-value="Expiration date"]',
issueDate: '[data-test-row-value="Issue date"]',
issuingCa: '[data-test-value-div="Issuing CA"] [data-test-masked-input]',
privateKey: '[data-test-value-div="Private key"] [data-test-masked-input]',
issuingCa: '[data-test-value-div="Issuing CA"] [data-test-certificate-card]',
privateKey: '[data-test-value-div="Private key"] [data-test-certificate-card]',
revocationTime: '[data-test-row-value="Revocation time"]',
serialNumber: '[data-test-row-value="Serial number"]',
};
4 changes: 2 additions & 2 deletions ui/tests/helpers/pki/pki-generate-root.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ export const SELECTORS = {
urlField: '[data-test-urls-section] [data-test-input]',
// Shown values after save
saved: {
certificate: '[data-test-value-div="Certificate"] [data-test-masked-input]',
certificate: '[data-test-value-div="Certificate"] [data-test-certificate-card]',
commonName: '[data-test-row-value="Common name"]',
issuerName: '[data-test-row-value="Issuer name"]',
issuerLink: '[data-test-value-div="Issuer ID"] a',
keyName: '[data-test-row-value="Key name"]',
keyLink: '[data-test-value-div="Key ID"] a',
privateKey: '[data-test-value-div="Private key"] [data-test-masked-input]',
privateKey: '[data-test-value-div="Private key"] [data-test-certificate-card]',
serialNumber: '[data-test-row-value="Serial number"]',
},
};
1 change: 1 addition & 0 deletions ui/tests/helpers/pki/pki-issuer-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

export const SELECTORS = {
configure: '[data-test-pki-issuer-configure]',
copyButtonByName: (name) => `[data-test-value-div="${name}"] [data-test-copy-button]`,
crossSign: '[data-test-pki-issuer-cross-sign]',
defaultGroup: '[data-test-details-group="default"]',
download: '[data-test-issuer-download]',
Expand Down
19 changes: 19 additions & 0 deletions ui/tests/helpers/pki/values.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f38f806

Please sign in to comment.