UNPKG

passbolt-styleguide

Version:

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

348 lines (321 loc) 12.2 kB
/** * Passbolt ~ Open source password manager for teams * Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * * Licensed under GNU Affero General Public License version 3 of the or any later version. * For full copyright and license information, please see the LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @copyright Copyright (c) 2022 Passbolt SA (https://www.passbolt.com) * @license https://opensource.org/licenses/AGPL-3.0 AGPL License * @link https://www.passbolt.com Passbolt(tm) * @since 3.6.0 */ import React from "react"; import PropTypes from "prop-types"; import {withAppContext} from "../AppContext"; import {withSso} from "../SsoContext"; import SsoProviders from "../../components/Administration/ManageSsoSettings/SsoProviders.data"; // The authentication login workflow states. export const AuthenticationLoginWorkflowStates = { ACCEPT_NEW_SERVER_KEY: "Accept new server key", LOADING: "Loading", SIGN_IN: "Sign in", SIGN_IN_SSO: "Sign in SSO", SIGN_IN_ERROR: "Sign in error", SIGNING_IN: "Signing in", INITIATE_ACCOUNT_RECOVERY: "Initiate account recovery", HELP_CREDENTIALS_LOST: "Help credentials lost", UNEXPECTED_ERROR: "Unexpected error", CHECK_MAILBOX: "Check mailbox", }; /** * The authentication login context provider. * Handle the business logic of the login and the manage the workflow. * @type {React.Context<{}>} */ export const AuthenticationLoginContext = React.createContext({ // Workflow data. state: null, // The current login workflow state. serverKey: null, // The server key, state used when the server key changed. error: null, // The current error if any, state used when an unexpected error occurred. // Public workflow mutators. checkPassphrase: () => { }, // Whenever a user passphrase check is required. signIn: () => { }, // Whenever a user sign-in is required. handleSsoSignIn: () => { }, // Whenever a user sign-in via SSO is required. handleSwitchAccount: () => { }, // Whenever a switch account is required. acceptNewServerKey: () => { }, // Whenever a new server gpg key is accepted. needHelpCredentialsLost: () => { }, // Whenever a user lost its passphrase. requestHelpCredentialsLost: () => { }, // Whenever the user wants to request help because it lost its credentials. goToValidatePassphrase: () => { }, // Whenever the users wants to go to the validate passphrase. handleSsoLoginError: () => { }, // Handles the SSO login error getSsoProvider: () => { }, // Returns the current SSO provider if any isSsoAvailable: () => { }, // Returns true is the SSO feature is enabled }); /** * The authentication login context provider. * Handle the business logic of the login and the manage the workflow. */ export class AuthenticationLoginContextProvider extends React.Component { /** * Default constructor * @param props The component props */ constructor(props) { super(props); this.state = this.defaultState; } /** * Returns the default component state */ get defaultState() { return { // Workflow data. state: AuthenticationLoginWorkflowStates.LOADING, // The current login workflow state. serverKey: null, // The server key, state used when the server key changed. error: null, // The current error if any. // Public workflow mutators. checkPassphrase: this.checkPassphrase.bind(this), // Whenever a user passphrase check is required. signIn: this.signIn.bind(this), // Whenever a user sign-in is required. handleSsoSignIn: this.handleSsoSignIn.bind(this), // Whenever a user sign-in via SSO is required. handleSwitchAccount: this.handleSwitchAccount.bind(this), // Whenever a switch account is required. acceptNewServerKey: this.acceptNewServerKey.bind(this), // Whenever a new server gpg key is accepted. needHelpCredentialsLost: this.needHelpCredentialsLost.bind(this), // Whenever a user lost its passphrase. requestHelpCredentialsLost: this.requestHelpCredentialsLost.bind(this), // Whenever the user wants to request help because it lost its credentials. goToValidatePassphrase: this.goToValidatePassphrase.bind(this), // Whenever the users wants to go to the validate passphrase. handleSsoLoginError: this.handleSsoLoginError.bind(this), // Handles the SSO login error getSsoProvider: this.getSsoProvider.bind(this), // Returns the current SSO provider if any isSsoAvailable: this.isSsoAvailable.bind(this), // Returns the current SSO provider if any handleSwitchToPassphrase: this.handleSwitchToPassphrase.bind(this), // Whenever the user want to sign-in via passphrase handleSwitchToSso: this.handleSwitchToSso.bind(this), // Whenever the user want to sign-in via sso }; } /** * Whenever the component is initialized */ async componentDidMount() { await this.initialize(); } /** * Initialize the authentication login workflow * @returns {Promise<void>} */ async initialize() { await this.props.ssoContext.loadSsoConfiguration(); await this.verifyServerKey(); } /** * Verify the server key. * @returns {Promise<void>} */ async verifyServerKey() { try { await this.props.context.port.request('passbolt.auth.verify-server-key'); } catch (error) { await this.handleVerifyServerKeyFailure(error); return; } if (this.isSsoAvailable()) { this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN_SSO}); } else { this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN}); } } /** * Whenever the verify server key has been done with failure * @param error An error occurred while the server key verification */ async handleVerifyServerKeyFailure(error) { if (error.name === "KeyIsExpiredError") { // Nothing to do. @todo document why? } else if (error.name === "ServerKeyChangedError") { const serverKey = await this.props.context.port.request('passbolt.auth.get-server-key'); await this.setState({state: AuthenticationLoginWorkflowStates.ACCEPT_NEW_SERVER_KEY, serverKey}); } else if (error.name === "UserNotFoundError") { // This case should be treated by the background page itself, and the login form should not be displayed. } else { await this.setState({state: AuthenticationLoginWorkflowStates.UNEXPECTED_ERROR, error: error}); } } /** * Whenever the user wants to check its passphrase. * @param {string} passphrase The user passphrase * @returns {Promise<void>} */ async checkPassphrase(passphrase) { try { await this.props.context.port.request('passbolt.auth.verify-passphrase', passphrase); } catch (error) { if (error.name === "InvalidMasterPasswordError" || error.name === "GpgKeyError") { // Expected errors controlled by the component CheckPassphrase, throw it. throw error; } else { await this.setState({state: AuthenticationLoginWorkflowStates.UNEXPECTED_ERROR, error: error}); } } } /** * Whenever the user wants to sign in. * @param {string} passphrase The user passphrase * @param {boolean} rememberMe (Optional) Should the passphrase remembered? default false. * @returns {Promise<void>} */ async signIn(passphrase, rememberMe = false) { await this.setState({state: AuthenticationLoginWorkflowStates.SIGNING_IN}); try { await this.props.context.port.request('passbolt.auth.login', passphrase, rememberMe); this.props.context.port.request('passbolt.auth.post-login-redirect'); } catch (error) { await this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN_ERROR, error}); } } /** * Whenever the user wants to sign in using SSO. * @returns {Promise<void>} */ async handleSsoSignIn() { try { await this.props.ssoContext.runSignInProcess(); this.setState({state: AuthenticationLoginWorkflowStates.SIGNING_IN}); } catch (e) { if (e.name !== "UserAbortsOperationError") { this.handleSsoLoginError(e); } } } /** * Whenever the user want to sign-in via SSO * @returns {Promise<void>} */ async handleSwitchToSso() { this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN_SSO}); } /** * Whenever the user want to sign-in via passphrase * @returns {Promise<void>} */ async handleSwitchToPassphrase() { this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN}); } /** * Whenever the user wants to switch account. */ handleSwitchAccount() { const url = `${this.props.context.userSettings.getTrustedDomain()}/users/recover?locale=${this.props.context.locale}`; window.open(url, '_parent', 'noopener,noreferrer'); } /** * Handles the SSO login error * @param {error} e */ handleSsoLoginError(e) { this.setState({state: AuthenticationLoginWorkflowStates.UNEXPECTED_ERROR, error: e}); } /** * Whenever the user accepts the new server key. * @returns {Promise<void>} */ async acceptNewServerKey() { try { await this.props.context.port.request('passbolt.auth.replace-server-key'); this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN}); } catch (error) { await this.setState({state: AuthenticationLoginWorkflowStates.UNEXPECTED_ERROR, error: error}); } } /** * Whenever the user lost its passphrase. * @returns {Promise<void>} */ async needHelpCredentialsLost() { const canIUserAccountRecovery = this.props.context.siteSettings.canIUse('accountRecovery'); if (canIUserAccountRecovery) { await this.setState({state: AuthenticationLoginWorkflowStates.INITIATE_ACCOUNT_RECOVERY}); } else { await this.setState({state: AuthenticationLoginWorkflowStates.HELP_CREDENTIALS_LOST}); } } /** * Whenever the user wants to request help because it lost its credentials. * @returns {Promise<void>} */ async requestHelpCredentialsLost() { try { await this.props.context.port.request('passbolt.auth.request-help-credentials-lost'); await this.setState({state: AuthenticationLoginWorkflowStates.CHECK_MAILBOX}); } catch (error) { await this.setState({state: AuthenticationLoginWorkflowStates.UNEXPECTED_ERROR, error: error}); } } /** * Whenever the users wants to go to the validate passphrase. * @returns {Promise<void>} */ async goToValidatePassphrase() { await this.setState({state: AuthenticationLoginWorkflowStates.SIGN_IN}); } /** * Returns the current SSO provider if any, null otherwise * @returns {object} */ getSsoProvider() { const ssoProvider = this.props.ssoContext.getProvider(); if (!ssoProvider) { return null; } return SsoProviders.find(provider => provider.id === ssoProvider); } /** * Returns true if the user has an SSO kit registered locally * @returns {boolean} */ isSsoAvailable() { return this.props.ssoContext.hasUserAnSsoKit(); } /** * Render the component * @returns {JSX} */ render() { return ( <AuthenticationLoginContext.Provider value={this.state}> {this.props.children} </AuthenticationLoginContext.Provider> ); } } AuthenticationLoginContextProvider.propTypes = { context: PropTypes.any, // The application context children: PropTypes.any, // The children components ssoContext: PropTypes.object, // The SSO user context }; export default withAppContext(withSso(AuthenticationLoginContextProvider)); /** * Authentication login context consumer HOC * @param {React.Component} WrappedComponent The component to wrap */ export function withAuthenticationLoginContext(WrappedComponent) { return class WithAuthenticationContext extends React.Component { render() { return ( <AuthenticationLoginContext.Consumer> { AuthenticationLoginContext => <WrappedComponent authenticationLoginContext={AuthenticationLoginContext} {...this.props} /> } </AuthenticationLoginContext.Consumer> ); } }; }