UNPKG

passbolt-styleguide

Version:

Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.

303 lines (282 loc) 10.4 kB
import React from "react"; import PropTypes from "prop-types"; import { withRouter } from "react-router-dom"; import { Trans, withTranslation } from "react-i18next"; import SpinnerSVG from "../../../img/svg/spinner.svg"; import Password from "../../../shared/components/Password/Password"; import SsoProviders from "../../../react-extension/components/Administration/ManageSsoSettings/SsoProviders.data"; import { withSso } from "../../contexts/SsoContext"; import { withAppContext } from "../../../shared/context/AppContext/AppContext"; class LoginPage extends React.Component { constructor(props) { super(props); this.state = this.initState(); this.initEventHandlers(); this.passphraseInputRef = React.createRef(); } initEventHandlers() { this.handleFormSubmit = this.handleFormSubmit.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.handleSwitchToPassphrase = this.handleSwitchToPassphrase.bind(this); this.handleSwitchToSso = this.handleSwitchToSso.bind(this); this.handleSignInWithSso = this.handleSignInWithSso.bind(this); } initState() { return { error: "", ssoError: null, processing: false, rememberMe: false, displaySso: false, isSsoAvailable: false, isReady: false, }; } /** * Whenever the component is mounted */ async componentDidMount() { this.initDefaultRememberMeChoice(); const ssoLocalConfiguredProvider = await this.props.ssoContext.loadSsoConfiguration(); if (ssoLocalConfiguredProvider) { this.setState({ isSsoAvailable: true, displaySso: true, isReady: true }); } else { this.setState({ isReady: true }, () => this.focusOnPassphrase()); } } /** * Initialise the rememberMe choice with the latest choice made by the user or false if none * @returns {Promise<void>} */ async initDefaultRememberMeChoice() { const defaultRememberMeChoice = await this.props.context.port.request( "passbolt.remember-me.get-user-latest-choice", ); if (defaultRememberMeChoice !== this.state.rememberMe) { this.setState({ rememberMe: defaultRememberMeChoice }); } } /** * Put the focus on the passphrase input */ focusOnPassphrase() { this.passphraseInputRef.current.focus(); } async handleFormSubmit(event) { event.preventDefault(); this.setState({ processing: true, error: "" }); try { await this.login(); } catch (error) { this.setState({ error: error.message, processing: false, }); // Force the focus onto the passphrase input. The autoFocus attribute only works with the first rendering. this.focusOnPassphrase(); } } async login() { let passphrase = this.passphraseInputRef.current.value; await this.props.context.port.request("passbolt.auth.login", passphrase, this.state.rememberMe); passphrase = null; this.passphraseInputRef.current.value = null; await this.handleLoginSuccess(); } async handleLoginSuccess() { const isMfaRequired = await this.props.context.port.request("passbolt.auth.is-mfa-required"); if (!isMfaRequired) { await this.props.loginSuccessCallback(); this.props.history.push("/webAccessibleResources/quickaccess/home"); } else { await this.props.mfaRequiredCallback(); } } handleInputChange(event) { const target = event.target; const value = target.type === "checkbox" ? target.checked : target.value; const name = target.name; this.setState({ [name]: value, }); } /** * Switches the UI to the SSO mode. */ handleSwitchToSso(event) { event.preventDefault(); this.setState({ displaySso: true, ssoError: null }); } /** * Switches the UI to the passphrase mode. */ handleSwitchToPassphrase(event) { event.preventDefault(); this.setState({ displaySso: false }); } /** * Handle the click on the SSO login button. * @returns {Promise<void>} */ async handleSignInWithSso(event) { event.preventDefault(); this.setState({ processing: true, ssoError: "" }); //save the current window blur behaviour option const currentWindowBlurState = this.props.context.shouldCloseAtWindowBlur; this.props.context.setWindowBlurBehaviour(false); try { await this.props.ssoContext.runSignInProcess(); await this.handleLoginSuccess(); } catch (e) { if (e.name === "SsoSettingsChangedError") { await this.props.context.closeWindow(); } if (e.name !== "UserAbortsOperationError") { this.setState({ ssoError: e.message }); } } finally { this.setState({ processing: false, }); //rollback the current window blur behaviour option this.props.context.setWindowBlurBehaviour(currentWindowBlurState); } } /** * Returns true if SSO is enabled and configured for Azure. * @return {bool} */ get isSsoLocalKitPresent() { const ssoProvider = this.props.ssoContext.getProvider(); const isProviderAvailable = SsoProviders.find((provider) => ssoProvider === provider.id); return isProviderAvailable; } /** * Returns the provider information of the current SSO provider configured. * @return {object} */ get ssoProviderData() { const ssoProvider = this.props.ssoContext.getProvider(); if (!ssoProvider) { return null; } return SsoProviders.find((provider) => provider.id === ssoProvider); } render() { const ssoProviderData = this.ssoProviderData; return ( <div className="quickaccess-login"> <div className="login-form"> {!this.state.displaySso && this.state.isReady && ( <form onSubmit={this.handleFormSubmit}> <div className="form-container"> <div className="input text required"> <label htmlFor="username"> <Trans>Username</Trans> </label> <input className="required" maxLength="50" type="text" id="username" required="required" value={this.props.context.userSettings.username} disabled="disabled" /> </div> <div className="input text passphrase required"> <label htmlFor="passphrase"> <Trans>Passphrase</Trans> </label> <div className="password with-token"> <Password name="passphrase" placeholder={this.props.t("Passphrase")} id="passphrase" autoComplete="off" inputRef={this.passphraseInputRef} preview={true} securityToken={this.props.context.userSettings.getSecurityToken()} disabled={this.state.processing} /> </div> {this.state.error && <div className="error-message">{this.state.error}</div>} </div> {this.props.canRememberMe && ( <div className="input checkbox"> <input type="checkbox" name="rememberMe" id="remember-me" checked={this.state.rememberMe} onChange={this.handleInputChange} disabled={this.state.processing} /> <label htmlFor="remember-me"> <Trans>Remember until I log out.</Trans> </label> </div> )} </div> <div className="submit-wrapper"> <button type="submit" className={`button primary big full-width ${this.state.processing ? "processing" : ""}`} role="button" disabled={this.state.processing} > <Trans>login</Trans> {this.state.processing && <SpinnerSVG />} </button> {this.state.isSsoAvailable && ( <a className="show-sso-form-button" onClick={this.handleSwitchToSso}> <Trans>Sign in with Single Sign-On.</Trans> </a> )} </div> </form> )} {this.state.displaySso && this.state.isReady && ( <> <div className="form-actions sso-login-form"> {this.isSsoLocalKitPresent && ( <a className={`button sso-login-button ${this.state.processing ? "disabled" : ""} ${ssoProviderData.id}`} onClick={this.handleSignInWithSso} disabled={this.state.processing} > <span className="provider-logo">{ssoProviderData.icon}</span> {this.props.t(`Sign in with {{providerName}}`, { providerName: ssoProviderData.name })} </a> )} <a className="show-passphrase-form-button" onClick={this.handleSwitchToPassphrase}> <Trans>Sign in with my passphrase.</Trans> </a> {this.state.ssoError && ( <div className="error-message"> <Trans>An error occured during the sign-in via SSO.</Trans> <br /> {this.state.ssoError} </div> )} </div> </> )} </div> </div> ); } } LoginPage.propTypes = { context: PropTypes.any, // The application context ssoContext: PropTypes.object, // The SSO context canRememberMe: PropTypes.bool, // True if the remember me flag must be displayed loginSuccessCallback: PropTypes.func, mfaRequiredCallback: PropTypes.func, // Match, location and history props are injected by the withRouter decoration call. match: PropTypes.object, location: PropTypes.object, history: PropTypes.object, t: PropTypes.func, // The translation function }; export default withAppContext(withRouter(withSso(withTranslation("common")(LoginPage))));