UI - guard page redesign (#4779)

* add NavHeader component
* use NavHeader in SplashPage component and application.hbs
* let download button take a block
* add RadialProgress component
* use RadialProgress in ShamirFlow component
* style up the RadialProgress component
* update ember-basic-dropdown, ember-basic-dropdown-hover
* rework operation token generation workflow
* directly depend on ember-maybe-in-element
This commit is contained in:
Matthew Irish
2018-06-26 16:35:47 -05:00
committed by GitHub
parent 70664da044
commit c8b64523ee
62 changed files with 1172 additions and 526 deletions

View File

@@ -4,7 +4,7 @@ import hbs from 'htmlbars-inline-precompile';
const { computed } = Ember;
export default Ember.Component.extend({
layout: hbs`{{actionText}}`,
layout: hbs`{{#if hasBlock}} {{yield}} {{else}} {{actionText}} {{/if}}`,
tagName: 'a',
role: 'button',
attributeBindings: ['role', 'download', 'href'],

View File

@@ -0,0 +1,10 @@
import Ember from 'ember';
export default Ember.Component.extend({
'data-test-hover-copy': true,
classNameBindings: 'alwaysShow:hover-copy-button-static:hover-copy-button',
copyValue: null,
alwaysShow: false,
tooltipText: 'Copy',
});

View File

@@ -3,6 +3,7 @@ import hbs from 'htmlbars-inline-precompile';
const { computed } = Ember;
const GLYPHS_WITH_SVG_TAG = [
'download',
'folder',
'file',
'hidden',

View File

@@ -0,0 +1,6 @@
import Ember from 'ember';
export default Ember.Component.extend({
'data-test-navheader': true,
tagName: 'header',
});

View File

@@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: '',
});

View File

@@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: '',
});

View File

@@ -0,0 +1,5 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: '',
});

View File

@@ -0,0 +1,29 @@
import Ember from 'ember';
const { computed } = Ember;
export default Ember.Component.extend({
'data-test-radial-progress': true,
tagName: 'svg',
classNames: 'radial-progress',
attributeBindings: ['size:width', 'size:height', 'viewBox'],
progressDecimal: null,
size: 20,
strokeWidth: 1,
viewBox: computed('size', function() {
let s = this.get('size');
return `0 0 ${s} ${s}`;
}),
centerValue: computed('size', function() {
return this.get('size') / 2;
}),
r: computed('size', 'strokeWidth', function() {
return (this.get('size') - this.get('strokeWidth')) / 2;
}),
c: computed('r', function() {
return 2 * Math.PI * this.get('r');
}),
dashArrayOffset: computed('c', 'progressDecimal', function() {
return this.get('c') * (1 - this.get('progressDecimal'));
}),
});

View File

@@ -34,7 +34,7 @@ export default Ember.Component.extend({
}),
isPerformance: computed.equal('mode', 'performance'),
replicationEnabled: replicationAttr('replicationEnabled'),
replicationUnsupported: replicationAttr('replicationUnsupported'),
replicationUnsupported: computed.equal('cluster.mode', 'unsupported'),
replicationDisabled: replicationAttr('replicationDisabled'),
syncProgressPercent: replicationAttr('syncProgressPercent'),
syncProgress: replicationAttr('syncProgress'),

View File

