cherry pick (#23264)

This commit is contained in:
claire bontempo 2023-09-22 14:29:05 -07:00 committed by GitHub
parent 36452c0849
commit ab1f3c8b83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 27 deletions

3
changelog/23260.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
ui: Adds warning before downloading KV v2 secret values
```

View File

@ -34,6 +34,7 @@ import { assert } from '@ember/debug';
* @param {function} [fetchData] - function that fetches data and returns download content
* @param {string} [extension='txt'] - file extension, the download service uses this to determine the mimetype
* @param {boolean} [stringify=false] - argument to stringify the data before passing to the File constructor
* @param {callback} [onSuccess] - callback from parent to invoke if download is successful
*/
export default class DownloadButton extends Component {
@ -73,6 +74,9 @@ export default class DownloadButton extends Component {
try {
this.download.miscExtension(this.filename, this.content, this.extension);
this.flashMessages.info(`Downloading ${this.filename}`);
if (this.args.onSuccess) {
this.args.onSuccess();
}
} catch (error) {
this.flashMessages.danger(errorMessage(error, 'There was a problem downloading. Please try again.'));
}

View File

@ -35,15 +35,9 @@
</CopyButton>
{{/if}}
{{#if @allowDownload}}
<DownloadButton
class="button download-button"
@filename={{or @name "secret-value"}}
@data={{@value}}
@stringify={{true}}
aria-label="Download secret value"
>
<Icon @name="download" />
</DownloadButton>
<button type="button" class="button download-button" {{on "click" (fn (mut this.modalOpen) true)}}>
<Icon data-test-download-icon @name="download" />
</button>
{{/if}}
<button
onclick={{this.toggleMask}}
@ -55,4 +49,34 @@
>
<Icon @name={{if this.showValue "eye" "eye-off"}} />
</button>
</div>
</div>
{{! CONFIRM DOWNLOAD MODAL }}
{{#if @allowDownload}}
<Modal
@title="Download secret value?"
@onClose={{action (mut this.modalOpen) false}}
@isActive={{this.modalOpen}}
@type="warning"
>
<section class="modal-card-body">
This download is
<strong>unencrypted</strong>. Are you sure you want to download this secret data as plaintext?
</section>
<footer class="modal-card-foot modal-card-foot-outlined">
<DownloadButton
class="button is-primary"
@filename={{or @name "secret-value"}}
@data={{@value}}
@stringify={{true}}
aria-label="Download secret value"
@onSuccess={{fn (mut this.modalOpen) false}}
>
Download
</DownloadButton>
<button type="button" class="button is-secondary" {{on "click" (fn (mut this.modalOpen) false)}}>
Cancel
</button>
</footer>
</Modal>
{{/if}}

View File

@ -27,12 +27,14 @@ import autosize from 'autosize';
* @param name {String} - The key correlated to the value. Used for the download file name.
* @param [onChange=Callback] {Function|action} - Callback triggered on change, sends new value. Must set the value of @value
* @param [allowCopy=false] {bool} - Whether or not the input should render with a copy button.
* @param [allowDownload=false] {bool} - Renders a download button that prompts a confirmation modal to download the secret value
* @param [displayOnly=false] {bool} - Whether or not to display the value as a display only `pre` element or as an input.
*
*/
export default class MaskedInputComponent extends Component {
textareaId = 'textarea-' + guidFor(this);
@tracked showValue = false;
@tracked modalOpen = false;
constructor() {
super(...arguments);

View File

@ -6,29 +6,27 @@
<html>
<head>
<meta charset="utf-8">
<meta charset="utf-8" />
<title>Vault Tests</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{{content-for "head"}}
{{content-for "test-head"}}
{{content-for "head"}} {{content-for "test-head"}}
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css">
<link rel="stylesheet" href="{{rootURL}}assets/vault.css">
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css">
<link rel="stylesheet" href="{{rootURL}}assets/vendor.css" />
<link rel="stylesheet" href="{{rootURL}}assets/vault.css" />
<link rel="stylesheet" href="{{rootURL}}assets/test-support.css" />
{{content-for "head-footer"}}
{{content-for "test-head-footer"}}
{{content-for "head-footer"}} {{content-for "test-head-footer"}}
</head>
<body>
{{content-for "body"}}
{{content-for "test-body"}}
{{content-for "body"}} {{content-for "test-body"}}
<div id="qunit"></div>
<div id="qunit-fixture">
<div id="ember-testing-container">
<div id="ember-testing"></div>
<div id="modal-wormhole"></div>
</div>
</div>
@ -38,7 +36,6 @@
<script src="{{rootURL}}assets/vault.js"></script>
<script src="{{rootURL}}assets/tests.js"></script>
{{content-for "body-footer"}}
{{content-for "test-body-footer"}}
{{content-for "body-footer"}} {{content-for "test-body-footer"}}
</body>
</html>

View File

@ -5,7 +5,7 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, focus, triggerKeyEvent, typeIn, fillIn } from '@ember/test-helpers';
import { render, focus, triggerKeyEvent, typeIn, fillIn, click } from '@ember/test-helpers';
import { create } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
@ -44,8 +44,14 @@ module('Integration | Component | masked input', function (hooks) {
});
test('it renders a download button when allowDownload is true', async function (assert) {
await render(hbs`<MaskedInput @allowDownload={{true}} />`);
assert.ok(component.downloadButtonIsPresent);
await render(hbs`<MaskedInput @allowDownload={{true}} /> <div id="modal-wormhole"></div>
`);
assert.ok(component.downloadIconIsPresent);
await click('[data-test-download-icon]');
assert.ok(component.downloadButtonIsPresent, 'clicking download icon opens modal with download button');
assert;
});
test('it shortens all outputs when displayOnly and masked', async function (assert) {

View File

@ -8,6 +8,7 @@ import { clickable, isPresent } from 'ember-cli-page-object';
export default {
textareaIsPresent: isPresent('[data-test-textarea]'),
copyButtonIsPresent: isPresent('[data-test-copy-button]'),
downloadIconIsPresent: isPresent('[data-test-download-icon]'),
downloadButtonIsPresent: isPresent('[data-test-download-button]'),
toggleMasked: clickable('[data-test-button="toggle-masked"]'),
};