client/src/components/settings/forms/EditUserRolesDialog/index.js (1,323 lines of code) (raw):

/* * Copyright 2017-2022 EPAM Systems, Inc. (https://www.epam.com/) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import React from 'react'; import {observer, inject} from 'mobx-react'; import {computed} from 'mobx'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { Modal, Row, Button, message, Icon, Table, Select, Tabs } from 'antd'; import UserInfoSummary from './UserInfoSummary'; import User from '../../../../models/user/User'; import Roles from '../../../../models/user/Roles'; import MetadataUpdateKeys from '../../../../models/metadata/MetadataUpdateKeys'; import MetadataDeleteKeys from '../../../../models/metadata/MetadataDeleteKeys'; import RoleAssign from '../../../../models/user/RoleAssign'; import RoleRemove from '../../../../models/user/RoleRemoveFromUser'; import UserUpdate from '../../../../models/user/UserUpdate'; import UserBlock from '../../../../models/user/UserBlock'; import Runners from '../../../../models/user/Runners'; import RunnersUpdate from '../../../../models/user/RunnersUpdate'; import { AssignCredentialProfiles, LoadEntityCredentialProfiles } from '../../../../models/cloudCredentials'; import roleModel from '../../../../utils/roleModel'; import displayDate from '../../../../utils/displayDate'; import { CONTENT_PANEL_KEY, METADATA_PANEL_KEY, SplitPanel } from '../../../special/splitPanel'; import Metadata, {ApplyChanges} from '../../../special/metadata/Metadata'; import InstanceTypesManagementForm from '../InstanceTypesManagementForm'; import AWSRegionTag from '../../../special/AWSRegionTag'; import UserName from '../../../special/UserName'; import ShareWithForm from '../../../runs/logs/forms/ShareWithForm'; import {CP_CAP_RUN_CAPABILITIES} from '../../../pipelines/launch/form/utilities/parameters'; import MuteEmailNotifications from '../../../special/metadata/special/mute-email-notifications'; import PermissionsForm from '../../../roleModel/PermissionsForm'; import { runAsPermissionsEqual } from '../../../runs/logs/forms/configure-run-as-permissions/utilities'; import styles from './EditUserRolesDialog.css'; const RESTRICTED_METADATA_KEYS = [ MuteEmailNotifications.metadataKey ].filter(Boolean); @roleModel.authenticationInfo @inject('dataStorages', 'metadataCache', 'cloudCredentialProfiles', 'impersonation', 'preferences') @inject((common, params) => ({ userInfo: params.user ? new User(params.user.id) : null, userId: params.user ? params.user.id : null, roles: new Roles(), credentialProfiles: params.user ? new LoadEntityCredentialProfiles(params.user.id, true) : null, runners: params.user ? new Runners(params.user.id) : null })) @observer export default class EditUserRolesDialog extends React.Component { static propTypes = { visible: PropTypes.bool, user: PropTypes.shape({ id: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), userName: PropTypes.string }), onClose: PropTypes.func, onUserDelete: PropTypes.func }; state = { activeTab: 'user', selectedRole: null, defaultStorageId: undefined, defaultStorageIdInitial: undefined, defaultStorageInitialized: false, metadata: undefined, roles: [], rolesInitial: [], rolesInitialized: false, instanceTypesChanged: false, operationInProgress: false, profiles: [], profilesInitial: [], profilesInitialized: false, defaultProfileId: undefined, defaultProfileIdInitial: undefined, defaultProfileIdInitialized: false, runners: [], runnersInitial: [], runnersInitialized: false, shareDialogOpened: false }; instanceTypesForm; componentDidMount () { this.updateValues(); } componentDidUpdate (prevProps, prevState, snapshot) { if (prevProps.visible !== this.props.visible) { this.reload(); } else { this.updateValues(); } } @computed get isAdmin () { if (!this.props.authenticatedUserInfo.loaded) { return false; } const { admin } = this.props.authenticatedUserInfo.value; return admin; } onInstanceTypesFormInitialized = (form) => { this.instanceTypesForm = form; }; updateValues = () => { const { defaultStorageInitialized, defaultProfileIdInitialized, rolesInitialized, profilesInitialized, runnersInitialized } = this.state; const state = {}; if (!defaultStorageInitialized && this.props.userInfo && this.props.userInfo.loaded) { state.defaultStorageId = this.props.userInfo.value.defaultStorageId; state.defaultStorageIdInitial = this.props.userInfo.value.defaultStorageId; state.defaultStorageInitialized = true; } if (!defaultProfileIdInitialized && this.props.userInfo && this.props.userInfo.loaded) { state.defaultProfileId = this.props.userInfo.value.defaultProfileId; state.defaultProfileIdInitial = this.props.userInfo.value.defaultProfileId; state.defaultProfileIdInitialized = true; } if (!rolesInitialized && this.props.userInfo && this.props.userInfo.loaded) { state.roles = (this.props.userInfo.value.roles || []).map(r => r); state.rolesInitial = (this.props.userInfo.value.roles || []).map(r => r); state.rolesInitialized = true; } if ( !profilesInitialized && this.props.credentialProfiles && this.props.credentialProfiles.loaded ) { state.profiles = (this.props.credentialProfiles.value || []).map(o => o.id); state.profilesInitial = (this.props.credentialProfiles.value || []).map(o => o.id); state.profilesInitialized = true; } if ( !runnersInitialized && this.props.runners && this.props.runners.loaded ) { state.runners = (this.props.runners.value || []).slice(); state.runnersInitial = (this.props.runners.value || []).slice(); state.runnersInitialized = true; } if (Object.keys(state).length > 0) { this.setState(state); } }; splitRoleName = (name) => { if (name && name.toLowerCase().indexOf('role_') === 0) { return name.substring('role_'.length); } return name; }; renderRolesList = (roles, actionComponentFn) => { const rolesSort = (roleA, roleB) => { if (roleA.name > roleB.name) { return 1; } else if (roleA.name < roleB.name) { return -1; } return 0; }; const columns = [ { dataIndex: 'name', className: 'role-name-column', key: 'name', render: (name, role) => role.predefined ? name : this.splitRoleName(name) }, { key: 'action', className: 'role-actions-column', render: (role) => actionComponentFn && actionComponentFn(role) } ]; return ( <Table rowKey="id" locale={{emptyText: 'No roles found'}} className={styles.table} showHeader={false} pagination={false} loading={this.props.roles.pending || (this.props.userInfo && this.props.userInfo.pending)} columns={columns} dataSource={roles.sort(rolesSort)} rowClassName={role => `role-${role.name}`} size="small" /> ); }; assignRole = () => { if (!this.props.roles.loaded) { return; } const {roles} = this.state; const [role] = (this.props.roles.value || []) .filter((r) => `${r.id}` === `${this.state.selectedRole}`); if (role) { roles.push(role); this.setState({roles, selectedRole: null}); } }; removeRole = (roleId) => { const {roles} = this.state; const [role] = roles .filter((r) => `${r.id}` === `${roleId}`); if (role) { const index = roles.indexOf(role); if (index >= 0) { roles.splice(index, 1); this.setState({roles}); } } }; renderUserRolesList = () => { const {roles, rolesInitialized} = this.state; if (!rolesInitialized) { return null; } return this.renderRolesList( roles, (role) => { return ( <Row type="flex" justify="end"> <Button id="delete-role-button" size="small" type="danger" onClick={() => this.removeRole(role.id)} disabled={this.state.operationInProgress || !this.isAdmin} > <Icon type="delete" /> </Button> </Row> ); } ); }; onClose = () => { this.setState({ activeTab: 'user', search: null, defaultStorageId: undefined, defaultStorageIdInitial: undefined, defaultStorageInitialized: false, metadata: undefined, roles: [], rolesInitial: [], rolesInitialized: false, instanceTypesChanged: false, profiles: [], profilesInitial: [], profilesInitialized: false, defaultProfileId: undefined, defaultProfileIdInitial: undefined, defaultProfileIdInitialized: false, runners: [], runnersInitial: [], runnersInitialized: false }, this.props.onClose); }; get availableRoles () { const {roles, rolesInitialized} = this.state; if (!rolesInitialized || !this.props.roles.loaded) { return []; } return (this.props.roles.value || []) .map(r => r) .filter(r => roles.filter(uR => uR.id === r.id).length === 0); } get addedRoles () { const {roles, rolesInitial} = this.state; return roles .filter(r => rolesInitial.filter(rI => rI.id === r.id).length === 0); } get removedRoles () { const {roles, rolesInitial} = this.state; return rolesInitial .filter(rI => roles.filter(r => r.id === rI.id).length === 0); } get profilesModified () { const {profiles, profilesInitial} = this.state; const initial = [...(new Set(profilesInitial))]; const current = [...(new Set(profiles))]; if (initial.length === current.length) { for (let i = 0; i < initial.length; i++) { if (current.indexOf(initial[i]) === -1) { return true; } } return false; } return true; } get runnersModified () { const { runners = [], runnersInitial = [] } = this.state; if (runnersInitial.length === runners.length) { for (let i = 0; i < runnersInitial.length; i++) { const runner = runnersInitial[i]; if (!runners.find(r => runAsPermissionsEqual(r, runner))) { return true; } } return false; } return true; } @computed get dataStorages () { if (this.props.dataStorages.loaded) { return (this.props.dataStorages.value || []) .filter(d => roleModel.writeAllowed(d)).map(d => d); } return []; } @computed get cloudCredentialProfiles () { if (this.props.cloudCredentialProfiles.loaded) { return (this.props.cloudCredentialProfiles.value || []) .map(o => o); } return []; } get defaultStorageId () { const {dataStorages} = this.props; const {defaultStorageId} = this.state; if (defaultStorageId && dataStorages.loaded) { const dataStorage = (dataStorages.value || []).find(d => d.id === +defaultStorageId); if (!dataStorage) { return this.isAdmin ? undefined : `Access is denied`; } return `${defaultStorageId}`; } return undefined; } get defaultProfileId () { const {cloudCredentialProfiles} = this.props; const {defaultProfileId} = this.state; if (cloudCredentialProfiles.loaded) { const profile = (cloudCredentialProfiles.value || []) .find(d => d.id === +defaultProfileId); if (!profile) { return this.isAdmin ? undefined : `Access is denied`; } return `${defaultProfileId}`; } return undefined; } get modified () { const { metadata, defaultStorageId, defaultStorageIdInitial, instanceTypesChanged, defaultProfileId, defaultProfileIdInitial } = this.state; return !!metadata || defaultStorageId !== defaultStorageIdInitial || defaultProfileId !== defaultProfileIdInitial || this.addedRoles.length > 0 || this.removedRoles.length > 0 || this.profilesModified || this.runnersModified || instanceTypesChanged; } @computed get restrictedMetadataKeys () { if (this.isAdmin) { return []; } const {preferences} = this.props; return [ ...RESTRICTED_METADATA_KEYS, ...(preferences.metadataSystemKeys || []) ]; } addRoleInputChanged = (id) => { this.setState({ selectedRole: id }); }; onDelete = () => { const deleteUser = () => { this.props.onUserDelete && this.props.onUserDelete(this.props.user.id); }; Modal.confirm({ title: `Are you sure you want to delete user ${this.props.user.userName}?`, content: 'This operation cannot be undone.', style: { wordWrap: 'break-word' }, onOk () { deleteUser(); } }); }; onBlockUnBlock = (block) => { const blockUser = async () => { const hide = message.loading( `${block ? 'Blocking' : 'Unblocking'} user ${this.props.user.userName}...` ); const request = new UserBlock(this.props.userId, block); await request.fetch(); if (request.error) { hide(); message.error(request.error, 5); } else { await this.props.userInfo.fetch(); hide(); } }; const {userName} = this.props.user; Modal.confirm({ title: `Are you sure you want to ${block ? 'block' : 'unblock'} user ${userName}?`, style: { wordWrap: 'break-word' }, onOk () { blockUser(); } }); }; operationWrapper = (fn) => { return (...opts) => { this.setState({ operationInProgress: true }, async () => { await fn(...opts); this.setState({ operationInProgress: false }); }); }; }; openShareDialog = () => { this.setState({ shareDialogOpened: true }); }; closeShareDialog = () => { this.setState({ shareDialogOpened: false }); }; saveShareSids = async (sids = []) => { const mapSid = (sid) => { const { isPrincipal, ...rest } = sid; return { principal: isPrincipal, ...rest }; }; this.setState({ runners: sids.map(mapSid) }, this.closeShareDialog); }; saveChanges = async () => { if (this.modified) { const mainHide = message.loading('Updating user info...', 0); const { defaultStorageId, defaultStorageIdInitial, metadata, instanceTypesChanged, defaultProfileId, defaultProfileIdInitial, runners } = this.state; if (defaultStorageId !== defaultStorageIdInitial) { const hide = message.loading('Updating default data storage...', -1); const request = new UserUpdate(this.props.userId); await request.send({defaultStorageId}); if (request.error) { hide(); message.error(request.error, 5); mainHide(); return; } else { this.props.userInfo && await this.props.userInfo.fetch(); hide(); } } if (this.addedRoles.length > 0) { const hide = message.loading('Assigning roles...', 0); const requests = this.addedRoles.map(role => new RoleAssign( role.id, this.props.userInfo.value.id )); await Promise.all(requests.map(request => request.send({}))); const [error] = requests .map(request => request.error) .filter(Boolean); hide(); if (error) { message.error(error, 5); mainHide(); return; } } if (this.removedRoles.length > 0) { const hide = message.loading('Removing roles...', 0); const requests = this.removedRoles.map(role => new RoleRemove( role.id, this.props.userInfo.value.id )); await Promise.all(requests.map(request => request.send({}))); const [error] = requests .map(request => request.error) .filter(Boolean); hide(); if (error) { mainHide(); message.error(error, 5); return; } } if (this.profilesModified || defaultProfileId !== defaultProfileIdInitial) { const hide = message.loading('Assigning credential profiles...', 0); const request = new AssignCredentialProfiles( this.props.userInfo.value.id, true, this.state.profiles, defaultProfileId ); await request.send(); hide(); if (request.error) { message.error(request.error, 5); mainHide(); return; } } if (this.runnersModified) { const hide = message.loading( ( <span> Processing <i>Run As</i> permissions... </span> ), 0 ); const request = new RunnersUpdate( this.props.userInfo.value.id ); await request.send(runners); hide(); if (request.error) { message.error(request.error, 5); mainHide(); return; } } if ( this.addedRoles.length > 0 || this.removedRoles.length > 0 || this.profilesModified || defaultProfileId !== defaultProfileIdInitial ) { await this.props.userInfo.fetch(); await roleModel.refreshAuthenticationInfo(this); } if (metadata) { const hide = message.loading('Updating attributes...', 0); const fetchMetadata = this.props.metadataCache .getMetadata(this.props.userId, 'PIPELINE_USER'); await fetchMetadata.fetchIfNeededOrWait(); if (fetchMetadata.error) { hide(); mainHide(); message.error(fetchMetadata.error, 5); return; } const {data = {}} = (fetchMetadata.value || [])[0] || {}; const modified = {}; const removed = {}; Object.keys(metadata || {}) .forEach((key) => { if (!data.hasOwnProperty(key)) { modified[key] = { value: metadata[key].value, type: metadata[key].type }; } else if (data[key].value !== metadata[key].value) { modified[key] = { value: metadata[key].value, type: metadata[key].type || data[key].type }; } }); Object.keys(data || {}) .forEach(key => { if (!this.restrictedMetadataKeys.includes(key) && !metadata.hasOwnProperty(key)) { removed[key] = { value: data[key].value, type: data[key].type }; } }); if (Object.keys(removed).length > 0) { const removeRequest = new MetadataDeleteKeys(); await removeRequest.send({ entity: { entityId: this.props.userId, entityClass: 'PIPELINE_USER' }, data: removed }); if (removeRequest.error) { hide(); mainHide(); message.error(removeRequest.error, 5); return; } } if (Object.keys(modified).length > 0) { const updateRequest = new MetadataUpdateKeys(); await updateRequest.send({ entity: { entityId: this.props.userId, entityClass: 'PIPELINE_USER' }, data: modified }); if (updateRequest.error) { hide(); mainHide(); message.error(updateRequest.error, 5); return; } } hide(); await this.props.metadataCache.getMetadata(this.props.userId, 'PIPELINE_USER').fetch(); } if (this.instanceTypesForm && instanceTypesChanged) { await this.instanceTypesForm.apply(); } mainHide(); } this.onClose(); }; onChangeMetadata = (metadata) => { this.setState({metadata}); }; onChangeDefaultStorageId = (id) => { this.setState({defaultStorageId: id ? +id : undefined}); }; onChangeDefaultProfileId = (id) => { this.setState({defaultProfileId: id ? +id : undefined}); }; onChangeCredentialProfiles = (ids) => { let {defaultProfileId} = this.state; if (defaultProfileId && (ids || []).map(o => +o).indexOf(+defaultProfileId) === -1) { defaultProfileId = undefined; } this.setState({ profiles: (ids || []).map(id => +id), defaultProfileId }); }; onInstanceTypesModified = (modified) => { this.setState({instanceTypesChanged: modified}); }; revertChanges = (callback) => { if (this.instanceTypesForm) { this.instanceTypesForm.reset(); } const { defaultStorageIdInitial, defaultProfileIdInitial, rolesInitial, profilesInitial, runnersInitial } = this.state; this.setState({ defaultStorageId: defaultStorageIdInitial, defaultProfileId: defaultProfileIdInitial, roles: rolesInitial.map(r => r), metadata: undefined, profiles: profilesInitial.slice(), runners: runnersInitial.slice() }, callback); }; reload = () => { this.setState({ defaultStorageInitialized: false, metadata: undefined, roles: [], rolesInitial: [], rolesInitialized: false, instanceTypesChanged: false, profiles: [], profilesInitial: [], profilesInitialized: false, defaultProfileIdInitialized: false, runners: [], runnersInitial: [], runnersInitialized: false, activeTab: 'user' }, () => this.revertChanges(this.updateValues)); }; renderRunners = () => { const {runners} = this.state; const { runners: runnersRequest, roles: rolesRequest } = this.props; if (runnersRequest && runnersRequest.pending && !runnersRequest.loaded) { return ( <Icon type="loading" /> ); } const renderRole = (roleName) => { if (rolesRequest && rolesRequest.loaded) { const role = (rolesRequest.value || []).find(r => r.name === roleName); if (role && !role.predefined) { return this.splitRoleName(roleName); } } return roleName; }; if (runners && runners.length > 0) { return runners .map((s, index, array) => { return ( <span key={s.name} style={{marginRight: 5, whiteSpace: 'nowrap'}} > { s.principal ? (<UserName userName={s.name} />) : renderRole(s.name) } { index < array.length - 1 ? ',' : undefined } </span> ); }); } if (!this.isAdmin) { return 'not specified'; } return 'configure'; }; onImpersonate = () => { const {user, impersonation} = this.props; if (user && user.userName && impersonation) { impersonation.impersonate(user.userName) .then(error => { if (error) { message.error(error, 5); } }); } }; renderUserInfo = () => { const {userName, registrationDate, attributes} = this.props.user || {}; return ( <div className={styles.userInfo}> <div className={styles.header}> <UserName userName={userName} /> </div> <table className={styles.attributes}> <tbody> <tr> <td className={styles.info}>User name:</td> <td>{userName}</td> </tr> { registrationDate && ( <tr> <td className={styles.info}>Registration date:</td> <td>{displayDate(registrationDate, 'D MMMM YYYY')}</td> </tr> ) } { Object.entries(attributes || {}) .map(([key, value]) => ( <tr key={key}> <td className={styles.info}>{key}:</td> <td>{value}</td> </tr> )) } </tbody> </table> </div> ); } renderUserRolesTab = () => { const { user } = this.props; const readOnly = !this.isAdmin; const metadataReadOnly = readOnly && !roleModel.writeAllowed(user); const {metadata} = this.state; const credentialProfilesPending = this.props.credentialProfiles ? this.props.credentialProfiles.pending : false; const runnersPending = this.props.runners ? this.props.runners.pending : false; return ( <SplitPanel style={{flex: 1, height: 'unset', overflow: 'auto'}} contentInfo={[ { key: CONTENT_PANEL_KEY, containerStyle: { display: 'flex', flexDirection: 'column', overflowX: 'hidden' }, size: { priority: 0, percentMinimum: 33, percentDefault: 60 } }, { key: 'METADATA_AND_INSTANCE_MANAGEMENT', size: { keepPreviousSize: true, priority: 2, percentDefault: 40, pxMinimum: 200 } } ]}> <div style={{display: 'flex', flexDirection: 'column', height: '100%'}} key={CONTENT_PANEL_KEY}> {this.renderUserInfo()} <Row type="flex" style={{marginBottom: 10}} align="middle"> <span style={{marginRight: 5, fontWeight: 'bold', width: 160}}> Default data storage: </span> <Select allowClear showSearch disabled={this.state.operationInProgress || readOnly} value={this.defaultStorageId} style={{flex: 1}} onChange={this.onChangeDefaultStorageId} size="small" filterOption={(input, option) => option.props.name.toLowerCase().indexOf(input.toLowerCase()) >= 0 || option.props.pathMask.toLowerCase().indexOf(input.toLowerCase()) >= 0 }> { this.dataStorages.map(d => { return ( <Select.Option key={d.id} value={`${d.id}`} title={d.name} name={d.name} pathMask={d.pathMask}> <b>{d.name}</b> ({d.pathMask}) </Select.Option> ); }) } </Select> </Row> <Row type="flex" style={{marginBottom: 10}} align="middle"> <span style={{marginRight: 5, fontWeight: 'bold', width: 160}}> Add role or group: </span> <div style={{flex: 1}} id="find-role-select-container"> <Select disabled={this.state.operationInProgress || readOnly} value={this.state.selectedRole} size="small" showSearch style={{width: '100%'}} allowClear placeholder="Add role or group" optionFilterProp="children" onChange={this.addRoleInputChanged} filterOption={ (input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 }> { this.availableRoles.map(t => <Select.Option key={t.id} value={`${t.id}`}> {t.predefined ? t.name : this.splitRoleName(t.name)} </Select.Option> ) } </Select> </div> <div style={{paddingLeft: 10, textAlign: 'right'}}> <Button id="add-role-button" size="small" onClick={this.assignRole} disabled={ this.state.selectedRole === null || this.state.selectedRole === undefined || this.state.operationInProgress || readOnly }> <Icon type="plus" /> Add </Button> </div> </Row> {this.renderUserRolesList()} </div> <SplitPanel orientation="vertical" key="METADATA_AND_INSTANCE_MANAGEMENT" contentInfo={[ { key: METADATA_PANEL_KEY, title: 'Attributes', containerStyle: { display: 'flex', flexDirection: 'column' }, size: { keepPreviousSize: true, priority: 2, percentDefault: 50, pxMinimum: 200 } }, { key: 'INSTANCE_MANAGEMENT', title: 'Launch options', containerStyle: { display: 'flex', flexDirection: 'column', overflow: 'auto' }, size: { keepPreviousSize: true, priority: 2, percentDefault: 50, pxMinimum: 200 } } ]}> <Metadata readOnly={this.state.operationInProgress || metadataReadOnly} key={METADATA_PANEL_KEY} entityId={this.props.userId} entityClass="PIPELINE_USER" applyChanges={ApplyChanges.callback} onChange={this.onChangeMetadata} value={metadata} extraKeys={[CP_CAP_RUN_CAPABILITIES]} restrictedKeys={this.restrictedMetadataKeys} /> <div key="INSTANCE_MANAGEMENT" > <div style={{marginTop: 5, padding: 2}}> <span style={{fontWeight: 'bold', float: 'left'}} > Can run as this user: </span> <a onClick={readOnly ? undefined : this.openShareDialog} style={Object.assign({ marginLeft: 5, wordBreak: 'break-word', cursor: readOnly ? 'default' : 'pointer', pointerEvents: readOnly ? 'none' : undefined })} className={ classNames({ 'cp-text': readOnly }) } > {this.renderRunners()} </a> <ShareWithForm endpointsAvailable disabled={readOnly} visible={this.state.shareDialogOpened} roles={ this.props.roles.loaded ? (this.props.roles.value || []).slice() : [] } sids={ this.state.runners.map(runner => ({ ...runner, isPrincipal: runner.principal })) } pending={runnersPending} onSave={this.saveShareSids} onClose={this.closeShareDialog} runAsUserConfiguration /> </div> <InstanceTypesManagementForm className={styles.instanceTypesManagementForm} key="instance types management form" disabled={this.state.operationInProgress || readOnly} resourceId={this.props.userId} level="USER" onInitialized={this.onInstanceTypesFormInitialized} onModified={this.onInstanceTypesModified} showApplyButton={false} /> <div style={{marginTop: 5, padding: 2, fontWeight: 'bold', width: 160}}> Cloud Credentials Profiles </div> <div style={{padding: '0 2px'}} > <Select allowClear showSearch mode="multiple" disabled={ this.state.operationInProgress || readOnly || credentialProfilesPending } value={this.state.profiles.map(o => `${o}`)} style={{width: '100%'}} onChange={this.onChangeCredentialProfiles} filterOption={(input, option) => option.props.name.toLowerCase().indexOf(input.toLowerCase()) >= 0 }> { this.cloudCredentialProfiles.map(d => ( <Select.Option key={`${d.id}`} value={`${d.id}`} name={d.profileName} title={d.profileName} > <AWSRegionTag provider={d.cloudProvider} showProvider displayName={false} displayFlag={false} /> <span>{d.profileName}</span> </Select.Option> )) } </Select> </div> <div style={{marginTop: 5, padding: 2, fontWeight: 'bold', width: 160}}> Default Credentials Profile </div> <div style={{padding: '0 2px'}} > <Select allowClear showSearch disabled={ this.state.operationInProgress || readOnly || this.state.profiles.length === 0 || credentialProfilesPending } value={this.defaultProfileId} style={{width: '100%'}} onChange={this.onChangeDefaultProfileId} filterOption={(input, option) => option.props.name.toLowerCase().indexOf(input.toLowerCase()) >= 0 }> { this.cloudCredentialProfiles .filter(d => this.state.profiles.indexOf(+d.id) >= 0) .map(d => ( <Select.Option key={`${d.id}`} value={`${d.id}`} name={d.profileName} title={d.profileName} > <AWSRegionTag provider={d.cloudProvider} showProvider displayName={false} displayFlag={false} /> <span>{d.profileName}</span> </Select.Option> )) } </Select> </div> </div> </SplitPanel> </SplitPanel> ); }; onChangeTab = (key) => { this.setState({activeTab: key}); }; renderFooter = () => { const readOnly = !this.isAdmin; const {activeTab} = this.state; let blocked = false; if (this.props.userInfo.loaded) { blocked = this.props.userInfo.value.blocked; } if (activeTab === 'user') { return ( <Row type="flex" justify="space-between"> <div> <Button disabled={readOnly} id="delete-user-button" type="danger" onClick={this.onDelete}>DELETE</Button> <Button disabled={readOnly} type="danger" onClick={() => this.onBlockUnBlock(!blocked)} > { blocked ? 'UNBLOCK' : 'BLOCK' } </Button> </div> <div> <Button id="revert-changes-edit-user-form" onClick={() => this.revertChanges()} disabled={readOnly || !this.modified} > REVERT </Button> <Button onClick={() => { this.revertChanges(); this.onClose(); }} > CANCEL </Button> <Button id="close-edit-user-form" type="primary" disabled={!this.modified} onClick={this.operationWrapper(this.saveChanges)} > OK </Button> </div> </Row> ); } return ( <Row type="flex" justify="end"> <Button id="close-edit-user-form" type="primary" onClick={() => this.onClose()} > OK </Button> </Row> ); }; renderTabs = () => { const {activeTab} = this.state; let blocked = false; if (this.props.userInfo.loaded) { blocked = this.props.userInfo.value.blocked; } return ( <Tabs activeKey={activeTab} onChange={this.onChangeTab} > <Tabs.TabPane tab={( <div> <span> PROFILE </span> { blocked && <span style={{fontStyle: 'italic', marginLeft: 5}} > - blocked </span> } </div> )} key="user" /> { this.isAdmin && ( <Tabs.TabPane tab="STATISTICS" key="user-statistics" /> ) } { this.isAdmin && ( <Tabs.TabPane tab="PERMISSIONS" key="permissions" /> ) } </Tabs> ); }; renderContent = () => { const { user, userId } = this.props; const {activeTab} = this.state; if (activeTab === 'user-statistics') { return ( <UserInfoSummary user={user} style={{flex: 1, overflow: 'auto'}} /> ); } if (activeTab === 'permissions') { return ( <PermissionsForm objectType={'PIPELINE_USER'} objectIdentifier={userId} showOwner={false} /> ); } return this.renderUserRolesTab(); }; renderImpersonateButton = () => { const {activeTab} = this.state; const {user} = this.props; if ( activeTab === 'user' && user && roleModel.executeAllowed(user) ) { return ( <Button type="primary" onClick={this.onImpersonate} className={styles.impersonateBtn} > IMPERSONATE </Button> ); } return null; }; render () { if (!this.props.userInfo) { return null; } const {activeTab} = this.state; return ( <Modal width="80%" closable={activeTab === 'permissions'} style={{ top: 20 }} bodyStyle={{ height: '80vh' }} maskClosable={activeTab === 'permissions'} onCancel={this.onClose} footer={activeTab === 'permissions' ? false : this.renderFooter()} visible={this.props.visible} > <div className={styles.modalContainer}> {this.renderTabs()} {this.renderImpersonateButton()} {this.renderContent()} </div> </Modal> ); } }