mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-29 17:52:32 +00:00
Glimmerize Splash Page (#24104)
* make splash page view only block content * change invocation of component * address some of the pr comments * add test coverage * remove conditional because of issue with it always showing * solve for mfa errors * move altcontent outside
This commit is contained in:
21
ui/app/components/splash-page.hbs
Normal file
21
ui/app/components/splash-page.hbs
Normal file
@@ -0,0 +1,21 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<div class="splash-page-container section is-flex-v-centered-tablet is-flex-grow-1 is-fullwidth">
|
||||
<div class="columns is-centered is-gapless is-fullwidth">
|
||||
<div class="column is-4-desktop is-6-tablet">
|
||||
<div class="splash-page-header" data-test-splash-page-header>
|
||||
{{yield to="header"}}
|
||||
</div>
|
||||
<div class="splash-page-sub-header" data-test-splash-page-sub-header>
|
||||
{{yield to="subHeader"}}
|
||||
</div>
|
||||
<div class="login-form box is-paddingless is-relative" data-test-splash-page-content>
|
||||
{{yield to="content"}}
|
||||
</div>
|
||||
{{yield to="footer"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module SplashPage
|
||||
* SplashPage component is used as a landing page with a box horizontally and center aligned on the page. It's used as the login landing page.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <SplashPage >
|
||||
* content here
|
||||
* </SplashPage
|
||||
* ```
|
||||
* @param {boolean} [hasAltContent] - boolean to bypass container styling
|
||||
* @param {boolean} [showTruncatedNavBar = true] - boolean to hide or show the navBar. By default this is true.
|
||||
*
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class SplashPage extends Component {
|
||||
@service version;
|
||||
@service auth;
|
||||
@service store;
|
||||
|
||||
get showTruncatedNavBar() {
|
||||
// default is true unless showTruncatedNavBar is defined as false
|
||||
return this.args.showTruncatedNavBar === false ? false : true;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
{{! bypass container styling }}
|
||||
{{#if @hasAltContent}}
|
||||
{{yield (hash altContent=(component "splash-page/splash-content"))}}
|
||||
{{else}}
|
||||
<div class="splash-page-container section is-flex-v-centered-tablet is-flex-grow-1 is-fullwidth">
|
||||
<div class="columns is-centered is-gapless is-fullwidth">
|
||||
<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="splash-page-sub-header">
|
||||
{{yield (hash sub-header=(component "splash-page/splash-header"))}}
|
||||
</div>
|
||||
<div class="login-form box is-paddingless is-relative">
|
||||
{{yield (hash content=(component "splash-page/splash-content"))}}
|
||||
</div>
|
||||
{{yield (hash footer=(component "splash-page/splash-content"))}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
@@ -3,126 +3,130 @@
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<SplashPage @hasAltContent={{this.mfaErrors}} as |Page|>
|
||||
<Page.altContent>
|
||||
<div class="has-top-margin-xxl" data-test-mfa-error>
|
||||
<EmptyState
|
||||
@title="Unauthorized"
|
||||
@message="Multi-factor authentication is required, but failed. Go back and try again, or contact your administrator."
|
||||
@icon="alert-circle"
|
||||
@bottomBorder={{true}}
|
||||
@subTitle={{join ". " this.mfaErrors}}
|
||||
class="is-shadowless"
|
||||
>
|
||||
<Hds::Button @text="Go back" @icon="chevron-left" @color="tertiary" {{on "click" (action "onMfaErrorDismiss")}} />
|
||||
</EmptyState>
|
||||
</div>
|
||||
</Page.altContent>
|
||||
<Page.header>
|
||||
{{#if this.oidcProvider}}
|
||||
<div class="box is-shadowless is-flex-v-centered" data-test-auth-logo>
|
||||
<LogoEdition aria-label="Sign in with Hashicorp Vault" />
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="is-flex-v-centered has-bottom-margin-xxl">
|
||||
<div class="brand-icon-large">
|
||||
<Icon @name="vault" @size="24" @stretched={{true}} />
|
||||
{{#if this.mfaErrors}}
|
||||
<div class="has-top-margin-xxl" data-test-mfa-error>
|
||||
<EmptyState
|
||||
@title="Unauthorized"
|
||||
@message="Multi-factor authentication is required, but failed. Go back and try again, or contact your administrator."
|
||||
@icon="alert-circle"
|
||||
@bottomBorder={{true}}
|
||||
@subTitle={{join ". " this.mfaErrors}}
|
||||
class="is-shadowless"
|
||||
>
|
||||
<Hds::Button @text="Go back" @icon="chevron-left" @color="tertiary" {{on "click" (action "onMfaErrorDismiss")}} />
|
||||
</EmptyState>
|
||||
</div>
|
||||
{{else}}
|
||||
<SplashPage>
|
||||
<:header>
|
||||
{{#if this.oidcProvider}}
|
||||
<div class="box is-shadowless is-flex-v-centered" data-test-auth-logo>
|
||||
<LogoEdition aria-label="Sign in with Hashicorp Vault" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-flex-row">
|
||||
{{#if this.mfaAuthData}}
|
||||
<Hds::Button
|
||||
@text="Back to login"
|
||||
@icon="arrow-left"
|
||||
@isIconOnly={{true}}
|
||||
@color="tertiary"
|
||||
{{on "click" (fn (mut this.mfaAuthData) null)}}
|
||||
/>
|
||||
{{else if this.waitingForOktaNumberChallenge}}
|
||||
<Hds::Button
|
||||
@text="Back to login"
|
||||
@icon="arrow-left"
|
||||
@isIconOnly={{true}}
|
||||
@color="tertiary"
|
||||
{{on "click" (action "cancelAuthentication")}}
|
||||
/>
|
||||
{{/if}}
|
||||
<h1 class="title is-3">
|
||||
{{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}}
|
||||
</h1>
|
||||
</div>
|
||||
{{/if}}
|
||||
</Page.header>
|
||||
{{#unless this.mfaAuthData}}
|
||||
{{#if (has-feature "Namespaces")}}
|
||||
<Page.sub-header>
|
||||
<Toolbar class="toolbar-namespace-picker">
|
||||
<div class="field is-horizontal" data-test-namespace-toolbar>
|
||||
<div class="field-label is-normal">
|
||||
<label class="is-label" for="namespace">Namespace</label>
|
||||
</div>
|
||||
{{#if this.managedNamespaceRoot}}
|
||||
<div class="field-label">
|
||||
<span class="has-text-grey" data-test-managed-namespace-root>/{{this.managedNamespaceRoot}}</span>
|
||||
{{else}}
|
||||
<div class="is-flex-v-centered has-bottom-margin-xxl">
|
||||
<div class="brand-icon-large">
|
||||
<Icon @name="vault" @size="24" @stretched={{true}} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-flex-row">
|
||||
{{#if this.mfaAuthData}}
|
||||
<Hds::Button
|
||||
@text="Back to login"
|
||||
@icon="arrow-left"
|
||||
@isIconOnly={{true}}
|
||||
@color="tertiary"
|
||||
{{on "click" (fn (mut this.mfaAuthData) null)}}
|
||||
/>
|
||||
{{else if this.waitingForOktaNumberChallenge}}
|
||||
<Hds::Button
|
||||
@text="Back to login"
|
||||
@icon="arrow-left"
|
||||
@isIconOnly={{true}}
|
||||
@color="tertiary"
|
||||
{{on "click" (action "cancelAuthentication")}}
|
||||
/>
|
||||
{{/if}}
|
||||
<h1 class="title is-3">
|
||||
{{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}}
|
||||
</h1>
|
||||
</div>
|
||||
{{/if}}
|
||||
</:header>
|
||||
|
||||
<:subHeader>
|
||||
{{#if (has-feature "Namespaces")}}
|
||||
{{#unless this.mfaAuthData}}
|
||||
<Toolbar class="toolbar-namespace-picker">
|
||||
<div class="field is-horizontal" data-test-namespace-toolbar>
|
||||
<div class="field-label is-normal">
|
||||
<label class="is-label" for="namespace">Namespace</label>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input
|
||||
data-test-auth-form-ns-input
|
||||
value={{this.namespaceInput}}
|
||||
placeholder={{if this.managedNamespaceRoot "/ (Default)" "/ (Root)"}}
|
||||
oninput={{perform this.updateNamespace value="target.value"}}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
name="namespace"
|
||||
id="namespace"
|
||||
class="input"
|
||||
type="text"
|
||||
disabled={{this.oidcProvider}}
|
||||
/>
|
||||
{{#if this.managedNamespaceRoot}}
|
||||
<div class="field-label">
|
||||
<span class="has-text-grey" data-test-managed-namespace-root>/{{this.managedNamespaceRoot}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="field-body">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<input
|
||||
data-test-auth-form-ns-input
|
||||
value={{this.namespaceInput}}
|
||||
placeholder={{if this.managedNamespaceRoot "/ (Default)" "/ (Root)"}}
|
||||
{{on "input" (perform this.updateNamespace value="target.value")}}
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
name="namespace"
|
||||
id="namespace"
|
||||
class="input"
|
||||
type="text"
|
||||
disabled={{this.oidcProvider}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Toolbar>
|
||||
</Page.sub-header>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
<Page.content>
|
||||
{{#if this.mfaAuthData}}
|
||||
<Mfa::MfaForm
|
||||
@clusterId={{this.model.id}}
|
||||
@authData={{this.mfaAuthData}}
|
||||
@onSuccess={{action "onMfaSuccess"}}
|
||||
@onError={{fn (mut this.mfaErrors)}}
|
||||
/>
|
||||
{{else}}
|
||||
<AuthForm
|
||||
@wrappedToken={{this.wrappedToken}}
|
||||
@cluster={{this.model}}
|
||||
@namespace={{this.namespaceQueryParam}}
|
||||
@redirectTo={{this.redirectTo}}
|
||||
@selectedAuth={{this.authMethod}}
|
||||
@onSuccess={{action "onAuthResponse"}}
|
||||
@setOktaNumberChallenge={{fn (mut this.waitingForOktaNumberChallenge)}}
|
||||
@waitingForOktaNumberChallenge={{this.waitingForOktaNumberChallenge}}
|
||||
@setCancellingAuth={{fn (mut this.cancelAuth)}}
|
||||
@cancelAuthForOktaNumberChallenge={{this.cancelAuth}}
|
||||
/>
|
||||
{{/if}}
|
||||
</Page.content>
|
||||
<Page.footer>
|
||||
<div class="has-short-padding">
|
||||
<p class="help has-text-grey-dark" data-test-auth-helptext>
|
||||
{{#if this.oidcProvider}}
|
||||
Once you log in, you will be redirected back to your application. If you require login credentials, contact your
|
||||
administrator.
|
||||
{{else}}
|
||||
Contact your administrator for login credentials
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
</Page.footer>
|
||||
</SplashPage>
|
||||
</Toolbar>
|
||||
{{/unless}}
|
||||
{{/if}}
|
||||
</:subHeader>
|
||||
|
||||
<:content>
|
||||
{{#if this.mfaAuthData}}
|
||||
<Mfa::MfaForm
|
||||
@clusterId={{this.model.id}}
|
||||
@authData={{this.mfaAuthData}}
|
||||
@onSuccess={{action "onMfaSuccess"}}
|
||||
@onError={{fn (mut this.mfaErrors)}}
|
||||
/>
|
||||
{{else}}
|
||||
<AuthForm
|
||||
@wrappedToken={{this.wrappedToken}}
|
||||
@cluster={{this.model}}
|
||||
@namespace={{this.namespaceQueryParam}}
|
||||
@redirectTo={{this.redirectTo}}
|
||||
@selectedAuth={{this.authMethod}}
|
||||
@onSuccess={{action "onAuthResponse"}}
|
||||
@setOktaNumberChallenge={{fn (mut this.waitingForOktaNumberChallenge)}}
|
||||
@waitingForOktaNumberChallenge={{this.waitingForOktaNumberChallenge}}
|
||||
@setCancellingAuth={{fn (mut this.cancelAuth)}}
|
||||
@cancelAuthForOktaNumberChallenge={{this.cancelAuth}}
|
||||
/>
|
||||
{{/if}}
|
||||
</:content>
|
||||
|
||||
<:footer>
|
||||
<div class="has-short-padding">
|
||||
<p class="help has-text-grey-dark" data-test-auth-helptext>
|
||||
{{#if this.oidcProvider}}
|
||||
Once you log in, you will be redirected back to your application. If you require login credentials, contact your
|
||||
administrator.
|
||||
{{else}}
|
||||
Contact your administrator for login credentials.
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
</:footer>
|
||||
</SplashPage>
|
||||
{{/if}}
|
||||
@@ -3,18 +3,13 @@
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<SplashPage as |Page|>
|
||||
{{#if (and this.model.usingRaft (not this.prefersInit))}}
|
||||
<Page.header>
|
||||
<SplashPage>
|
||||
<:header>
|
||||
{{#if (and this.model.usingRaft (not this.prefersInit))}}
|
||||
<h1 class="title is-4">
|
||||
Raft Storage
|
||||
</h1>
|
||||
</Page.header>
|
||||
<Page.content>
|
||||
<RaftJoin @onDismiss={{action (mut this.prefersInit) true}} />
|
||||
</Page.content>
|
||||
{{else if this.keyData}}
|
||||
<Page.header>
|
||||
{{else if this.keyData}}
|
||||
{{#let (or this.keyData.recovery_keys this.keyData.keys) as |keyArray|}}
|
||||
<h1 class="title is-4">
|
||||
Vault has been initialized!
|
||||
@@ -26,8 +21,16 @@
|
||||
{{/if}}
|
||||
</h1>
|
||||
{{/let}}
|
||||
</Page.header>
|
||||
<Page.content>
|
||||
{{else}}
|
||||
<h1 class="title h5">
|
||||
Let's set up the initial set of root keys that you will need in case of an emergency.
|
||||
</h1>
|
||||
{{/if}}
|
||||
</:header>
|
||||
<:content>
|
||||
{{#if (and this.model.usingRaft (not this.prefersInit))}}
|
||||
<RaftJoin @onDismiss={{action (mut this.prefersInit) true}} />
|
||||
{{else if this.keyData}}
|
||||
<div class="box is-marginless is-shadowless">
|
||||
<div class="content">
|
||||
<p>
|
||||
@@ -104,14 +107,7 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Page.content>
|
||||
{{else}}
|
||||
<Page.header>
|
||||
<h1 class="title h5">
|
||||
Let's set up the initial set of root keys that you’ll need in case of an emergency
|
||||
</h1>
|
||||
</Page.header>
|
||||
<Page.content>
|
||||
{{else}}
|
||||
<form
|
||||
{{action
|
||||
"initCluster"
|
||||
@@ -217,6 +213,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Page.content>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</:content>
|
||||
</SplashPage>
|
||||
@@ -3,11 +3,11 @@
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
~}}
|
||||
|
||||
<SplashPage @showTruncatedNavBar={{false}} as |Page|>
|
||||
<Page.header>
|
||||
<SplashPage>
|
||||
<:header>
|
||||
<h1 class="title is-4">MFA setup</h1>
|
||||
</Page.header>
|
||||
<Page.content>
|
||||
</:header>
|
||||
<:content>
|
||||
<div class="auth-form" data-test-mfa-form>
|
||||
<div class="box">
|
||||
{{#if (eq this.onStep 1)}}
|
||||
@@ -31,5 +31,5 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</Page.content>
|
||||
</:content>
|
||||
</SplashPage>
|
||||
@@ -23,13 +23,14 @@
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<SplashPage as |Page|>
|
||||
<Page.header>
|
||||
<SplashPage>
|
||||
<:header>
|
||||
<h1 class="title is-3">
|
||||
Unseal Vault
|
||||
</h1>
|
||||
</Page.header>
|
||||
<Page.content>
|
||||
</:header>
|
||||
|
||||
<:content>
|
||||
<div class="box is-borderless is-shadowless is-marginless">
|
||||
<p class="title is-5">
|
||||
{{capitalize this.model.name}}
|
||||
@@ -57,8 +58,9 @@
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
</Page.content>
|
||||
<Page.footer>
|
||||
</:content>
|
||||
|
||||
<:footer>
|
||||
<div class="box is-borderless is-shadowless">
|
||||
<p>
|
||||
<ExternalLink @href="https://developer.hashicorp.com/vault/docs/concepts/seal">
|
||||
@@ -66,6 +68,6 @@
|
||||
</ExternalLink>
|
||||
</p>
|
||||
</div>
|
||||
</Page.footer>
|
||||
</:footer>
|
||||
</SplashPage>
|
||||
{{/if}}
|
||||
37
ui/tests/integration/components/splash-page-test.js
Normal file
37
ui/tests/integration/components/splash-page-test.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | splash-page', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it should render', async function (assert) {
|
||||
assert.expect(4);
|
||||
await render(hbs`<SplashPage>
|
||||
<:header>
|
||||
Header here
|
||||
</:header>
|
||||
<:subHeader>
|
||||
sub header
|
||||
</:subHeader>
|
||||
<:content>
|
||||
content
|
||||
</:content>
|
||||
<:footer>
|
||||
<div data-test-footer>footer</div>
|
||||
</:footer>
|
||||
|
||||
</SplashPage>
|
||||
`);
|
||||
assert.dom('[data-test-splash-page-header]').includesText('Header here', 'Header renders');
|
||||
assert.dom('[data-test-splash-page-sub-header]').includesText('sub header', 'SubHeader renders');
|
||||
assert.dom('[data-test-splash-page-content]').includesText('content', 'Content renders');
|
||||
assert.dom('[data-test-footer]').includesText('footer', 'Footer renders');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user