passbolt-styleguide
Version:
Passbolt styleguide contains common styling assets used by the different sites, plugin, etc.
570 lines (539 loc) • 29.5 kB
JavaScript
/**
* Passbolt ~ Open source password manager for teams
* Copyright (c) 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) Passbolt SA (https://www.passbolt.com)
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
* @link https://www.passbolt.com Passbolt(tm)
* @since 2.13.0
*/
import React from "react";
import PropTypes from "prop-types";
import Icon from "../../../../shared/components/Icons/Icon";
import {withAdministrationWorkspace} from "../../../contexts/AdministrationWorkspaceContext";
import {Trans, withTranslation} from "react-i18next";
import Select from "../../Common/Select/Select";
import DisplayAdministrationUserDirectoryActions from "../DisplayAdministrationWorkspaceActions/DisplayAdministrationUserDirectoryActions/DisplayAdministrationUserDirectoryActions";
import UserDirectoryFormService from '../../../../shared/services/forms/userDirectory/UserDirectoryFormService';
import {withAdminUserDirectory} from "../../../contexts/Administration/AdministrationUserDirectory/AdministrationUserDirectoryContext";
/**
* This component allows to display the MFA for the administration
*/
class DisplayUserDirectoryAdministration extends React.Component {
/**
* Constructor
* @param {Object} props
*/
constructor(props) {
super(props);
this.state = this.defaultState;
this.userDirectoryFormService = UserDirectoryFormService.getInstance(this.props.adminUserDirectoryContext, this.props.t);
this.bindCallbacks();
}
/**
* Get default state
* @returns {*}
*/
get defaultState() {
return {
hasFieldFocus: false, // true if the form field has focus
};
}
/**
* ComponentDidMount
* Invoked immediately after component is inserted into the tree
* @return {void}
*/
async componentDidMount() {
this.props.administrationWorkspaceContext.setDisplayAdministrationWorkspaceAction(DisplayAdministrationUserDirectoryActions);
this.props.adminUserDirectoryContext.findUserDirectorySettings();
}
/**
* componentWillUnmount
* Use to clear the data from the form in case the user put something that needs to be cleared.
*/
componentWillUnmount() {
this.props.administrationWorkspaceContext.resetDisplayAdministrationWorkspaceAction();
this.props.adminUserDirectoryContext.clearContext();
UserDirectoryFormService.killInstance();
this.userDirectoryFormService = null;
}
/**
* Bind callbacks methods
*/
bindCallbacks() {
this.handleCredentialTitleClicked = this.handleCredentialTitleClicked.bind(this);
this.handleDirectoryConfigurationTitleClicked = this.handleDirectoryConfigurationTitleClicked.bind(this);
this.handleSynchronizationOptionsTitleClicked = this.handleSynchronizationOptionsTitleClicked.bind(this);
this.handleFieldFocus = this.handleFieldFocus.bind(this);
this.handleFieldBlur = this.handleFieldBlur.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
/**
* Handle the click on the credential title
* @param {UserDirectory} userDirectory state
*/
handleCredentialTitleClicked() {
const settings = this.props.adminUserDirectoryContext.getSettings();
this.props.adminUserDirectoryContext.setSettings("openCredentials", !settings.openCredentials);
}
/**
* Handle the click on the credential title
* @param {UserDirectory} userDirectory state
*/
handleDirectoryConfigurationTitleClicked() {
const settings = this.props.adminUserDirectoryContext.getSettings();
this.props.adminUserDirectoryContext.setSettings('openDirectoryConfiguration', !settings.openDirectoryConfiguration);
}
/**
* Handle the click on the credential title
* @param {UserDirectory} userDirectory state
*/
handleSynchronizationOptionsTitleClicked() {
const settings = this.props.adminUserDirectoryContext.getSettings();
this.props.adminUserDirectoryContext.setSettings('openSynchronizationOptions', !settings.openSynchronizationOptions);
}
/**
* Handle form input changes.
* @params {ReactEvent} The react event
* @returns {void}
*/
handleInputChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.props.adminUserDirectoryContext.setSettings(name, value);
this.validateInput(name, value);
}
/**
* validate the input
* @params {string} The input name
* @params {string} The input valude
* @returns {void}
*/
validateInput(name, value) {
switch (name) {
case 'host':
this.userDirectoryFormService.validateHostInput(value);
break;
case 'domain':
this.userDirectoryFormService.validateDomainInput(value);
break;
case 'port':
this.userDirectoryFormService.validatePortInput(value);
break;
}
}
/**
* Handle field focus
*/
handleFieldFocus() {
this.setState({hasFieldFocus: true});
}
/**
* Handle field blur
*/
handleFieldBlur() {
this.setState({hasFieldFocus: false});
}
/**
* Should input be disabled? True if state is loading or processing
* @returns {boolean}
*/
hasAllInputDisabled() {
const settings = this.props.adminUserDirectoryContext.getSettings();
return settings.processing || settings.loading;
}
/**
* If user directory is checked
* @returns {boolean}
*/
isUserDirectoryChecked() {
return this.props.adminUserDirectoryContext.getSettings().userDirectoryToggle;
}
/**
* If active directory is checked
*/
isActiveDirectoryChecked() {
return this.props.adminUserDirectoryContext.getSettings().directoryType === "ad";
}
/**
* If open ldap is checked
*/
isOpenLdapChecked() {
return this.props.adminUserDirectoryContext.getSettings().directoryType === "openldap";
}
/**
* If use email prefix is checked
*/
isUseEmailPrefixChecked() {
return this.props.adminUserDirectoryContext.getSettings().useEmailPrefix;
}
/**
* Get users allowed to be default admin
*/
getUsersAllowedToBeDefaultAdmin() {
const users = this.props.adminUserDirectoryContext.getUsers();
if (users !== null) {
const usersFiltered = users.filter(user => user.active === true && user.role.name === "admin");
return usersFiltered && usersFiltered.map(user => ({value: user.id, label: this.displayUser(user)}));
}
return [];
}
/**
* Get users allowed to be default group admin
*/
getUsersAllowedToBeDefaultGroupAdmin() {
const users = this.props.adminUserDirectoryContext.getUsers();
if (users !== null) {
const usersFiltered = users.filter(user => user.active === true);
return usersFiltered && usersFiltered.map(user => ({value: user.id, label: this.displayUser(user)}));
}
return [];
}
/**
* display user firstname, lastname and username
* @param user
* @returns {string}
*/
displayUser(user) {
return `${user.profile.first_name} ${user.profile.last_name} (${user.username})`;
}
/**
* get the connection type
*/
get connectionType() {
return [
{value: "plain", label: "ldap://"},
{value: "ssl", label: "ldaps:// (ssl)"},
{value: "tls", label: "ldaps:// (tls)"},
];
}
/**
* Render the component
* @returns {JSX}
*/
render() {
const settings = this.props.adminUserDirectoryContext.getSettings();
const errors = this.props.adminUserDirectoryContext.getErrors();
const isSubmitted = this.props.adminUserDirectoryContext.isSubmitted();
return (
<div className="row">
<div className="ldap-settings col7 main-column">
<h3>
<span className="input toggle-switch form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="userDirectoryToggle"
onChange={this.handleInputChange} checked={settings.userDirectoryToggle} disabled={this.hasAllInputDisabled()}
id="userDirectoryToggle"/>
<label htmlFor="userDirectoryToggle"><Trans>Users Directory</Trans></label>
</span>
</h3>
{!this.isUserDirectoryChecked() &&
<p className="description">
<Trans>No Users Directory is configured. Enable it to synchronise your users and groups with passbolt.</Trans>
</p>
}
{this.isUserDirectoryChecked() &&
<>
<p className="description">
<Trans>A Users Directory is configured. The users and groups of passbolt will synchronize with it.</Trans>
</p>
<div className={`accordion section-general ${settings.openCredentials ? "" : "closed"}`}>
<h4 className="accordion-header">
<a onClick={this.handleCredentialTitleClicked}>
{settings.openCredentials && <Icon name="caret-down"/>}
{!settings.openCredentials && <Icon name="caret-right"/>}
<Trans>Credentials</Trans>
</a>
</h4>
<div className="accordion-content">
<div className="radiolist required">
<label><Trans>Directory type</Trans></label>
<div className="input radio ad openldap form-element ">
<div className="input radio">
<input type="radio" value="ad" onChange={this.handleInputChange} name="directoryType"
checked={this.isActiveDirectoryChecked()} id="directoryTypeAd"
disabled={this.hasAllInputDisabled()}/>
<label htmlFor="directoryTypeAd"><Trans>Active Directory</Trans></label>
</div>
<div className="input radio">
<input type="radio" value="openldap" onChange={this.handleInputChange} name="directoryType"
checked={this.isOpenLdapChecked()} id="directoryTypeOpenLdap"
disabled={this.hasAllInputDisabled()}/>
<label htmlFor="directoryTypeOpenLdap"><Trans>Open Ldap</Trans></label>
</div>
</div>
</div>
<div className={`input text required ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Server url</Trans></label>
<div className={`input text singleline connection_info ad openldap ${this.state.hasFieldFocus ? "no-focus" : ""}`}>
<input id="server-input" type="text" className="required host ad openldap form-element" name="host"
value={settings.host} onChange={this.handleInputChange}
placeholder={this.props.t("host")} disabled={this.hasAllInputDisabled()}/>
<div className="protocol" onBlur={this.handleFieldBlur} onFocus={this.handleFieldFocus}>
<Select className="inline" name="connectionType" items={this.connectionType} value={settings.connectionType} onChange={this.handleInputChange} disabled={this.hasAllInputDisabled()}/>
</div>
<div className="port ad openldap">
<input id="port-input" type="number" className="required in-field form-element" name="port"
value={settings.port} onChange={this.handleInputChange}
onBlur={this.handleFieldBlur} onFocus={this.handleFieldFocus}
disabled={this.hasAllInputDisabled()}/>
</div>
</div>
{errors.hostError && isSubmitted &&
<div id="server-input-feedback" className="error-message">{errors.hostError}</div>
}
{errors.portError && isSubmitted &&
<div id="port-input-feedback" className="error-message">{errors.portError}</div>
}
</div>
<div className="singleline clearfix">
<div className={`input text first-field ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Username</Trans></label>
<input id="username-input" type="text" className="fluid form-element" name="username"
value={settings.username} onChange={this.handleInputChange} placeholder={this.props.t("Username")}
disabled={this.hasAllInputDisabled()}/>
</div>
<div className={`input text last-field ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Password</Trans></label>
<input id="password-input" className="fluid form-element" name="password"
value={settings.password} onChange={this.handleInputChange} placeholder={this.props.t("Password")} type="password"
disabled={this.hasAllInputDisabled()}/>
</div>
</div>
<div className={`input text required ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Domain</Trans></label>
<input id="domain-name-input" type="text" name="domain" value={settings.domain}
onChange={this.handleInputChange} className="required fluid form-element"
placeholder="domain.ext" disabled={this.hasAllInputDisabled()}/>
{errors.domainError && isSubmitted &&
<div id="domain-name-input-feedback" className="error-message">{errors.domainError}</div>
}
</div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Base DN</Trans></label>
<input id="base-dn-input" type="text" name="baseDn" value={settings.baseDn}
onChange={this.handleInputChange} className="fluid form-element" placeholder="OU=OrgUsers,DC=mydomain,DC=local"
disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>The base DN (default naming context) for the domain.</Trans> <Trans>If this is empty then it will be queried from the RootDSE.</Trans>
</div>
</div>
</div>
</div>
<div
className={`accordion section-directory-configuration ${settings.openDirectoryConfiguration ? "" : "closed"}`}>
<h4 className="accordion-header">
<a onClick={this.handleDirectoryConfigurationTitleClicked}>
{settings.openDirectoryConfiguration && <Icon name="caret-down"/>}
{!settings.openDirectoryConfiguration && <Icon name="caret-right"/>}
<Trans>Directory configuration</Trans>
</a>
</h4>
<div className="accordion-content">
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Group path</Trans></label>
<input id="group-path-input" type="text" name="groupPath" value={settings.groupPath}
onChange={this.handleInputChange} className="required fluid form-element" placeholder={this.props.t("Group path")}
disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>Group path is used in addition to the base DN while searching groups.</Trans> <Trans>Leave empty if users and groups are in the same DN.</Trans>
</div>
</div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>User path</Trans></label>
<input id="user-path-input" type="text" name="userPath" value={settings.userPath}
onChange={this.handleInputChange} className="required fluid form-element" placeholder={this.props.t("User path")}
disabled={this.hasAllInputDisabled()}/>
<div className="help-message"><Trans>User path is used in addition to base DN while searching users.</Trans></div>
</div>
{this.isOpenLdapChecked() &&
<div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Group object class</Trans></label>
<input id="group-object-class-input" type="text" name="groupObjectClass"
value={settings.groupObjectClass} onChange={this.handleInputChange} className="required fluid"
placeholder="GroupObjectClass" disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>For Openldap only. Defines which group object to use.</Trans> (<Trans>Default</Trans>: posixGroup)
</div>
</div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>User object class</Trans></label>
<input id="user-object-class-input" type="text" name="userObjectClass"
value={settings.userObjectClass} onChange={this.handleInputChange} className="required fluid form-element"
placeholder="UserObjectClass" disabled={this.hasAllInputDisabled()}/>
<div className="help-message"><Trans>For Openldap only. Defines which user object to use.</Trans> (<Trans>Default</Trans>: inetOrgPerson)
</div>
</div>
<div className={`input text openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Use email prefix / suffix?</Trans></label>
<div className="input toggle-switch openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="useEmailPrefix"
value={settings.useEmailPrefix} onChange={this.handleInputChange} id="use-email-prefix-suffix-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="use-email-prefix-suffix-toggle-button">
<Trans>Build email based on a prefix and suffix?</Trans>
</label>
</div>
<div className="help-message">
<Trans>Use this option when user entries do not include an email address by default</Trans>
</div>
</div>
{this.isUseEmailPrefixChecked() &&
<div className="singleline clearfix" id="use-email-prefix-suffix-options">
<div className={`input text first-field openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Email prefix</Trans></label>
<input id="email-prefix-input" type="text" name="emailPrefix" checked={settings.emailPrefix}
onChange={this.handleInputChange} className="required fluid form-element" placeholder={this.props.t("Username")}
disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>The attribute you would like to use for the first part of the email (usually username).</Trans>
</div>
</div>
<div className={`input text last-field openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Email suffix</Trans></label>
<input id="email-suffix-input" type="text" name="emailSuffix" value={settings.emailSuffix}
onChange={this.handleInputChange} className="required form-element"
placeholder={this.props.t("@your-domain.com")} disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>The domain name part of the email (@your-domain-name).</Trans>
</div>
</div>
</div>
}
</div>
}
</div>
</div>
<div
className={`accordion section-sync-options ${settings.openSynchronizationOptions ? "" : "closed"}`}>
<h4 className="accordion-header">
<a onClick={this.handleSynchronizationOptionsTitleClicked}>
{settings.openSynchronizationOptions && <Icon name="caret-down"/>}
{!settings.openSynchronizationOptions && <Icon name="caret-right"/>}
<Trans>Synchronization options</Trans>
</a>
</h4>
<div className="accordion-content">
<div className={`select-wrapper input required ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Default admin</Trans></label>
<Select items={this.getUsersAllowedToBeDefaultAdmin()}
id="default-user-select"
name="defaultAdmin"
value={settings.defaultAdmin}
onChange={this.handleInputChange}
disabled={this.hasAllInputDisabled()}
search={true}/>
<div className="help-message">
<Trans>The default admin user is the user that will perform the operations for the the directory.</Trans>
</div>
</div>
<div className={`select-wrapper input required ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Default group admin</Trans></label>
<Select items={this.getUsersAllowedToBeDefaultGroupAdmin()}
id="default-group-admin-user-select"
name="defaultGroupAdmin"
value={settings.defaultGroupAdmin}
onChange={this.handleInputChange}
disabled={this.hasAllInputDisabled()}
search={true}/>
<div className="help-message">
<Trans>The default group manager is the user that will be the group manager of newly created groups.</Trans>
</div>
</div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Groups parent group</Trans></label>
<input id="groups-parent-group-input" type="text" name="groupsParentGroup"
value={settings.groupsParentGroup} onChange={this.handleInputChange} className="fluid form-element" placeholder={this.props.t("Group name")}
disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>Synchronize only the groups which are members of this group.</Trans>
</div>
</div>
<div className={`input text ad openldap ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Users parent group</Trans></label>
<input id="users-parent-group-input" type="text" name="usersParentGroup"
value={settings.usersParentGroup} onChange={this.handleInputChange} className="fluid form-element" placeholder={this.props.t("Group name")}
disabled={this.hasAllInputDisabled()}/>
<div className="help-message">
<Trans>Synchronize only the users which are members of this group.</Trans>
</div>
</div>
{this.isActiveDirectoryChecked() &&
<div className={`input text clearfix ad ${this.hasAllInputDisabled() ? 'disabled' : ''}`}>
<label><Trans>Enabled users only</Trans></label>
<div className="input toggle-switch ad form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="enabledUsersOnly"
checked={settings.enabledUsersOnly} onChange={this.handleInputChange} id="enabled-users-only-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="enabled-users-only-toggle-button"><Trans>Only synchronize enabled users (AD)</Trans></label>
</div>
</div>
}
<div className="input text clearfix ad openldap">
<label><Trans>Sync operations</Trans></label>
<div className="col6">
<div className="input toggle-switch ad openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="createUsers"
checked={settings.createUsers} onChange={this.handleInputChange} id="sync-users-create-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="sync-users-create-toggle-button"><Trans>Create users</Trans></label>
</div>
<div className="input toggle-switch ad openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="deleteUsers"
checked={settings.deleteUsers} onChange={this.handleInputChange} id="sync-users-delete-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="sync-users-delete-toggle-button"><Trans>Delete users</Trans></label>
</div>
</div>
<div className="col6 last">
<div className="input toggle-switch ad openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="createGroups"
checked={settings.createGroups} onChange={this.handleInputChange} id="sync-groups-create-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="sync-groups-create-toggle-button"><Trans>Create groups</Trans></label>
</div>
<div className="input toggle-switch ad openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="deleteGroups"
checked={settings.deleteGroups} onChange={this.handleInputChange} id="sync-groups-delete-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="sync-groups-delete-toggle-button"><Trans>Delete groups</Trans></label>
</div>
<div className="input toggle-switch ad openldap form-element">
<input type="checkbox" className="toggle-switch-checkbox checkbox" name="updateGroups"
checked={settings.updateGroups} onChange={this.handleInputChange} id="sync-groups-update-toggle-button"
disabled={this.hasAllInputDisabled()}/>
<label className="text" htmlFor="sync-groups-update-toggle-button"><Trans>Update groups</Trans></label>
</div>
</div>
</div>
</div>
</div>
</>
}
</div>
<div className="col4 last">
<div className="sidebar-help">
<h3><Trans>Need help?</Trans></h3>
<p><Trans>Check out our ldap configuration guide.</Trans></p>
<a className="button" href="https://help.passbolt.com/configure/ldap" target="_blank" rel="noopener noreferrer">
<Icon name="document"/>
<span><Trans>Read the documentation</Trans></span>
</a>
</div>
</div>
</div>
);
}
}
DisplayUserDirectoryAdministration.propTypes = {
adminUserDirectoryContext: PropTypes.object, // The user directory workspace context
administrationWorkspaceContext: PropTypes.object, // The administration workspace context
t: PropTypes.func, // The translation function
};
export default withAdminUserDirectory(withAdministrationWorkspace(withTranslation('common')(DisplayUserDirectoryAdministration)));