client/src/components/roleModel/PermissionsForm.js (1,058 lines of code) (raw):
/*
* Copyright 2017-2019 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 PropTypes from 'prop-types';
import {
Alert,
AutoComplete,
Button,
Checkbox,
Col,
Icon,
Modal,
Popover,
Row,
Table,
Select
} from 'antd';
import {isObservableArray, observable, computed} from 'mobx';
import {inject, observer} from 'mobx-react';
import classNames from 'classnames';
import GrantGet from '../../models/grant/GrantGet';
import GetAllPermissions from '../../models/grant/GetAllPermissions';
import UserFind from '../../models/user/UserFind';
import GroupFind from '../../models/user/GroupFind';
import Roles from '../../models/user/Roles';
import styles from './PermissionsForm.css';
import roleModel from '../../utils/roleModel';
import UserName from '../special/UserName';
import compareSubObjects from './utilities/compare-sub-objects';
import {
applyPermissionChanges,
filterRemovePermissionBySid,
findPermissionByPermission,
findPermissionBySidFn,
getPermissionChanges,
getPermissionsHash,
permissionSidsEqual
} from './utilities/permissions';
export const PERMISSION_COLUMNS = {
allow: 'allow',
deny: 'deny'
};
export const PERMISSIONS = {
read: 'read',
write: 'qwrite',
execute: 'execute'
};
function plural (count, noun) {
return `${noun}${count > 1 ? 's' : ''}`;
}
const MAX_SUB_OBJECTS_WARNINGS_TO_SHOW = 5;
const ALL_ALLOWED_MASK = roleModel.buildPermissionsMask(1, 1, 1, 1, 1, 1);
function findMaskForSubject (config, subject, isPrincipal, defaultMask = 0) {
if (typeof config === 'number') {
return config;
}
if (config && (Array.isArray(config) || isObservableArray(config))) {
const all = config
.find((aMask) => /^all$/i.test(aMask.role));
const rule = config
.find((aMask) => !isPrincipal &&
(subject || '').toLowerCase() === (aMask.role || '').toLowerCase());
if (rule) {
return rule.mask;
}
if (all) {
return all.mask;
}
}
return defaultMask;
}
@inject('usersInfo')
@inject(({routing, authenticatedUserInfo}, params) => ({
authenticatedUserInfo,
grant: new GrantGet(params.objectIdentifier, params.objectType),
roles: new Roles()
}))
@observer
export default class PermissionsForm extends React.Component {
state = {
findUserVisible: false,
findGroupVisible: false,
selectedPermission: undefined,
groupSearchString: undefined,
selectedUser: undefined,
owner: undefined,
ownerInput: undefined,
fetching: false,
fetchedUsers: [],
roleName: undefined,
subObjectsPermissions: [],
searchUserTouched: false,
pending: false,
error: undefined,
permissions: [],
originalPermissions: [],
originalOwner: undefined,
entity: undefined
};
@observable
groupFind;
static propTypes = {
objectType: PropTypes.string,
objectIdentifier: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
readonly: PropTypes.bool,
defaultMask: PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.shape({
mask: PropTypes.number,
role: PropTypes.string
}))
]),
enabledMask: PropTypes.oneOfType([
PropTypes.number,
PropTypes.arrayOf(PropTypes.shape({
mask: PropTypes.number,
role: PropTypes.string
}))
]),
subObjectsPermissionsMaskToCheck: PropTypes.number,
subObjectsToCheck: PropTypes.arrayOf(PropTypes.shape({
entityId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
entityClass: PropTypes.string,
name: PropTypes.node,
description: PropTypes.node
})),
subObjectsPermissionsErrorTitle: PropTypes.node,
showOwner: PropTypes.bool,
refreshPermissionsAfterUpdate: PropTypes.bool,
permissionsColumns: PropTypes.arrayOf(PropTypes.string),
availablePermissions: PropTypes.arrayOf(PropTypes.string)
};
static defaultProps = {
enabledMask: ALL_ALLOWED_MASK,
subObjectsPermissionsMaskToCheck: 0,
showOwner: true,
permissionsColumns: [PERMISSION_COLUMNS.allow, PERMISSION_COLUMNS.deny],
availablePermissions: [PERMISSIONS.read, PERMISSIONS.write, PERMISSIONS.execute]
}
@computed
get allUsers () {
if (this.props.usersInfo.loaded) {
return this.props.usersInfo.value || [];
}
return [];
}
get permissionsChanged () {
const {
originalOwner,
owner,
permissions,
originalPermissions
} = this.state;
return getPermissionsHash(permissions) !== getPermissionsHash(originalPermissions) ||
owner !== originalOwner;
}
findUser = (value) => {
this._ownerFetchId = {};
const fetchId = this._ownerFetchId;
this.setState({
ownerInput: value,
fetching: true,
selectedUser: undefined
}, async () => {
const request = new UserFind(value);
await request.fetch();
if (fetchId === this._ownerFetchId) {
let fetchedUsers = [];
if (!request.error) {
fetchedUsers = (request.value || []).map(u => u);
}
this.setState({
fetching: false,
fetchedUsers
});
}
});
};
onUserSelect = (key) => {
const [user] = this.state.fetchedUsers.filter(u => `${u.id}` === `${key}`);
if (user) {
this.setState({
ownerInput: user.userName,
owner: user.userName
});
}
};
onUserFindInputChanged = (value) => {
this.setState({selectedUser: value});
};
onGroupFindInputChanged = (value) => {
this.selectedGroup = value;
if (value && value.length) {
this.groupFind = new GroupFind(value);
this.groupFind.fetch();
} else {
this.groupFind = undefined;
}
this.setState({groupSearchString: value});
};
renderGroupAndUsersActions = () => {
return (
<span className={styles.actions}>
<Button disabled={this.props.readonly} size="small" onClick={this.openFindUserDialog}>
<Icon type="user-add" />
</Button>
<Button disabled={this.props.readonly} size="small" onClick={this.openFindGroupDialog}>
<Icon type="usergroup-add" />
</Button>
</span>
);
};
splitRoleName = (name) => {
if (name && name.toLowerCase().indexOf('role_') === 0) {
return name.substring('role_'.length);
}
return name;
};
findGroupDataSource = () => {
const {permissions = [], groupSearchString} = this.state;
const {roles: rolesRequest} = this.props;
const existingGroups = new Set(
permissions
.filter(p => p.sid && !p.sid.principal)
.map(p => p.sid.name)
);
const roles = (rolesRequest.loaded && groupSearchString) ? (
(rolesRequest.value || [])
.filter(r => r.name.toLowerCase().indexOf(groupSearchString.toLowerCase()) >= 0)
.filter(r => !existingGroups.has(r.name))
.map(r => r.predefined ? r.name : this.splitRoleName(r.name))
) : [];
if (this.groupFind && !this.groupFind.pending && !this.groupFind.error) {
return [...roles, ...(this.groupFind.value || []).map(g => g)];
}
return [...roles];
};
selectedGroup = undefined;
openFindUserDialog = () => {
this.setState({
findUserVisible: true
});
};
closeFindUserDialog = () => {
this.setState({
selectedUser: undefined,
findUserVisible: false
});
};
getDefaultMaskForSubject = (subject, isPrincipal) => {
const {
defaultMask = []
} = this.props;
return findMaskForSubject(defaultMask, subject, isPrincipal, 0);
};
getEnabledMaskForSubject = (subject, isPrincipal) => {
const {
enabledMask = []
} = this.props;
return findMaskForSubject(enabledMask, subject, isPrincipal, ALL_ALLOWED_MASK);
};
onSelectUser = () => {
this.grantPermission(
this.state.selectedUser,
true,
this.getDefaultMaskForSubject(this.state.selectedUser, true)
);
this.closeFindUserDialog();
};
openFindGroupDialog = () => {
this.selectedGroup = undefined;
this.setState({findGroupVisible: true, groupSearchString: undefined});
};
closeFindGroupDialog = () => {
this.setState({findGroupVisible: false, groupSearchString: undefined});
};
onSelectGroup = async () => {
const [role] = (this.props.roles.loaded ? this.props.roles.value || [] : [])
.filter(r => !r.predefined && this.splitRoleName(r.name) === this.selectedGroup);
const roleName = role ? role.name : this.selectedGroup;
this.grantPermission(
roleName,
false,
this.getDefaultMaskForSubject(roleName, false)
);
this.closeFindGroupDialog();
};
grantPermission = (name, principal, mask) => {
const {
permissions = []
} = this.state;
const sid = {name, principal};
const newPermissions = permissions.slice();
const idx = newPermissions.findIndex((p) => permissionSidsEqual(p.sid, sid));
if (idx >= 0) {
newPermissions.splice(idx, 1, {
sid,
mask
});
} else {
newPermissions.push({
sid,
mask
});
}
const selectedPermission = newPermissions
.find(findPermissionBySidFn(sid));
this.setState({
permissions: newPermissions,
selectedPermission
});
};
removeUserOrGroupClicked = (item) => (event) => {
event.stopPropagation();
const {
permissions = [],
selectedPermission
} = this.state;
const {sid} = item;
const newPermissions = permissions
.filter(filterRemovePermissionBySid(sid));
const selectFirstPermission = !selectedPermission ||
permissionSidsEqual(selectedPermission.sid, sid);
this.setState({
permissions: newPermissions,
selectedPermission: selectFirstPermission ? undefined : selectedPermission
}, () => {
if (selectFirstPermission) {
this.selectFirstPermission();
}
});
};
onAllowDenyValueChanged = (permissionMask, allowDenyMask, allowRead = false) => async (event) => {
const mask = (1 | 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4 | 1 << 5) ^ permissionMask;
let newValue = 0;
if (event.target.checked) {
newValue = allowDenyMask;
}
const {selectedPermission} = this.state;
if (selectedPermission) {
let {mask: currentMask, sid} = selectedPermission;
currentMask = (currentMask & mask) | newValue;
if (allowRead && event.target.checked) {
currentMask = (currentMask & (1 << 2 | 1 << 3 | 1 << 4 | 1 << 5)) | 1;
}
this.grantPermission(sid.name, sid.principal, currentMask);
}
};
renderSubObjectsWarnings = () => {
const {subObjectsPermissionsErrorTitle} = this.props;
const {permissions: granted = []} = this.state;
const {subObjectsPermissionsMaskToCheck} = this.props;
const {subObjectsPermissions} = this.state;
const check = {
read: roleModel.readPermissionEnabled(subObjectsPermissionsMaskToCheck),
write: roleModel.writePermissionEnabled(subObjectsPermissionsMaskToCheck),
execute: roleModel.executePermissionEnabled(subObjectsPermissionsMaskToCheck)
};
const warnings = [];
for (let d = 0; d < granted.length; d++) {
const {mask, sid = {}} = granted[d];
const maskToCheck = mask & subObjectsPermissionsMaskToCheck;
const {name, principal} = sid;
const rolesToCheck = [];
if (principal) {
const userInfo = this.allUsers.find(u => u.name === name);
if (userInfo && userInfo.roles) {
rolesToCheck.push(
...(userInfo.roles || []).map(({name}) => ({name, principal: false}))
);
}
} else {
rolesToCheck.push({name: 'ROLE_USER', principal: false});
}
for (let o = 0; o < subObjectsPermissions.length; o++) {
const subObjectPermission = subObjectsPermissions[o];
const {
read,
write,
execute
} = roleModel.checkObjectPermissionsConflict(
maskToCheck,
sid,
rolesToCheck,
subObjectPermission.owner,
subObjectPermission.permissions
);
if (check.read && read) {
// Read conflict
warnings.push((
<span>
{subObjectPermission.object.name}: read denied for <b>{name}</b>
</span>
));
}
if (check.write && write) {
// Write conflict
warnings.push((
<span>
{subObjectPermission.object.name}: write denied for <b>{name}</b>
</span>
));
}
if (check.execute && execute) {
// Execute conflict
warnings.push((
<span>
{subObjectPermission.object.name}: execute denied for <b>{name}</b>
</span>
));
}
}
}
if (warnings.length > 0) {
const title = subObjectsPermissionsErrorTitle && (
<div style={{marginBottom: 5}}>
{subObjectsPermissionsErrorTitle}
</div>
);
const content = (
<div>
{
warnings.map((warning, index) => (
<div key={index}>
{warning}
</div>
))
}
</div>
);
if (warnings.length > MAX_SUB_OBJECTS_WARNINGS_TO_SHOW) {
const rest = warnings.length - MAX_SUB_OBJECTS_WARNINGS_TO_SHOW;
return (
<Alert
showIcon
style={{marginBottom: 5}}
message={(
<div>
{title}
{
warnings.slice(0, MAX_SUB_OBJECTS_WARNINGS_TO_SHOW).map((warning, index) => (
<div key={index}>
{warning}
</div>
))
}
<div>
<Popover
content={content}
>
<a>
... and {rest} more {plural(rest, 'warning')}
</a>
</Popover>
</div>
</div>
)}
type="warning"
/>
);
}
return (
<Alert
showIcon
style={{marginBottom: 5}}
message={(
<div>
{title}
{content}
</div>
)}
type="warning"
/>
);
}
};
renderUserName = (user) => {
if (user.attributes) {
const getAttributesValues = () => {
const values = [];
for (let key in user.attributes) {
if (user.attributes.hasOwnProperty(key)) {
values.push(user.attributes[key]);
}
}
return values;
};
const attributesString = getAttributesValues().join(', ');
return (
<Row type="flex" style={{flexDirection: 'column'}}>
<Row>{user.userName}</Row>
<Row><span style={{fontSize: 'smaller'}}>{attributesString}</span></Row>
</Row>
);
} else {
return user.userName;
}
};
renderUserPermission = () => {
const {
selectedPermission,
pending
} = this.state;
const {permissionsColumns, availablePermissions} = this.props;
if (selectedPermission) {
const {
sid = {}
} = selectedPermission;
const {
name,
principal
} = sid;
const enabledMask = this.getEnabledMaskForSubject(name, principal);
const columns = [
{
title: 'Permissions',
dataIndex: 'permission',
render: (name, item) => {
if (!item.allowed && !item.denied) {
return (<span>{name} <i style={{fontSize: 'smaller'}}>(inherit)</i></span>);
}
return name;
}
},
permissionsColumns.includes(PERMISSION_COLUMNS.allow) ? {
title: 'Allow',
width: 50,
className: styles.userAllowDenyActions,
render: (item) => (
<Checkbox
disabled={
pending ||
this.props.readonly ||
((item.allowMask & enabledMask) === 0)
}
checked={item.allowed}
onChange={
this.onAllowDenyValueChanged(
item.allowMask | item.denyMask,
item.allowMask,
!item.isRead
)
}
/>
)
} : null,
permissionsColumns.includes(PERMISSION_COLUMNS.deny) ? {
title: 'Deny',
width: 50,
className: styles.userAllowDenyActions,
render: (item) => (
<Checkbox
disabled={
pending ||
this.props.readonly ||
((item.denyMask & enabledMask) === 0)
}
checked={item.denied}
onChange={
this.onAllowDenyValueChanged(item.allowMask | item.denyMask, item.denyMask)
}
/>
)
} : null
].filter(Boolean);
const data = [
availablePermissions.includes(PERMISSIONS.read) ? {
permission: 'Read',
allowMask: 1,
denyMask: 1 << 1,
allowed: roleModel.readAllowed(selectedPermission, true),
denied: roleModel.readDenied(selectedPermission, true),
isRead: true
} : null,
availablePermissions.includes(PERMISSIONS.write) ? {
permission: 'Write',
allowMask: 1 << 2,
denyMask: 1 << 3,
allowed: roleModel.writeAllowed(selectedPermission, true),
denied: roleModel.writeDenied(selectedPermission, true)
} : null,
availablePermissions.includes(PERMISSIONS.execute) ? {
permission: 'Execute',
allowMask: 1 << 4,
denyMask: 1 << 5,
allowed: roleModel.executeAllowed(selectedPermission, true),
denied: roleModel.executeDenied(selectedPermission, true)
} : null
].filter(Boolean);
return (
<Table
style={{marginTop: 10}}
key="user permissions"
loading={pending}
showHeader
size="small"
columns={columns}
pagination={false}
rowKey={(item) => item.permission}
dataSource={data} />
);
}
return undefined;
};
renderUsers = () => {
const {
pending,
error,
permissions: data = [],
selectedPermission
} = this.state;
if (error) {
return <Alert type="warning" message={error} />;
}
const getSidName = (name, principal) => {
const {roles: rolesRequest} = this.props;
const rolesList = (rolesRequest.loaded ? (rolesRequest.value || []) : []).map(r => r);
if (principal) {
return <UserName userName={name} />;
} else {
const [role] = rolesList.filter(r => !r.predefined && r.name === name);
if (role) {
return this.splitRoleName(name);
} else {
return name;
}
}
};
const columns = [
{
key: 'icon',
className: styles.userIcon,
render: (item) => {
if (item.sid.principal) {
return <Icon type="user" />;
}
return <Icon type="team" />;
}
},
{
dataIndex: 'sid.name',
key: 'name',
render: (name, item) => getSidName(name, item.sid.principal)
},
{
key: 'actions',
className: styles.userActions,
render: (item) => (
<Row>
<Button
disabled={pending || this.props.readonly}
onClick={this.removeUserOrGroupClicked(item)}
size="small">
<Icon type="delete" />
</Button>
</Row>
)
}
];
const getRowClassName = (item) => {
if (!selectedPermission || selectedPermission.sid.name !== item.sid.name) {
return styles.row;
}
return classNames(styles.selectedRow, 'cp-edit-permissions-selected-row');
};
const selectPermission = (item) => {
this.setState({selectedPermission: item});
};
const title = (
<Row>
<Col span={12}>
<b>Groups and users</b>
</Col>
<Col span={12} style={{textAlign: 'right'}}>
{this.renderGroupAndUsersActions()}
</Col>
</Row>
);
return [
<Table
className={styles.table}
key="users table"
style={{
maxHeight: 200,
overflowY: 'auto'
}}
rowClassName={getRowClassName}
onRowClick={selectPermission}
loading={pending}
title={() => title}
showHeader={false}
size="small"
columns={columns}
pagination={false}
rowKey={(item) => item.sid.name}
dataSource={(data || []).map(p => p)} />,
this.renderUserPermission()
];
};
isAdmin = () => {
if (!this.props.authenticatedUserInfo.loaded) {
return false;
}
return this.props.authenticatedUserInfo.value.admin;
};
renderOwner = () => {
const {
pending,
error,
owner,
ownerInput,
originalOwner,
fetchedUsers = []
} = this.state;
if (!pending && !error && originalOwner && this.props.showOwner) {
const isAdminOrOwner = this.isAdmin() ||
originalOwner === this.props.authenticatedUserInfo.value.userName;
if (isAdminOrOwner) {
const onBlur = () => {
this.setState({
ownerInput: undefined
});
};
return (
<Row
className={styles.ownerContainer}
type="flex"
style={{margin: '0px 5px 10px', height: 22}}
align="middle"
>
<span style={{marginRight: 5}}>Owner: </span>
<AutoComplete
size="small"
style={{flex: 1}}
placeholder="Change owner"
optionLabelProp="text"
value={
ownerInput === undefined
? owner
: ownerInput
}
onBlur={onBlur}
onSelect={this.onUserSelect}
onSearch={this.findUser}>
{
fetchedUsers.map(user => {
return (
<AutoComplete.Option
key={user.id}
text={user.userName}>
{this.renderUserName(user)}
</AutoComplete.Option>
);
})
}
</AutoComplete>
</Row>
);
}
return (
<Row
className={styles.ownerContainer}
type="flex"
style={{margin: '0px 5px 10px', height: 22}}
align="middle"
>
<span style={{marginRight: 5}}>Owner: </span>
<b id="object-owner" style={{paddingLeft: 4}}>{owner}</b>
</Row>
);
}
return null;
};
revertChanges = () => {
const {
originalOwner,
originalPermissions,
selectedPermission
} = this.state;
this.setState({
permissions: [...originalPermissions],
selectedPermission: originalPermissions.find(findPermissionByPermission(selectedPermission)),
owner: originalOwner,
ownerInput: undefined
});
};
applyChanges = () => {
const {
owner,
originalOwner,
permissions,
originalPermissions
} = this.state;
const {
objectType,
objectIdentifier,
refreshPermissionsAfterUpdate = false
} = this.props;
const changes = getPermissionChanges({
owner,
originalOwner,
permissions,
originalPermissions
});
if (changes.changed) {
this.setState({pending: true});
(async () => {
const success = await applyPermissionChanges(changes, objectIdentifier, objectType);
this.setState({pending: false}, () => {
if (success) {
if (refreshPermissionsAfterUpdate) {
this.objectChanged();
} else {
this.setState({
permissions: (permissions || []).map((o) => ({...o})),
originalPermissions: (permissions || []).map((o) => ({...o})),
originalOwner: owner,
owner: owner,
error: undefined
}, () => this.selectFirstPermission());
}
}
});
})();
}
};
render () {
const {
pending
} = this.state;
const {permissionsChanged} = this;
return (
<Row>
{this.renderOwner()}
{this.renderSubObjectsWarnings()}
{this.renderUsers()}
{!this.props.readonly && (
<div className={styles.permissionsFormFooter}>
<Button
className={styles.permissionsFormAction}
disabled={!permissionsChanged || pending}
onClick={this.revertChanges}
>
REVERT
</Button>
<Button
className={styles.permissionsFormAction}
disabled={!permissionsChanged || pending}
type="primary"
onClick={this.applyChanges}
>
APPLY
</Button>
</div>
)}
<Modal
title="Select user"
onCancel={this.closeFindUserDialog}
onOk={this.onSelectUser}
footer={(
<Row type="flex" justify="end">
<Button
onClick={this.closeFindUserDialog}
style={{marginRight: 5}}
>
Cancel
</Button>
<Button
type="primary"
disabled={pending}
onClick={this.onSelectUser}
>
OK
</Button>
</Row>
)}
visible={this.state.findUserVisible}>
<Select
disabled={!this.props.usersInfo.loaded}
placeholder="Enter the account info"
style={{width: '100%'}}
showSearch
value={this.state.selectedUser}
onSelect={this.onUserFindInputChanged}
filterOption={(input, option) => option.props.attributes
.map(o => o.toLowerCase())
.find(o => o.includes((input || '').toLowerCase()))
}
onSearch={(value) => this.setState({
searchUserTouched: value.length > 2}
)}
onFocus={() => this.setState({searchUserTouched: false})}
notFoundContent={this.state.searchUserTouched
? 'Not found'
: 'Start typing to filter users...'
}
>
{
this.state.searchUserTouched ? (
this.allUsers
.map(user => (
<Select.Option
key={user.name}
value={user.name}
attributes={
[
user.name,
...Object.values(user.attributes || {})
]
}
>
<UserName userName={user.name} />
</Select.Option>
))
) : null
}
</Select>
</Modal>
<Modal
title="Select group"
onCancel={this.closeFindGroupDialog}
onOk={this.onSelectGroup}
footer={(
<Row type="flex" justify="end">
<Button
onClick={this.closeFindGroupDialog}
style={{marginRight: 5}}
>
Cancel
</Button>
<Button
type="primary"
disabled={pending}
onClick={this.onSelectGroup}
>
OK
</Button>
</Row>
)}
visible={this.state.findGroupVisible}>
<AutoComplete
value={this.selectedGroup}
style={{width: '100%'}}
dataSource={this.findGroupDataSource()}
onChange={this.onGroupFindInputChanged}
placeholder="Enter the group name" />
</Modal>
</Row>
);
}
selectFirstPermission = () => {
const {permissions = []} = this.state;
if (permissions && permissions.length > 0) {
const [first] = permissions;
this.setState({selectedPermission: first});
}
};
fetchSubObjectsPermissions = () => {
const wrapPermissionsFetch = (subObject) => new Promise((resolve) => {
const request = new GetAllPermissions(subObject.entityId, subObject.entityClass);
request.fetch()
.then(() => {
if (request.loaded) {
const {owner, permissions = []} = request.value || {};
resolve({object: subObject, permissions, owner});
} else {
resolve({object: subObject, permissions: []});
}
})
.catch(() => {
resolve({object: subObject, permissions: []});
});
});
this.setState({
subObjectsPermissions: []
}, () => {
Promise.all(
(this.props.subObjectsToCheck || []).map(wrapPermissionsFetch)
)
.then(payloads => {
this.setState({
subObjectsPermissions: payloads
});
});
});
};
componentDidMount () {
this.objectChanged();
this.fetchSubObjectsPermissions();
}
objectChanged = () => {
const {
objectIdentifier,
objectType
} = this.props;
this._token = {};
const token = this._token;
this.setState({
selectedPermission: undefined,
pending: true,
error: undefined,
permissions: [],
originalPermissions: [],
originalOwner: undefined,
owner: undefined,
ownerInput: undefined
});
const commit = (fn) => {
if (token === this._token) {
fn();
}
};
(async () => {
try {
const request = new GrantGet(objectIdentifier, objectType);
await request.fetch();
commit(() => {
if (!request.loaded || request.error) {
this.setState({
error: request.error || 'Error fetching permissions',
pending: false
});
} else {
const {
permissions,
entity
} = request.value || {};
this.setState({
permissions: (permissions || []).map((o) => ({...o})),
originalPermissions: (permissions || []).map((o) => ({...o})),
originalOwner: entity ? entity.owner : undefined,
owner: entity ? entity.owner : undefined,
pending: false,
error: undefined
}, () => this.selectFirstPermission());
}
});
} catch (error) {
commit(() => this.setState({
pending: false,
error: error.message
}));
}
})();
};
componentDidUpdate (prevProps) {
if (this.props.objectIdentifier !== prevProps.objectIdentifier) {
this.objectChanged();
}
if (!compareSubObjects(this.props.subObjectsToCheck, prevProps.subObjectsToCheck)) {
this.fetchSubObjectsPermissions();
}
}
componentWillUnmount () {
this._token = {};
}
}