-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI - ent init #5428
UI - ent init #5428
Changes from 7 commits
9026da4
b88895f
133ec61
77cddb9
c34e3dc
4d86dff
8bbcdb6
66cdab1
1cbac24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,10 @@ export default { | |
onEntry: { type: 'render', level: 'feature', component: 'wizard/init-setup' }, | ||
}, | ||
save: { | ||
on: { TOUNSEAL: 'unseal' }, | ||
on: { | ||
TOUNSEAL: 'unseal', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These look slightly weird, like they should have an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yeah this was more keeping in line with what's there already - I'll make a note to add underscores later as we've got more work to do on the on boarding stuff. |
||
TOLOGIN: 'login', | ||
}, | ||
onEntry: { type: 'render', level: 'feature', component: 'wizard/init-save-keys' }, | ||
}, | ||
unseal: { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,3 @@ | ||
import { computed } from '@ember/object'; | ||
import { alias, and, equal } from '@ember/object/computed'; | ||
import DS from 'ember-data'; | ||
const { attr } = DS; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unrelated, but you ought to be able to use modules for Ember Data as well now. import Model from 'ember-data/model';
import attr from 'ember-data/attr'; There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Huh I guess the codemod doesn't do that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oddly the guides still show the old way - gonna hold off on this now and do it all at once in a different PR. |
||
|
@@ -24,13 +23,10 @@ export default DS.Model.extend({ | |
sealThreshold: alias('t'), | ||
sealNumShares: alias('n'), | ||
version: attr('string'), | ||
type: attr('string'), | ||
|
||
//https://www.vaultproject.io/docs/http/sys-leader.html | ||
haEnabled: attr('boolean'), | ||
isSelf: attr('boolean'), | ||
leaderAddress: attr('string'), | ||
|
||
type: computed(function() { | ||
return this.constructor.modelName; | ||
}), | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,32 @@ | ||
<SplashPage as |Page|> | ||
{{#if keyData}} | ||
<Page.header> | ||
<h1 class="title is-4"> | ||
Vault has been initialized! {{#if (eq keyData.keys.length 1)}} | ||
Here is your key. | ||
{{else}} | ||
Here are your {{pluralize keyData.keys.length "key"}}. | ||
{{/if}} | ||
</h1> | ||
{{#with (or keyData.recovery_keys keyData.keys) as |keyArray|}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooo and it can yield multiple items 😙👌 |
||
<h1 class="title is-4"> | ||
Vault has been initialized! {{#if (eq keyArray.length 1)}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any harm in moving the conditional to the next line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope! I'lld do that |
||
Here is your key. | ||
{{else}} | ||
Here are your {{pluralize keyArray.length "key"}}. | ||
{{/if}} | ||
</h1> | ||
{{/with}} | ||
</Page.header> | ||
<Page.content> | ||
<div class="box is-marginless is-shadowless"> | ||
<div class="content"> | ||
<p> | ||
Please securely distribute the keys below. When the Vault is re-sealed, restarted, or stopped, you must provide at least <strong class="has-text-danger">{{secret_threshold}}</strong> of these keys to unseal it again. | ||
Vault does not store the master key. Without at least <strong class="has-text-danger">{{secret_threshold}}</strong> keys, your Vault will remain permanently sealed. | ||
{{#if keyData.recovery_keys}} | ||
Please securely distribute the keys below. Certain privileged operations in Vault such as rekeying the | ||
barrier or generating a new root token will require you to provide | ||
at least <strong class="has-text-danger">{{secret_threshold}}</strong> of these keys to perform the | ||
operation. | ||
{{else}} | ||
Please securely distribute the keys below. When the Vault is re-sealed, restarted, or stopped, you must | ||
provide at least <strong class="has-text-danger">{{secret_threshold}}</strong> of these keys to unseal it | ||
again. | ||
Vault does not store the master key. Without at least <strong class="has-text-danger">{{secret_threshold}}</strong> | ||
keys, your Vault will remain permanently sealed. | ||
{{/if}} | ||
</p> | ||
</div> | ||
<div class="message is-list is-highlight has-copy-button" tabindex="-1"> | ||
|
@@ -26,8 +38,8 @@ | |
<code class="is-word-break">{{keyData.root_token}}</code> | ||
</div> | ||
</div> | ||
{{#each (if keyData.keys_base64 keyData.keys_base64 keyData.keys) as |key index| }} | ||
<div class="message is-list has-copy-button" tabindex="-1"> | ||
{{#each (or keyData.recovery_keys_base64 keyData.recovery_keys keyData.keys_base64 keyData.keys) as |key index| }} | ||
<div data-test-key-box class="message is-list has-copy-button" tabindex="-1"> | ||
<HoverCopyButton @copyValue={{key}} /> | ||
<div class="message-body"> | ||
<h4 class="title is-7 is-marginless"> | ||
|
@@ -40,27 +52,21 @@ | |
</div> | ||
<div class="box is-marginless is-shadowless"> | ||
<div class="field is-grouped-split"> | ||
{{#if model.sealed}} | ||
<div class="control"> | ||
{{#if (and model.sealed (not keyData.recovery_keys))}} | ||
<div data-test-advance-button class="control"> | ||
{{#link-to 'vault.cluster.unseal' model.name class="button is-primary"}} | ||
Continue to Unseal | ||
Continue to Unseal | ||
{{/link-to}} | ||
</div> | ||
{{else}} | ||
<div class="control"> | ||
{{#link-to 'vault.cluster.auth' model.name class="button is-primary"}} | ||
Continue to Authenticate | ||
<div data-test-advance-button class="control"> | ||
{{#link-to 'vault.cluster.auth' model.name class=(concat (if model.sealed 'is-loading ' '') 'button is-primary') disabled=model.sealed}} | ||
Continue to Authenticate | ||
{{/link-to}} | ||
</div> | ||
{{/if}} | ||
<DownloadButton | ||
@data={{keyData}} | ||
@filename={{keyFilename}} | ||
@mime="application/json" | ||
@extension="json" | ||
@class="button is-ghost" | ||
@stringify={{true}} | ||
> | ||
<DownloadButton @data={{keyData}} @filename={{keyFilename}} @mime="application/json" @extension="json" @class="button is-ghost" | ||
@stringify={{true}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is some weird formatting. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ugh yes js-beautify's default which VSCode uses does "break attrs only after max line length" by default - fixed it to break all attrs which is also kinda weird, but more easily read imo. |
||
<ICon @glyph="download" @size=16 /> Download Keys | ||
</DownloadButton> | ||
</div> | ||
|
@@ -73,7 +79,8 @@ | |
</h1> | ||
</Page.header> | ||
<Page.content> | ||
<form {{action 'initCluster' (hash | ||
<form | ||
{{action 'initCluster' (hash | ||
secret_shares=secret_shares | ||
secret_threshold=secret_threshold | ||
pgp_keys=pgp_keys | ||
|
@@ -83,16 +90,15 @@ | |
) | ||
on="submit" | ||
}} | ||
id="init" | ||
> | ||
id="init"> | ||
<div class="box is-marginless is-shadowless"> | ||
<MessageError @errors={{errors}} /> | ||
<div class="field"> | ||
<label for="key-shares" class="is-label"> | ||
Key Shares | ||
</label> | ||
<div class="control"> | ||
{{input class="input" autocomplete="off" name="key-shares" type="number" step="1" min="1" pattern="[0-9]*" value=secret_shares}} | ||
{{input data-test-key-shares="true" class="input" autocomplete="off" name="key-shares" type="number" step="1" min="1" pattern="[0-9]*" value=secret_shares}} | ||
</div> | ||
<p class="help has-text-grey"> | ||
The number of key shares to split the master key into | ||
|
@@ -103,20 +109,15 @@ | |
Key Threshold | ||
</label> | ||
<div class="control"> | ||
{{input class="input" autocomplete="off" name="key-threshold" type="number" step="1" min="1" pattern="[0-9]*" value=secret_threshold}} | ||
{{input data-test-key-threshold="true" class="input" autocomplete="off" name="key-threshold" type="number" step="1" min="1" pattern="[0-9]*" value=secret_threshold}} | ||
</div> | ||
<p class="help has-text-grey"> | ||
The number of key shares required to reconstruct the master key | ||
</p> | ||
</div> | ||
|
||
<ToggleButton | ||
@openLabel="Encrypt Output with PGP" | ||
@closedLabel="Encrypt Output with PGP" | ||
@toggleTarget={{this}} | ||
@toggleAttr="use_pgp" | ||
@class="is-block" | ||
/> | ||
<ToggleButton @openLabel="Encrypt Output with PGP" @closedLabel="Encrypt Output with PGP" @toggleTarget={{this}} | ||
@toggleAttr="use_pgp" @class="is-block" /> | ||
{{#if use_pgp}} | ||
<div class="box init-box"> | ||
<p class="help has-text-grey"> | ||
|
@@ -125,13 +126,8 @@ | |
<PgpList @listLength={{secret_shares}} @onDataUpdate={{action 'setKeys'}} /> | ||
</div> | ||
{{/if}} | ||
<ToggleButton | ||
@openLabel="Encrypt Root Token with PGP" | ||
@closedLabel="Encrypt Root Token with PGP" | ||
@toggleTarget={{this}} | ||
@toggleAttr="use_pgp_for_root" | ||
@class="is-block" | ||
/> | ||
<ToggleButton @openLabel="Encrypt Root Token with PGP" @closedLabel="Encrypt Root Token with PGP" | ||
@toggleTarget={{this}} @toggleAttr="use_pgp_for_root" @class="is-block" /> | ||
{{#if use_pgp_for_root}} | ||
<div class="box init-box"> | ||
<p class="help has-text-grey"> | ||
|
@@ -142,11 +138,7 @@ | |
{{/if}} | ||
</div> | ||
<div class="box is-marginless is-shadowless"> | ||
<button | ||
type="submit" | ||
class="button is-primary {{if loading 'is-loading'}}" | ||
disabled={{loading}} | ||
> | ||
<button data-test-init-submit type="submit" class="button is-primary {{if loading 'is-loading'}}" disabled={{loading}}> | ||
Initialize | ||
</button> | ||
|
||
|
@@ -157,4 +149,4 @@ | |
</form> | ||
</Page.content> | ||
{{/if}} | ||
</SplashPage> | ||
</SplashPage> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Intentional lack of newline or fallout from switching to VSCode 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😱 also another VSCode thing which I think I've fixed... |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { module, test } from 'qunit'; | ||
import { setupApplicationTest } from 'ember-qunit'; | ||
|
||
import initPage from 'vault/tests/pages/init'; | ||
import Pretender from 'pretender'; | ||
|
||
const HEALTH_RESPONSE = { | ||
initialized: false, | ||
sealed: true, | ||
standby: true, | ||
performance_standby: false, | ||
replication_performance_mode: 'unknown', | ||
replication_dr_mode: 'unknown', | ||
server_time_utc: 1538066726, | ||
version: '0.11.0+prem', | ||
}; | ||
|
||
const CLOUD_SEAL_RESPONSE = { | ||
keys: [], | ||
keys_base64: [], | ||
recovery_keys: [ | ||
'1659986a8d56b998b175b6e259998f3c064c061d256c2a331681b8d122fedf0db4', | ||
'4d34c58f56e4f077e3b74f9e8db2850fc251ac3f16e952441301eedc462addeb84', | ||
'3b3cbdf4b2f5ac1e809ff1bb72fd9778e460856561728a871a9370345bd52e97f4', | ||
'aa99b46e2ed5d837ee9824b7894b24987be2f32c81ab9ff5ce9e07d2012eaf4158', | ||
'c2bf6d71d8db8ae09b26177ed393ecb274740fe9ab51884eaa00ac113a74c08ba7', | ||
], | ||
recovery_keys_base64: [ | ||
'FlmYao1WuZixdbbiWZmPPAZMBh0lbCozFoG40SL+3w20', | ||
'TTTFj1bk8Hfjt0+ejbKFD8JRrD8W6VJEEwHu3EYq3euE', | ||
'Ozy99LL1rB6An/G7cv2XeORghWVhcoqHGpNwNFvVLpf0', | ||
'qpm0bi7V2DfumCS3iUskmHvi8yyBq5/1zp4H0gEur0FY', | ||
'wr9tcdjbiuCbJhd+05PssnR0D+mrUYhOqgCsETp0wIun', | ||
], | ||
root_token: '48dF3Drr1jl4ayM0jcHrN4NC', | ||
}; | ||
const SEAL_RESPONSE = { | ||
keys: [ | ||
'1659986a8d56b998b175b6e259998f3c064c061d256c2a331681b8d122fedf0db4', | ||
'4d34c58f56e4f077e3b74f9e8db2850fc251ac3f16e952441301eedc462addeb84', | ||
'3b3cbdf4b2f5ac1e809ff1bb72fd9778e460856561728a871a9370345bd52e97f4', | ||
], | ||
keys_base64: [ | ||
'FlmYao1WuZixdbbiWZmPPAZMBh0lbCozFoG40SL+3w20', | ||
'TTTFj1bk8Hfjt0+ejbKFD8JRrD8W6VJEEwHu3EYq3euE', | ||
'Ozy99LL1rB6An/G7cv2XeORghWVhcoqHGpNwNFvVLpf0', | ||
], | ||
root_token: '48dF3Drr1jl4ayM0jcHrN4NC', | ||
}; | ||
|
||
const CLOUD_SEAL_STATUS_RESPONSE = { | ||
type: 'awskms', | ||
sealed: true, | ||
initialized: false, | ||
}; | ||
const SEAL_STATUS_RESPONSE = { | ||
type: 'shamir', | ||
sealed: true, | ||
initialized: false, | ||
}; | ||
|
||
module('Acceptance | init', function(hooks) { | ||
setupApplicationTest(hooks); | ||
|
||
let setInitResponse = (server, resp) => { | ||
server.put('/v1/sys/init', () => { | ||
return [200, { 'Content-Type': 'application/json' }, JSON.stringify(resp)]; | ||
}); | ||
}; | ||
let setStatusResponse = (server, resp) => { | ||
server.get('/v1/sys/seal-status', () => { | ||
return [200, { 'Content-Type': 'application/json' }, JSON.stringify(resp)]; | ||
}); | ||
}; | ||
hooks.beforeEach(function() { | ||
this.server = new Pretender(); | ||
this.server.get('/v1/sys/health', () => { | ||
return [200, { 'Content-Type': 'application/json' }, JSON.stringify(HEALTH_RESPONSE)]; | ||
}); | ||
}); | ||
|
||
hooks.afterEach(function() { | ||
this.server.shutdown(); | ||
}); | ||
|
||
test('cloud seal init', async function(assert) { | ||
setInitResponse(this.server, CLOUD_SEAL_RESPONSE); | ||
setStatusResponse(this.server, CLOUD_SEAL_STATUS_RESPONSE); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like these helpers. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks! maybe overkill for 2 tests, but felt like it cleaned things up nicely |
||
await initPage.init(5, 3); | ||
assert.equal( | ||
initPage.keys.length, | ||
CLOUD_SEAL_RESPONSE.recovery_keys.length, | ||
'shows all of the recovery keys' | ||
); | ||
assert.equal(initPage.buttonText, 'Continue to Authenticate', 'links to authenticate'); | ||
let { requestBody } = this.server.handledRequests.findBy('url', '/v1/sys/init'); | ||
requestBody = JSON.parse(requestBody); | ||
for (let attr of ['recovery_shares', 'recovery_threshold']) { | ||
assert.ok(requestBody[attr], `requestBody includes cloud seal specific attribute: ${attr}`); | ||
} | ||
}); | ||
|
||
test('shamir seal init', async function(assert) { | ||
setInitResponse(this.server, SEAL_RESPONSE); | ||
setStatusResponse(this.server, SEAL_STATUS_RESPONSE); | ||
|
||
await initPage.init(3, 2); | ||
assert.equal(initPage.keys.length, SEAL_RESPONSE.keys.length, 'shows all of the recovery keys'); | ||
assert.equal(initPage.buttonText, 'Continue to Unseal', 'links to unseal'); | ||
|
||
let { requestBody } = this.server.handledRequests.findBy('url', '/v1/sys/init'); | ||
requestBody = JSON.parse(requestBody); | ||
for (let attr of ['recovery_shares', 'recovery_threshold']) { | ||
assert.notOk(requestBody[attr], `requestBody does not include cloud seal specific attribute: ${attr}`); | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lol no, habit from components