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:
Angel Garbarino
2023-11-27 10:21:35 -07:00
committed by GitHub
parent 904c08e1e4
commit 0ca6135f68
11 changed files with 211 additions and 241 deletions

View 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>

View File

@@ -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;
}
}

View File

@@ -1,10 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@@ -1,10 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@@ -1,10 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@@ -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}}

View File

@@ -3,126 +3,130 @@
SPDX-License-Identifier: BUSL-1.1 SPDX-License-Identifier: BUSL-1.1
~}} ~}}
<SplashPage @hasAltContent={{this.mfaErrors}} as |Page|> {{#if this.mfaErrors}}
<Page.altContent> <div class="has-top-margin-xxl" data-test-mfa-error>
<div class="has-top-margin-xxl" data-test-mfa-error> <EmptyState
<EmptyState @title="Unauthorized"
@title="Unauthorized" @message="Multi-factor authentication is required, but failed. Go back and try again, or contact your administrator."
@message="Multi-factor authentication is required, but failed. Go back and try again, or contact your administrator." @icon="alert-circle"
@icon="alert-circle" @bottomBorder={{true}}
@bottomBorder={{true}} @subTitle={{join ". " this.mfaErrors}}
@subTitle={{join ". " this.mfaErrors}} class="is-shadowless"
class="is-shadowless" >
> <Hds::Button @text="Go back" @icon="chevron-left" @color="tertiary" {{on "click" (action "onMfaErrorDismiss")}} />
<Hds::Button @text="Go back" @icon="chevron-left" @color="tertiary" {{on "click" (action "onMfaErrorDismiss")}} /> </EmptyState>
</EmptyState> </div>
</div> {{else}}
</Page.altContent> <SplashPage>
<Page.header> <:header>
{{#if this.oidcProvider}} {{#if this.oidcProvider}}
<div class="box is-shadowless is-flex-v-centered" data-test-auth-logo> <div class="box is-shadowless is-flex-v-centered" data-test-auth-logo>
<LogoEdition aria-label="Sign in with Hashicorp Vault" /> <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}} />
</div> </div>
</div> {{else}}
<div class="is-flex-row"> <div class="is-flex-v-centered has-bottom-margin-xxl">
{{#if this.mfaAuthData}} <div class="brand-icon-large">
<Hds::Button <Icon @name="vault" @size="24" @stretched={{true}} />
@text="Back to login" </div>
@icon="arrow-left" </div>
@isIconOnly={{true}} <div class="is-flex-row">
@color="tertiary" {{#if this.mfaAuthData}}
{{on "click" (fn (mut this.mfaAuthData) null)}} <Hds::Button
/> @text="Back to login"
{{else if this.waitingForOktaNumberChallenge}} @icon="arrow-left"
<Hds::Button @isIconOnly={{true}}
@text="Back to login" @color="tertiary"
@icon="arrow-left" {{on "click" (fn (mut this.mfaAuthData) null)}}
@isIconOnly={{true}} />
@color="tertiary" {{else if this.waitingForOktaNumberChallenge}}
{{on "click" (action "cancelAuthentication")}} <Hds::Button
/> @text="Back to login"
{{/if}} @icon="arrow-left"
<h1 class="title is-3"> @isIconOnly={{true}}
{{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}} @color="tertiary"
</h1> {{on "click" (action "cancelAuthentication")}}
</div> />
{{/if}} {{/if}}
</Page.header> <h1 class="title is-3">
{{#unless this.mfaAuthData}} {{if (or this.mfaAuthData this.waitingForOktaNumberChallenge) "Authenticate" "Sign in to Vault"}}
{{#if (has-feature "Namespaces")}} </h1>
<Page.sub-header> </div>
<Toolbar class="toolbar-namespace-picker"> {{/if}}
<div class="field is-horizontal" data-test-namespace-toolbar> </:header>
<div class="field-label is-normal">
<label class="is-label" for="namespace">Namespace</label> <:subHeader>
</div> {{#if (has-feature "Namespaces")}}
{{#if this.managedNamespaceRoot}} {{#unless this.mfaAuthData}}
<div class="field-label"> <Toolbar class="toolbar-namespace-picker">
<span class="has-text-grey" data-test-managed-namespace-root>/{{this.managedNamespaceRoot}}</span> <div class="field is-horizontal" data-test-namespace-toolbar>
<div class="field-label is-normal">
<label class="is-label" for="namespace">Namespace</label>
</div> </div>
{{/if}} {{#if this.managedNamespaceRoot}}
<div class="field-body"> <div class="field-label">
<div class="field"> <span class="has-text-grey" data-test-managed-namespace-root>/{{this.managedNamespaceRoot}}</span>
<div class="control"> </div>
<input {{/if}}
data-test-auth-form-ns-input <div class="field-body">
value={{this.namespaceInput}} <div class="field">
placeholder={{if this.managedNamespaceRoot "/ (Default)" "/ (Root)"}} <div class="control">
oninput={{perform this.updateNamespace value="target.value"}} <input
autocomplete="off" data-test-auth-form-ns-input
spellcheck="false" value={{this.namespaceInput}}
name="namespace" placeholder={{if this.managedNamespaceRoot "/ (Default)" "/ (Root)"}}
id="namespace" {{on "input" (perform this.updateNamespace value="target.value")}}
class="input" autocomplete="off"
type="text" spellcheck="false"
disabled={{this.oidcProvider}} name="namespace"
/> id="namespace"
class="input"
type="text"
disabled={{this.oidcProvider}}
/>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </Toolbar>
</Toolbar> {{/unless}}
</Page.sub-header> {{/if}}
{{/if}} </:subHeader>
{{/unless}}
<Page.content> <:content>
{{#if this.mfaAuthData}} {{#if this.mfaAuthData}}
<Mfa::MfaForm <Mfa::MfaForm
@clusterId={{this.model.id}} @clusterId={{this.model.id}}
@authData={{this.mfaAuthData}} @authData={{this.mfaAuthData}}
@onSuccess={{action "onMfaSuccess"}} @onSuccess={{action "onMfaSuccess"}}
@onError={{fn (mut this.mfaErrors)}} @onError={{fn (mut this.mfaErrors)}}
/> />
{{else}} {{else}}
<AuthForm <AuthForm
@wrappedToken={{this.wrappedToken}} @wrappedToken={{this.wrappedToken}}
@cluster={{this.model}} @cluster={{this.model}}
@namespace={{this.namespaceQueryParam}} @namespace={{this.namespaceQueryParam}}
@redirectTo={{this.redirectTo}} @redirectTo={{this.redirectTo}}
@selectedAuth={{this.authMethod}} @selectedAuth={{this.authMethod}}
@onSuccess={{action "onAuthResponse"}} @onSuccess={{action "onAuthResponse"}}
@setOktaNumberChallenge={{fn (mut this.waitingForOktaNumberChallenge)}} @setOktaNumberChallenge={{fn (mut this.waitingForOktaNumberChallenge)}}
@waitingForOktaNumberChallenge={{this.waitingForOktaNumberChallenge}} @waitingForOktaNumberChallenge={{this.waitingForOktaNumberChallenge}}
@setCancellingAuth={{fn (mut this.cancelAuth)}} @setCancellingAuth={{fn (mut this.cancelAuth)}}
@cancelAuthForOktaNumberChallenge={{this.cancelAuth}} @cancelAuthForOktaNumberChallenge={{this.cancelAuth}}
/> />
{{/if}} {{/if}}
</Page.content> </:content>
<Page.footer>
<div class="has-short-padding"> <:footer>
<p class="help has-text-grey-dark" data-test-auth-helptext> <div class="has-short-padding">
{{#if this.oidcProvider}} <p class="help has-text-grey-dark" data-test-auth-helptext>
Once you log in, you will be redirected back to your application. If you require login credentials, contact your {{#if this.oidcProvider}}
administrator. Once you log in, you will be redirected back to your application. If you require login credentials, contact your
{{else}} administrator.
Contact your administrator for login credentials {{else}}
{{/if}} Contact your administrator for login credentials.
</p> {{/if}}
</div> </p>
</Page.footer> </div>
</SplashPage> </:footer>
</SplashPage>
{{/if}}

View File

@@ -3,18 +3,13 @@
SPDX-License-Identifier: BUSL-1.1 SPDX-License-Identifier: BUSL-1.1
~}} ~}}
<SplashPage as |Page|> <SplashPage>
{{#if (and this.model.usingRaft (not this.prefersInit))}} <:header>
<Page.header> {{#if (and this.model.usingRaft (not this.prefersInit))}}
<h1 class="title is-4"> <h1 class="title is-4">
Raft Storage Raft Storage
</h1> </h1>
</Page.header> {{else if this.keyData}}
<Page.content>
<RaftJoin @onDismiss={{action (mut this.prefersInit) true}} />
</Page.content>
{{else if this.keyData}}
<Page.header>
{{#let (or this.keyData.recovery_keys this.keyData.keys) as |keyArray|}} {{#let (or this.keyData.recovery_keys this.keyData.keys) as |keyArray|}}
<h1 class="title is-4"> <h1 class="title is-4">
Vault has been initialized! Vault has been initialized!
@@ -26,8 +21,16 @@
{{/if}} {{/if}}
</h1> </h1>
{{/let}} {{/let}}
</Page.header> {{else}}
<Page.content> <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="box is-marginless is-shadowless">
<div class="content"> <div class="content">
<p> <p>
@@ -104,14 +107,7 @@
/> />
</div> </div>
</div> </div>
</Page.content> {{else}}
{{else}}
<Page.header>
<h1 class="title h5">
Let's set up the initial set of root keys that youll need in case of an emergency
</h1>
</Page.header>
<Page.content>
<form <form
{{action {{action
"initCluster" "initCluster"
@@ -217,6 +213,6 @@
</div> </div>
</div> </div>
</form> </form>
</Page.content> {{/if}}
{{/if}} </:content>
</SplashPage> </SplashPage>

View File

@@ -3,11 +3,11 @@
SPDX-License-Identifier: BUSL-1.1 SPDX-License-Identifier: BUSL-1.1
~}} ~}}
<SplashPage @showTruncatedNavBar={{false}} as |Page|> <SplashPage>
<Page.header> <:header>
<h1 class="title is-4">MFA setup</h1> <h1 class="title is-4">MFA setup</h1>
</Page.header> </:header>
<Page.content> <:content>
<div class="auth-form" data-test-mfa-form> <div class="auth-form" data-test-mfa-form>
<div class="box"> <div class="box">
{{#if (eq this.onStep 1)}} {{#if (eq this.onStep 1)}}
@@ -31,5 +31,5 @@
{{/if}} {{/if}}
</div> </div>
</div> </div>
</Page.content> </:content>
</SplashPage> </SplashPage>

View File

@@ -23,13 +23,14 @@
</div> </div>
</div> </div>
{{else}} {{else}}
<SplashPage as |Page|> <SplashPage>
<Page.header> <:header>
<h1 class="title is-3"> <h1 class="title is-3">
Unseal Vault Unseal Vault
</h1> </h1>
</Page.header> </:header>
<Page.content>
<:content>
<div class="box is-borderless is-shadowless is-marginless"> <div class="box is-borderless is-shadowless is-marginless">
<p class="title is-5"> <p class="title is-5">
{{capitalize this.model.name}} {{capitalize this.model.name}}
@@ -57,8 +58,9 @@
/> />
{{/if}} {{/if}}
</div> </div>
</Page.content> </:content>
<Page.footer>
<:footer>
<div class="box is-borderless is-shadowless"> <div class="box is-borderless is-shadowless">
<p> <p>
<ExternalLink @href="https://developer.hashicorp.com/vault/docs/concepts/seal"> <ExternalLink @href="https://developer.hashicorp.com/vault/docs/concepts/seal">
@@ -66,6 +68,6 @@
</ExternalLink> </ExternalLink>
</p> </p>
</div> </div>
</Page.footer> </:footer>
</SplashPage> </SplashPage>
{{/if}} {{/if}}

View 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');
});
});