diff --git a/changelog/11284.txt b/changelog/11284.txt new file mode 100644 index 000000000..8c0332a3b --- /dev/null +++ b/changelog/11284.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Obscure secret values on input and displayOnly fields like certificates. +``` \ No newline at end of file diff --git a/ui/app/components/text-file.js b/ui/app/components/text-file.js index e1d72a078..0f82d11cb 100644 --- a/ui/app/components/text-file.js +++ b/ui/app/components/text-file.js @@ -1,84 +1,84 @@ -import Component from '@ember/component'; +import Component from '@glimmer/component'; import { set } from '@ember/object'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { guidFor } from '@ember/object/internals'; -export default Component.extend({ - 'data-test-component': 'text-file', - classNames: ['box', 'is-fullwidth', 'is-marginless', 'is-shadowless'], - classNameBindings: ['inputOnly:is-paddingless'], +/** + * @module TextFile + * `TextFile` components are file upload components where you can either toggle to upload a file or enter text. + * + * @example + * + * + * @param [inputOnly] {bool} - When true, only the file input will be rendered + * @param [helpText] {string} - Text underneath label. + * @param file {object} - * Object in the shape of: + * { + * value: 'file contents here', + * fileName: 'nameOfFile.txt', + * enterAsText: boolean ability to enter as text + * } + * @param [onChange=Function.prototype] {Function|action} - A function to call when the value of the input changes. + * @param [label=null] {string} - Text to use as the label for the file input. If null, a default will be rendered. + */ - /* - * @public - * @param Object - * Object in the shape of: - * { - * value: 'file contents here', - * fileName: 'nameOfFile.txt', - * enterAsText: bool - * } - */ - file: null, +export default class TextFile extends Component { + fileHelpText = 'Select a file from your computer'; + textareaHelpText = 'Enter the value as text'; + elementId = guidFor(this); + index = ''; - index: null, - onChange: () => {}, + @tracked file = null; + @tracked showValue = false; - /* - * @public - * @param Boolean - * When true, only the file input will be rendered - */ - inputOnly: false, - - /* - * @public - * @param String - * Text to use as the label for the file input - * If null, a default will be rendered - */ - label: null, - - /* - * @public - * @param String - * Text to use as help under the file input - * If null, a default will be rendered - */ - fileHelpText: 'Select a file from your computer', - - /* - * @public - * @param String - * Text to use as help under the textarea in text-input mode - * If null, a default will be rendered - */ - textareaHelpText: 'Enter the value as text', + get inputOnly() { + return this.args.inputOnly || false; + } + get label() { + return this.args.label || null; + } readFile(file) { const reader = new FileReader(); reader.onload = () => this.setFile(reader.result, file.name); reader.readAsText(file); - }, + } setFile(contents, filename) { - this.onChange(this.index, { value: contents, fileName: filename }); - }, + this.args.onChange(this.index, { value: contents, fileName: filename }); + } - actions: { - pickedFile(e) { - const { files } = e.target; - if (!files.length) { - return; - } - for (let i = 0, len = files.length; i < len; i++) { - this.readFile(files[i]); - } - }, - updateData(e) { - const file = this.file; - set(file, 'value', e.target.value); - this.onChange(this.index, this.file); - }, - clearFile() { - this.onChange(this.index, { value: '' }); - }, - }, -}); + @action + pickedFile(e) { + e.preventDefault(); + const { files } = e.target; + if (!files.length) { + return; + } + for (let i = 0, len = files.length; i < len; i++) { + this.readFile(files[i]); + } + } + @action + updateData(e) { + e.preventDefault(); + let file = this.args.file; + set(file, 'value', e.target.value); + this.args.onChange(this.index, file); + } + @action + clearFile() { + this.args.onChange(this.index, { value: '' }); + } + @action + toggleMask() { + this.showValue = !this.showValue; + } +} diff --git a/ui/app/models/auth-config/ldap.js b/ui/app/models/auth-config/ldap.js index d28130f22..18b801474 100644 --- a/ui/app/models/auth-config/ldap.js +++ b/ui/app/models/auth-config/ldap.js @@ -9,7 +9,7 @@ export default AuthConfig.extend({ useOpenAPI: true, certificate: attr({ label: 'Certificate', - editType: 'textarea', + editType: 'file', }), fieldGroups: computed('newFields', function() { let groups = [ diff --git a/ui/app/models/pki-ca-certificate.js b/ui/app/models/pki-ca-certificate.js index 08ebd372b..255df6f02 100644 --- a/ui/app/models/pki-ca-certificate.js +++ b/ui/app/models/pki-ca-certificate.js @@ -143,6 +143,7 @@ export default Certificate.extend({ csr: attr('string', { editType: 'textarea', label: 'CSR', + masked: true, }), expiration: attr(), diff --git a/ui/app/models/pki-certificate.js b/ui/app/models/pki-certificate.js index 73bd6895e..9a5a481cf 100644 --- a/ui/app/models/pki-certificate.js +++ b/ui/app/models/pki-certificate.js @@ -63,14 +63,20 @@ export default Model.extend({ defaultValue: false, }), - certificate: attr('string'), + certificate: attr('string', { + masked: true, + }), issuingCa: attr('string', { label: 'Issuing CA', + masked: true, }), caChain: attr('string', { label: 'CA chain', + masked: true, + }), + privateKey: attr('string', { + masked: true, }), - privateKey: attr('string'), privateKeyType: attr('string'), serialNumber: attr('string'), diff --git a/ui/app/styles/app.scss b/ui/app/styles/app.scss index 7f8fbdb71..d8385bb21 100644 --- a/ui/app/styles/app.scss +++ b/ui/app/styles/app.scss @@ -1,3 +1,12 @@ @import 'ember-basic-dropdown'; @import 'ember-power-select'; @import './core'; + +@mixin font-face($name) { + @font-face { + font-family: $name; + src: url('/ui/fonts/#{$name}.woff2') format('woff2'), url('/ui/fonts/#{$name}.woff') format('woff'); + } +} + +@include font-face('obscure'); diff --git a/ui/app/styles/components/masked-input.scss b/ui/app/styles/components/masked-input.scss index 124f2a164..65f03e0cb 100644 --- a/ui/app/styles/components/masked-input.scss +++ b/ui/app/styles/components/masked-input.scss @@ -1,13 +1,12 @@ +.masked-font { + color: $ui-gray-300; +} + .masked-input { display: flex; align-items: center; } -.masked-input.masked.display-only, -.masked-input:not(.masked) { - align-items: start; -} - .has-label .masked-input { padding-top: $spacing-s; } @@ -91,10 +90,6 @@ color: $grey-light; } -.masked-input:not(.masked) .masked-input-toggle { - color: $blue; -} - .masked-input .input:focus + .masked-input-toggle { background: rgba($white, 0.95); } diff --git a/ui/app/styles/components/text-file.scss b/ui/app/styles/components/text-file.scss new file mode 100644 index 000000000..f7dd0f8f7 --- /dev/null +++ b/ui/app/styles/components/text-file.scss @@ -0,0 +1,14 @@ +.text-file { + .has-icon-right { + display: flex; + width: 97%; + + .textarea { + line-height: inherit; + } + } + .button.masked-input-toggle, + .button.copy-button { + display: flex; + } +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 1f658dfdf..3f51210fc 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -99,6 +99,7 @@ @import './components/splash-page'; @import './components/status-menu'; @import './components/tabs'; +@import './components/text-file'; @import './components/token-expire-warning'; @import './components/toolbar'; @import './components/tool-tip'; diff --git a/ui/app/styles/core/forms.scss b/ui/app/styles/core/forms.scss index b31b44fe0..7db94e48a 100644 --- a/ui/app/styles/core/forms.scss +++ b/ui/app/styles/core/forms.scss @@ -11,6 +11,13 @@ label { } } +.masked-font { + font-family: obscure; + font-size: $size-medium; + letter-spacing: 2px; + color: $ui-gray-300; +} + .label { color: $grey-darker; text-transform: uppercase; diff --git a/ui/app/templates/components/config-pki-ca.hbs b/ui/app/templates/components/config-pki-ca.hbs index 95283b3a9..889946e88 100644 --- a/ui/app/templates/components/config-pki-ca.hbs +++ b/ui/app/templates/components/config-pki-ca.hbs @@ -9,7 +9,27 @@ {{#if (or model.certificate model.csr)}} {{#each model.attrs as |attr|}} - {{info-table-row data-test-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name)}} + {{#if attr.options.masked}} + + + + {{else if (eq attr.name "expiration")}} + {{info-table-row + data-test-table-row + label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) + value=(date-format (get model attr.name) 'MMM dd, yyyy hh:mm:ss a') + }} + {{else}} + {{info-table-row + data-test-table-row + label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) + value=(get model attr.name) + }} + {{/if}} {{/each}}
@@ -70,7 +90,17 @@ data-test-warning /> {{#each model.attrs as |attr|}} - {{info-table-row data-test-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name)}} + {{#if attr.options.masked}} + + + + {{else}} + {{info-table-row data-test-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name)}} + {{/if}} {{/each}}
diff --git a/ui/app/templates/components/generate-credentials.hbs b/ui/app/templates/components/generate-credentials.hbs index f5344fb43..3d0f7111b 100644 --- a/ui/app/templates/components/generate-credentials.hbs +++ b/ui/app/templates/components/generate-credentials.hbs @@ -49,12 +49,14 @@ (eq attr.name "secretKey") (eq attr.name "securityToken") (eq attr.name "privateKey") + attr.options.masked )}} {{else}} diff --git a/ui/app/templates/components/pki-cert-show.hbs b/ui/app/templates/components/pki-cert-show.hbs index ef980d1ca..3e19c7d65 100644 --- a/ui/app/templates/components/pki-cert-show.hbs +++ b/ui/app/templates/components/pki-cert-show.hbs @@ -15,7 +15,17 @@ {{#if (eq attr.type "object")}} {{else}} - + {{#if attr.options.masked}} + + + + {{else}} + + {{/if}} {{/if}} {{/each}}
diff --git a/ui/app/templates/components/text-file.hbs b/ui/app/templates/components/text-file.hbs index 932b9422b..fab1724a0 100644 --- a/ui/app/templates/components/text-file.hbs +++ b/ui/app/templates/components/text-file.hbs @@ -1,13 +1,13 @@ -{{#unless inputOnly}} +{{#unless this.inputOnly}}
{{/unless}} -
- {{#if file.enterAsText}} -
+
+ {{#if @file.enterAsText}} +
+ >{{@file.value}} +

- {{textareaHelpText}} + {{this.textareaHelpText}}

{{else}}

- {{fileHelpText}} + {{this.fileHelpText}}

{{/if}}
diff --git a/ui/app/templates/partials/secret-backend-settings/ssh.hbs b/ui/app/templates/partials/secret-backend-settings/ssh.hbs index a7fb7a5e9..d2908ab5f 100644 --- a/ui/app/templates/partials/secret-backend-settings/ssh.hbs +++ b/ui/app/templates/partials/secret-backend-settings/ssh.hbs @@ -5,13 +5,20 @@ Public key
-