Merge "refactor: Refactor user"

This commit is contained in:
Zuul 2022-06-09 04:00:57 +00:00 committed by Gerrit Code Review
commit d133705f6f
19 changed files with 518 additions and 1562 deletions

View File

@ -790,6 +790,7 @@
"Edit Share Metadata": "Edit Share Metadata",
"Edit Subnet": "Edit Subnet",
"Edit System Permission": "Edit System Permission",
"Edit User": "Edit User",
"Edit User Group": "Edit User Group",
"Edit VPN": "Edit VPN",
"Edit VPN EndPoint Groups": "Edit VPN EndPoint Groups",
@ -1326,7 +1327,6 @@
"MacVTap": "MacVTap",
"Macau": "Macau",
"Madagascar": "Madagascar",
"Main Project": "Main Project",
"Maintained": "Maintained",
"Maintenance": "Maintenance",
"Malawi": "Malawi",
@ -1351,7 +1351,6 @@
"Manageable": "Manageable",
"Management": "Management",
"Management Reason": "Management Reason",
"Manager user": "Manager user",
"Manager user group": "Manager user group",
"Manu": "Manu",
"Manual backup": "Manual backup",
@ -1958,7 +1957,6 @@
"Select Project": "Select Project",
"Select Project Role": "Select Project Role",
"Select Snapshot": "Select Snapshot",
"Select System Role": "Select System Role",
"Select User Group": "Select User Group",
"Select a domain": "Select a domain",
"Select a region": "Select a region",
@ -2161,6 +2159,7 @@
"System Info": "System Info",
"System Load": "System Load",
"System Reader": "System Reader",
"System Roles": "System Roles",
"System Running Time": "System Running Time",
"System is error, please try again later.": "System is error, please try again later.",
"TCP": "TCP",
@ -2189,7 +2188,6 @@
"The Republic of Macedonia": "The Republic of Macedonia",
"The Republic of South Sudan": "The Republic of South Sudan",
"The SSH key is a way to remotely log in to the instance. The cloud platform only helps to keep the public key. Please keep your private key properly.": "The SSH key is a way to remotely log in to the instance. The cloud platform only helps to keep the public key. Please keep your private key properly.",
"The affiliated Domain cannot be modified after creation": "The affiliated Domain cannot be modified after creation",
"The amphora instance is required for load balancing service setup and is not recommended": "The amphora instance is required for load balancing service setup and is not recommended",
"The associated floating IP, virtual adapter, volume and other resources will be automatically disassociated.": "The associated floating IP, virtual adapter, volume and other resources will be automatically disassociated.",
"The certificate contains information such as the public key and signature of the certificate. The extension of the certificate is \"pem\" or \"crt\", you can directly enter certificate content or upload certificate file.": "The certificate contains information such as the public key and signature of the certificate. The extension of the certificate is \"pem\" or \"crt\", you can directly enter certificate content or upload certificate file.",
@ -2393,6 +2391,7 @@
"User Data": "User Data",
"User Detail": "User Detail",
"User Edit": "User Edit",
"User Group": "User Group",
"User Group Detail": "User Group Detail",
"User Group ID/Name": "User Group ID/Name",
"User Group Name": "User Group Name",
@ -2485,7 +2484,6 @@
"Whether the boot device should be set only for the next reboot, or persistently.": "Whether the boot device should be set only for the next reboot, or persistently.",
"Which Network Interface provider to use when plumbing the network connections for this Node": "Which Network Interface provider to use when plumbing the network connections for this Node",
"Windows": "Windows",
"Work Num": "Work Num",
"Workdir": "Workdir",
"Working Directory": "Working Directory",
"X86 Architecture": "X86 Architecture",
@ -2515,6 +2513,7 @@
"application credential": "application credential",
"associate floating ip": "associate floating ip",
"attach interface": "attach interface",
"authorized by group ": "authorized by group ",
"availability zones": "availability zones",
"available": "available",
"backup": "backup",
@ -2619,7 +2618,6 @@
"domain": "domain",
"domains": "domains",
"e.g. 2001:Db8::/48": "e.g. 2001:Db8::/48",
"edit": "edit",
"edit baremetal node": "edit baremetal node",
"edit default pool": "edit default pool",
"edit health monitor": "edit health monitor",

View File

@ -790,6 +790,7 @@
"Edit Share Metadata": "编辑共享元数据",
"Edit Subnet": "编辑子网",
"Edit System Permission": "编辑系统角色",
"Edit User": "编辑用户",
"Edit User Group": "编辑用户组",
"Edit VPN": "编辑VPN",
"Edit VPN EndPoint Groups": "编辑VPN端点组",
@ -1326,7 +1327,6 @@
"MacVTap": "",
"Macau": "澳门",
"Madagascar": "马达加斯加",
"Main Project": "主项目",
"Maintained": "维护",
"Maintenance": "运维管理",
"Malawi": "马拉维",
@ -1351,7 +1351,6 @@
"Manageable": "可管理",
"Management": "维护",
"Management Reason": "维护原因",
"Manager user": "管理用户",
"Manager user group": "管理用户组",
"Manu": "手动",
"Manual backup": "手动备份",
@ -1958,7 +1957,6 @@
"Select Project": "选择项目",
"Select Project Role": "选择项目角色",
"Select Snapshot": "选择快照",
"Select System Role": "选择系统角色",
"Select User Group": "选择用户组",
"Select a domain": "请选择Domain",
"Select a region": "请选择Region",
@ -2161,6 +2159,7 @@
"System Info": "系统信息",
"System Load": "系统负载",
"System Reader": "系统只读权限",
"System Roles": "系统角色",
"System Running Time": "",
"System is error, please try again later.": "系统出错,请稍后再试。",
"TCP": "",
@ -2189,7 +2188,6 @@
"The Republic of Macedonia": "马其顿",
"The Republic of South Sudan": "南苏丹共和国",
"The SSH key is a way to remotely log in to the instance. The cloud platform only helps to keep the public key. Please keep your private key properly.": "SSH 密钥是一种远程登录云主机的方式,云平台只帮助保管公钥,请妥善保管自己的私钥。",
"The affiliated Domain cannot be modified after creation": "所属域不可修改",
"The amphora instance is required for load balancing service setup and is not recommended": "amphora 相关的云主机为负载均衡服务搭建所需,不建议选择",
"The associated floating IP, virtual adapter, volume and other resources will be automatically disassociated.": "绑定的浮动IP、网卡、云硬盘等资源将自动解绑。",
"The certificate contains information such as the public key and signature of the certificate. The extension of the certificate is \"pem\" or \"crt\", you can directly enter certificate content or upload certificate file.": "证书包含证书的公钥和签名等信息证书扩展名为”pem”或”crt”您可直接输入证书内容或上传证书文件。",
@ -2393,6 +2391,7 @@
"User Data": "用户数据",
"User Detail": "用户详情",
"User Edit": "编辑用户",
"User Group": "用户组",
"User Group Detail": "用户组详情",
"User Group ID/Name": "用户组ID/名称",
"User Group Name": "用户组名称",
@ -2485,7 +2484,6 @@
"Whether the boot device should be set only for the next reboot, or persistently.": "是否永久使用该引导设置。",
"Which Network Interface provider to use when plumbing the network connections for this Node": "当为这个节点连接网络时,使用哪个网络接口提供者",
"Windows": "",
"Work Num": "工号",
"Workdir": "工作目录",
"Working Directory": "工作目录",
"X86 Architecture": "X86架构",
@ -2515,6 +2513,7 @@
"application credential": "应用凭证",
"associate floating ip": "绑定浮动IP",
"attach interface": "挂载网卡",
"authorized by group ": "由组授权",
"availability zones": "可用域",
"available": "可用",
"backup": "备份",
@ -2619,7 +2618,6 @@
"domain": "域",
"domains": "域",
"e.g. 2001:Db8::/48": "",
"edit": "编辑",
"edit baremetal node": "编辑裸机节点",
"edit default pool": "编辑资源池",
"edit health monitor": "编辑健康检查器",

View File