@@ -9,6 +9,8 @@ const DEFAULTS = {
errors: [],
threshold: null,
progress: null,
pgp_key: null,
haveSavedPGPKey: false,
started: false,
generateWithPGP: false,
pgpKeyFile: { value: '' },
@@ -32,7 +34,7 @@ export default Component.extend(DEFAULTS, {
return this._super(...arguments);
},
onShamirSuccess: _ => _,
onShamirSuccess() {},
// can be overridden w/an attr
isComplete(data) {
return data.complete === true;
@@ -76,6 +78,28 @@ 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) {
return 'chooseMethod';
}
if (otp) {
return 'beginGenerationWithOTP';
}
if (generateWithPGP) {
if (pgp_key && haveSavedPGPKey) {
return 'beginGenerationWithPGP';
} else {
return 'providePGPKey';
}
}
}),
extractData(data) {
const isGenerate = this.get('generateAction');
const hasStarted = this.get('started');
@@ -113,6 +137,12 @@ export default Component.extend(DEFAULTS, {
},
actions: {
reset() {
this.reset();
this.set('encoded_token', null);
this.set('otp', null);
},
onSubmit(data) {
if (!data.key) {
return;
@@ -135,8 +165,10 @@ export default Component.extend(DEFAULTS, {
this.set('pgpKeyFile', keyFile);
},
clearToken() {
this.set('encoded_token', null);
savePGPKey() {
if (this.get('pgp_key')) {
this.set('haveSavedPGPKey', true);
}
},
},
});

View File

@@ -1,13 +1,14 @@
import Ember from 'ember';
const { computed } = Ember;
export default Ember.Component.extend({
threshold: null,
progress: null,
classNames: ['shamir-progress'],
progressPercent: Ember.computed('threshold', 'progress', function() {
progressDecimal: computed('threshold', 'progress', function() {
const { threshold, progress } = this.getProperties('threshold', 'progress');
if (threshold && progress) {
return progress / threshold * 100;
return progress / threshold;
}
return 0;
}),

View File

@@ -1,5 +1,14 @@
import Ember from 'ember';
const { computed, inject } = Ember;
export default Ember.Component.extend({
version: inject.service(),
auth: inject.service(),
store: inject.service(),
tagName: '',
activeCluster: computed('auth.activeCluster', function() {
return this.get('store').peekRecord('cluster', this.get('auth.activeCluster'));
}),
});

View File

@@ -7,14 +7,17 @@ export default Ember.Component.extend({
cluster: computed.alias('currentCluster.cluster'),
auth: inject.service(),
type: 'cluster',
itemTag: null,
partialName: computed('type', function() {
return `partials/status/${this.get('type')}`;
let type = this.get('type');
let partial = type === 'replication-status' ? 'replication' : type;
return `partials/status/${partial}`;
}),
glyphName: computed('type', function() {
const glyphs = {
cluster: 'unlocked',
user: 'android-person',
replication: 'replication',
'replication-status': 'replication',
};
return glyphs[this.get('type')];
}),

View File

@@ -3,6 +3,9 @@ import Ember from 'ember';
const { computed } = Ember;
export default Ember.Component.extend({
modalContainer: computed(function() {
return document.getElementById('modal-wormhole');
}),
isAnimated: false,
isActive: false,
tagName: 'span',

View File

@@ -23,7 +23,7 @@ const MESSAGE_TYPES = {
class: 'is-highlight',
glyphClass: 'has-text-highlight',
glyph: 'alert-circled',
text: 'Attention',
text: 'Warning',
},
};

View File

@@ -142,7 +142,7 @@
}
}
.page-container > header {
.page-container > header:not(.page-header) {
background: linear-gradient(to right, #191a1c, #1b212d);
}

View File

@@ -0,0 +1,24 @@
.has-copy-button {
position: relative;
color: $grey;
}
.hover-copy-button,
.hover-copy-button-static {
position: absolute;
top: 0.5rem;
right: 0.5rem;
}
.hover-copy-button {
opacity: 0;
pointer-events: none;
transition: opacity $speed ease-in-out;
will-change: opacity;
}
.has-copy-button:hover .hover-copy-button,
.has-copy-button:focus .hover-copy-button,
.hover-copy-button .copy-button:focus {
opacity: 1;
pointer-events: auto;
}

View File

@@ -1,5 +1,18 @@
.init-illustration {
position: absolute;
left: calc(50% - 100px);
top: -94px;
.init-box {
position: relative;
z-index: 10;
}
.init-illustration {
bottom: 0;
right: 0;
overflow: hidden;
position: absolute;
height: 200px;
width: 200px;
}
.init-illustration svg {
position: absolute;
right: -50px;
bottom: -50px;
opacity: 0.8;
}

View File

@@ -1,5 +1,5 @@
.message-in-page {
margin-bottom: 2rem;
margin-bottom: 1rem;
position: relative;
.close-button {

View File

@@ -28,7 +28,6 @@
background: transparent;
box-shadow: none;
border: none;
color: $menu-item-color;
display: block;
height: auto;
font-size: $size-7;
@@ -37,7 +36,11 @@
text-align: left;
text-decoration: none;
width: 100%;
}
button.link,
a {
color: $menu-item-color;
&:hover {
background-color: $menu-item-hover-background-color;
color: $menu-item-hover-color;
@@ -79,6 +82,15 @@
}
}
.popup-menu-content p {
box-shadow: none;
padding-top: $size-10;
font-weight: $font-weight-semibold;
}
.popup-menu-content .level-left {
flex-shrink: 1;
}
.popup-menu-trigger {
height: 2rem;
min-width: 0;

View File

@@ -0,0 +1,12 @@
.radial-progress {
transform: rotate(-90deg) translateX(-20%);
}
.radial-progress circle {
stroke: rgba($grey-light, 0.5);
transition: stroke-dashoffset $speed ease-in;
will-change: stroke-dashoffset;
stroke-linecap: round;
}
.radial-progress circle.progress-fill {
stroke: $green;
}

View File

@@ -0,0 +1,15 @@
a.splash-page-logo {
color: $white;
svg {
transform: scale(.5);
transform-origin: left;
fill: currentColor;
}
}
.splash-page-container {
margin: $size-2 0;
}
.splash-page-header {
padding: .75rem 1.5rem;
}

View File

@@ -23,8 +23,7 @@
color: $grey-dark;
font-weight: $font-weight-semibold;
text-decoration: none;
padding: $size-6 0;
margin: 0 $size-4;
padding: $size-6 $size-8 $size-8;
border-bottom: 2px solid transparent;
transition: border-color $speed;

View File

@@ -0,0 +1,5 @@
.unseal-warning.message,
.unseal-warning .message-body {
border-radius: 0;
margin-bottom: 0;
}

View File

@@ -50,6 +50,7 @@
@import "./components/env-banner";
@import "./components/form-section";
@import "./components/global-flash";
@import "./components/hover-copy-button";
@import "./components/init-illustration";
@import "./components/info-table-row";
@import "./components/input-hint";
@@ -61,12 +62,15 @@
@import "./components/message-in-page";
@import "./components/page-header";
@import "./components/popup-menu";
@import "./components/radial-progress";
@import "./components/role-item";
@import "./components/shamir-progress";
@import "./components/sidebar";
@import "./components/splash-page";
@import "./components/status-menu";
@import "./components/sub-nav";
@import "./components/token-expire-warning";
@import "./components/tool-tip";
@import "./components/unseal-warning";
@import "./components/upgrade-overlay";
@import "./components/vault-loading";

View File

@@ -1,6 +1,7 @@
.footer {
border-top: $base-border;
padding: $size-3 1.5rem;
margin-top: auto;
span:not(:first-child) {
display: inline-block;

View File

@@ -1,73 +1,73 @@
<div class="page-container">
{{#if showNav}}
<header data-test-header-with-nav class="{{if consoleOpen 'panel-open'}} {{if consoleFullscreen ' panel-fullscreen'}}">
<nav class="navbar has-dark-grey-gradient is-grouped-split">
<div class="navbar-brand">
{{#home-link class="navbar-item has-text-white has-current-color-fill"}}
{{partial 'svg/vault-logo'}}
{{/home-link}}
<NavHeader data-test-header-with-nav @class="{{if consoleOpen 'panel-open'}} {{if consoleFullscreen ' panel-fullscreen'}}" as |Nav|>
<Nav.home>
<HomeLink @class="navbar-item has-text-white has-current-color-fill">
{{partial 'svg/vault-logo'}}
</HomeLink>
</Nav.home>
<Nav.items>
<div class="navbar-item">
<button type="button" class="button is-transparent" {{action 'toggleConsole'}} data-test-console-toggle>
{{#if consoleOpen}}
{{i-con glyph="console-active" size=24}}
{{i-con glyph="chevron-up" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}
{{else}}
{{i-con glyph="console" size=24}}
{{i-con glyph="chevron-down" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}
{{/if}}
</button>
</div>
<div class="navbar-end is-divider-list is-flex">
<div class="navbar-item">
<button type="button" class="button is-transparent" {{action 'toggleConsole'}} data-test-console-toggle>
{{#if consoleOpen}}
{{i-con glyph="console-active" size=24}}
{{i-con glyph="chevron-up" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}
{{else}}
{{i-con glyph="console" size=24}}
{{i-con glyph="chevron-down" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}
{{/if}}
</button>
</div>
<div class="navbar-item">
{{status-menu}}
</div>
<div class="navbar-item">
{{status-menu type="user"}}
</div>
<div class="navbar-item">
{{status-menu}}
</div>
</nav>
<ul class="navbar-sections tabs tabs-subnav">
<li class="{{if (is-active-route 'vault.cluster.secrets') 'is-active'}}">
<a href="{{href-to "vault.cluster.secrets" activeClusterName}}">
Secrets
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.access') 'is-active'}}">
<a href="{{href-to "vault.cluster.access" activeClusterName}}">
Access
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.replication') 'is-active'}}">
{{#if activeCluster.anyReplicationEnabled}}
{{status-menu type="replication"}}
{{else}}
<a href="{{href-to "vault.cluster.replication" activeClusterName}}">
Replication
{{#if (is-version "OSS")}}
{{edition-badge edition="Enterprise"}}
{{/if}}
<div class="navbar-item">
{{status-menu type="user"}}
</div>
</Nav.items>
<Nav.main>
<ul class="navbar-sections tabs tabs-subnav">
<li class="{{if (is-active-route 'vault.cluster.secrets') 'is-active'}}">
<a href="{{href-to "vault.cluster.secrets" activeClusterName}}">
Secrets
</a>
{{/if}}
</li>
<li class="{{if (is-active-route (array 'vault.cluster.policies' 'vault.cluster.policy')) 'is-active'}}">
<a href="{{href-to "vault.cluster.policies" "acl" current-when='vault.cluster.policies vault.cluster.policy'}}">
Policies
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.tools') 'is-active'}}">
<a href="{{href-to "vault.cluster.tools" activeClusterName}}">
Tools
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.settings') 'is-active'}}">
<a href="{{href-to "vault.cluster.settings" activeClusterName}}">
Settings
</a>
</li>
</ul>
{{console/ui-panel isFullscreen=consoleFullscreen}}
</header>
</li>
<li class="{{if (is-active-route 'vault.cluster.access') 'is-active'}}">
<a href="{{href-to "vault.cluster.access" activeClusterName}}">
Access
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.replication') 'is-active'}}">
{{#if activeCluster.anyReplicationEnabled}}
{{status-menu type="replication"}}
{{else}}
<a href="{{href-to "vault.cluster.replication" activeClusterName}}">
Replication
{{#if (is-version "OSS")}}
{{edition-badge edition="Enterprise"}}
{{/if}}
</a>
{{/if}}
</li>
<li class="{{if (is-active-route (array 'vault.cluster.policies' 'vault.cluster.policy')) 'is-active'}}">
<a href="{{href-to "vault.cluster.policies" "acl" current-when='vault.cluster.policies vault.cluster.policy'}}">
Policies
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.tools') 'is-active'}}">
<a href="{{href-to "vault.cluster.tools" activeClusterName}}">
Tools
</a>
</li>
<li class="{{if (is-active-route 'vault.cluster.settings') 'is-active'}}">
<a href="{{href-to "vault.cluster.settings" activeClusterName}}">
Settings
</a>
</li>
</ul>
{{console/ui-panel isFullscreen=consoleFullscreen}}
</Nav.main>
</NavHeader>
{{/if}}
<div class="global-flash">
{{#each flashMessages.queue as |flash|}}

View File

@@ -20,8 +20,8 @@
{{message-error errorMessage=error data-test-auth-error=true}}
{{/if}}
{{partial providerPartialName}}
<div class="box has-slim-padding is-shadowless">
{{#unless (eq selectedAuthBackend.type "token")}}
{{#unless (eq selectedAuthBackend.type "token")}}
<div class="box has-slim-padding is-shadowless">
{{toggle-button toggleTarget=this toggleAttr="useCustomPath"}}
<div class="field">
{{#if useCustomPath}}
@@ -43,10 +43,10 @@
</p>
{{/if}}
</div>
{{/unless}}
</div>
</div>
{{/unless}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<button data-test-auth-submit=true type="submit" disabled={{loading}} class="button is-primary {{if loading 'is-loading'}}" id="auth-submit">
Sign In
</button>

View File

@@ -0,0 +1,17 @@
<ToolTip @renderInPlace={{true}} @onClose={{action (mut tooltipText) "Copy"}} as |T|>
<T.trigger data-test-tooltip-trigger tabindex=false>
<CopyButton
data-test-hover-copy-button
@clipboardText={{copyValue}}
@class="copy-button button is-compact is-transparent"
@success={{action (mut tooltipText) "Copied!"}}
>
<ICon @glyph="copy" aria-hidden="true" @size=16 />
</CopyButton>
</T.trigger>
<T.content @class="tool-tip">
<div class="box" data-test-hover-copy-tooltip-text>
{{tooltipText}}
</div>
</T.content>
</ToolTip>

View File

@@ -0,0 +1,11 @@
<nav class="navbar has-dark-grey-gradient is-grouped-split">
<div class="navbar-brand" data-test-navheader-home>
{{yield (hash home=(component 'nav-header/home'))}}
</div>
<div class="navbar-end is-divider-list is-flex" data-test-navheader-items>
{{yield (hash items=(component 'nav-header/items'))}}
</div>
</nav>
<div data-test-navheader-main>
{{yield (hash main=(component 'nav-header/main'))}}
</div>

View File

@@ -0,0 +1 @@
{{yield}}

View File

@@ -0,0 +1 @@
{{yield}}

View File

@@ -0,0 +1 @@
{{yield}}

View File

@@ -0,0 +1,20 @@
<circle
data-test-path
class="path"
cx="{{centerValue}}"
cy="{{centerValue}}"
r="{{r}}"
fill="none"
stroke-width="{{strokeWidth}}"
/>
<circle
data-test-progress
class="progress-fill"
cx="{{centerValue}}"
cy="{{centerValue}}"
r="{{r}}"
fill="none"
stroke-width="{{strokeWidth}}"
stroke-dasharray="{{c}}"
stroke-dashoffset="{{dashArrayOffset}}"
/>

View File

@@ -1,139 +1,173 @@
{{#if encoded_token}}
<div class="box is-marginless is-shadowless">
<div class="message is-list">
<div class="message is-list has-copy-button" tabindex="-1">
<HoverCopyButton @copyValue={{encoded_token}} />
<div class="message-body">
<h4 class="title is-7 is-uppercase is-marginless">
<h4 class="title is-7 is-marginless">
Encoded Operation Token
</h4>
<code class="is-word-break">{{encoded_token}}</code>
</div>
</div>
<p class="has-text-grey">
<p>
If you entered a One Time Password, you can use the Vault CLI to decode the Token:
</p>
<div class="message is-list">
<div class="message-body">
<h4 class="title is-7 is-uppercase is-marginless">
DR Operation Token Command
</h4>
<code class="is-word-break">
vault operator generate-root -otp="[enter your otp here]" -decode="{{encoded_token}}"
</code>
</div>
<div class="message is-list has-copy-button" tabindex="-1">
{{#with (if otp
(concat 'vault operator generate-root -otp="' otp '" -decode="' encoded_token '"')
(concat 'vault operator generate-root -otp="<enter your otp here>" -decode="' encoded_token '"')
) as |cmd|}}
<HoverCopyButton @copyValue={{cmd}} />
<div class="message-body">
<h4 class="title is-7 is-marginless">
DR Operation Token Command
</h4>
<code class="is-word-break">{{cmd}}</code>
</div>
{{/with}}
</div>
{{#copy-button
clipboardText=(concat 'vault operator generate-root -otp="<enter your otp here>" -decode="' encoded_token '"')
class="button is-compact"
buttonType="button"
success=(action (set-flash-message 'CLI command copied!'))
}}
Copy CLI command
{{/copy-button}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
{{#copy-button
clipboardText=encoded_token
class="button is-primary"
buttonType="button"
success=(action (set-flash-message 'OTP copied!'))
}}
Copy Encoded Operation Token
{{/copy-button}}
<button type="button" class="button" {{action 'clearToken'}}>
<div class="box is-marginless is-shadowless">
<button type="button" class="button" {{action 'reset'}}>
Clear Token
</button>
</div>
{{else if (and generateAction (not started))}}
<form {{action 'startGenerate' (hash otp=otp pgp_key=pgp_key) on="submit"}} id="shamir">
<div class="box is-marginless is-shadowless">
{{message-error errors=errors}}
<p class="has-text-grey">
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 a PGP Key to encrypt the resultant operation token.
</p>
<div class="field box is-shadowless is-fullwidth is-marginless">
<div class="level">
<div class="level-left">
<div class="b-checkbox">
{{input type="checkbox"
id="generateWithPGP"
class="styled"
checked=generateWithPGP
}}
<label for="generateWithPGP" class="is-label">
PGP Encrypt Operation Token
</label>
{{#if (eq generateStep 'chooseMethod')}}
<div class="box is-marginless is-shadowless">
<p>
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.
</p>
</div>
<div class="box is-shadowless field is-grouped is-grouped-centered">
<div class="control">
<button type="button" class="button is-primary" {{action (mut generateWithPGP) true}}>
Provide PGP Key
</button>
</div>
<div class="control">
<span class="button is-white is-static">
or
</span>
</div>
<div class="control">
<button type="button" class="button is-primary" {{action "generateOTP"}}>
Generate OTP
</button>
</div>
</div>
{{/if}}
{{#if (eq generateStep 'providePGPKey')}}
<div class="box is-marginless is-shadowless">
<p>
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.
</p>
{{pgp-file index='' key=pgpKeyFile onChange=(action 'setKey')}}
</div>
<div class="field is-grouped box is-marginless is-shadowless">
<div class="control">
<button type="button" class="button" {{action "reset"}}>
Back
</button>
</div>
<div class="control">
<button type="button" disabled={{not pgp_key}} class="button is-primary" {{action "savePGPKey"}}>
Use PGP Key
</button>
</div>
</div>
{{/if}}
{{#if (eq generateStep 'beginGenerationWithPGP')}}
<div class="box is-marginless is-shadowless">
<p>
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.
</p>
<div class="message is-list has-copy-button" tabindex="-1">
<HoverCopyButton @copyValue={{pgp_key}} />
<div class="message-body">
<h4 class="title is-7 is-marginless">
PGP Key {{pgpKeyFile.fileName}}
</h4>
<code class="is-word-break">{{pgp_key}}</code>
</div>
</div>
{{#unless generateWithPGP}}
<div class="level-right">
<div class="control">
<button type="button" class="button is-compact" {{action "generateOTP"}}>
Generate OTP
</button>
</div>
</div>
{{/unless}}
</div>
</div>
{{#if generateWithPGP}}
{{pgp-file index='' key=pgpKeyFile onChange=(action 'setKey')}}
{{else}}
{{#if otp}}
<div class="message is-list">
<div class="field is-grouped box is-marginless is-shadowless">
<div class="control">
<button type="button" class="button" {{action "reset"}}>
Back
</button>
</div>
<div class="control">
<button type="submit" disabled={{and (not pgp_key) (not otp)}} class="button is-primary">
Generate Operation Token
</button>
</div>
</div>
{{/if}}
{{#if (eq generateStep 'beginGenerationWithOTP')}}
<div class="box is-marginless is-shadowless">
<p>
Below is the generated OTP. This will be used to encrypt the generated Operation Token.
<em class="has-text-danger has-text-weight-semibold">
Make sure to save this, as you will need it later to decrypt the Operation Token.
</em>
Next we'll enter portions of the master key to generate an Operation Token. Click the "Generate Operation Token" button to proceed.
</p>
<div class="message is-list has-copy-button" tabindex="-1">
<HoverCopyButton @copyValue={{otp}} />
<div class="message-body">
<h4 class="title is-7 is-uppercase is-marginless">
<h4 class="title is-7 is-marginless">
One Time Password
</h4>
<code class="is-word-break">{{otp}}</code>
</div>
</div>
{{/if}}
<div class="field is-grouped">
{{#if otp}}
<div class="control">
{{#copy-button
clipboardText=otp
class="button is-compact"
buttonType="button"
success=(action (set-flash-message 'OTP copied!'))
}}
Copy OTP
{{/copy-button}}
</div>
<p class="has-text-grey help-text is-pulled-right">Make sure to save this value for later.</p>
{{/if}}
</div>
<div class="field is-grouped box is-marginless is-shadowless">
<div class="control">
<button type="button" class="button" {{action "reset"}}>
Back
</button>
</div>
<div class="control">
<button type="submit" disabled={{and (not pgp_key) (not otp)}} class="button is-primary">
Generate Operation Token
</button>
</div>
</div>
{{/if}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<button type="submit" disabled={{and (not pgp_key) (not otp)}} class="button is-primary">
Generate Operation Token
</button>
</div>
</form>
{{else}}
<form {{action 'onSubmit' (hash key=key) on="submit"}} id="shamir">
<div class="box is-marginless is-shadowless">
<div class="box is-shadowless no-padding-bottom is-marginless">
{{message-error errors=errors}}
<div class="box has-slim-padding is-shadowless is-marginless">
{{#if hasBlock}}
{{yield}}
{{else}}
<p> {{formText}} </p>
{{/if}}
</div>
{{#if hasBlock}}
{{yield}}
{{else}}
<p>{{formText}}</p>
{{/if}}
</div>
<div class="box is-marginless is-shadowless">
<div class="field">
<label for="key" class="is-label">
Key
Master Key Portion
</label>
<div class="control">
{{input class="input"type="password" name="key" value=key data-test-shamir-input=true}}
</div>
</div>
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<div class="columns is-mobile">
<div class="column is-narrow">
<button
@@ -147,10 +181,10 @@
</div>
<div class="column is-flex-v-centered is-flex-end">
{{#if (or started hasProgress)}}
{{shamir-progress
threshold=threshold
progress=progress
}}
<ShamirProgress
@threshold={{threshold}}
@progress={{progress}}
/>
{{/if}}
</div>
</div>

View File

@@ -1,10 +1,10 @@
<div class="level">
<div class="level-left">
<span class="has-text-grey is-size-8 shamir-progress-progress">
{{progress}} / {{threshold}} keys provided
{{progress}}/{{threshold}} keys provided
</span>
</div>
<div class="level-right is-marginless">
<progress max="100" value="{{progressPercent}}" class="progress is-success is-rounded"></progress>
<RadialProgress @progressDecimal={{progressDecimal}} />
</div>
</div>

View File

@@ -1,15 +1,24 @@
<div class="is-flex-v-centered-tablet is-flex-1 is-fullwidth">
<NavHeader as |Nav|>
<Nav.home>
<HomeLink @class="navbar-item splash-page-logo">
{{partial "svg/vault-edition-logo"}}
</HomeLink>
</Nav.home>
<Nav.items>
{{#if (and activeCluster.unsealed version.isEnterprise)}}
<div class="navbar-item">
<StatusMenu @type="replication-status" @itemTag="p" />
</div>
{{/if}}
</Nav.items>
</NavHeader>
<div class="splash-page-container section is-flex-v-centered-tablet is-flex-1 is-fullwidth">
<div class="columns is-centered is-gapless is-fullwidth">
<div class="column is-4-desktop is-7-tablet">
<div class="column is-4-desktop is-6-tablet">
<div class="splash-page-header">
{{yield (hash header=(component 'splash-page/splash-header'))}}
</div>
<div class="login-form box is-paddingless is-relative">
<div class="hero has-dark-grey-gradient">
<div class="hero-body">
<div class="has-current-color-fill has-text-white">
{{partial "svg/vault-edition-logo"}}
</div>
{{yield (hash header=(component 'splash-page/splash-header'))}}
</div>
</div>
{{yield (hash content=(component 'splash-page/splash-content'))}}
</div>
{{yield (hash footer=(component 'splash-page/splash-content')) }}

View File

@@ -5,7 +5,7 @@
{{i-con
glyph=glyphName
class="has-text-dark-grey auto-width"
size=16
size=18
aria-label=ariaLabel
}}
</div>
@@ -19,7 +19,7 @@
{{i-con
glyph=glyphName
class="has-text-white auto-width"
size=16
size=20
aria-label=ariaLabel
}}
{{i-con glyph="chevron-down" aria-hidden="true" size=8 class="has-text-white auto-width is-status-chevron"}}

View File

@@ -1,4 +1,6 @@
{{#basic-dropdown-hover
onOpen=onOpen
onClose=onClose
renderInPlace=renderInPlace
verticalPosition=verticalPosition
horizontalPosition=horizontalPosition

View File

@@ -2,6 +2,6 @@
{{yield}}
</button>
{{#ember-wormhole to="modal-wormhole"}}
{{#maybe-in-element modalContainer false}}
{{partial "partials/upgrade-overlay"}}
{{/ember-wormhole}}
{{/maybe-in-element}}

View File

@@ -1,6 +1,6 @@
{{#if (and (eq replicationMode 'dr') (eq model.replicationAttrs.modeForUrl 'secondary'))}}
<div class="box is-marginless is-shadowless">
<p class="has-text-grey">
<p>
This cluster is currently running as a DR Replication Secondary.
Promote the cluster to a primary by entering DR Operation Token.
</p>
@@ -12,19 +12,30 @@
{{input class="input" id="dr_operation_token" name="dr_operation_token" value=dr_operation_token}}
</div>
</div>
<div class="field">
<label for="primary_cluster_addr" class="is-label">
Primary cluster address <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input class="input" id="primary_cluster_addr" name="primary_cluster_addr" value=primary_cluster_addr}}
<ToggleButton
@class="is-block"
@toggleAttr="showOptions"
@toggleTarget={{this}}
@openLabel="Hide Options"
@closedLabel="Options"
/>
{{#if showOptions}}
<div class="box is-marginless">
<div class="field">
<label for="primary_cluster_addr" class="is-label">
Primary cluster address <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input class="input" id="primary_cluster_addr" name="primary_cluster_addr" value=primary_cluster_addr}}
</div>
<p class="help">
Overrides the cluster address that the primary gives to secondary nodes.
</p>
</div>
</div>
<p class="help has-text-grey">
Overrides the cluster address that the primary gives to secondary nodes.
</p>
</div>
{{/if}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<div class="field">
<div class="control">
{{#confirm-action

View File

@@ -1,7 +1,9 @@
<div class="level is-mobile">
<div class="level-left is-flex-1">
<div>
{{#if (or replicationUnsupported (and (eq mode 'performance') (not version.hasPerfReplication)))}}
{{#if replicationUnsupported}}
Unsupported
{{else if (and (eq mode 'performance') (not version.hasPerfReplication))}}
<p>
Upgrade to Vault Enterprise Premium to use Performance Replication.
</p>
@@ -22,7 +24,11 @@
</code>
</span>
{{else}}
Enable
{{#if (eq tagName 'a')}}
Enable
{{else}}
Disabled
{{/if}}
{{/if}}
</div>
</div>

View File

@@ -1,6 +1,6 @@
{{#if (and (eq replicationMode 'dr') (eq model.replicationAttrs.modeForUrl 'secondary'))}}
<div class="box is-marginless is-shadowless">
<p class="has-text-grey">
<p>
Change a secondary clusters assigned primary cluster using a secondary
activation token. This does not wipe all data in the cluster.
</p>
@@ -20,43 +20,54 @@
{{textarea value=token id="secondary-token" name="secondary-token" class="textarea"}}
</div>
</div>
<div class="field">
<label for="primary_api_addr" class="is-label">
Primary API address <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input class="input" value=primary_api_addr id="primary_api_addr" name="primary_api_addr"}}
<ToggleButton
@class="is-block"
@toggleAttr="showOptions"
@toggleTarget={{this}}
@openLabel="Hide Options"
@closedLabel="Options"
/>
{{#if showOptions}}
<div class="box is-marginless">
<div class="field">
<label for="primary_api_addr" class="is-label">
Primary API address <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input class="input" value=primary_api_addr id="primary_api_addr" name="primary_api_addr"}}
</div>
<p class="help">
Set this to the API address (normal Vault address) to override the
value embedded in the token.
</p>
</div>
<div class="field">
<label for="ca_file" class="is-label">
CA file <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input value=ca_file id="ca_file" name="ca_file" class="input"}}
</div>
<p class="help">
Specifies the path to a CA root file (PEM format) that the secondary can use when unwrapping the token from the primary.
</p>
</div>
<div class="field">
<label for="ca_path" class="is-label">
CA path <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input value=ca_path id="ca_path" name="ca_file" class="input"}}
</div>
<p class="help">
Specifies the path to a CA root directory containing PEM-format files that the secondary can use when unwrapping the token from the primary.
</p>
</div>
</div>
<p class="help has-text-grey">
Set this to the API address (normal Vault address) to override the
value embedded in the token.
</p>
</div>
<div class="field">
<label for="ca_file" class="is-label">
CA file <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input value=ca_file id="ca_file" name="ca_file" class="input"}}
</div>
<p class="help has-text-grey">
Specifies the path to a CA root file (PEM format) that the secondary can use when unwrapping the token from the primary.
</p>
</div>
<div class="field">
<label for="ca_path" class="is-label">
CA path <em class="is-optional">(optional)</em>
</label>
<div class="control">
{{input value=ca_path id="ca_path" name="ca_file" class="input"}}
</div>
<p class="help has-text-grey">
Specifies the path to a CA root directory containing PEM-format files that the secondary can use when unwrapping the token from the primary.
</p>
</div>
{{/if}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<div class="field">
<div class="control">
{{#confirm-action

View File

@@ -8,7 +8,7 @@
mode="dr"
display='menu'
cluster=cluster
tagName='a'
tagName=(or itemTag 'a')
click=(action d.actions.close)
}}
</li>
@@ -21,7 +21,7 @@
mode="performance"
display='menu'
cluster=cluster
tagName='a'
tagName=(or itemTag 'a')
click=(action d.actions.close)
}}
</li>

View File

@@ -0,0 +1,3 @@
<svg width="{{size}}" height="{{size}}" viewBox="0 0 16 16">
<path d="M8.75,9.01776695 L11.5355339,6.23223305 L12.5961941,7.29289322 L8,11.8890873 L3.40380592,7.29289322 L4.46446609,6.23223305 L7.25,9.01776695 L7.25,1.71446609 L8.75,1.71446609 L8.75,9.01776695 Z M11.75,13.1704102 L11.75,11.25 L13.25,11.25 L13.25,14.6704102 L2.75,14.6704102 L2.75,11.25 L4.25,11.25 L4.25,13.1704102 L11.75,13.1704102 Z" fill-rule="nonzero"></path>
</svg>

After

Width:  |  Height:  |  Size: 442 B

View File

@@ -1,30 +1,22 @@
{{#splash-page as |s|}}
{{#s.header}}
<div class="has-text-white">
{{!-- {{i-con glyph="unlocked" size=20}} {{capitalize model.name}} is {{if model.unsealed 'unsealed' 'sealed'}} --}}
<SplashPage as |Page|>
<Page.header>
<h1 class="title is-3">
Sign in to Vault
</div>
{{/s.header}}
{{#s.content}}
<div class="message is-highlight is-marginless">
<div class="message-body is-shadowless">
<div class="columns is-multiline">
<div class="column is-narrow" data-test-cluster-status="true">
{{i-con glyph="unlocked" size=20}} {{capitalize model.name}} is {{if model.unsealed 'unsealed' 'sealed'}}
</div>
</div>
</div>
</div>
{{auth-form
cluster=model
redirectTo=redirectTo
selectedAuthType=with
}}
{{/s.content}}
{{#s.footer}}
</h1>
</Page.header>
<Page.content>
<AuthForm
@cluster={{model}}
@redirectTo={{redirectTo}}
@selectedAuthType={{with}}
/>
</Page.content>
<Page.footer>
<div class="has-short-padding">
<p class="help has-text-grey-dark">
Contact your administrator for login credentials
</p>
</div>
{{/s.footer}}
{{/splash-page}}
</Page.footer>
</SplashPage>

View File

@@ -1,42 +1,36 @@
{{#splash-page as |s|}}
{{#s.header}}
<div class="has-text-white-bis">
Connect to Vault and initialize
</div>
{{/s.header}}
{{#s.content}}
{{#if keyData}}
<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>
</Page.header>
<Page.content>
<div class="box is-marginless is-shadowless">
<div class="columns is-mobile">
<div class="column is-half">
<h1 class="title is-5">
Vault has been initialized!
</h1>
</div>
<div class="column is-half is-relative">
<div class="init-illustration">
{{partial "svg/initialize"}}
</div>
</div>
</div>
<div class="message is-highlight is-relative">
<div class="message-body">
<h4 class="title is-uppercase is-7 is-marginless">
Initial Root Token
</h4>
<code class="is-word-break">{{keyData.root_token}}</code>
</div>
</div>
<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.
</p>
</div>
<div class="message is-list is-highlight has-copy-button" tabindex="-1">
<HoverCopyButton @alwaysShow=true @copyValue={{keyData.root_token}} />
<div class="message-body">
<h4 class="title is-7 is-marginless">
Initial Root Token
</h4>
<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">
<div class="message is-list has-copy-button" tabindex="-1">
<HoverCopyButton @copyValue={{key}} />
<div class="message-body">
<h4 class="title is-7 is-uppercase is-marginless">
<h4 class="title is-7 is-marginless">
Key {{add index 1}}
</h4>
<code class="is-word-break">{{key}}</code>
@@ -44,7 +38,7 @@
</div>
{{/each}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<div class="field is-grouped-split">
{{#if model.sealed}}
<div class="control">
@@ -59,18 +53,26 @@
{{/link-to}}
</div>
{{/if}}
{{download-button
actionText="Download Keys"
data=keyData
filename=keyFilename
mime="application/json"
extension="json"
class="button"
stringify=true
}}
<DownloadButton
@data={{keyData}}
@filename={{keyFilename}}
@mime="application/json"
@extension="json"
@class="button is-ghost"
@stringify={{true}}
>
<ICon @glyph="download" @size=16 /> Download Keys
</DownloadButton>
</div>
</div>
{{else}}
</Page.content>
{{else}}
<Page.header>
<h1 class="title h5">
Let's set up the initial set of master keys that youll need in case of an emergency
</h1>
</Page.header>
<Page.content>
<form {{action 'initCluster' (hash
secret_shares=secret_shares
secret_threshold=secret_threshold
@@ -84,80 +86,62 @@
id="init"
>
<div class="box is-marginless is-shadowless">
<div class="columns is-mobile">
<div class="column is-half">
<h1 class="title is-5">
Lets set up the initial set of master keys and the backend data store structure
</h1>
</div>
<div class="column is-half is-relative">
<div class="init-illustration">
{{partial "svg/initialize"}}
</div>
<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}}
</div>
<p class="help has-text-grey">
The number of key shares to split the master key into
</p>
</div>
{{message-error errors=errors}}
<div class="columns is-mobile">
<div class="column is-half">
<div class="field">
<label for="key-shares" class="is-label">
Key Shares
</label>
<div class="control">
{{input class="input" 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
</p>
</div>
</div>
<div class="column is-half">
<div class="field">
<label for="key-threshold" class="is-label">
Key Threshold
</label>
<div class="control">
{{input class="input" 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>
<div class="field">
<label for="key-threshold" class="is-label">
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}}
</div>
<p class="help has-text-grey">
The number of key shares required to reconstruct the master key
</p>
</div>
{{toggle-button
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">
<div class="box init-box">
<p class="help has-text-grey">
The output unseal keys will be encrypted and hex-encoded, in order, with the given public keys.
</p>
{{pgp-list listLength=secret_shares onDataUpdate=(action 'setKeys')}}
<PgpList @listLength={{secret_shares}} @onDataUpdate={{action 'setKeys'}} />
</div>
{{/if}}
{{toggle-button
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">
<div class="box init-box">
<p class="help has-text-grey">
The root unseal key will be encrypted and hex-encoded with the given public key.
</p>
{{pgp-list listLength=1 onDataUpdate=(action 'setRootKey')}}
<PgpList @listLength=1 @onDataUpdate={{action 'setRootKey'}} />
</div>
{{/if}}
</div>
<div class="box is-marginless is-shadowless has-background-white-bis">
<div class="box is-marginless is-shadowless">
<button
type="submit"
class="button is-primary {{if loading 'is-loading'}}"
@@ -165,8 +149,12 @@
>
Initialize
</button>
<div class="init-illustration">
{{partial "svg/initialize"}}
</div>
</div>
</form>
{{/if}}
{{/s.content}}
{{/splash-page}}
</Page.content>
{{/if}}
</SplashPage>

View File

@@ -1,15 +1,20 @@
{{#splash-page as |s|}}
{{#s.header}}
<div class="box has-short-padding is-shadowless has-text-white has-background-transparent">
Disaster Recovery Secondary is enabled
</div>
{{/s.header}}
{{#s.content}}
<SplashPage as |Page|>
<Page.header>
<h1 class="title is-4">
Disaster Recovery Secondary is&nbsp;enabled
</h1>
</Page.header>
<Page.content>
<nav class="tabs sub-nav is-marginless">
<ul>
<li class="{{if (eq action '') 'is-active'}}">
{{#link-to 'vault.cluster.replication-dr-promote' (query-params action='')}}
Operation Token
Operation token
{{/link-to}}
</li>
<li class="{{if (eq action 'update') 'is-active'}}">
{{#link-to 'vault.cluster.replication-dr-promote' (query-params action='update')}}
Update primary
{{/link-to}}
</li>
<li class="{{if (eq action 'promote') 'is-active'}}">
@@ -17,47 +22,37 @@
Promote
{{/link-to}}
</li>
<li class="{{if (eq action 'update') 'is-active'}}">
{{#link-to 'vault.cluster.replication-dr-promote' (query-params action='update')}}
Update Primary
{{/link-to}}
</li>
</ul>
</nav>
{{#if (eq action 'promote')}}
<div class="message is-warning is-shadowless is-marginless">
<div class="message-body">
<div class="columns is-mobile is-variable is-1">
<div class="column is-narrow">
{{i-con glyph="alert-circled" size=28 excludeIconClass=true}}
</div>
<div class="column">
<p class="is-size-8">
<strong>
<em>Caution</em>: Vault replication is not designed for active-active usage and enabling two performance primaries should never be done, as it can lead to data loss if they or their secondaries are ever reconnected.
</strong>
</p>
</div>
</div>
</div>
</div>
{{replication-actions replicationMode="dr" selectedAction="promote" model=model}}
<MessageInPage data-test-cluster-status @type="warning" @class="unseal-warning">
<em>Caution</em>: Vault replication is not designed for active-active usage and enabling two performance primaries should never be done, as it can lead to data loss if they or their secondaries are ever reconnected.
</MessageInPage>
<ReplicationActions
@replicationMode="dr"
@selectedAction="promote"
@model={{model}}
/>
{{/if}}
{{#if (eq action 'update')}}
{{replication-actions replicationMode="dr" selectedAction="updatePrimary" model=model}}
<ReplicationActions
@replicationMode="dr"
@selectedAction="updatePrimary"
@model={{model}}
/>
{{/if}}
{{#unless action}}
{{#shamir-flow
action="generate-dr-operation-token"
buttonText="Promote cluster"
fetchOnInit=true
generateAction=true
}}
<p class="has-text-grey">
<ShamirFlow
@action="generate-dr-operation-token"
@buttonText="Promote cluster"
@fetchOnInit=true
@generateAction=true
>
<p>
Generate an Operation Token by entering a portion of the master key.
Once all portions are entered, the generated operation token may be used to manage your Seondary Disaster Recovery cluster.
</p>
{{/shamir-flow}}
</ShamirFlow>
{{/unless}}
{{/s.content}}
{{/splash-page}}
</Page.content>
</SplashPage>

View File

@@ -1,31 +1,25 @@
{{#splash-page as |s|}}
{{#s.header}}
<div class="has-text-white-bis">
<SplashPage as |Page|>
<Page.header>
<h1 class="title is-3">
Unseal Vault
</div>
{{/s.header}}
{{#s.content}}
<div class="message is-highlight is-marginless">
<div class="message-body is-shadowless">
<div class="columns is-multiline">
<div class="column is-narrow" data-test-cluster-status="true">
{{i-con glyph="unlocked" size=20}} {{capitalize model.name}} is {{if model.unsealed 'unsealed' 'sealed'}}
</div>
</div>
</div>
</div>
{{#shamir-flow
action="unseal"
onShamirSuccess=(action 'transitionToCluster')
buttonText="Unseal"
thresholdPath="t"
isComplete=(action 'isUnsealed')
threshold=model.sealThreshold
progress=model.sealProgress
}}
<p class="has-text-grey">
</h1>
</Page.header>
<Page.content>
<MessageInPage data-test-cluster-status @type="warning" @class="unseal-warning">
{{capitalize model.name}} is {{if model.unsealed 'unsealed' 'sealed'}}
</MessageInPage>
<ShamirFlow
@action="unseal"
@onShamirSuccess={{action 'transitionToCluster'}}
@buttonText="Unseal"
@thresholdPath="t"
@isComplete={{action 'isUnsealed'}}
@threshold={{model.sealThreshold}}
@progress={{model.sealProgress}}
>
<p class="has-text-grey-dark">
Unseal the vault by entering a portion of the master key. Once all portions are entered, the vault will be unsealed.
</p>
{{/shamir-flow}}
{{/s.content}}
{{/splash-page}}
</ShamirFlow>
</Page.content>
</SplashPage>

View File

@@ -1,12 +1,10 @@
<header data-test-header-without-nav>
<nav class="navbar has-dark-grey-gradient is-grouped-split">
<div class="navbar-brand">
{{#home-link class="navbar-item has-text-white has-current-color-fill"}}
{{partial 'svg/vault-logo'}}
{{/home-link}}
</div>
</nav>
</header>
<NavHeader data-test-header-without-nav as |Nav|>
<Nav.home>
<HomeLink @class="navbar-item splash-page-logo">
{{partial "svg/vault-edition-logo"}}
</HomeLink>
</Nav.home>
</NavHeader>
<section class="section">
<div class="container">
{{#if (eq model.httpStatus 404)}}

View File

@@ -47,8 +47,8 @@
"ember-ajax": "^3.0.0",
"ember-angle-bracket-invocation-polyfill": "^1.0.2",
"ember-api-actions": "^0.1.8",
"ember-basic-dropdown": "^0.33.5",
"ember-basic-dropdown-hover": "^0.2.0",
"ember-basic-dropdown": "^1.0.0",
"ember-basic-dropdown-hover": "^0.5.0",
"ember-cli": "~2.16.0",
"ember-cli-autoprefixer": "^0.8.1",
"ember-cli-babel": "^6.3.0",
@@ -81,6 +81,7 @@
"ember-fetch": "^3.4.3",
"ember-href-to": "^1.13.0",
"ember-load-initializers": "^1.0.0",
"ember-maybe-in-element": "^0.1.3",
"ember-moment": "7.0.0-beta.5",
"ember-qunit-assert-helpers": "^0.1.3",
"ember-radio-button": "^1.1.1",

View File

@@ -36,10 +36,7 @@ test('seal then unseal', function(assert) {
pollCluster();
});
andThen(() => {
assert.ok(
find('[data-test-cluster-status]').text().includes('is unsealed'),
'ui indicates the vault is unsealed'
);
assert.equal(find('[data-test-cluster-status]').length, 0, 'ui does not show sealed warning');
assert.ok(currentURL().match(/\/vault\/auth/), 'vault is ready to authenticate');
});
});

View File

@@ -0,0 +1,39 @@
import { moduleForComponent, test } from 'ember-qunit';
import { create } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
import copyButton from 'vault/tests/pages/components/hover-copy-button';
import { triggerSuccess } from '../../helpers/ember-cli-clipboard';
const component = create(copyButton);
moduleForComponent('hover-copy-button', 'Integration | Component | hover copy button', {
integration: true,
beforeEach() {
component.setContext(this);
},
afterEach() {
component.removeContext();
},
});
test('it shows success message in tooltip', function(assert) {
this.set('copyValue', 'foo');
this.render(
hbs`<div class="has-copy-button" tabindex="-1">{{hover-copy-button copyValue=copyValue}}</div>`
);
component.focusContainer();
assert.ok(component.buttonIsVisible);
component.mouseEnter();
assert.equal(component.tooltipText, 'Copy', 'shows copy');
triggerSuccess(this, '[data-test-hover-copy-button]');
assert.equal(component.tooltipText, 'Copied!', 'shows success message');
});
test('it has the correct class when alwaysShow is true', function(assert) {
this.set('copyValue', 'foo');
this.render(hbs`{{hover-copy-button alwaysShow=true copyValue=copyValue}}`);
assert.ok(component.buttonIsVisible);
assert.ok(component.wrapperClass.includes('hover-copy-button-static'));
});

View File

@@ -0,0 +1,39 @@
import { moduleForComponent, test } from 'ember-qunit';
import { create } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
import navHeader from 'vault/tests/pages/components/nav-header';
const component = create(navHeader);
moduleForComponent('nav-header', 'Integration | Component | nav header', {
integration: true,
beforeEach() {
component.setContext(this);
},
afterEach() {
component.removeContext();
},
});
test('it renders', function(assert) {
this.render(hbs`
{{#nav-header as |h|}}
{{#h.home}}
Home!
{{/h.home}}
{{#h.items}}
Some Items
{{/h.items}}
{{#h.main}}
Main stuff here
{{/h.main}}
{{/nav-header}}
`);
assert.ok(component.ele, 'renders the outer element');
assert.equal(component.homeText.trim(), 'Home!', 'renders home content');
assert.equal(component.itemsText.trim(), 'Some Items', 'renders items content');
assert.equal(component.mainText.trim(), 'Main stuff here', 'renders items content');
});

View File

@@ -0,0 +1,33 @@
import { moduleForComponent, test } from 'ember-qunit';
import { create } from 'ember-cli-page-object';
import hbs from 'htmlbars-inline-precompile';
import radialProgress from 'vault/tests/pages/components/radial-progress';
const component = create(radialProgress);
moduleForComponent('radial-progress', 'Integration | Component | radial progress', {
integration: true,
beforeEach() {
component.setContext(this);
},
afterEach() {
component.removeContext();
},
});
test('it renders', function(assert) {
let circumference = 19 / 2 * Math.PI * 2;
this.render(hbs`{{radial-progress progressDecimal=0.5}}`);
assert.equal(component.viewBox, '0 0 20 20');
assert.equal(component.height, '20');
assert.equal(component.width, '20');
assert.equal(component.strokeWidth, '1');
assert.equal(component.r, 19 / 2);
assert.equal(component.cx, 10);
assert.equal(component.cy, 10);
assert.equal(component.strokeDash, circumference);
assert.equal(component.strokeDashOffset, circumference * 0.5);
});

View File

@@ -8,11 +8,6 @@ let response = {
complete: false,
};
let percent = () => {
const percent = response.progress / response.required * 100;
return percent.toFixed(4);
};
let adapter = {
foo() {
return Ember.RSVP.resolve(response);
@@ -34,14 +29,10 @@ moduleForComponent('shamir-flow', 'Integration | Component | shamir flow', {
});
test('it renders', function(assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.on('myAction', function(val) { ... });
this.render(hbs`{{shamir-flow formText="like whoa"}}`);
assert.equal(this.$('form p').text().trim(), 'like whoa', 'renders formText inline');
// Template block usage:
this.render(hbs`
{{#shamir-flow formText="like whoa"}}
<p>whoa again</p>
@@ -55,11 +46,7 @@ test('it renders', function(assert) {
{{shamir-flow progress=1 threshold=5}}
`);
assert.equal(
this.$('.shamir-progress .progress').val(),
1 / 5 * 100,
'renders progress bar with the appropriate value'
);
assert.ok(this.$('.shamir-progress').text().includes('1/5 keys provided'), 'displays textual progress');
this.set('errors', ['first error', 'this is fine']);
this.render(hbs`
@@ -74,10 +61,9 @@ test('it sends data to the passed action', function(assert) {
{{shamir-flow key=key action='foo' thresholdPath='required'}}
`);
this.$('[data-test-shamir-submit]').click();
assert.equal(
this.$('.shamir-progress .progress').val(),
percent(),
'renders progress bar with correct percent value'
assert.ok(
this.$('.shamir-progress').text().includes(`${response.progress}/${response.required} keys provided`),
'displays the correct progress'
);
});
@@ -103,9 +89,8 @@ test('it fetches progress on init when fetchOnInit is true', function(assert) {
this.render(hbs`
{{shamir-flow action='foo' fetchOnInit=true}}
`);
assert.equal(
this.$('.shamir-progress .progress').val(),
percent(),
'renders progress bar with correct percent value'
assert.ok(
this.$('.shamir-progress').text().includes(`${response.progress}/${response.required} keys provided`),
'displays the correct progress'
);
});

View File

@@ -7,10 +7,10 @@ moduleForComponent('upgrade-link', 'Integration | Component | upgrade link', {
test('it renders with overlay', function(assert) {
this.render(hbs`
<div class="upgrade-link-container">
{{#upgrade-link data-test-link}}upgrade{{/upgrade-link}}
</div>
<div id="modal-wormhole"></div>
<div id="modal-wormhole"></div>
<div class="upgrade-link-container">
{{#upgrade-link data-test-link}}upgrade{{/upgrade-link}}
</div>
`);
assert.equal(this.$('.upgrade-link-container button').text().trim(), 'upgrade', 'renders link content');
@@ -28,10 +28,10 @@ test('it renders with overlay', function(assert) {
test('it adds custom classes', function(assert) {
this.render(hbs`
<div id="modal-wormhole"></div>
<div class="upgrade-link-container">
{{#upgrade-link linkClass="button upgrade-button"}}upgrade{{/upgrade-link}}
</div>
<div id="modal-wormhole"></div>
`);
assert.equal(

View File

@@ -0,0 +1,9 @@
import { attribute, isVisible, triggerable, focusable, text } from 'ember-cli-page-object';
export default {
focusContainer: focusable('.has-copy-button'),
mouseEnter: triggerable('mouseenter', '[data-test-tooltip-trigger]'),
tooltipText: text('[data-test-hover-copy-tooltip-text]'),
wrapperClass: attribute('class', '[data-test-hover-copy]'),
buttonIsVisible: isVisible('[data-test-hover-copy-button]'),
};

View File

@@ -0,0 +1,8 @@
import { text, isPresent } from 'ember-cli-page-object';
export default {
ele: isPresent('[data-test-navheader]'),
homeText: text('[data-test-navheader-home]'),
itemsText: text('[data-test-navheader-items]'),
mainText: text('[data-test-navheader-main]'),
};

View File

@@ -0,0 +1,13 @@
import { attribute } from 'ember-cli-page-object';
export default {
viewBox: attribute('viewBox', '[data-test-radial-progress]'),
height: attribute('height', '[data-test-radial-progress]'),
width: attribute('width', '[data-test-radial-progress]'),
cx: attribute('cx', '[data-test-path]'),
cy: attribute('cy', '[data-test-path]'),
r: attribute('r', '[data-test-path]'),
strokeWidth: attribute('stroke-width', '[data-test-path]'),
strokeDash: attribute('stroke-dasharray', '[data-test-progress]'),
strokeDashOffset: attribute('stroke-dashoffset', '[data-test-progress]'),
};

View File

@@ -3,6 +3,8 @@ import './helpers/flash-message';
import { setResolver } from 'ember-qunit';
import { start } from 'ember-cli-qunit';
import { useNativeEvents } from 'ember-cli-page-object/extend';
useNativeEvents();
setResolver(resolver);
start();

View File

@@ -192,6 +192,12 @@ amd-name-resolver@1.0.0:
dependencies:
ensure-posix-path "^1.0.1"
amd-name-resolver@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/amd-name-resolver/-/amd-name-resolver-1.2.0.tgz#fc41b3848824b557313897d71f8d5a0184fbe679"
dependencies:
ensure-posix-path "^1.0.1"
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -463,6 +469,14 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
dependencies:
chalk "^1.1.3"
esutils "^2.0.2"
js-tokens "^3.0.2"
babel-core@^5.0.0:
version "5.8.38"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558"
@@ -538,6 +552,30 @@ babel-core@^6.14.0, babel-core@^6.24.1:
slash "^1.0.0"
source-map "^0.5.0"
babel-core@^6.26.0:
version "6.26.3"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
dependencies:
babel-code-frame "^6.26.0"
babel-generator "^6.26.0"
babel-helpers "^6.24.1"
babel-messages "^6.23.0"
babel-register "^6.26.0"
babel-runtime "^6.26.0"
babel-template "^6.26.0"
babel-traverse "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
convert-source-map "^1.5.1"
debug "^2.6.9"
json5 "^0.5.1"
lodash "^4.17.4"
minimatch "^3.0.4"
path-is-absolute "^1.0.1"
private "^0.1.8"
slash "^1.0.0"
source-map "^0.5.7"
babel-generator@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc"
@@ -551,6 +589,19 @@ babel-generator@^6.25.0:
source-map "^0.5.0"
trim-right "^1.0.1"
babel-generator@^6.26.0:
version "6.26.1"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
dependencies:
babel-messages "^6.23.0"
babel-runtime "^6.26.0"
babel-types "^6.26.0"
detect-indent "^4.0.0"
jsesc "^1.3.0"
lodash "^4.17.4"
source-map "^0.5.7"
trim-right "^1.0.1"
babel-helper-builder-binary-assignment-operator-visitor@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664"
@@ -996,6 +1047,14 @@ babel-polyfill@^6.16.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
babel-polyfill@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153"
dependencies:
babel-runtime "^6.26.0"
core-js "^2.5.0"
regenerator-runtime "^0.10.5"
babel-preset-env@^1.5.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
@@ -1031,6 +1090,41 @@ babel-preset-env@^1.5.1:
invariant "^2.2.2"
semver "^5.3.0"
babel-preset-env@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
dependencies:
babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-syntax-trailing-function-commas "^6.22.0"
babel-plugin-transform-async-to-generator "^6.22.0"
babel-plugin-transform-es2015-arrow-functions "^6.22.0"
babel-plugin-transform-es2015-block-scoped-functions "^6.22.0"
babel-plugin-transform-es2015-block-scoping "^6.23.0"
babel-plugin-transform-es2015-classes "^6.23.0"
babel-plugin-transform-es2015-computed-properties "^6.22.0"
babel-plugin-transform-es2015-destructuring "^6.23.0"
babel-plugin-transform-es2015-duplicate-keys "^6.22.0"
babel-plugin-transform-es2015-for-of "^6.23.0"
babel-plugin-transform-es2015-function-name "^6.22.0"
babel-plugin-transform-es2015-literals "^6.22.0"
babel-plugin-transform-es2015-modules-amd "^6.22.0"
babel-plugin-transform-es2015-modules-commonjs "^6.23.0"
babel-plugin-transform-es2015-modules-systemjs "^6.23.0"
babel-plugin-transform-es2015-modules-umd "^6.23.0"
babel-plugin-transform-es2015-object-super "^6.22.0"
babel-plugin-transform-es2015-parameters "^6.23.0"
babel-plugin-transform-es2015-shorthand-properties "^6.22.0"
babel-plugin-transform-es2015-spread "^6.22.0"
babel-plugin-transform-es2015-sticky-regex "^6.22.0"
babel-plugin-transform-es2015-template-literals "^6.22.0"
babel-plugin-transform-es2015-typeof-symbol "^6.23.0"
babel-plugin-transform-es2015-unicode-regex "^6.22.0"
babel-plugin-transform-exponentiation-operator "^6.22.0"
babel-plugin-transform-regenerator "^6.22.0"
browserslist "^3.2.6"
invariant "^2.2.2"
semver "^5.3.0"
babel-register@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.24.1.tgz#7e10e13a2f71065bdfad5a1787ba45bca6ded75f"
@@ -1043,6 +1137,18 @@ babel-register@^6.24.1:
mkdirp "^0.5.1"
source-map-support "^0.4.2"
babel-register@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
dependencies:
babel-core "^6.26.0"
babel-runtime "^6.26.0"
core-js "^2.5.0"
home-or-tmp "^2.0.0"
lodash "^4.17.4"
mkdirp "^0.5.1"
source-map-support "^0.4.15"
babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b"
@@ -1050,6 +1156,13 @@ babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
babel-runtime@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
babel-template@^6.24.1, babel-template@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
@@ -1060,6 +1173,16 @@ babel-template@^6.24.1, babel-template@^6.25.0:
babylon "^6.17.2"
lodash "^4.2.0"
babel-template@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
dependencies:
babel-runtime "^6.26.0"
babel-traverse "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
lodash "^4.17.4"
babel-traverse@^6.24.1, babel-traverse@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
@@ -1074,6 +1197,20 @@ babel-traverse@^6.24.1, babel-traverse@^6.25.0:
invariant "^2.2.0"
lodash "^4.2.0"
babel-traverse@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
dependencies:
babel-code-frame "^6.26.0"
babel-messages "^6.23.0"
babel-runtime "^6.26.0"
babel-types "^6.26.0"
babylon "^6.18.0"
debug "^2.6.8"
globals "^9.18.0"
invariant "^2.2.2"
lodash "^4.17.4"
babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
version "6.25.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
@@ -1083,6 +1220,15 @@ babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.25.0:
lodash "^4.2.0"
to-fast-properties "^1.0.1"
babel-types@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
dependencies:
babel-runtime "^6.26.0"
esutils "^2.0.2"
lodash "^4.17.4"
to-fast-properties "^1.0.3"
babel5-plugin-strip-class-callcheck@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/babel5-plugin-strip-class-callcheck/-/babel5-plugin-strip-class-callcheck-5.1.0.tgz#77d4a40c8614d367b8a21a53908159806dba5f91"
@@ -1099,6 +1245,10 @@ babylon@^6.17.2:
version "6.17.4"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
babylon@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
backbone@^1.1.2:
version "1.3.3"
resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.3.3.tgz#4cc80ea7cb1631ac474889ce40f2f8bc683b2999"
@@ -1350,6 +1500,21 @@ broccoli-babel-transpiler@^6.1.2:
rsvp "^3.5.0"
workerpool "^2.2.1"
broccoli-babel-transpiler@^6.4.2:
version "6.4.3"
resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.4.3.tgz#06e399298d41700cdc10d675b1d808a89ef6b2d0"
dependencies:
babel-core "^6.26.0"
broccoli-funnel "^2.0.1"
broccoli-merge-trees "^2.0.0"
broccoli-persistent-filter "^1.4.3"
clone "^2.0.0"
hash-for-dep "^1.2.3"
heimdalljs-logger "^0.1.7"
json-stable-stringify "^1.0.0"
rsvp "^4.8.2"
workerpool "^2.3.0"
broccoli-brocfile-loader@^0.18.0:
version "0.18.0"
resolved "https://registry.yarnpkg.com/broccoli-brocfile-loader/-/broccoli-brocfile-loader-0.18.0.tgz#2e86021c805c34ffc8d29a2fb721cf273e819e4b"
@@ -1613,7 +1778,7 @@ broccoli-persistent-filter@^1.0.3, broccoli-persistent-filter@^1.1.6, broccoli-p
symlink-or-copy "^1.0.1"
walk-sync "^0.3.1"
broccoli-persistent-filter@^1.1.5:
broccoli-persistent-filter@^1.1.5, broccoli-persistent-filter@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.3.tgz#3511bc52fc53740cda51621f58a28152d9911bc1"
dependencies:
@@ -1763,6 +1928,13 @@ browserslist@^2.11.3:
caniuse-lite "^1.0.30000792"
electron-to-chromium "^1.3.30"
browserslist@^3.2.6:
version "3.2.8"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6"
dependencies:
caniuse-lite "^1.0.30000844"
electron-to-chromium "^1.3.47"
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -1914,6 +2086,10 @@ caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805:
version "1.0.30000830"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000830.tgz#cb96b8a2dd3cbfe04acea2af3c4e894249095328"
caniuse-lite@^1.0.30000844:
version "1.0.30000855"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000855.tgz#d5a26a9093b932d6266bf4ed9294b41b84945d14"
capture-exit@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-1.2.0.tgz#1c5fcc489fd0ab00d4f1ac7ae1072e3173fbab6f"
@@ -2344,6 +2520,10 @@ convert-source-map@^1.1.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
cookie-signature@1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
@@ -2372,6 +2552,10 @@ core-js@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
core-js@^2.5.0:
version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
core-object@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/core-object/-/core-object-1.1.0.tgz#86d63918733cf9da1a5aae729e62c0a88e66ad0a"
@@ -2498,7 +2682,7 @@ debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.4.
dependencies:
ms "2.0.0"
debug@2.6.9, debug@^2.3.3:
debug@2.6.9, debug@^2.3.3, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -2716,6 +2900,10 @@ electron-to-chromium@^1.3.30:
version "1.3.42"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.42.tgz#95c33bf01d0cc405556aec899fe61fd4d76ea0f9"
electron-to-chromium@^1.3.47:
version "1.3.48"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz#d3b0d8593814044e092ece2108fc3ac9aea4b900"
ember-ajax@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ember-ajax/-/ember-ajax-3.0.0.tgz#8f21e9da0c1d433cf879aa855fce464d517e9ab5"
@@ -2735,29 +2923,28 @@ ember-api-actions@^0.1.8:
dependencies:
ember-cli-babel "^6.0.0"
ember-assign-helper@0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ember-assign-helper/-/ember-assign-helper-0.1.1.tgz#217f221f37781b64657bd371d9da911768c3fbd1"
ember-assign-helper@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ember-assign-helper/-/ember-assign-helper-0.1.2.tgz#3d1d575f3d4457b3662b214d4c6e671bd462cad0"
dependencies:
ember-cli-babel "^5.1.7"
ember-cli-babel "^6.0.0-beta.9"
ember-basic-dropdown-hover@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/ember-basic-dropdown-hover/-/ember-basic-dropdown-hover-0.2.0.tgz#bbedb70a6858562bb6ad00c55c26406cac3a8264"
ember-basic-dropdown-hover@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/ember-basic-dropdown-hover/-/ember-basic-dropdown-hover-0.5.0.tgz#130b86d19442a8e8e855ea8009f7f991f59390f2"
dependencies:
ember-assign-helper "0.1.1"
ember-basic-dropdown "^0.33.5"
ember-cli-babel "^5.1.7"
ember-cli-htmlbars "^1.1.1"
ember-basic-dropdown@^0.33.5:
version "0.33.5"
resolved "https://registry.yarnpkg.com/ember-basic-dropdown/-/ember-basic-dropdown-0.33.5.tgz#39986d4cc6732edf43fb51eabb70790e99e8ae2c"
dependencies:
ember-cli-babel "^6.8.1"
ember-assign-helper "^0.1.2"
ember-basic-dropdown "^1.0.0"
ember-cli-babel "^6.12.0"
ember-cli-htmlbars "^2.0.3"
ember-native-dom-helpers "^0.5.3"
ember-wormhole "^0.5.2"
ember-basic-dropdown@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ember-basic-dropdown/-/ember-basic-dropdown-1.0.1.tgz#413f929572028d613024966726992fb45351880c"
dependencies:
ember-cli-babel "^6.12.0"
ember-cli-htmlbars "^2.0.3"
ember-maybe-in-element "^0.1.3"
ember-cli-autoprefixer@^0.8.1:
version "0.8.1"
@@ -2828,6 +3015,24 @@ ember-cli-babel@^6.0.0-beta.7, ember-cli-babel@^6.1.0, ember-cli-babel@^6.6.0, e
clone "^2.0.0"
ember-cli-version-checker "^2.0.0"
ember-cli-babel@^6.0.0-beta.9, ember-cli-babel@^6.11.0, ember-cli-babel@^6.12.0:
version "6.14.1"
resolved "https://registry.yarnpkg.com/ember-cli-babel/-/ember-cli-babel-6.14.1.tgz#796339229035910b625593caffbc2683792ada68"
dependencies:
amd-name-resolver "1.2.0"
babel-plugin-debug-macros "^0.1.11"
babel-plugin-ember-modules-api-polyfill "^2.3.0"
babel-plugin-transform-es2015-modules-amd "^6.24.0"
babel-polyfill "^6.26.0"
babel-preset-env "^1.7.0"
broccoli-babel-transpiler "^6.4.2"
broccoli-debug "^0.6.4"
broccoli-funnel "^2.0.0"
broccoli-source "^1.1.0"
clone "^2.0.0"
ember-cli-version-checker "^2.1.2"
semver "^5.5.0"
ember-cli-broccoli-sane-watcher@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/ember-cli-broccoli-sane-watcher/-/ember-cli-broccoli-sane-watcher-2.0.4.tgz#f43f42f75b7509c212fb926cd9aea86ae19264c6"
@@ -2921,7 +3126,7 @@ ember-cli-htmlbars-inline-precompile@^0.4.3:
hash-for-dep "^1.0.2"
silent-error "^1.1.0"
ember-cli-htmlbars@^1.0.10, ember-cli-htmlbars@^1.1.1:
ember-cli-htmlbars@^1.0.10:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-1.3.4.tgz#461289724b34af372a6a0c4b6635819156963353"
dependencies:
@@ -3402,6 +3607,12 @@ ember-maybe-import-regenerator@^0.1.5:
ember-cli-babel "^6.0.0-beta.4"
regenerator-runtime "^0.9.5"
ember-maybe-in-element@^0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/ember-maybe-in-element/-/ember-maybe-in-element-0.1.3.tgz#1c89be49246e580c1090336ad8be31e373f71b60"
dependencies:
ember-cli-babel "^6.11.0"
ember-moment@7.0.0-beta.5:
version "7.0.0-beta.5"
resolved "https://registry.yarnpkg.com/ember-moment/-/ember-moment-7.0.0-beta.5.tgz#b62c144d32f6ad0acaadd588ba93f4ddeb72ba89"
@@ -3564,13 +3775,6 @@ ember-try@^0.2.15:
rsvp "^3.0.17"
semver "^5.1.0"
ember-wormhole@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/ember-wormhole/-/ember-wormhole-0.5.2.tgz#cc0ceb7db4f8b8da0fd852edc81d75cb1dcd92f1"
dependencies:
ember-cli-babel "^6.0.0"
ember-cli-htmlbars "^1.1.1"
encodeurl@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
@@ -4582,7 +4786,7 @@ globals@^6.4.0:
version "6.4.1"
resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f"
globals@^9.0.0, globals@^9.14.0, globals@^9.17.0:
globals@^9.0.0, globals@^9.14.0, globals@^9.17.0, globals@^9.18.0:
version "9.18.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
@@ -5364,7 +5568,7 @@ js-tokens@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae"
js-tokens@^3.0.0:
js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@@ -5429,7 +5633,7 @@ json5@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d"
json5@^0.5.0:
json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
@@ -6671,7 +6875,7 @@ path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
path-is-absolute@^1.0.0:
path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@@ -6875,6 +7079,10 @@ private@^0.1.6, private@~0.1.5:
version "0.1.7"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1"
private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
@@ -7127,10 +7335,14 @@ regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
regenerator-runtime@^0.10.0:
regenerator-runtime@^0.10.0, regenerator-runtime@^0.10.5:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
regenerator-runtime@^0.9.5:
version "0.9.6"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.9.6.tgz#d33eb95d0d2001a4be39659707c51b0cb71ce029"
@@ -7439,6 +7651,10 @@ rsvp@^4.7.0:
version "4.7.0"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.7.0.tgz#dc1b0b1a536f7dec9d2be45e0a12ad4197c9fd96"
rsvp@^4.8.2:
version "4.8.2"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.2.tgz#9d5647108735784eb13418cdddb56f75b919d722"
rsvp@~3.0.6:
version "3.0.21"
resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.0.21.tgz#49c588fe18ef293bcd0ab9f4e6756e6ac433359f"
@@ -7549,7 +7765,7 @@ semver@^4.1.0:
version "4.3.6"
resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da"
semver@^5.4.1:
semver@^5.4.1, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
@@ -7801,6 +8017,12 @@ source-map-support@^0.2.10:
dependencies:
source-map "0.1.32"
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
dependencies:
source-map "^0.5.6"
source-map-support@^0.4.2:
version "0.4.15"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
@@ -7831,6 +8053,10 @@ source-map@^0.5.0, source-map@^0.5.6, source-map@~0.5.0, source-map@~0.5.1:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
source-map@^0.5.7:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@@ -8238,7 +8464,7 @@ to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
to-fast-properties@^1.0.0, to-fast-properties@^1.0.1:
to-fast-properties@^1.0.0, to-fast-properties@^1.0.1, to-fast-properties@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
@@ -8645,6 +8871,12 @@ workerpool@^2.2.1:
dependencies:
object-assign "4.1.1"
workerpool@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-2.3.0.tgz#86c5cbe946b55e7dc9d12b1936c8801a6e2d744d"
dependencies:
object-assign "4.1.1"
wrap-ansi@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85"