Skip to content

Commit

Permalink
UI: Fix copy button for PKI ca_chain certificate card (#25399)
Browse files Browse the repository at this point in the history
* use format-copy-value helper for copy text in Hds::Copy::Button

* delete helper and revert masked input

* update certificate card to format to string

* add test

* add changelog;
  • Loading branch information
hellobontempo authored Feb 14, 2024
1 parent 483da17 commit 2f2e018
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 25 deletions.
3 changes: 3 additions & 0 deletions changelog/25399.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:bug
ui: Fix PKI ca_chain display so value can be copied to clipboard
```
2 changes: 1 addition & 1 deletion ui/app/styles/helper-classes/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
}

.is-medium-width {
width: calc($desktop / 3);
width: calc($desktop / 2.5);
}

.is-medium-height {
Expand Down
6 changes: 3 additions & 3 deletions ui/lib/core/addon/components/certificate-card.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</span>
<div class="has-left-margin-m is-min-width-0 is-flex-grow-1">
<p class="has-text-weight-bold" data-test-certificate-label>
{{this.format}}
{{this.certLabel}}
</p>
<code class="is-size-8 truncate-second-line has-text-grey" data-test-certificate-value>
{{@data}}
Expand All @@ -24,9 +24,9 @@
<Hds::Copy::Button
@text="Copy"
@isIconOnly={{true}}
@textToCopy={{@data}}
@textToCopy={{this.copyValue}}
class="transparent"
data-test-copy-button={{or @data true}}
data-test-copy-button={{or this.copyValue true}}
/>
</div>
</Hds::Card::Container>
29 changes: 20 additions & 9 deletions ui/lib/core/addon/components/certificate-card.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,35 @@ import Component from '@glimmer/component';
* <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)
* @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() {
get certLabel() {
if (!this.args.data) return '';

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

if (value.substring(0, 11) === '-----BEGIN ' || this.args.isPem === true) {
return 'PEM Format';
}
return 'DER Format';
}

get copyValue() {
const { data } = this.args;
if (!data) return data;
const type = Array.isArray(data) ? 'array' : typeof data;
switch (type) {
case 'string':
return data;
case 'array':
return data.join('\n');
case 'object':
// unlikely for certificates but just in case
return JSON.stringify(data);
default:
return data.toString();
}
}
}
2 changes: 1 addition & 1 deletion ui/lib/pki/addon/components/page/pki-issuer-details.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@
@value={{get @issuer attr.name}}
@formatDate={{if attr.options.formatDate "MMM d yyyy HH:mm:ss a zzzz"}}
@alwaysRender={{true}}
@addCopyButton={{(eq attr.name "issuerId")}}
@addCopyButton={{eq attr.name "issuerId"}}
/>
{{/if}}
{{/each}}
Expand Down
43 changes: 32 additions & 11 deletions ui/tests/integration/components/certificate-card-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ 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';
import { rootPem, rootDer } from 'vault/tests/helpers/pki/values';

const SELECTORS = {
label: '[data-test-certificate-label]',
Expand All @@ -31,35 +30,57 @@ module('Integration | Component | certificate-card', function (hooks) {
});

test('it renders with an example PEM Certificate', async function (assert) {
const certificate = rootPem;
this.set('certificate', certificate);
this.certificate = rootPem;
await render(hbs`<CertificateCard @data={{this.certificate}} />`);

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.value).hasText(this.certificate, 'The data rendered is correct');
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
assert
.dom(SELECTORS.copyButton)
.hasAttribute('data-test-copy-button', this.certificate, 'copy value is the same as data');
});

test('it renders with an example DER Certificate', async function (assert) {
const certificate = rootDer;
this.set('certificate', certificate);
this.certificate = rootDer;
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.value).hasText(this.certificate, 'The data rendered is correct');
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
assert
.dom(SELECTORS.copyButton)
.hasAttribute('data-test-copy-button', this.certificate, 'copy value is the same as data');
});

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);
this.certificate = 'example-certificate-text';
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.value).hasText(this.certificate, 'The data rendered is correct');
});

test('it renders with an example CA Chain', async function (assert) {
this.caChain = [
'-----BEGIN CERTIFICATE-----\nMIIDIDCCA...\n-----END CERTIFICATE-----\n',
'-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBA...\n-----END RSA PRIVATE KEY-----\n',
];

await render(hbs`<CertificateCard @data={{this.caChain}} />`);

assert.dom(SELECTORS.label).hasText('PEM Format', 'The label text is PEM Format');
assert.dom(SELECTORS.value).hasText(this.caChain.join(','), 'The data rendered is correct');
assert.dom(SELECTORS.icon).exists('The certificate icon exists');
assert.dom(SELECTORS.copyButton).exists('The copy button exists');
assert
.dom(SELECTORS.copyButton)
.hasAttribute(
'data-test-copy-button',
this.caChain.join('\n'),
'copy value is array converted to a string'
);
});
});

0 comments on commit 2f2e018

Please sign in to comment.