mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 01:32:33 +00:00
Display CertificateCard instead of MaskedInput for certificates in PKI (#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
This commit is contained in:
3
changelog/22160.txt
Normal file
3
changelog/22160.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
```release-note:improvement
|
||||
ui: display CertificateCard instead of MaskedInput for certificates in PKI
|
||||
```
|
||||
@@ -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', {
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")}}
|
||||
>
|
||||
|
||||
40
ui/lib/core/addon/components/certificate-card.js
Normal file
40
ui/lib/core/addon/components/certificate-card.js
Normal file
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -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}}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}}>
|
||||
|
||||
@@ -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>
|
||||
@@ -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}}
|
||||
|
||||
@@ -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
|
||||
@@ -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}}
|
||||
|
||||
@@ -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"))}}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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');
|
||||
@@ -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
|
||||
@@ -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(),
|
||||
|
||||
@@ -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"]',
|
||||
};
|
||||
|
||||
@@ -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"]',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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]',
|
||||
|
||||
@@ -26,6 +26,25 @@ o8I9DD+uBHknwByRLXSDmgggwgOYsyTg/IfYoHlLHDD3CaOpkCvUCZvM9bI7nrlx
|
||||
DU3c2oZTc0mPYGft6U8mVwLqfYTcEduGidTLAQPE5w==
|
||||
-----END CERTIFICATE-----`;
|
||||
|
||||
export const rootDer = `MIIDJjCCAg6gAwIBAgIUZwx170kTAaGFKeyiG3Di
|
||||
GpwhKvcwDQYJKoZIhvcNAQELBQAwETEPMA0GA1UEAxMGMTExMTExMB4XDTIzMDgw
|
||||
OTIxMzk0NloXDTIzMDkxMDIxNDAxNlowETEPMA0GA1UEAxMGMTExMTExMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3Hm1gjKWDdXuRLZIk3dDabbzlH+Y
|
||||
2e4rklkMGlrnNqju2+7iIGZa2q8rQ4jEZ3sesSsqGHUEJ2sIG5HnRhl5yawCr9NS
|
||||
uJP+3zsNueQLQDj6tEnuN0STZQuEJKc+yeept8JGAD0SGnB+THGUYf3if0D8sDT1
|
||||
nHj3XihtnTG3fN62iKyx2Y95WKrVmT1MnpGjbp4HkRvrHSR8PKyq9Q6YyZkIYbfW
|
||||
DH3adq6gmiJITzozaUT6efftPOVPr5LLTPKAl3BAmoc8ypM/H1IPaE1Z7ef9lV9w
|
||||
gazvoJZEsc59hskTWF3ZLcWIxAjcq7u6IX2+dU/A0DmCY6GKmmcZ9W5A9wIDAQAB
|
||||
o3YwdDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
|
||||
b2qEtlDZl/ws00ftFQJX6bjoOckwHwYDVR0jBBgwFoAUb2qEtlDZl/ws00ftFQJX
|
||||
6bjoOckwEQYDVR0RBAowCIIGMTExMTExMA0GCSqGSIb3DQEBCwUAA4IBAQAFI1H8
|
||||
EOw+YcequlJp1ucCpTRArLUhH0t+l7hQAqwORGQevEP6Ml63dRrZCcke7esrpnL9
|
||||
7ijKw/PjgoyrM4QS3wAYm8nDm7cZH+f//A2X6WFnvozwKdmDRkacEjMOAe/XU+qh
|
||||
jdtiETEnUGVH65ulyimKitU5SHV0GNfToKnU/SFBks0bQvglIii0YwgHvSoW1++7
|
||||
arCjfZqWLdRe7MHfrLpLr4gaebfxSrZfn3utgm+DsJVba3B9JnOZO+yzTiEw6UkJ
|
||||
rcmZDy0x1/OaCcYHKai4RegsiQ0QrIEI+iC1N6U0PGiGf/V23eoTR0+5H6qngDz2
|
||||
GzXrbHFAPQbtweCf`;
|
||||
|
||||
export const issuerPemBundle = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDRTCCAi2gAwIBAgIUdKagCL6TnN5xLkwhPbNY8JEcY0YwDQYJKoZIhvcNAQEL
|
||||
|
||||
@@ -7,51 +7,58 @@ import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { rootPem } from 'vault/tests/helpers/pki/values';
|
||||
import { rootDer } from 'vault/tests/helpers/pki/values';
|
||||
|
||||
const SELECTORS = {
|
||||
label: '[data-test-certificate-label]',
|
||||
value: '[data-test-certificate-value]',
|
||||
icon: '[data-test-certificate-icon]',
|
||||
copyButton: '[data-test-copy-button]',
|
||||
};
|
||||
|
||||
module('Integration | Component | certificate-card', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders without a certificate value', async function (assert) {
|
||||
test('it renders', async function (assert) {
|
||||
await render(hbs`<CertificateCard />`);
|
||||
|
||||
assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is correct');
|
||||
assert.dom(SELECTORS.value).hasNoText('The is no value for the certificate');
|
||||
assert.dom(SELECTORS.label).hasNoText('There is no label because there is no value');
|
||||
assert.dom(SELECTORS.value).hasNoText('There is no value because none was provided');
|
||||
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
|
||||
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
|
||||
});
|
||||
|
||||
test('it renders with a small example value for certificate ', async function (assert) {
|
||||
await render(hbs`<CertificateCard @certificateValue="test"/>`);
|
||||
|
||||
assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is correct');
|
||||
assert.dom(SELECTORS.value).hasText('test', 'The value for the certificate is correct');
|
||||
});
|
||||
|
||||
test('it renders with an example Kubernetes CA Certificate', async function (assert) {
|
||||
const certificate = `
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
|
||||
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
|
||||
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
|
||||
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
|
||||
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
|
||||
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
|
||||
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
|
||||
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
|
||||
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
|
||||
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
|
||||
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
|
||||
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
|
||||
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
|
||||
-----END CERTIFICATE-----
|
||||
`;
|
||||
test('it renders with an example PEM Certificate', async function (assert) {
|
||||
const certificate = rootPem;
|
||||
this.set('certificate', certificate);
|
||||
await render(hbs`<CertificateCard @certificateValue={{this.certificate}}/>`);
|
||||
await render(hbs`<CertificateCard @data={{this.certificate}} />`);
|
||||
|
||||
assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is correct');
|
||||
assert.dom(SELECTORS.value).hasText(certificate, 'The value for the CA Certificate is correct');
|
||||
assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is PEM Format');
|
||||
assert.dom(SELECTORS.value).hasText(certificate, 'The data rendered is correct');
|
||||
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
|
||||
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
|
||||
});
|
||||
|
||||
test('it renders with an example DER Certificate', async function (assert) {
|
||||
const certificate = rootDer;
|
||||
this.set('certificate', certificate);
|
||||
await render(hbs`<CertificateCard @data={{this.certificate}} />`);
|
||||
|
||||
assert.dom(SELECTORS.label).hasText('DER Format', 'The label text is DER Format');
|
||||
assert.dom(SELECTORS.value).hasText(certificate, 'The data rendered is correct');
|
||||
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
|
||||
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
|
||||
});
|
||||
|
||||
test('it renders with the PEM Format label regardless of the value provided when @isPem is true', async function (assert) {
|
||||
const certificate = 'example-certificate-text';
|
||||
this.set('certificate', certificate);
|
||||
await render(hbs`<CertificateCard @data={{this.certificate}} @isPem={{true}}/>`);
|
||||
|
||||
assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is PEM Format');
|
||||
assert.dom(SELECTORS.value).hasText(certificate, 'The data rendered is correct');
|
||||
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
|
||||
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,14 +87,18 @@ module('Integration | Component | kubernetes | Page::Configuration', function (h
|
||||
assert
|
||||
.dom('[data-test-row-value="Kubernetes host"]')
|
||||
.hasText(this.config.kubernetesHost, 'Kubernetes host value renders');
|
||||
|
||||
assert.dom('[data-test-row-label="Certificate"]').exists('Certificate label renders');
|
||||
assert.dom('[data-test-certificate-card]').exists('Certificate card component renders');
|
||||
assert
|
||||
.dom('[data-test-certificate-icon]')
|
||||
.hasClass('flight-icon-certificate', 'Certificate card icon renders');
|
||||
assert.dom('[data-test-certificate-label]').hasText('PEM Format', 'Certificate card label renders');
|
||||
.hasClass('flight-icon-certificate', 'Certificate icon renders');
|
||||
assert
|
||||
.dom('[data-test-certificate-card] [data-test-copy-button]')
|
||||
.exists('Certificate copy button renders');
|
||||
assert.dom('[data-test-certificate-label]').hasText('PEM Format', 'Certificate label renders');
|
||||
assert
|
||||
.dom('[data-test-certificate-value]')
|
||||
.hasText(this.config.kubernetesCaCert, 'Certificate card value renders');
|
||||
assert.dom('[data-test-certificate-copy]').exists('Certificate copy button renders');
|
||||
.hasText(this.config.kubernetesCaCert, 'Certificate value renders');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -91,8 +91,8 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function (
|
||||
.dom('[data-test-component="info-table-row"]')
|
||||
.exists({ count: 5 }, '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');
|
||||
.dom('[data-test-value-div="Certificate"] [data-test-certificate-card]')
|
||||
.exists('Certificate card renders for certificate');
|
||||
assert.dom('[data-test-value-div="Serial number"] code').exists('Serial number renders as monospace');
|
||||
|
||||
await click('[data-test-pki-cert-download-button]');
|
||||
@@ -132,18 +132,18 @@ module('Integration | Component | pki | Page::PkiCertificateDetails', function (
|
||||
.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');
|
||||
.dom('[data-test-value-div="Certificate"] [data-test-certificate-card]')
|
||||
.exists('Certificate card 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');
|
||||
.dom('[data-test-value-div="CA Chain"] [data-test-certificate-card]')
|
||||
.exists('Certificate card renders for CA Chain');
|
||||
assert
|
||||
.dom('[data-test-value-div="Issuing CA"] [data-test-masked-input]')
|
||||
.exists('Issuing CA shows with masked value');
|
||||
.dom('[data-test-value-div="Issuing CA"] [data-test-certificate-card]')
|
||||
.exists('Certificate card renders for Issuing CA');
|
||||
assert
|
||||
.dom('[data-test-value-div="Private key"] [data-test-masked-input]')
|
||||
.exists('Private key shows with masked value');
|
||||
.dom('[data-test-value-div="Private key"] [data-test-certificate-card]')
|
||||
.exists('Certificate card renders for private key');
|
||||
|
||||
await click('[data-test-pki-cert-download-button]');
|
||||
const { serialNumber, certificate } = this.model;
|
||||
|
||||
@@ -206,7 +206,7 @@ module('Integration | Component | page/pki-issuer-rotate-root', function (hooks)
|
||||
assert.dom(SELECTORS.infoRowValue('Certificate')).exists();
|
||||
assert.dom(SELECTORS.infoRowValue('Issuer name')).exists();
|
||||
assert.dom(SELECTORS.infoRowValue('Issuing CA')).exists();
|
||||
assert.dom(`${SELECTORS.infoRowValue('Private key')} .masked-input`).hasClass('allow-copy');
|
||||
assert.dom(SELECTORS.infoRowValue('Private key')).exists();
|
||||
assert.dom(`${SELECTORS.infoRowValue('Private key type')} span`).hasText('rsa');
|
||||
assert.dom(SELECTORS.infoRowValue('Serial number')).hasText(this.returnedData.serial_number);
|
||||
assert.dom(SELECTORS.infoRowValue('Key ID')).hasText(this.returnedData.key_id);
|
||||
|
||||
@@ -75,4 +75,20 @@ module('Integration | Component | pki key details page', function (hooks) {
|
||||
assert.dom(SELECTORS.keyDeleteButton).doesNotExist('does not render delete button if no permission');
|
||||
assert.dom(SELECTORS.keyEditLink).doesNotExist('does not render edit button if no permission');
|
||||
});
|
||||
|
||||
test('it renders the private key as a <CertificateCard> component when there is a private key', async function (assert) {
|
||||
this.model.privateKey = 'private-key-value';
|
||||
await render(
|
||||
hbs`
|
||||
<Page::PkiKeyDetails
|
||||
@key={{this.model}}
|
||||
@canDelete={{false}}
|
||||
@canEdit={{false}}
|
||||
/>
|
||||
`,
|
||||
{ owner: this.engine }
|
||||
);
|
||||
|
||||
assert.dom('[data-test-certificate-card]').exists('Certificate card renders for the private key');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,13 +113,13 @@ module('Integration | Component | pki-generate-csr', function (hooks) {
|
||||
'renders Next steps alert banner'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-value-div="CSR"] [data-test-masked-input] button')
|
||||
.dom('[data-test-value-div="CSR"] [data-test-certificate-card] 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')
|
||||
.dom('[data-test-value-div="Private key"] [data-test-certificate-card] button')
|
||||
.hasAttribute('data-clipboard-text', this.model.privateKey, 'it renders copyable private_key');
|
||||
assert
|
||||
.dom('[data-test-value-div="Private key type"]')
|
||||
@@ -144,7 +144,7 @@ module('Integration | Component | pki-generate-csr', function (hooks) {
|
||||
'renders Next steps alert banner'
|
||||
);
|
||||
assert
|
||||
.dom('[data-test-value-div="CSR"] [data-test-masked-input] button')
|
||||
.dom('[data-test-value-div="CSR"] [data-test-certificate-card] button')
|
||||
.hasAttribute('data-clipboard-text', this.model.csr, 'it renders copyable csr');
|
||||
assert
|
||||
.dom('[data-test-value-div="Key ID"] button')
|
||||
|
||||
@@ -81,6 +81,36 @@ module('Integration | Component | page/pki-issuer-details', function (hooks) {
|
||||
assert.dom(SELECTORS.configure).doesNotExist();
|
||||
});
|
||||
|
||||
test('it renders correct details by default', async function (assert) {
|
||||
await render(
|
||||
hbs`
|
||||
<Page::PkiIssuerDetails @issuer={{this.issuer}} />
|
||||
<div id="modal-wormhole"></div>
|
||||
`,
|
||||
this.context
|
||||
);
|
||||
|
||||
// Default group details:
|
||||
assert.dom(SELECTORS.defaultGroup).exists('Default group of details exists');
|
||||
assert.dom(SELECTORS.valueByName('Certificate')).exists('Certificate detail exists');
|
||||
assert.dom(SELECTORS.copyButtonByName('Certificate')).exists('Certificate is copyable');
|
||||
assert.dom(SELECTORS.valueByName('CA Chain')).exists('CA Chain detail exists');
|
||||
assert.dom(SELECTORS.copyButtonByName('CA Chain')).exists('CA Chain is copyable');
|
||||
assert.dom(SELECTORS.valueByName('Common name')).exists('Common name detail exists');
|
||||
assert.dom(SELECTORS.valueByName('Issuer name')).exists('Issuer name detail exists');
|
||||
assert.dom(SELECTORS.valueByName('Issuer ID')).exists('Issuer ID detail exists');
|
||||
assert.dom(SELECTORS.copyButtonByName('Issuer ID')).exists('Issuer ID is copyable');
|
||||
assert.dom(SELECTORS.valueByName('Default key ID')).exists('Default key ID detail exists');
|
||||
|
||||
// Issuer URLs group details:
|
||||
assert.dom(SELECTORS.urlsGroup).exists('Issuer URLs group of details exists');
|
||||
assert.dom(SELECTORS.valueByName('Issuing certificates')).exists('Issuing certificates detail exists');
|
||||
assert
|
||||
.dom(SELECTORS.valueByName('CRL distribution points'))
|
||||
.exists('CRL distribution points detail exists');
|
||||
assert.dom(SELECTORS.valueByName('OCSP servers')).exists('OCSP servers detail exists');
|
||||
});
|
||||
|
||||
test('it renders parsing error banner if issuer certificate contains unsupported OIDs', async function (assert) {
|
||||
this.issuer.parsedCertificate = {
|
||||
common_name: 'fancy-cert-unsupported-subj-and-ext-oids',
|
||||
|
||||
@@ -71,9 +71,9 @@ module('Integration | Component | pki-sign-intermediate-form', function (hooks)
|
||||
request_id: 'some-id',
|
||||
data: {
|
||||
serial_number: '31:52:b9:09:40',
|
||||
ca_chain: ['-----root pem------'],
|
||||
issuing_ca: '-----issuing ca------',
|
||||
certificate: '-----certificate------',
|
||||
ca_chain: ['-----BEGIN CERTIFICATE-----'],
|
||||
issuing_ca: '-----BEGIN CERTIFICATE-----',
|
||||
certificate: '-----BEGIN CERTIFICATE-----',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -86,13 +86,13 @@ module('Integration | Component | pki-sign-intermediate-form', function (hooks)
|
||||
await click(selectors.saveButton);
|
||||
[
|
||||
{ label: 'Serial number' },
|
||||
{ label: 'CA Chain', masked: true },
|
||||
{ label: 'Certificate', masked: true },
|
||||
{ label: 'Issuing CA', masked: true },
|
||||
].forEach(({ label, masked }) => {
|
||||
{ label: 'CA Chain', isCertificate: true },
|
||||
{ label: 'Certificate', isCertificate: true },
|
||||
{ label: 'Issuing CA', isCertificate: true },
|
||||
].forEach(({ label, isCertificate }) => {
|
||||
assert.dom(selectors.rowByName(label)).exists();
|
||||
if (masked) {
|
||||
assert.dom(selectors.valueByName(label)).hasText('***********', `${label} is masked`);
|
||||
if (isCertificate) {
|
||||
assert.dom(selectors.valueByName(label)).includesText('PEM Format', `${label} is isCertificate`);
|
||||
} else {
|
||||
assert.dom(selectors.valueByName(label)).hasText('31:52:b9:09:40', `Renders ${label}`);
|
||||
assert.dom(`${selectors.valueByName(label)} a`).exists(`${label} is a link`);
|
||||
|
||||
Reference in New Issue
Block a user