From 610fe599c29601889f73b37f06965661bf698cd3 Mon Sep 17 00:00:00 2001 From: Matthew Irish Date: Mon, 19 Nov 2018 15:14:24 -0600 Subject: [PATCH] UI secondary dr recovery token fix (#5818) * use the OTP that the server provides instead of generating one in the JS client * fix button styling * differentiate between OTP and encoded token and encrypted token in the template --- ui/app/adapters/cluster.js | 2 +- ui/app/components/shamir-flow.js | 39 ++-- ui/app/styles/core/buttons.scss | 7 +- ui/app/templates/components/shamir-flow.hbs | 205 +++++++++----------- 4 files changed, 109 insertions(+), 144 deletions(-) diff --git a/ui/app/adapters/cluster.js b/ui/app/adapters/cluster.js index c806bdd1c..8f0fe15bd 100644 --- a/ui/app/adapters/cluster.js +++ b/ui/app/adapters/cluster.js @@ -180,7 +180,7 @@ export default ApplicationAdapter.extend({ generateDrOperationToken(data, options) { const verb = options && options.checkStatus ? 'GET' : 'PUT'; let url = `${this.buildURL()}/replication/dr/secondary/generate-operation-token/`; - if (!data || data.pgp_key || data.otp) { + if (!data || data.pgp_key || data.attempt) { // start the generation url = url + 'attempt'; } else { diff --git a/ui/app/components/shamir-flow.js b/ui/app/components/shamir-flow.js index 3d915b6d8..8e89f4db9 100644 --- a/ui/app/components/shamir-flow.js +++ b/ui/app/components/shamir-flow.js @@ -3,7 +3,6 @@ import { gt } from '@ember/object/computed'; import { camelize } from '@ember/string'; import Component from '@ember/component'; import { get, computed } from '@ember/object'; -import base64js from 'base64-js'; const DEFAULTS = { key: null, @@ -62,18 +61,18 @@ export default Component.extend(DEFAULTS, { hasProgress: gt('progress', 0), actionSuccess(resp) { - let { onUpdate, isComplete, onShamirSuccess, thresholdPath } = this.getProperties( - 'onUpdate', - 'isComplete', - 'onShamirSuccess', - 'thresholdPath' - ); + let { onUpdate, isComplete, onShamirSuccess, thresholdPath } = this; let threshold = get(resp, thresholdPath); let props = { ...resp, threshold, }; this.stopLoading(); + // if we have an OTP, but update doesn't include one, + // we don't want to null it out + if (this.otp && !props.otp) { + delete props.otp; + } this.setProperties(props); onUpdate(props); if (isComplete(props)) { @@ -91,19 +90,11 @@ export default Component.extend(DEFAULTS, { } }, - generateStep: computed('generateWithPGP', 'haveSavedPGPKey', 'otp', 'pgp_key', function() { - let { generateWithPGP, otp, pgp_key, haveSavedPGPKey } = this.getProperties( - 'generateWithPGP', - 'otp', - 'pgp_key', - 'haveSavedPGPKey' - ); - if (!generateWithPGP && !pgp_key && !otp) { + generateStep: computed('generateWithPGP', 'haveSavedPGPKey', 'pgp_key', function() { + let { generateWithPGP, pgp_key, haveSavedPGPKey } = this; + if (!generateWithPGP && !pgp_key) { return 'chooseMethod'; } - if (otp) { - return 'beginGenerationWithOTP'; - } if (generateWithPGP) { if (pgp_key && haveSavedPGPKey) { return 'beginGenerationWithPGP'; @@ -133,7 +124,7 @@ export default Component.extend(DEFAULTS, { } return { - otp: data.otp, + attempt: data.attempt, }; }, @@ -144,6 +135,7 @@ export default Component.extend(DEFAULTS, { this.set('loading', true); const adapter = this.get('store').adapterFor('cluster'); const method = adapter[action]; + method .call(adapter, data, { checkStatus }) .then(resp => this.actionSuccess(resp), (...args) => this.actionError(...args)); @@ -164,15 +156,12 @@ export default Component.extend(DEFAULTS, { }, startGenerate(data) { + if (this.generateAction) { + data.attempt = true; + } this.attemptProgress(this.extractData(data)); }, - generateOTP() { - const bytes = new window.Uint8Array(16); - window.crypto.getRandomValues(bytes); - this.set('otp', base64js.fromByteArray(bytes)); - }, - setKey(_, keyFile) { this.set('pgp_key', keyFile.value); this.set('pgpKeyFile', keyFile); diff --git a/ui/app/styles/core/buttons.scss b/ui/app/styles/core/buttons.scss index f802a935f..92ad01ba4 100644 --- a/ui/app/styles/core/buttons.scss +++ b/ui/app/styles/core/buttons.scss @@ -13,8 +13,7 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12); min-width: 6rem; padding: $size-10 $size-8; text-decoration: none; - transition: background-color $speed, border-color $speed, box-shadow $speed, - color $speed; + transition: background-color $speed, border-color $speed, box-shadow $speed, color $speed; vertical-align: middle; &.is-icon { @@ -45,7 +44,7 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12); @each $name, $pair in $colors { $color: nth($pair, 1); - @if $name == "primary" { + @if $name == 'primary' { $color: $blue; } $color-invert: nth($pair, 2); @@ -211,8 +210,10 @@ $button-box-shadow-standard: 0 3px 1px 0 rgba($black, 0.12); } } +.button.auto-width, .button .icon.auto-width { width: auto; + min-width: auto; margin: 0 !important; } diff --git a/ui/app/templates/components/shamir-flow.hbs b/ui/app/templates/components/shamir-flow.hbs index 21bcb6def..4ce757024 100644 --- a/ui/app/templates/components/shamir-flow.hbs +++ b/ui/app/templates/components/shamir-flow.hbs @@ -4,28 +4,39 @@

- Encoded Operation Token + {{#if otp}} + Encoded Operation Token + {{else}} + Encrypted Operation Token + {{/if}}

{{encoded_token}}
-

- If you entered a One Time Password, you can use the Vault CLI to decode the Token: -

-
- {{#with (if otp - (concat 'vault operator generate-root -otp="' otp '" -decode="' encoded_token '"') - (concat 'vault operator generate-root -otp="" -decode="' encoded_token '"') - ) as |cmd|}} - + {{#if otp}} +
+

- DR Operation Token Command + One Time Password (otp)

- {{cmd}} + {{otp}}
- {{/with}} -
+
+
+ {{#let + (concat 'vault operator generate-root -otp="' otp '" -decode="' encoded_token '"') + as |cmd|}} + +
+

+ DR Operation Token Command +

+ {{cmd}} +
+ {{/let}} +
+ {{/if}}
{{else if (and generateAction (not started))}} -
- {{message-error errors=errors}} - {{#if (eq generateStep 'chooseMethod')}} -
-

- Updating or promoting this cluster requires an operation token. Let's generate one by - inputting the master key shares. To get started you'll need to generate a One Time Password - (OTP) or provide a PGP Key to encrypt the resultant operation token. -

+ + {{message-error errors=errors}} + {{#if (eq generateStep 'chooseMethod')}} +
+

+ Updating or promoting this cluster requires an operation token. Let's generate one by + inputting the master key shares. If you'd like to encrypt the token with a PGP Key, please click "Provide PGP Key" below, otherwise we can begin generation of the Operation Token. +

+
+
+
+
-
-
- -
-
- - or - -
-
- -
+
+ + or +
- {{/if}} - - {{#if (eq generateStep 'providePGPKey')}} - -
+
+ +
+
+ {{/if}} + {{#if (eq generateStep 'providePGPKey')}} +

Choose a PGP Key from your computer or paste the contents of one in the form below. This key will be used to Encrypt the generated Operation Token.

{{pgp-file index='' key=pgpKeyFile onChange=(action 'setKey')}} +
+
+
+
- -
-
- -
-
- +
+ +
+
+ {{/if}} + {{#if (eq generateStep 'beginGenerationWithPGP')}} +
+

+ Below is the base-64 encoded PGP Key that will be used to encrypt the generated Operation Token. + Next we'll enter portions of the master key to generate an Operation Token. Click the "Generate Operation Token" button to proceed. +

+
+ +
+

+ PGP Key {{pgpKeyFile.fileName}} +

+ {{pgp_key}}
- {{/if}} - {{#if (eq generateStep 'beginGenerationWithPGP')}} -
-

- Below is the base-64 encoded PGP Key that will be used to encrypt the generated Operation Token. - Next we'll enter portions of the master key to generate an Operation Token. Click the "Generate Operation Token" button to proceed. -

-
- -
-

- PGP Key {{pgpKeyFile.fileName}} -

- {{pgp_key}} -
-
+
+
+
+
-
-
- -
-
- -
+
+
- {{/if}} - {{#if (eq generateStep 'beginGenerationWithOTP')}} -
-

- Below is the generated OTP. This will be used to encrypt the generated Operation Token. - - Make sure to save this, as you will need it later to decrypt the Operation Token. - - Next we'll enter portions of the master key to generate an Operation Token. Click the "Generate Operation Token" button to proceed. -

-
- -
-

- One Time Password -

- {{otp}} -
-
-
-
-
- -
-
- -
-
- {{/if}} +
+ {{/if}} {{else}}