passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
361 lines (337 loc) • 13.7 kB
JavaScript
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) 2021 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) 2021 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.3.0
*/
import React from "react";
import PropTypes from "prop-types";
import { Trans, withTranslation } from "react-i18next";
import { Link, withRouter } from "react-router-dom";
import Tabs from "../../../react-extension/components/Common/Tab/Tabs";
import Tab from "../../../react-extension/components/Common/Tab/Tab";
import ConfigurePassphraseGenerator from "../../../shared/components/GeneratePassword/ConfigurePassphraseGenerator";
import ConfigurePasswordGenerator from "../../../shared/components/GeneratePassword/ConfigurePasswordGenerator";
import { SecretGenerator } from "../../../shared/lib/SecretGenerator/SecretGenerator";
import { withPrepareResourceContext } from "../../contexts/PrepareResourceContext";
import Transition from "react-transition-group/cjs/Transition";
import SpinnerSVG from "../../../img/svg/spinner.svg";
import Password from "../../../shared/components/Password/Password";
import PasswordComplexity from "../../../shared/components/PasswordComplexity/PasswordComplexity";
import CloseSvg from "../../../img/svg/close.svg";
import DiceSVG from "../../../img/svg/dice.svg";
import CopySVG from "../../../img/svg/copy.svg";
import HealthCheckSuccessSvg from "../../../img/svg/healthcheck_success.svg";
import CaretLeftSVG from "../../../img/svg/caret_left.svg";
import ClipboardServiceWorkerService from "../../../shared/services/serviceWorker/clipboard/clipboardServiceWorkerService";
class GeneratePasswordPage extends React.Component {
constructor(props) {
super(props);
this.state = this.defaultState;
this.initEventHandlers();
this.clipboardServiceWorkerService = new ClipboardServiceWorkerService(props.context.port);
this.copySecretDefault = React.createRef();
this.copySecretProcessing = React.createRef();
this.copySecretDone = React.createRef();
}
get defaultState() {
return {
password: "",
generatorSettings: null,
isObfuscated: true, // True if the passphrase should not be visible
copySecretState: "default",
processing: false,
passwordEntropy: null,
};
}
/**
* Whenever the component has been mounted
*/
componentDidMount() {
const generatorSettings = this.props.prepareResourceContext.getSettings();
const password = this.generatePassword(generatorSettings);
this.setState({ generatorSettings, password });
}
/**
* Initialize the event handlers
*/
initEventHandlers() {
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.handleViewPasswordToggle = this.handleViewPasswordToggle.bind(this);
this.handleGeneratePasswordButtonClick = this.handleGeneratePasswordButtonClick.bind(this);
this.handleGeneratorConfigurationChanged = this.handleGeneratorConfigurationChanged.bind(this);
this.handlePasswordGeneratorConfigurationChanged = this.handlePasswordGeneratorConfigurationChanged.bind(this);
this.handlePassphraseGeneratorConfigurationChanged = this.handlePassphraseGeneratorConfigurationChanged.bind(this);
this.handleCopyPassword = this.handleCopyPassword.bind(this);
this.handleGoBackClick = this.handleGoBackClick.bind(this);
}
/**
* Handle when the generator configuration has changed
* @param generatorConfiguration The generator configuration
*/
handleGeneratorConfigurationChanged(generatorSettings) {
const password = this.generatePassword(generatorSettings);
this.setState({ generatorSettings, password });
}
/**
* Handle when the password generator configuration has changed
* @param generator The generator configuration
*/
handlePasswordGeneratorConfigurationChanged(generator) {
const settings = JSON.parse(JSON.stringify(this.state.generatorSettings));
settings.password_generator_settings = generator;
this.handleGeneratorConfigurationChanged(settings);
}
/**
* Handle when the passphrase generator configuration has changed
* @param generator The generator configuration
*/
handlePassphraseGeneratorConfigurationChanged(generator) {
const settings = JSON.parse(JSON.stringify(this.state.generatorSettings));
settings.passphrase_generator_settings = generator;
this.handleGeneratorConfigurationChanged(settings);
}
/**
* Handle when the generator type has changed
* @param generatorConfiguration The generator configuration
*/
handleGeneratorTypeChanged(generatorType) {
const generatorConfiguration = JSON.parse(JSON.stringify(this.state.generatorSettings));
generatorConfiguration.default_generator = generatorType;
this.handleGeneratorConfigurationChanged(generatorConfiguration);
}
/**
* Handle when one wants to generate password
*/
handleGeneratePasswordButtonClick() {
const password = this.generatePassword(this.state.generatorSettings);
this.setState({ password });
}
/**
* Handle form input change.
* @params {ReactEvent} The react event.
*/
handleInputChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value,
});
}
/**
* Handle view password button click.
*/
handleViewPasswordToggle() {
if (this.state.processing) {
return;
}
this.setState({ isObfuscated: !this.state.isObfuscated });
}
/**
* Handle the submission of the generated password.
* @params {ReactEvent} The react event
*/
handleSubmit(event) {
event.preventDefault();
this.setState({ processing: true });
this.props.prepareResourceContext.onPasswordGenerated(this.state.password, this.state.generatorSettings);
this.props.history.goBack();
}
/**
* Whenever one wants to copy the password
*/
async handleCopyPassword() {
this.setState({ copySecretState: "processing" });
this.clipboardServiceWorkerService.copyTemporarily(this.state.password);
this.setState({ copySecretState: "done" });
setTimeout(() => {
this.setState({ copySecretState: "default" });
}, 3000);
}
/**
* Generate the password
* @param {object} generatorConfiguration
* @returns {string} the generated password
*/
generatePassword(generatorConfiguration) {
const password = SecretGenerator.generate(generatorConfiguration);
const passwordEntropy = password?.length > 0 ? SecretGenerator.entropy(password) : null;
this.setState({ passwordEntropy });
return password;
}
handleGoBackClick(ev) {
ev.preventDefault();
this.props.history.goBack();
}
isPasswordEmpty() {
return this.state.password === "";
}
hasGeneratorConfiguration() {
return Boolean(this.state.generatorSettings);
}
get translate() {
return this.props.t;
}
render() {
const generatorConfiguration = this.state.generatorSettings;
return (
<div className="generate-password">
<div className="back-link">
<a
href="#"
className="primary-action"
onClick={this.handleGoBackClick}
title={this.translate("Cancel the operation")}
>
<CaretLeftSVG />
<span className="primary-action-title">
<Trans>Generate password</Trans>
</span>
</a>
<Link
to="/webAccessibleResources/quickaccess/home"
className="secondary-action button-transparent button"
title={this.translate("Cancel")}
>
<CloseSvg />
<span className="visually-hidden">
<Trans>Cancel</Trans>
</span>
</Link>
</div>
<form onSubmit={this.handleSubmit} noValidate>
<div className="form-container">
<div className="input-password-wrapper input">
<label htmlFor="generate-resource-password-form-password">
<Trans>Password</Trans>
</label>
<div className="password-button-inline">
<Password
id="generate-resource-password-form-password"
name="password"
autoComplete="off"
readOnly={true}
placeholder={this.translate("Password")}
preview={true}
value={this.state.password}
onChange={this.handleInputChange}
disabled={this.state.processing}
/>
<a onClick={this.handleGeneratePasswordButtonClick} className="password-generate button button-icon">
<DiceSVG />
<span className="visually-hidden">
<Trans>Generate</Trans>
</span>
</a>
<a onClick={this.handleCopyPassword} className="copy-to-clipboard button button-icon">
<Transition
in={this.state.copySecretState === "default"}
appear={false}
timeout={500}
nodeRef={this.copySecretDefault}
>
{(status) => (
<span
className={`transition fade-${status} ${this.state.copySecretState !== "default" ? "visually-hidden" : ""}`}
>
<CopySVG />
</span>
)}
</Transition>
<Transition
in={this.state.copySecretState === "processing"}
appear={true}
timeout={500}
nodeRef={this.copySecretProcessing}
>
{(status) => (
<span
className={`transition fade-${status} ${this.state.copySecretState !== "processing" ? "visually-hidden" : ""}`}
>
<SpinnerSVG />
</span>
)}
</Transition>
<Transition
in={this.state.copySecretState === "done"}
appear={true}
timeout={500}
nodeRef={this.copySecretDone}
>
{(status) => (
<span
className={`transition fade-${status} ${this.state.copySecretState !== "done" ? "visually-hidden" : ""}`}
>
<HealthCheckSuccessSvg />
</span>
)}
</Transition>
<span className="visually-hidden">
<Trans>Copy</Trans>
</span>
</a>
</div>
<PasswordComplexity entropy={this.state.passwordEntropy} />
</div>
{this.hasGeneratorConfiguration() && (
<Tabs activeTabName={generatorConfiguration.default_generator}>
<Tab
key={"password"}
name={this.props.t("password")}
type={"password"}
onClick={() => this.handleGeneratorTypeChanged("password")}
>
{generatorConfiguration.default_generator === "password" && (
<ConfigurePasswordGenerator
configuration={generatorConfiguration.password_generator_settings}
onConfigurationChanged={this.handlePasswordGeneratorConfigurationChanged}
/>
)}
</Tab>
<Tab
key={"passphrase"}
name={this.props.t("passphrase")}
type={"passphrase"}
onClick={() => this.handleGeneratorTypeChanged("passphrase")}
>
{generatorConfiguration.default_generator === "passphrase" && (
<ConfigurePassphraseGenerator
configuration={generatorConfiguration.passphrase_generator_settings}
onConfigurationChanged={this.handlePassphraseGeneratorConfigurationChanged}
/>
)}
</Tab>
</Tabs>
)}
</div>
<div className="submit-wrapper input">
<button
type="submit"
className={`button primary big full-width ${this.state.processing ? "processing" : ""}`}
disabled={this.state.processing || this.isPasswordEmpty()}
>
<Trans>Apply</Trans>
{this.state.processing && <SpinnerSVG />}
</button>
</div>
</form>
</div>
);
}
}
GeneratePasswordPage.propTypes = {
context: PropTypes.any, // The application context
prepareResourceContext: PropTypes.any, // The password generator context
history: PropTypes.any, // The history router
t: PropTypes.func, // The translation function
};
export default withRouter(withPrepareResourceContext(withTranslation("common")(GeneratePasswordPage)));