From ab1f3c8b83dff6d19cb97dce09abb31128f654b9 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:29:05 -0700 Subject: [PATCH] cherry pick (#23264) --- changelog/23260.txt | 3 ++ .../core/addon/components/download-button.js | 4 ++ ui/lib/core/addon/components/masked-input.hbs | 44 ++++++++++++++----- ui/lib/core/addon/components/masked-input.js | 2 + ui/tests/index.html | 25 +++++------ .../components/masked-input-test.js | 12 +++-- ui/tests/pages/components/masked-input.js | 1 + 7 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 changelog/23260.txt diff --git a/changelog/23260.txt b/changelog/23260.txt new file mode 100644 index 000000000..52de9b805 --- /dev/null +++ b/changelog/23260.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Adds warning before downloading KV v2 secret values +``` \ No newline at end of file diff --git a/ui/lib/core/addon/components/download-button.js b/ui/lib/core/addon/components/download-button.js index 49f17f8a5..45d4e3fbf 100644 --- a/ui/lib/core/addon/components/download-button.js +++ b/ui/lib/core/addon/components/download-button.js @@ -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.')); } diff --git a/ui/lib/core/addon/components/masked-input.hbs b/ui/lib/core/addon/components/masked-input.hbs index c08edb9eb..551b9c4d7 100644 --- a/ui/lib/core/addon/components/masked-input.hbs +++ b/ui/lib/core/addon/components/masked-input.hbs @@ -35,15 +35,9 @@ {{/if}} {{#if @allowDownload}} - - - + {{/if}} - \ No newline at end of file + + +{{! CONFIRM DOWNLOAD MODAL }} +{{#if @allowDownload}} + + + + +{{/if}} \ No newline at end of file diff --git a/ui/lib/core/addon/components/masked-input.js b/ui/lib/core/addon/components/masked-input.js index 1729b95d2..ffae45c12 100644 --- a/ui/lib/core/addon/components/masked-input.js +++ b/ui/lib/core/addon/components/masked-input.js @@ -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); diff --git a/ui/tests/index.html b/ui/tests/index.html index b8c5799e9..37974a1f5 100644 --- a/ui/tests/index.html +++ b/ui/tests/index.html @@ -6,29 +6,27 @@ - + Vault Tests - - + + - {{content-for "head"}} - {{content-for "test-head"}} + {{content-for "head"}} {{content-for "test-head"}} - - - + + + - {{content-for "head-footer"}} - {{content-for "test-head-footer"}} + {{content-for "head-footer"}} {{content-for "test-head-footer"}} - {{content-for "body"}} - {{content-for "test-body"}} + {{content-for "body"}} {{content-for "test-body"}}
+
@@ -38,7 +36,6 @@ - {{content-for "body-footer"}} - {{content-for "test-body-footer"}} + {{content-for "body-footer"}} {{content-for "test-body-footer"}} diff --git a/ui/tests/integration/components/masked-input-test.js b/ui/tests/integration/components/masked-input-test.js index e6feff1f6..b425da10a 100644 --- a/ui/tests/integration/components/masked-input-test.js +++ b/ui/tests/integration/components/masked-input-test.js @@ -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``); - assert.ok(component.downloadButtonIsPresent); + await render(hbs` +`); + 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) { diff --git a/ui/tests/pages/components/masked-input.js b/ui/tests/pages/components/masked-input.js index 61ec76b25..45b748f44 100644 --- a/ui/tests/pages/components/masked-input.js +++ b/ui/tests/pages/components/masked-input.js @@ -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"]'), };