@ -53,6 +53,10 @@ export class Detail extends Base {
dataIndex: 'name',
},
enabledColumn,
{
title: t('Affiliated Domain'),
dataIndex: 'domainName',
},
{
title: t('User Num'),
dataIndex: 'userCount',

View File

@ -46,7 +46,7 @@ export class ManagerUser extends ModalAction {
}
getUser() {
this.userStore.fetchAllWithDomain();
this.userStore.fetchList({ withProjectRole: false, withSystemRole: false });
}
getInitRoleMap() {

View File

@ -214,6 +214,11 @@ export class Projects extends Base {
)}${groupCount}`;
},
},
{
title: t('Affiliated Domain'),
dataIndex: 'domainName',
isHideable: true,
},
enabledColumn,
{
title: t('Tags'),

View File

@ -12,11 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import React from 'react';
import { inject, observer } from 'mobx-react';
import { UserStore } from 'stores/keystone/user';
import globalDomainStore from 'stores/keystone/domain';
import Base from 'containers/TabDetail';
import Credentials from 'src/pages/user-center/containers/Credentials';
import { emptyActionConfig } from 'utils/constants';
import { enabledColumn } from 'resources/keystone/domain';
import UserGroup from '../../UserGroup';
import Project from '../../Project';
@ -36,33 +37,13 @@ export class UserDetail extends Base {
}
get actionConfigs() {
return this.isAdminPage
? actionConfigs.adminConfigs
: actionConfigs.actionConfigs;
return this.isAdminPage ? actionConfigs : emptyActionConfig;
}
init() {
this.store = new UserStore();
this.domainStore = globalDomainStore;
this.getDomains();
}
getDomains() {
this.domainStore.fetchDomain();
}
get domainList() {
const { domains } = this.domainStore;
return domains || [];
}
goEdit = () => {
const {
params: { id },
} = this.props.match;
this.routing.push(`${this.listUrl}/edit/${id}`);
};
get detailInfos() {
return [
{
@ -70,25 +51,27 @@ export class UserDetail extends Base {
dataIndex: 'name',
},
enabledColumn,
{
title: t('System Roles'),
dataIndex: 'systemRoles',
render: (value) => {
if (!value || !value.length) {
return '-';
}
return (value || []).map((it) => <div key={it.id}>{it.name}</div>);
},
},
{
title: t('Real Name'),
dataIndex: 'real_name',
},
// {
// title: t('User Group Num'),
// dataIndex: 'group_num',
// dataIndex: 'groupCount',
// },
{
title: t('Affiliated Domain'),
dataIndex: 'domain_id',
isHideable: true,
render: (domain_id) => {
const domain = this.domainList.filter((it) => it.id === domain_id);
if (domain[0]) {
return domain[0].name;
}
return domain_id;
},
dataIndex: 'domainName',
},
{
title: t('Email'),

View File

@ -13,56 +13,61 @@
// limitations under the License.
import React from 'react';
import { has } from 'lodash';
import { inject, observer } from 'mobx-react';
import globalGroupStore from 'stores/keystone/user-group';
import { GroupStore } from 'stores/keystone/user-group';
import globalUserStore from 'stores/keystone/user';
import { FormAction } from 'containers/Action';
import { Select } from 'antd';
import globalProjectStore from 'stores/keystone/project';
import { ProjectStore } from 'stores/keystone/project';
import globalRoleStore from 'stores/keystone/role';
import globalDomainStore from 'stores/keystone/domain';
import {
getPasswordOtherRule,
phoneNumberValidate,
emailValidate,
} from 'utils/validate';
import { statusTypes } from 'resources/keystone/domain';
import {
statusTypes,
getDomainFormItem,
nameDomainColumns,
transferFilterOption,
} from 'resources/keystone/domain';
export class CreateForm extends FormAction {
export class Create extends FormAction {
constructor(props) {
super(props);
this.state = {
domain: 'default',
more: false,
newProjectRoles: {},
projectRoles: {},
};
}
init() {
this.store = globalUserStore;
this.userGroupStore = globalGroupStore;
this.projectStore = globalProjectStore;
this.userGroupStore = new GroupStore();
this.projectStore = new ProjectStore();
this.roleStore = globalRoleStore;
this.getUserGroup();
this.getProject();
this.getRole();
this.getUserGroups();
this.getProjects();
this.getRoles();
this.getDomains();
}
getDomains() {
this.store.fetchDomain();
globalDomainStore.fetchDomain();
}
getUserGroup() {
this.userGroupStore.fetchList();
getUserGroups() {
this.userGroupStore.fetchList({ withRole: false });
}
getProject() {
this.projectStore.fetchList();
getProjects() {
this.projectStore.fetchList({ withRole: false });
}
getRole() {
getRoles() {
this.roleStore.fetchList();
}
@ -97,13 +102,10 @@ export class CreateForm extends FormAction {
}
get defaultValue() {
const { domains } = this.store;
const { domain } = this.state;
const domainDefault = (domains || []).filter((it) => it.id === domain)[0];
const data = {
more: false,
enabled: statusTypes[0].value,
domain_id: domainDefault ? domainDefault.name : 'Default',
domain_id: 'default',
};
return data;
}
@ -129,96 +131,81 @@ export class CreateForm extends FormAction {
}));
}
// get roleList() {
// return (this.roleStore.list.data || []).map(it => ({
// label: it.name,
// value: it.id,
// }));
// }
// get adminRole() {
// return (this.roleStore.list.data || []).filter(it =>
// it.name === 'admin'
// )[0];
// }
onValuesChange = (changedFields) => {
if (has(changedFields, 'more')) {
this.setState({
more: changedFields.more,
});
}
};
static allowed = () => Promise.resolve(true);
get leftProjectTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
];
return nameDomainColumns;
}
get projectRoleList() {
const projectRole = this.roleStore.list.data || [];
return projectRole;
return this.roleStore.list.data || [];
}
userRolesList = (role_id) =>
projectRolesList = (projectId) =>
(this.projectRoleList || []).map((it) => ({
label: it.name,
value: it.id,
role_id,
projectId,
}));
defaultRoles = () => [this.projectRoleList[0].id];
defaultRoles = () => [(this.projectRoleList[0] || {}).id];
userRoleChange = (value, option) => {
const { newProjectRoles } = this.state;
const { role_id } = option;
newProjectRoles[role_id] = value;
this.setState({ newProjectRoles });
onSelectChange = (value, option, projectId) => {
const { projectRoles } = this.state;
if (value.length && option.length) {
projectRoles[projectId] = value;
} else {
projectRoles[projectId] = {};
}
this.setState({ projectRoles });
};
renderSelect = (projectId) => {
return (
<Select
size="small"
mode="multiple"
options={this.projectRolesList(projectId)}
defaultValue={this.defaultRoles()}
onChange={(value, option) => {
this.onSelectChange(value, option, projectId);
}}
/>
);
};
get rightProjectTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
...nameDomainColumns,
{
title: t('Select Project Role'),
dataIndex: 'id',
render: (id) => (
<Select
size="small"
options={this.userRolesList(id)}
defaultValue={this.defaultRoles()}
onChange={this.userRoleChange}
/>
),
render: (id) => this.renderSelect(id),
},
];
}
onChangeProject = (value) => {
const { projectRoles } = this.state;
(value || []).forEach((projectId) => {
if (!projectRoles[projectId]) {
projectRoles[projectId] = this.defaultRoles();
}
});
Object.keys(projectRoles).forEach((projectId) => {
if (!(value || []).includes(projectId)) {
delete projectRoles[projectId];
}
});
this.setState(projectRoles);
};
get leftUserGroupTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
];
return nameDomainColumns;
}
get rightUserGroupTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
];
return nameDomainColumns;
}
checkName = (rule, value) => {
@ -236,17 +223,22 @@ export class CreateForm extends FormAction {
};
get formItems() {
const { more, domain } = this.state;
const { more } = this.state;
const labelCol = {
xs: { span: 5 },
sm: { span: 6 },
};
const domainFormItem = getDomainFormItem(this);
const currentDomainFormItem = {
...domainFormItem,
labelCol,
colNum: 2,
};
return [
{
name: 'name',
label: t('User Name'),
type: 'input',
placeholder: t('Please input user name'),
validator: this.checkName,
extra: t('User name can not be duplicated'),
required: true,
@ -291,20 +283,7 @@ export class CreateForm extends FormAction {
labelCol,
colNum: 2,
},
{
name: 'domain_id',
label: t('Affiliated Domain'),
type: 'input',
// options: this.domainList,
// onChange: (e) => {
// this.setState({
// domain: e,
// });
// },
disabled: true,
colNum: 2,
labelCol,
},
currentDomainFormItem,
{
name: 'enabled',
label: t('Status'),
@ -315,14 +294,6 @@ export class CreateForm extends FormAction {
labelCol,
colNum: 2,
},
// {
// name: 'default_project_id',
// label: t('Main Project'),
// type: 'select',
// options: this.projectList,
// labelCol,
// colNum: 2,
// },
{
name: 'description',
label: t('Description'),
@ -339,13 +310,6 @@ export class CreateForm extends FormAction {
colNum: 2,
maxLength: 30,
},
// {
// name: 'work num',
// label: t('Work Num'),
// type: 'input',
// labelCol,
// colNum: 2,
// },
{
type: 'divider',
},
@ -360,12 +324,11 @@ export class CreateForm extends FormAction {
type: 'transfer',
leftTableColumns: this.leftProjectTable,
rightTableColumns: this.rightProjectTable,
dataSource: domain
? this.projects.filter((it) => it.domain_id === domain)
: [],
disabled: false,
dataSource: this.projects,
showSearch: true,
hidden: !more || !domain,
hidden: !more,
onChange: this.onChangeProject,
filterOption: transferFilterOption,
},
{
name: 'select_user_group',
@ -373,12 +336,10 @@ export class CreateForm extends FormAction {
type: 'transfer',
leftTableColumns: this.leftUserGroupTable,
rightTableColumns: this.rightUserGroupTable,
dataSource: domain
? this.userGroupList.filter((it) => it.domain_id === domain)
: [],
disabled: false,
dataSource: this.userGroupList,
showSearch: true,
hidden: !more || !domain,
hidden: !more,
filterOption: transferFilterOption,
},
];
}
@ -386,11 +347,11 @@ export class CreateForm extends FormAction {
onSubmit = async (values) => {
const { domain } = this.state;
values.defaultRole = this.projectRoleList[0].id;
values.newProjectRoles = this.state.newProjectRoles;
values.projectRoles = this.state.projectRoles;
values.domain_id = domain;
const { confirmPassword, more, ...rest } = values;
return this.store.create(rest);
};
}
export default inject('rootStore')(observer(CreateForm));
export default inject('rootStore')(observer(Create));

View File

@ -15,101 +15,44 @@
import { inject, observer } from 'mobx-react';
import { ModalAction } from 'containers/Action';
import globalUserStore from 'stores/keystone/user';
import globalDomainStore from 'stores/keystone/domain';
import { phoneNumberValidate, emailValidate } from 'utils/validate';
import parsePhoneNumberFromString from 'libphonenumber-js';
export class EditForm extends ModalAction {
init() {
this.store = globalUserStore;
this.domainStore = globalDomainStore;
this.getDomains();
this.getProject();
}
componentDidMount() {
const { item } = this.props;
const { id } = this.item;
this.store.fetchDetail({ ...item, id });
}
getDomains() {
this.domainStore.fetchDomain();
}
getProject() {
this.store.fetchProject();
}
static id = 'user-edit';
static title = t('Edit');
// static path(item) {
// return `/identity/user-admin/edit/${item.id}`;
// }
static policy = 'identity:update_user';
static allowed() {
return Promise.resolve(true);
}
get domainList() {
return (this.userGroupStore.list.data || []).map((it) => ({
label: it.name,
value: it.id,
}));
}
get projectList() {
const { projects } = this.store;
return (projects || []).map((it) => ({
label: it.name,
value: it.id,
}));
}
get data() {
return this.store.detail || [];
}
get name() {
const { name } = this.data;
return `${t('edit')} ${name}`;
get actionName() {
return t('Edit User');
}
get defaultValue() {
const {
const { name, email, phone, real_name, description, domain, domain_id } =
this.item;
const formattedPhone = parsePhoneNumberFromString(phone || '', 'CN') || {
countryCallingCode: '86',
nationalNumber: '',
};
const { countryCallingCode, nationalNumber } = formattedPhone;
return {
name,
domainName: (domain || {}).name || domain_id,
email,
phone,
phone: `+${countryCallingCode} ${nationalNumber}`,
real_name,
description,
domain_id,
default_project_id,
} = this.data;
const { domains } = this.domainStore;
const { projects } = this.store;
const domain = domains.filter((it) => it.id === domain_id)[0];
const project = projects.filter((it) => it.id === default_project_id)[0];
if (name && this.formRef.current) {
const formattedPhone = parsePhoneNumberFromString(phone || '', 'CN') || {
countryCallingCode: '86',
nationalNumber: '',
};
const { countryCallingCode, nationalNumber } = formattedPhone;
this.formRef.current.setFieldsValue({
name,
domain_id: domain ? domain.name : '',
default_project_id: project ? project.name : '',
email,
phone: `+${countryCallingCode} ${nationalNumber}`,
real_name,
description,
});
}
return {};
};
}
checkName = (rule, value) => {
@ -119,9 +62,9 @@ export class EditForm extends ModalAction {
const {
list: { data },
} = this.store;
const { name } = this.item;
const nameUsed = data.filter((it) => it.name === value);
if (nameUsed[0] && nameUsed[0].name !== name) {
const { id } = this.item;
const nameUsed = data.find((it) => it.name === value && it.id !== id);
if (nameUsed) {
return Promise.reject(t('Invalid: User name can not be duplicated'));
}
return Promise.resolve();
@ -159,10 +102,9 @@ export class EditForm extends ModalAction {
required: true,
},
{
name: 'domain_id',
name: 'domainName',
label: t('Affiliated Domain'),
type: 'input',
required: true,
disabled: true,
},
{

View File

@ -29,6 +29,10 @@ export default class ForbiddenAction extends ConfirmAction {
return t('Forbidden');
}
get isDanger() {
return true;
}
get actionName() {
return t('Forbidden User');
}

View File

@ -11,17 +11,22 @@
// 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 { inject, observer } from 'mobx-react';
import { Select } from 'antd';
import globalProjectStore from 'stores/keystone/project';
import globalUserStore from 'stores/keystone/user';
import { RoleStore } from 'stores/keystone/role';
import { ModalAction } from 'containers/Action';
import globalDomainStore from 'stores/keystone/domain';
import { UserStore } from 'stores/keystone/user';
import globalRoleStore from 'stores/keystone/role';
export class SystemRole extends ModalAction {
init() {
this.store = new UserStore();
this.roleStore = globalRoleStore;
this.getRoleList();
}
getRoleList() {
this.roleStore.fetchSystemRoles();
}
static id = 'edit-system-permission';
static title = t('Edit System Permission');
@ -30,318 +35,70 @@ export class SystemRole extends ModalAction {
return t('edit system permission');
}
async init() {
const systemRole = JSON.stringify(this.item.projectMapSystemRole);
this.state.domainDefault = this.item.domain_id;
this.state.projectRoles = JSON.parse(systemRole);
this.store = new RoleStore();
this.domainStore = globalDomainStore;
this.userStore = globalUserStore;
await this.getRoleList();
this.getDomains();
this.getUser();
}
getRoleList() {
return this.store.fetchList();
}
getDomains() {
this.domainStore.fetchDomain();
}
getUser() {
this.userStore.fetchProject();
}
static get modalSize() {
return 'large';
}
getModalSize() {
return 'large';
}
// get defaultSystemRoles() {
// const { projects } = this.item;
// const defaultProjects = Object.keys(projects);
// const systemRole = (this.store.list.data || []).filter(it =>
// ((it.name.indexOf('system_') !== -1) && (it.name.indexOf('_system_') === -1)) ||
// it.name === 'admin'
// );
// const systemRoleId = systemRole.map(it => it.id);
// const defaultSystemRoles = {};
// defaultProjects.forEach((project_id) => {
// const roles = projects[project_id].filter(role_id => systemRoleId.indexOf(role_id) !== -1);
// if (roles[0]) {
// defaultSystemRoles[project_id] = roles;
// }
// });
// return defaultSystemRoles;
// }
get domainList() {
const {
rootStore: { baseDomains },
} = this.props;
const { domains } = this.domainStore;
const domainList = (domains || []).filter(
(it) =>
baseDomains.indexOf(it.name) === -1 || it.id === this.item.domain_id
);
return domainList.map((it) => ({
get rolesList() {
return (this.roleStore.systemRoles.data || []).map((it) => ({
label: it.name,
value: it.id,
key: it.id,
}));
}
get item() {
const { item } = this.props;
item.roles = {};
return item;
}
get multipleMode() {
return 'multiple';
}
get projectList() {
return (this.userStore.projects || []).map((it) => ({
...it,
key: it.id,
}));
}
get systemRoleList() {
const systemRole = this.store.list.data || [];
return systemRole;
}
get adminRoleId() {
const adminRole = (this.store.list.data || []).filter(
(it) => it.name === 'admin'
);
return adminRole[0].id;
}
adminRoleList = (project_id) => {
const adminRole = (this.store.list.data || []).filter(
(it) => it.name === 'admin'
);
return adminRole.map((it) => ({
label: it.name,
value: it.id,
key: it.id,
project_id,
}));
};
projectRolesList = (project_id) =>
this.systemRoleList.map((it) => ({
label: it.name,
value: it.id,
key: it.id,
project_id,
}));
defaultRoles = (projectId) => {
const { roles, projects } = this.item;
const { projectRoles } = this.state;
const filterRoles = this.multipleMode ? projects : projectRoles;
if (!filterRoles[projectId]) {
roles[projectId] = [this.systemRoleList[0].id];
} else {
const usersSystemRole = filterRoles[projectId].filter((it) => {
const systemRole = this.systemRoleList.filter((role) => role.id === it);
if (systemRole[0]) {
return true;
}
return false;
});
return this.multipleMode ? usersSystemRole : usersSystemRole.slice(0, 1);
}
return roles[projectId];
};
static policy = 'identity:update_project';
static allowed = () => Promise.resolve(true);
get leftUserTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
];
}
get rightUserTable() {
return [
{
dataIndex: 'name',
title: t('Name'),
},
{
title: t('Select System Role'),
dataIndex: 'id',
render: (id) => this.renderSelect(id),
},
];
}
renderSelect = (id) => {
let disable = false;
if (this.item.projects && this.item.projects[id]) {
// eslint-disable-next-line prefer-destructuring
disable = this.item.projects[id].filter(
(it) => it === this.adminRoleId
)[0];
}
// for test e2e, will delete by next patch
localStorage.setItem('test-project-role', this.projectRolesList(id));
localStorage.setItem('test-total-role', this.systemRoleList);
localStorage.setItem('test-actual', 'can get localstorage');
return (
<Select
size="small"
mode={this.multipleMode}
options={disable ? this.adminRoleList(id) : this.projectRolesList(id)}
defaultValue={disable ? this.adminRoleId : this.defaultRoles(id)}
onChange={this.onSubChange}
disabled={disable}
/>
);
};
onSubChange = (value, option) => {
if (
(this.multipleMode && value.length && option.length) ||
(!this.multipleMode && value && option)
) {
const { projectRoles } = this.state;
const { project_id } = this.multipleMode ? option[0] : option;
projectRoles[project_id] = this.multipleMode ? value : [value];
this.setState({ projectRoles });
} else {
this.setState({ projectRoles: {} });
}
};
get checkedList() {
const { domains } = this.domainStore;
return (domains || []).map((it) => ({
label: it.name,
value: it.id,
key: it.id,
}));
}
get defaultValue() {
const { domain_id: domain } = this.item;
const data = {
domain_id: domain || 'default',
};
return data;
const { name, systemRoles = [] } = this.item;
const roles = systemRoles.map((it) => it.id);
const role = roles[0];
if (role) {
return { name, role };
}
return { name };
}
static policy = 'identity:list_roles';
static allowed(item, containerProps) {
const { match: { path = '' } = {} } = containerProps || {};
if (path.indexOf('domain-admin/detail') >= 0) {
return Promise.resolve(false);
}
return Promise.resolve(true);
}
get formItems() {
const { projects } = this.item;
const { domainDefault } = this.state;
return [
{
name: 'domain_id',
label: t('Affiliated Domain'),
type: 'select',
checkOptions: this.checkedList,
checkBoxInfo: t('Show All Domain'),
options: this.domainList,
onChange: (e) => {
this.setState({
domainDefault: e,
});
},
required: true,
name: 'name',
label: t('User'),
type: 'label',
iconType: 'user',
},
{
name: 'select_project',
type: 'transfer',
label: t('Project'),
leftTableColumns: this.leftUserTable,
rightTableColumns: this.rightUserTable,
dataSource: this.projectList
? this.projectList.filter((it) => it.domain_id === domainDefault)
: [],
disabled: false,
showSearch: true,
oriTargetKeys: projects ? Object.keys(projects) : [],
name: 'role',
label: t('Role'),
type: 'select',
options: this.rolesList,
loading: this.roleStore.systemRoles.isLoading,
},
];
}
onSubmit = async (values) => {
const { projectRoles } = this.state;
if (!this.multipleMode) {
// If it is not multiple choices, role only takes the first item of the array
Object.keys(projectRoles).forEach((key) => {
projectRoles[key] = projectRoles[key].slice(0, 1);
});
}
const { id: user_id, projects } = this.item;
const oldProjectRoles = projects;
const defaultProjects = Object.keys(oldProjectRoles);
const { role: newRole } = values;
const { systemRoles: oldRoles, id } = this.item;
const promiseList = [];
defaultProjects.forEach((id) => {
if (values.select_project && !values.select_project.includes(id)) {
(oldProjectRoles[id] || []).forEach((role_id) => {
promiseList.push(
globalProjectStore.removeUserRole({ id, user_id, role_id })
);
});
} else {
(oldProjectRoles[id] || []).forEach((role_id) => {
if (projectRoles[id] && !projectRoles[id].includes(role_id)) {
promiseList.push(
globalProjectStore.removeUserRole({ id, user_id, role_id })
);
}
});
const newRoles = newRole ? [newRole] : [];
const oldRoleIds = oldRoles.map((it) => it.id);
oldRoles.forEach((role) => {
const { id: roleId } = role;
if (!newRoles.includes(roleId)) {
promiseList.push(this.store.deleteSystemRole({ id, roleId }));
}
});
(values.select_project || []).forEach((id) => {
if (defaultProjects && !defaultProjects.includes(id)) {
if (projectRoles[id]) {
projectRoles[id].forEach((role_id) => {
promiseList.push(
globalProjectStore.assignUserRole({ id, user_id, role_id })
);
});
} else {
promiseList.push(
globalProjectStore.assignUserRole({
id,
user_id,
role_id: this.systemRoleList[0].id,
})
);
}
} else {
(projectRoles[id] || []).forEach((role_id) => {
if (
(oldProjectRoles[id] && !oldProjectRoles[id].includes(role_id)) ||
!oldProjectRoles[id]
) {
promiseList.push(
globalProjectStore.assignUserRole({ id, user_id, role_id })
);
}
});
newRoles.forEach((roleId) => {
if (!oldRoleIds.includes(roleId)) {
promiseList.push(this.store.assignSystemRole({ id, roleId }));
}
});
const results = await Promise.all(promiseList);
return results;
};
}
export default inject('rootStore')(observer(SystemRole));

View File

@ -17,23 +17,15 @@ import Delete from './Delete';
import Edit from './Edit';
import Enable from './Enable';
import Forbidden from './Forbidden';
import SystemPermission from './SystemRole';
import SystemRole from './SystemRole';
import Password from './Password';
const actionConfigs = {
rowActions: {
moreActions: [],
},
batchActions: [],
primaryActions: [],
};
const adminConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: SystemPermission,
action: SystemRole,
},
// {
// action: DomainPermission,
@ -59,4 +51,4 @@ const adminConfigs = {
primaryActions: [Create],
};
export default { actionConfigs, adminConfigs };
export default actionConfigs;

View File

@ -1,256 +0,0 @@
// Copyright 2021 99cloud
//
// 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 { inject, observer } from 'mobx-react';
import globalGroupStore from 'stores/keystone/user-group';
import globalUserStore from 'stores/keystone/user';
import { ModalAction } from 'containers/Action';
import globalProjectStore from 'stores/keystone/project';
import globalRoleStore from 'stores/keystone/role';
import { getPasswordOtherRule, phoneNumberValidate } from 'utils/validate';
import globalDomainStore from 'stores/keystone/domain';
import { statusTypes } from 'resources/keystone/domain';
export class CreateForm extends ModalAction {
init() {
this.store = globalUserStore;
this.userGroupStore = globalGroupStore;
this.projectStore = globalProjectStore;
this.roleStore = globalRoleStore;
this.domainStore = globalDomainStore;
this.getAllUser();
this.getUserGroup();
this.getProject();
this.getRole();
// this.getDomains();
}
get name() {
return t('Create User');
}
async getAllUser() {
this.allUser = await this.store.pureFetchList();
}
getUserGroup() {
this.userGroupStore.fetchList();
}
getProject() {
this.store.fetchProject();
}
getRole() {
this.roleStore.fetchList();
}
static id = 'user-create';
static title = t('Create User');
static policy = [
'identity:create_user',
'identity:update_user',
'identity:list_roles',
'identity:list_projects',
'identity:list_domains',
];
checkName = (rule, value) => {
if (!value) {
return Promise.reject(t('Please input'));
}
const nameUsed = this.allUser.find((i) => i.name === value);
if (nameUsed) {
return Promise.reject(t('Invalid: User name can not be duplicated'));
}
return Promise.resolve();
};
static allowed(item, containerProps) {
const {
match: { path },
} = containerProps;
if (path.indexOf('domain-admin/detail') >= 0) {
return Promise.resolve(false);
}
return Promise.resolve(true);
}
get defaultValue() {
const { name } = this.item;
const data = {
domain_name: name,
password: '',
confirmPassword: '',
enabled: statusTypes[0].value,
};
return data;
}
get nameForStateUpdate() {
return ['password', 'confirmPassword'];
}
get domainList() {
const { domains } = this.store;
return (domains || []).map((it) => ({
label: it.name,
value: it.id,
}));
}
get projectList() {
const { projects } = this.store;
return (projects || []).map((it) => ({
label: it.name,
value: it.id,
}));
}
get userGroupList() {
return (this.userGroupStore.list.data || []).map((it) => ({
...it,
key: it.id,
}));
}
get projects() {
return (this.projectStore.list.data || []).map((it) => ({
...it,
key: it.id,
}));
}
get roleList() {
return (this.roleStore.list.data || []).map((it) => ({
label: it.name,
value: it.id,
}));
}
static allowed = () => Promise.resolve(true);
get formItems() {
// const { more } = this.state;
// const labelCol = {
// xs: { span: 5 },
// sm: { span: 6 },
// };
return [
{
name: 'name',
label: t('User Name'),
type: 'input',
validator: this.checkName,
placeholder: t('Please input user name'),
required: true,
},
{
name: 'email',
label: t('Email'),
type: 'input',
required: true,
},
{
name: 'password',
label: t('Password'),
type: 'input-password',
required: true,
otherRule: getPasswordOtherRule('password'),
},
{
name: 'confirmPassword',
label: t('Confirm Password'),
type: 'input-password',
dependencies: ['password'],
required: true,
otherRule: getPasswordOtherRule('confirmPassword'),
},
{
name: 'phone',
label: t('Phone'),
validator: phoneNumberValidate,
type: 'phone',
required: true,
},
{
name: 'domain_name',
label: t('Affiliated Domain'),
type: 'input',
disabled: true,
help: t('The affiliated Domain cannot be modified after creation'),
},
{
name: 'enabled',
label: t('Status'),
type: 'radio',
optionType: 'default',
options: statusTypes,
required: true,
},
// {
// name: 'default_project_id',
// label: t('Main Project'),
// type: 'select',
// options: this.projectList,
// },
{
name: 'description',
label: t('Description'),
type: 'textarea',
},
{
name: 'real_name',
label: t('Real Name'),
type: 'input',
required: true,
},
];
}
get instanceName() {
const { name } = this.values || {};
return name;
}
onSubmit = (values) => {
const {
name,
email,
password,
phone,
enabled,
default_project_id,
description,
real_name,
} = values;
const { id: domain_id } = this.item;
const data = {
name,
email,
password,
phone,
domain_id,
enabled,
description,
real_name,
default_project_id,
};
return this.store.create(data);
};
}
export default inject('rootStore')(observer(CreateForm));

View File

@ -1,52 +0,0 @@
// Copyright 2021 99cloud
//
// 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 Create from './Create';
import Delete from '../actions/Delete';
import Edit from '../actions/Edit';
import Enable from '../actions/Enable';
import Forbidden from '../actions/Forbidden';
import Password from '../actions/Password';
const actionConfigs = {
rowActions: {
moreActions: [],
},
batchActions: [],
primaryActions: [],
};
const adminConfigs = {
rowActions: {
firstAction: Edit,
moreActions: [
{
action: Password,
},
{
action: Delete,
},
{
action: Enable,
},
{
action: Forbidden,
},
],
},
batchActions: [Delete],
primaryActions: [Create],
};
export default { actionConfigs, adminConfigs };

View File

@ -14,28 +14,15 @@
import React from 'react';
import { observer, inject } from 'mobx-react';
import { Badge, Table, Popover } from 'antd';
import Base from 'containers/List';
import globalUserStore, { UserStore } from 'stores/keystone/user';
import { yesNoOptions, emptyActionConfig } from 'utils/constants';
import { Link } from 'react-router-dom';
import { FileTextOutlined } from '@ant-design/icons';
import { enabledColumn } from 'resources/keystone/domain';
import actionConfigs from './actions';
import actionConfigsInDomain from './actionsInDomain';
export class User extends Base {
init() {
this.store = this.inDetailPage ? new UserStore() : globalUserStore;
this.getDomains();
}
getDomains() {
this.store.fetchDomain();
}
get tabs() {
return [];
}
get policy() {
@ -74,126 +61,79 @@ export class User extends Base {
return this.inDetailPage && path.includes('identity/role-admin');
}
getColumns() {
// const {
// match: { path },
// } = this.props;
const columns = [
getBaseColumns() {
return [
{
title: t('User ID/Name'),
dataIndex: 'name',
routeName: 'userDetailAdmin',
},
{
title: t('Project Scope'),
dataIndex: 'projects',
isHideable: true,
render: (value) => {
if (value && value.length) {
return value.map((it) => {
const { id, name } = it;
const url = `/identity/project-admin/detail/${id}`;
return <Link to={url}>{name}</Link>;
});
}
},
stringify: (value) => value.map((it) => it.name).join('; '),
},
{
title: t('Roles'),
dataIndex: 'project_roles',
isHideable: true,
render: (project_roles) => {
if (project_roles && project_roles[0]) {
return project_roles.map((it, idx) => <div key={idx}>{it}</div>);
}
},
},
{
title: t('Real Name'),
dataIndex: 'real_name',
isHideable: true,
},
{
title: t('Roles'),
dataIndex: 'projectRoles',
isHideable: true,
render: (_, record) => {
const { projects } = record;
return Object.values(projects).map((it) => {
const { roles = [] } = it || {};
return roles.map((role) => {
return <div key={role.id}>{role.name}</div>;
});
});
},
stringify: (_, record) => {
const { projects } = record;
return Object.values(projects).map((it) => {
const { roles = [] } = it || {};
return roles
.map((role) => {
return role.name;
})
.join(', ');
});
},
},
{
title: t('Project Scope'),
dataIndex: 'projects',
isHideable: true,
render: (value) => {
return Object.values(value).map((it) => {
const {
project: { id, name },
} = it;
const link = this.getLinkRender('projectDetail', name, {
id,
});
return <div key={id}>{link}</div>;
});
},
stringify: (value) => {
return Object.values(value)
.map((it) => {
const { project } = it;
return project.name;
})
.join('; ');
},
},
{
title: t('Affiliated Domain'),
dataIndex: 'domain_name',
dataIndex: 'domainName',
isHideable: true,
},
{
title: t('Project Num'),
dataIndex: 'project_num',
render: (project_num) => {
if (project_num === 0) {
return <Badge color="red" text={project_num} />;
}
return <Badge color="green" text={project_num} />;
},
},
{
title: t('Project Num'),
dataIndex: 'projectItems',
render: (_, record) => {
const { project_num } = record;
if (project_num === 0) {
return <Badge color="red" text={project_num} />;
}
const { projectItems = [] } = record;
const projectColumns = [
{
title: t('Project'),
dataIndex: 'name',
key: 'id',
render: (value, data) => {
const url = `/identity/project-admin/detail/${data.id}`;
return <Link to={url}>{value}</Link>;
},
},
{
title: t('Role'),
dataIndex: 'roles',
key: 'roles',
render: (value) => {
if (!value) {
return '-';
}
return value.map((it) => it.name).join(', ');
},
},
];
const table = (
<Table
columns={projectColumns}
dataSource={projectItems}
pagination={false}
rowKey="id"
size="small"
/>
);
return (
<>
<Badge color="green" text={project_num} />
<Popover
getPopupContainer={(node) => node.parentNode}
placement="right"
content={table}
destroyTooltipOnHide
>
<FileTextOutlined />
</Popover>
</>
);
},
stringify: (value, record) => {
const { project_num, projectItems = [] } = record;
const projectRoleStr = projectItems
.map((it) => {
const { name, roles } = it;
const roleStr = roles.map((role) => role.name).join(', ');
return `${name}: ${roleStr}`;
})
.join('\n');
return `${project_num}\n${projectRoleStr}`;
title: t('System Roles'),
dataIndex: 'systemRoles',
render: (value) => {
return (value || []).map((it) => <div key={it.id}>{it.name}</div>);
},
stringify: (value) => (value || []).map((it) => it.name).join('; '),
},
{
title: t('Email'),
@ -207,57 +147,34 @@ export class User extends Base {
},
enabledColumn,
];
if (!this.inDetailPage) {
}
getColumns() {
const columns = this.getBaseColumns();
if (!this.inDetailPage || this.inUserGroupDetail) {
return columns.filter(
(it) =>
!['project_roles', 'projects', 'project_num'].includes(it.dataIndex)
);
}
if (this.inUserGroupDetail) {
return columns.filter(
(it) =>
!['project_roles', 'projects', 'projectItems'].includes(it.dataIndex)
(it) => !['projectRoles', 'projects'].includes(it.dataIndex)
);
}
if (this.inDomainDetail) {
return columns.filter(
(it) =>
![
'project_roles',
'projects',
'domain_name',
'projectItems',
].includes(it.dataIndex)
(it) => !['domain', 'projects', 'projectRoles'].includes(it.dataIndex)
);
}
if (this.inRoleDetail) {
return columns.filter(
(it) =>
!['project_roles', 'project_num', 'projectItems'].includes(
it.dataIndex
)
);
return columns.filter((it) => !['projectRoles'].includes(it.dataIndex));
}
if (this.inProjectDetail) {
return columns.filter(
(it) => !['projects', 'projectItems'].includes(it.dataIndex)
);
return columns.filter((it) => !['projects'].includes(it.dataIndex));
}
return columns;
}
get actionConfigs() {
if (this.inDomainDetail) {
return this.isAdminPage
? actionConfigsInDomain.adminConfigs
: actionConfigsInDomain.actionConfigs;
if (this.inDetailPage) {
return emptyActionConfig;
}
if (!this.inDetailPage) {
return this.isAdminPage
? actionConfigs.adminConfigs
: actionConfigs.actionConfigs;
}
return emptyActionConfig;
return actionConfigs;
}
get searchFilters() {
@ -278,31 +195,32 @@ export class User extends Base {
];
}
async getData({ silent, ...params } = {}) {
updateFetchParams = (params) => {
const { match } = this.props;
const { id } = match.params || {};
const newParams = { ...params };
silent && (this.list.silent = true);
if (this.inDomainDetail) {
const { id } = match.params;
newParams.domainId = id;
await this.store.fetchListInDomainDetail(newParams);
} else if (this.inProjectDetail) {
const { id } = match.params;
newParams.projectId = id;
await this.store.fetchListInProjectDetail(newParams);
} else if (this.inUserGroupDetail) {
const { id } = match.params;
if (this.inUserGroupDetail) {
newParams.groupId = id;
await this.store.fetchListInGroupDetail(newParams);
newParams.withProjectRole = false;
newParams.withSystemRole = true;
} else if (this.inDomainDetail) {
newParams.domain_id = id;
newParams.withProjectRole = false;
newParams.withSystemRole = true;
} else if (this.inProjectDetail) {
newParams.projectId = id;
newParams.withProjectRole = true;
newParams.withSystemRole = true;
} else if (this.inRoleDetail) {
const { id } = match.params;
newParams.roleId = id;
await this.store.fetchListInRoleDetail(newParams);
} else {
await this.store.fetchList(newParams);
newParams.withProjectRole = true;
newParams.withSystemRole = true;
} else if (!this.inDetailPage) {
newParams.withProjectRole = false;
newParams.withSystemRole = true;
}
this.list.silent = false;
}
return newParams;
};
}
export default inject('rootStore')(observer(User));

View File

@ -47,7 +47,7 @@ export class ManageUser extends ModalAction {
}
getAllUser() {
this.userStore.fetchAllWithDomain();
this.userStore.fetchList({ withProjectRole: false, withSystemRole: false });
}
async getGroupUsers() {

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { action, observable } from 'mobx';
import { get } from 'lodash';
import List from 'stores/base-list';
import client from 'client';
import globalRootStore from 'stores/root';
@ -22,27 +21,9 @@ import globalGroupStore from 'stores/keystone/user-group';
import Base from 'stores/base';
export class UserStore extends Base {
@observable
domains = [];
@observable
roleAssignments = [];
@observable
userProjects = new List();
@observable
userGroups = new List();
@observable
projects = [];
@observable
systemRoles = [];
@observable
domainRoles = [];
get client() {
return client.keystone.users;
}
@ -75,13 +56,37 @@ export class UserStore extends Base {
return client.keystone.groups;
}
listFetchByClient(params, originParams) {
const { groupId } = originParams;
if (groupId) {
return this.groupClient.users.list(groupId, params);
}
return this.client.list(params);
}
get paramsFunc() {
return (params) => {
const {
id,
projectId,
groupId,
roleId,
withProjectRole,
withSystemRole,
all_projects,
...rest
} = params;
return rest;
};
}
@action
async create(values) {
const body = {};
const {
select_project = [],
select_project,
select_user_group = [],
newProjectRoles,
projectRoles,
defaultRole,
...other
} = values;
@ -90,54 +95,31 @@ export class UserStore extends Base {
this.isSubmitting = true;
const result = await this.client.create(body);
const {
user: { id: user_id },
user: { id: userId },
} = result;
const promiseList = [];
if (select_user_group[0] || select_project[0]) {
const newProjects = Object.keys(newProjectRoles);
select_user_group.forEach((id) => {
promiseList.push(
globalGroupStore.addGroupUsers({ id, userId: user_id })
);
select_user_group.forEach((groupId) => {
promiseList.push(this.addGroupUsers(groupId, userId));
});
Object.keys(projectRoles).forEach((projectId) => {
const roles = projectRoles[projectId];
roles.forEach((roleId) => {
promiseList.push(this.addProjectUser(projectId, userId, roleId));
});
select_project.forEach((id) => {
if (!newProjects.includes(id)) {
const roleId = defaultRole;
promiseList.push(
globalProjectStore.assignUserRole({ id, userId: user_id, roleId })
);
} else {
promiseList.push(
globalProjectStore.assignUserRole({
id,
userId: user_id,
roleId: newProjectRoles[id],
})
);
}
});
await Promise.all(promiseList);
}
});
await Promise.all(promiseList);
this.isSubmitting = false;
return result;
}
@action
async fetchDomain() {
const domainsResult = await this.domainClient.list();
this.domains = domainsResult.domains;
}
addGroupUsers = (groupId, userId) => {
return globalGroupStore.addGroupUsers({ id: groupId, userId });
};
get mapper() {
return (item) => {
const domain = this.domains.find((it) => it.id === item.domain_id);
if (domain) {
item.domain_name = domain.name;
item.domainName = domain.name;
}
return item;
};
}
addProjectUser = (projectId, userId, roleId) => {
return globalProjectStore.assignUserRole({ id: projectId, userId, roleId });
};
@action
async getUserProjects() {
@ -157,167 +139,167 @@ export class UserStore extends Base {
return projects;
}
@action
async getUserGroups() {
this.userGroups.update({
isLoading: true,
});
const {
user: {
user: { id },
},
} = globalRootStore;
const { groups } = await this.client.groups.list(id);
this.userGroups.update({
data: groups,
isLoading: false,
});
}
@action
getUserProjectRole = (user, roleAssignment, projectMapRole) => {
if (roleAssignment.user) {
getProjectMapRoles = (user, projectRoleAssignments, roles, projects) => {
const projectMapRoles = {};
const { id } = user;
projectRoleAssignments.forEach((roleAssignment) => {
const {
user: { id: user_id },
role: { id: role_id },
scope: { project: { id } = {} } = {},
scope: { project: { id: projectId } = {} } = {},
role: { id: roleId } = {},
user: { id: userId } = {},
} = roleAssignment;
if (id && user_id === user.id) {
if (projectMapRole[id]) {
projectMapRole[id].push(role_id);
if (userId === id && roleId && projectId) {
const roleItem = roles.find((it) => it.id === roleId);
if (!projectMapRoles[projectId]) {
const projectItem = projects.find((it) => it.id === projectId);
projectMapRoles[projectId] = {
project: projectItem,
roles: [roleItem],
};
} else {
projectMapRole[id] = [role_id];
projectMapRoles[projectId].roles = [
...projectMapRoles[projectId].roles,
roleItem,
];
}
}
}
});
return projectMapRoles;
};
getUserProjectWithRole = (projectMapRole, roles, projects) => {
return Object.keys(projectMapRole).map((key) => {
const item = projects.find((it) => it.id === key);
const roleItems = projectMapRole[key].map((roleId) =>
roles.find((it) => it.id === roleId)
);
item.roles = roleItems;
return item;
});
};
@action
async fetchList({
limit,
page,
sortKey,
sortOrder,
conditions,
timeFilter,
...filters
} = {}) {
this.list.isLoading = true;
// todo: no page, no limit, fetch all
const [roleAssignmentsResult, usersResult, roleResult, projectResult] =
await Promise.all([
this.roleAssignmentClient.list(),
this.client.list(),
this.roleClient.list(),
this.projectClient.list(),
]);
const { users } = usersResult;
const { roles } = roleResult;
const systemRoles = roles.filter(
(it) =>
(it.name.includes('system_') && !it.name.includes('_system_')) ||
it.name === 'admin'
);
const systemRoleId = systemRoles.map((it) => it.id);
users.map((user) => {
const projectMapRole = {}; // { project_id: [roles_id] }
const projectMapSystemRole = {}; // { project_id: [systemRoles_id] }
roleAssignmentsResult.role_assignments.forEach((roleAssignment) => {
this.getUserProjectRole(user, roleAssignment, projectMapRole);
});
this.getUsersSystemRole(
projectMapRole,
systemRoleId,
projectMapSystemRole
);
user.projectItems = this.getUserProjectWithRole(
projectMapRole,
roles,
projectResult.projects
);
user.projects = projectMapRole;
user.projectMapSystemRole = projectMapSystemRole;
user.project_num = Object.keys(projectMapRole).length;
return users;
});
const newData = users.map(this.mapper);
this.list.update({
data: newData,
total: newData.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
timeFilter,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return newData;
}
@action
async fetchDetail({ id, silent }) {
if (!silent) {
this.isLoading = true;
}
const [roleAssignmentsResult, usersResult, roleResult] = await Promise.all([
this.roleAssignmentClient.list(),
this.client.show(id),
this.roleClient.list(),
]);
const { roles } = roleResult;
const systemRoles = roles.filter(
(it) =>
(it.name.includes('system_') && !it.name.includes('_system_')) ||
it.name === 'admin'
);
const systemRoleId = systemRoles.map((it) => it.id);
const { user } = usersResult;
const projectMapRole = {};
const projectMapSystemRole = {};
roleAssignmentsResult.role_assignments.forEach((roleAssignment) => {
this.getUserProjectRole(user, roleAssignment, projectMapRole);
});
this.getUsersSystemRole(projectMapRole, systemRoleId, projectMapSystemRole);
user.projects = projectMapRole;
user.projectMapSystemRole = projectMapSystemRole;
user.project_num = Object.keys(projectMapRole).length;
this.detail = user;
this.isLoading = false;
return user;
}
@action
async pureFetchDetail({ id, silent }) {
return super.fetchDetail({ id, silent });
}
@action
getUsersSystemRole = (projectMapRole, systemRoleId, projectMapSystemRole) => {
const systemProject = Object.keys(projectMapRole);
systemProject.forEach((project_id) => {
const roles = projectMapRole[project_id].filter((role_id) =>
systemRoleId.includes(role_id)
);
if (roles[0]) {
projectMapSystemRole[project_id] = roles;
// eslint-disable-next-line no-unused-vars
getSystemRoles = (user, systemRoleAssignments, roles, projects) => {
const systemRoles = [];
const { id } = user || {};
systemRoleAssignments.forEach((roleAssignment) => {
const { role: { id: roleId } = {}, user: { id: userId } = {} } =
roleAssignment;
if (userId === id && roleId) {
const roleItem = roles.find((it) => it.id === roleId);
systemRoles.push(roleItem);
}
});
return systemRoles;
};
updateUser = (
user,
projectRoleAssignments,
systemAssigns,
roles,
projects,
domains
) => {
const projectMapRoles = this.getProjectMapRoles(
user,
projectRoleAssignments,
roles,
projects
);
const systemRoles = this.getSystemRoles(
user,
systemAssigns,
roles,
projects
);
const domain = domains.find((it) => it.id === user.domain_id);
return {
...user,
projects: projectMapRoles,
projectCount: Object.keys(projectMapRoles).length,
domain,
domainName: (domain || {}).name || user.domain_id,
systemRoles,
};
};
async listDidFetch(items, allProjects, filters) {
if (!items.length) {
return items;
}
const {
withProjectRole = true,
withSystemRole = true,
projectId,
roleId,
domain_id,
} = filters;
const withRole = withProjectRole || withSystemRole;
const params = {};
if (roleId) {
params['role.id'] = roleId;
}
if (projectId) {
params['scope.project.id'] = projectId;
}
const reqs = [
withProjectRole ? this.roleAssignmentClient.list(params) : null,
withSystemRole
? this.roleAssignmentClient.list({ 'scope.system': 'all' })
: null,
withRole ? this.roleClient.list() : null,
withProjectRole ? this.projectClient.list() : null,
domain_id ? null : this.domainClient.list(),
];
const [
projectRoleAssignmentsResult,
systemRoleAssignmentsResult,
roleResult,
projectResult,
domainResult,
] = await Promise.all(reqs);
const { roles = [] } = roleResult || {};
const { domains = [] } = domainResult || {};
const { role_assignments: assigns = [] } =
projectRoleAssignmentsResult || {};
const { role_assignments: systemAssigns = [] } =
systemRoleAssignmentsResult || {};
const { projects = [] } = projectResult || {};
const newItems = items.map((user) => {
return this.updateUser(
user,
assigns,
systemAssigns,
roles,
projects,
domains
);
});
if (projectId) {
return newItems.filter((it) => !!it.projectCount);
}
if (roleId) {
return newItems.filter((it) => {
const { projectCount, systemRoles } = it;
if (projectCount) {
return true;
}
const systemRole = systemRoles.find((role) => role.id === roleId);
return !!systemRole;
});
}
return newItems;
}
async detailDidFetch(item) {
const { id } = item;
const params = { 'user.id': id, 'scope.system': 'all' };
const reqs = [
this.roleAssignmentClient.list(params),
this.roleClient.list(),
this.domainClient.list(),
];
const [systemRoleAssignmentsResult, roleResult, domainResult] =
await Promise.all(reqs);
const { roles = [] } = roleResult || {};
const { domains = [] } = domainResult;
const { role_assignments: systemAssigns = [] } =
systemRoleAssignmentsResult || {};
return this.updateUser(item, [], systemAssigns, roles, [], domains);
}
@action
async enable({ id }) {
const reqBody = {
@ -351,50 +333,13 @@ export class UserStore extends Base {
}
@action
async fetchProject() {
const projectsResult = await this.projectClient.list();
this.projects = projectsResult.projects;
async assignSystemRole({ id, roleId }) {
return this.systemUserClient.roles.update(id, roleId);
}
@action
async fetchSystemRole({ id, projects }) {
this.systemRoles = [];
const project_id = projects[0].id;
const projectResult = await this.projectClient.users.roles.list(
project_id,
id
);
const systemRole = projectResult.roles.filter(
(it) => it.name.includes('system_') && !it.name.includes('_system_')
);
this.systemRoles = systemRole;
}
@action
async assignSystemRole({ id, role_id }) {
return this.systemUserClient.roles.update(id, role_id);
}
@action
async deleteSystemRole({ id, role_id }) {
return this.systemUserClient.delete(id, role_id);
}
@action
async fetchDomainRole({ id, domain_id }) {
this.domainRoles = [];
const rolesResult = await this.domainClient.users.roles.list(id, domain_id);
this.domainRoles = rolesResult.roles;
}
@action
async assignDomainRole({ id, role_id, domain_id }) {
return this.domainClient.users.roles.update(domain_id, id, role_id);
}
@action
async deleteDomainRole({ id, role_id, domain_id }) {
return this.domainClient.users.roles.delete(domain_id, id, role_id);
async deleteSystemRole({ id, roleId }) {
return this.systemUserClient.roles.delete(id, roleId);
}
@action
@ -410,240 +355,6 @@ export class UserStore extends Base {
};
return this.submitting(this.client.patch(id, reqBody));
}
@action
async fetchListInDomainDetail({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
const { domainId } = filters;
const params = {};
const result = await this.client.list(params);
let data = get(result, this.listResponseKey, []);
data = data.filter((it) => it.domain_id === domainId);
const items = data.map(this.mapper);
const newData = await this.listDidFetch(items);
Promise.all(newData.map((it) => this.client.projects.list(it.id))).then(
(projectResult) => {
newData.map((it, index) => {
const { projects } = projectResult[index];
it.projects = projects;
it.project_num = projects.length;
return it;
});
this.list.update({
data: newData,
total: items.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return items;
}
);
}
@action
async fetchListInProjectDetail({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
const { projectId } = filters;
const params = {};
const [roleAssignmentsResult, roleResult, result] = await Promise.all([
this.roleAssignmentClient.list(),
this.roleClient.list(),
this.client.list(params),
]);
const projectUserIds = [];
const userMapRole = {};
roleAssignmentsResult.role_assignments.forEach((roleAssignment) => {
if (roleAssignment.user) {
const {
user: { id: user_id },
role: { id: role_id },
scope: { project: { id } = {} } = {},
} = roleAssignment;
if (id && id === projectId) {
projectUserIds.push(user_id);
if (userMapRole[user_id]) {
userMapRole[user_id].push(role_id);
} else {
userMapRole[user_id] = [role_id];
}
}
}
});
let data = get(result, this.listResponseKey, []);
data = data.filter((it) => projectUserIds.includes(it.id));
const items = data.map(this.mapper);
const newData = await this.listDidFetch(items);
Promise.all(newData.map((it) => this.client.projects.list(it.id))).then(
(projectResult) => {
newData.map((it, index) => {
const { projects } = projectResult[index];
it.projects = projects;
it.project_num = projects.length;
it.project_roles = userMapRole[it.id].map(
(r) => roleResult.roles.filter((role) => role.id === r)[0].name
);
return it;
});
this.list.update({
data: newData,
total: items.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return items;
}
);
}
@action
async fetchListInGroupDetail({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
const { groupId } = filters;
const params = {};
const result = await this.groupClient.users.list(groupId, params);
const data = get(result, this.listResponseKey, []);
const items = data.map(this.mapper);
const newData = await this.listDidFetch(items);
Promise.all(newData.map((it) => this.client.projects.list(it.id))).then(
(projectResult) => {
newData.map((it, index) => {
const { projects } = projectResult[index];
it.projects = projects;
it.project_num = projects.length;
return it;
});
this.list.update({
data: newData,
total: items.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return items;
}
);
}
@action
async fetchListInRoleDetail({
limit,
page,
sortKey,
sortOrder,
conditions,
...filters
} = {}) {
this.list.isLoading = true;
const { roleId } = filters;
const params = {};
const [roleAssignmentsResult, projectResult, result] = await Promise.all([
this.roleAssignmentClient.list(),
this.projectClient.list(),
this.client.list(params),
]);
const projectRoleUsers = {};
const systemRoleUsers = {};
roleAssignmentsResult.role_assignments.forEach((roleAssignment) => {
if (roleAssignment.user) {
const {
user: { id: user_id },
role: { id: role_id },
scope: { project, system } = {},
} = roleAssignment;
if (role_id === roleId && project) {
const projectData = projectResult.projects.find(
(it) => it.id === project.id
);
if (projectRoleUsers[user_id]) {
projectRoleUsers[user_id].push(projectData);
} else {
projectRoleUsers[user_id] = [projectData];
}
} else if (role_id === roleId && system) {
systemRoleUsers[user_id] = system.all;
}
}
});
const data = get(result, this.listResponseKey, []);
const items = data
.filter((it) => projectRoleUsers[it.id] || systemRoleUsers[it.id])
.map((it) => ({
projects: projectRoleUsers[it.id] || [],
systemScope: systemRoleUsers[it.id] || [],
...it,
}));
const newData = items.map(this.mapper);
// const newData = await this.listDidFetch(items);
this.list.update({
data: newData,
total: newData.length || 0,
limit: Number(limit) || 10,
page: Number(page) || 1,
sortKey,
sortOrder,
filters,
isLoading: false,
...(this.list.silent ? {} : { selectedRowKeys: [] }),
});
return newData;
}
@action
async fetchAllWithDomain() {
this.list.isLoading = true;
await this.fetchDomain();
const result = await this.client.list();
const data = get(result, this.listResponseKey, []);
const items = data.map(this.mapper);
const newData = await this.listDidFetch(items);
this.list.update({
data: newData,
total: items.length || 0,
isLoading: false,
});
return items;
}
}
const globalUserStore = new UserStore();

View File

@ -20,9 +20,6 @@ import { getQueryString } from 'utils/index';
import { setLocalStorageItem } from 'utils/local-storage';
import { isEmpty, values } from 'lodash';
const checkItemPolicy = require('resources/skyline/policy').default;
const { onlyAdminCanReadPolicy } = require('resources/skyline/policy');
export class RootStore {
@observable
user = null;
@ -115,23 +112,35 @@ export class RootStore {
return this.getUserProfileAndPolicy();
}
checkAdminRole(roles) {
if (checkItemPolicy({ policy: onlyAdminCanReadPolicy })) {
async getUserSystemRoles(user) {
// only user admin or system roles has admin/reader can go to administrator
const { id, name } = user;
if (name === 'admin') {
return true;
}
const regex = /^[\w-_]*(system_admin|system_reader)$/;
return roles.some((role) => regex.test(role.name));
try {
const result = await client.keystone.systemUsers.roles.list(id);
const { roles = [] } = result;
return roles.some((it) => it.name === 'admin' || it.name === 'reader');
} catch (e) {
console.log(e);
return false;
}
}
@action
updateUserRoles(user) {
const { roles = [], base_roles = [], base_domains } = user || {};
async updateUserRoles(user) {
const {
roles = [],
base_roles = [],
base_domains,
user: userInfo = {},
} = user || {};
this.roles = roles;
this.baseRoles = base_roles;
this.baseDomains = base_domains;
// TODO: fix system/project admin/member/reader for W
this.hasAdminRole = checkItemPolicy({ policy: onlyAdminCanReadPolicy });
this.hasAdminPageRole = this.checkAdminRole(roles);
this.hasAdminPageRole = await this.getUserSystemRoles(userInfo);
this.hasAdminRole = this.hasAdminPageRole;
}
@action
@ -148,15 +157,9 @@ export class RootStore {
this.projectName = projectName;
this.license = license || {};
this.version = version;
this.endpoints = endpoints;
this.updateUserRoles(user);
this.setKeystoneToken(user);
this.endpoints = endpoints;
}
@action
async getUserPolices() {
const result = await this.client.policies.list();
this.policies = result.policies;
}
checkLicense(key) {
@ -183,7 +186,7 @@ export class RootStore {
this.client.profile(),
this.client.policies.list(),
]);
this.updateUser(profile, policies.policies || []);
await this.updateUser(profile, policies.policies || []);
return this.getNeutronExtensions();
}

View File

@ -57,16 +57,13 @@ describe('The Project Page', () => {
.clickModalActionSubmitButton();
});
// it('successfully manage user', () => {
// cy.tableSearchText(name)
// .clickActionInMore('Manage User')
// .wait(10000)
// .formTransfer('select_user', username)
// .formTransferRight('select_user', username)
// .wait(10000)
// .formSelect('select_user', 'admin')
// .clickModalActionSubmitButton();
// });
it('successfully manage user', () => {
cy.tableSearchText(name)
.clickActionInMore('Manage User')
.wait(10000)
.formTransfer('select_user', username)
.clickModalActionSubmitButton();
});
it('successfully manage user group', () => {
cy.tableSearchText(name)

View File

@ -54,8 +54,6 @@ describe('The User Page', () => {
.formButtonClick('more')
.wait(2000)
.formTransfer('select_project', projectName)
.formTransferRight('select_project', projectName)
.formSelect('select_project', 'admin')
.wait(2000)
.formTransfer('select_user_group', userGroupName)
.clickFormActionSubmitButton()
@ -75,20 +73,13 @@ describe('The User Page', () => {
cy.goBackToList(listUrl);
});
// it('successfully edit system permission', () => {
// cy.tableSearchText(name)
// .clickActionInMore('Edit System Permission')
// .wait(10000)
// .formTransfer('select_project', projectName2)
// .formTransferRight('select_project', projectName2)
// .wait(10000)
// .log('test-project-role', localStorage.getItem('test-project-role'))
// .log('test-total-role', localStorage.getItem('test-total-role'))
// .log('test-actual', localStorage.getItem('test-actual'))
// .wait(2000)
// .formSelect('select_project', 'admin')
// .clickModalActionSubmitButton();
// });
it('successfully edit system permission', () => {
cy.tableSearchText(name)
.clickActionInMore('Edit System Permission')
.wait(10000)
.formSelect('role', 'admin')
.clickModalActionSubmitButton();
});
it('successfully forbidden user', () => {
cy.tableSearchText(name).clickConfirmActionInMore('Forbidden');