Skip to content

Commit

Permalink
feat(mis): 新增租户允许使用已有用户作为管理员且用户可以更换租户功能 (#1165)
Browse files Browse the repository at this point in the history
### 背景
某些用户的认证系统不允许创建用户,同时在初次导入时已将已存在的用户都导入到default账户下,会有以下问题
- 无法修改该用户所属租户
- 新建租户时由于无法创建用户,且认证系统内所有已存在用户以及导入至default,导致无法新建租户。
### 改动
1. 新建租户时,允许用户将已有用户指定为该新建租户的租户管理员,并将该用户的所属租户改为此新建用户
2. 对于已有用户,允许修改其所属租户

ps: 以上两点都是针对scow已有用户并且已经没有账户关联关系。

平台管理的用户列表允许修改用户的所属租户,需要该用户没有任何账户关联。


![image](https://github.com/PKUHPC/SCOW/assets/130351655/08020e0e-629d-4ec3-9989-4ba1c4b6c502)



![1710383278675](https://github.com/PKUHPC/SCOW/assets/130351655/4110fb14-3063-4f61-bf8b-c5bb5d8a0e5d)


新建租户增加 指定已有用户作为租户管理员


![image](https://github.com/PKUHPC/SCOW/assets/130351655/62001ac4-cb2a-4764-8cff-5b113809629d)
  • Loading branch information
ZihanChen821 committed Mar 18, 2024
1 parent 84a6327 commit a097dd1
Show file tree
Hide file tree
Showing 20 changed files with 703 additions and 117 deletions.
8 changes: 8 additions & 0 deletions .changeset/curly-foxes-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@scow/mis-server": patch
"@scow/portal-web": patch
"@scow/mis-web": patch
"@scow/ai": patch
---

新增无账户关系的用户修改所属租户且可以作为新增租户的管理员功能
5 changes: 5 additions & 0 deletions .changeset/poor-cougars-film.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@scow/grpc-api": minor
---

server 增加 ChangeTenant 接口修改用户的所属租户,增加 CreateTenantWithExistingUserAsAdmin 接口允许创建租户时指定已有用户作为租户管理员
1 change: 1 addition & 0 deletions apps/ai/src/models/operationLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ export const OperationType: OperationTypeEnum = {
exportOperationLog: "exportOperationLog",
setAccountBlockThreshold: "setAccountBlockThreshold",
setAccountDefaultBlockThreshold: "setAccountDefaultBlockThreshold",
userChangeTenant: "userChangeTenant",
customEvent: "customEvent",
};

44 changes: 44 additions & 0 deletions apps/mis-server/src/services/tenant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { authUrl } from "src/config";
import { Account } from "src/entities/Account";
import { Tenant } from "src/entities/Tenant";
import { TenantRole, User } from "src/entities/User";
import { UserAccount } from "src/entities/UserAccount";
import { callHook } from "src/plugins/hookClient";
import { createUserInDatabase, insertKeyToNewUser } from "src/utils/createUser";

Expand Down Expand Up @@ -180,5 +181,48 @@ export const tenantServiceServer = plugin((server) => {

},

createTenantWithExistingUserAsAdmin: async ({ request, em }) => {

const { tenantName, userId, userName } = request;

const tenant = await em.findOne(Tenant, { name: tenantName });
if (tenant) {
throw <ServiceError>{
code: Status.ALREADY_EXISTS, message: "The tenant already exists", details: "TENANT_ALREADY_EXISTS",
};
}

const newTenant = new Tenant({ name: tenantName });


const user = await em.findOne(User, { userId, name: userName });

if (!user) {
throw <ServiceError>{
code: Status.NOT_FOUND, message: `User with userId ${userId} and name ${userName} is not found.`,
};
}

const userAccount = await em.findOne(UserAccount, { user: user });

if (userAccount) {
throw <ServiceError>{
code: Status.FAILED_PRECONDITION, message: `User ${userId} still maintains account relationship.`,
};
}

// 修改该用户的租户, 并且作为租户管理员
em.assign(user, { tenant: newTenant });
user.tenantRoles = [TenantRole.TENANT_ADMIN];

await em.persistAndFlush([user, newTenant]);

return [{
tenantName: newTenant.name,
adminUserId: user.userId,
}];

},

});
});
45 changes: 45 additions & 0 deletions apps/mis-server/src/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { blockUserInAccount, unblockUserInAccount } from "src/bl/block";
import { authUrl } from "src/config";
import { clusters } from "src/config/clusters";
import { Account } from "src/entities/Account";
import { Tenant } from "src/entities/Tenant";
import { PlatformRole, TenantRole, User } from "src/entities/User";
import { UserAccount, UserRole, UserStatus } from "src/entities/UserAccount";
import { callHook } from "src/plugins/hookClient";
Expand Down Expand Up @@ -801,5 +802,49 @@ export const userServiceServer = plugin((server) => {
},
];
},

changeTenant: async ({ request, em }) => {
const { userId, tenantName } = request;

const user = await em.findOne (User, { userId }, { populate: ["tenant"]});

if (!user) {
throw <ServiceError>{
code: Status.NOT_FOUND, message: `User ${userId} is not found.`, details: "USER_NOT_FOUND",
};
}

const userAccount = await em.findOne(UserAccount, { user: user });

if (userAccount) {
throw <ServiceError>{
code: Status.FAILED_PRECONDITION, message: `User ${userId} still maintains account relationship.`,
};
}

const oldTenant = user.tenant.getEntity();

if (oldTenant.name === tenantName) {
throw <ServiceError>{
code: Status.ALREADY_EXISTS, message: `User ${userId} is already in tenant ${tenantName}.`,
};
}

const newTenant = await em.findOne(Tenant, { name: tenantName });

if (!newTenant) {
throw <ServiceError>{
code: Status.NOT_FOUND, message: `Tenant ${tenantName} is not found.`, details: "TENANT_NOT_FOUND",
};
}

em.assign(user, { tenant: newTenant });

await em.persistAndFlush(user);

return [{}];

},

});
});
2 changes: 2 additions & 0 deletions apps/mis-web/src/apis/api.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ export const mockApi: MockApi<typeof api> = {
createdInAuth: false,
}),
createTenant: async () => ({ createdInAuth: true }),
createTenantWithExistingUserAsAdmin: async () => null,
validateToken: async () => MOCK_USER_INFO,

getOperationLogs: async () => ({ results: [{
Expand Down Expand Up @@ -477,6 +478,7 @@ export const mockApi: MockApi<typeof api> = {
endsAt: 1702889670000,
}]}),
getAlarmLogsCount: async () => ({ totalCount: 1 }),
changeTenant: async () => null,
};

export const MOCK_USER_INFO = {
Expand Down
4 changes: 4 additions & 0 deletions apps/mis-web/src/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ import type { BlockAccountSchema } from "src/pages/api/tenant/blockAccount";
import type { ChangePasswordAsTenantAdminSchema } from "src/pages/api/tenant/changePassword";
import type { CreateTenantSchema } from "src/pages/api/tenant/create";
import type { CreateAccountSchema } from "src/pages/api/tenant/createAccount";
import type { CreateTenantWithExistingUserAsAdminSchema } from "src/pages/api/tenant/createTenantWithExistingUserAsAdmin";
import type { GetAccountsSchema } from "src/pages/api/tenant/getAccounts";
import type { GetTenantsSchema } from "src/pages/api/tenant/getTenants";
import type { SetBlockThresholdSchema } from "src/pages/api/tenant/setBlockThreshold";
import type { SetDefaultAccountBlockThresholdSchema } from "src/pages/api/tenant/setDefaultAccountBlockThreshold";
import type { UnblockAccountSchema } from "src/pages/api/tenant/unblockAccount";
import type { AddUserToAccountSchema } from "src/pages/api/users/addToAccount";
import type { BlockUserInAccountSchema } from "src/pages/api/users/blockInAccount";
import type { ChangeTenantSchema } from "src/pages/api/users/changeTenant";
import type { CreateUserSchema } from "src/pages/api/users/create";
import type { GetAccountUsersSchema } from "src/pages/api/users/index";
import type { CancelJobChargeLimitSchema } from "src/pages/api/users/jobChargeLimit/cancel";
Expand Down Expand Up @@ -182,6 +184,7 @@ export const api = {
blockAccount: apiClient.fromTypeboxRoute<typeof BlockAccountSchema>("PUT", "/api/tenant/blockAccount"),
changePasswordAsTenantAdmin: apiClient.fromTypeboxRoute<typeof ChangePasswordAsTenantAdminSchema>("PATCH", "/api/tenant/changePassword"),
createTenant: apiClient.fromTypeboxRoute<typeof CreateTenantSchema>("POST", "/api/tenant/create"),
createTenantWithExistingUserAsAdmin: apiClient.fromTypeboxRoute<typeof CreateTenantWithExistingUserAsAdminSchema>("POST", "/api/tenant/createTenantWithExistingUserAsAdmin"),
createAccount: apiClient.fromTypeboxRoute<typeof CreateAccountSchema>("POST", "/api/tenant/createAccount"),
getAccounts: apiClient.fromTypeboxRoute<typeof GetAccountsSchema>("GET", "/api/tenant/getAccounts"),
getTenants: apiClient.fromTypeboxRoute<typeof GetTenantsSchema>("GET", "/api/tenant/getTenants"),
Expand All @@ -199,4 +202,5 @@ export const api = {
queryStorageUsage: apiClient.fromTypeboxRoute<typeof QueryStorageUsageSchema>("GET", "/api/users/storageUsage"),
unblockUserInAccount: apiClient.fromTypeboxRoute<typeof UnblockUserInAccountSchema>("PUT", "/api/users/unblockInAccount"),
unsetAdmin: apiClient.fromTypeboxRoute<typeof UnsetAdminSchema>("PUT", "/api/users/unsetAdmin"),
changeTenant: apiClient.fromTypeboxRoute<typeof ChangeTenantSchema>("PUT", "/api/users/changeTenant"),
};
23 changes: 23 additions & 0 deletions apps/mis-web/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,18 @@ export default {
success: "Modification successful",
fail: "Modification failed",
changePassword: "Change Password",
changeTenant: "Change Tenant",
},
createTenantForm: {
prompt: "Please enter the tenant name and create a new user as the administrator for this tenant.",
adminInfo: "Administrator Information",
userEmail: "User Email",
userPassword: "User Password",
confirmPassword: "Confirm Password",
userType: "User Type",
newUser: "New User",
existingUser: "Existing User",
createTenantWarningInfo: "Please ensure that the user no longer has any associated accounts",
},
ImportUsersTable: {
selectAccount: "Please select an account!",
Expand Down Expand Up @@ -327,6 +332,19 @@ export default {
accountNotFound: "Account not found.",
chargeFinish: "Charging completed!",
},
changeTenantModal: {
modifyTenant: "Modify Tenant",
newTenant: "New Tenant",
originalTenant: "Original Tenant",
userName: "User Name",
userId: "User ID",
newTenantNameRequired: "Please enter a new tenant",
userNotFound: "User Not Exist",
tenantNotFound: "Tenant Not Exist",
userStillMaintainsAccountRelationship: "User still maintains account relationship",
userAlreadyExistInThisTenant: "User already exists in this tenant",
createTenantWarningInfo: "Please ensure that the user no longer has any associated accounts",
},
},
commonComponent: {
exportFileModal: {
Expand Down Expand Up @@ -930,6 +948,9 @@ export default {
createTenantFailMessage: "Failed to create tenant",
createTenant: "Create Tenant",
unavailable:"This feature is not available in the current configuration",
userNotFound: "User Not Exist",
tenantExist: "Tenant Already Exists",
userStillMaintainsAccountRelationship: "User still maintains account relationship",
},
},
systemDebug: {
Expand Down Expand Up @@ -1098,6 +1119,7 @@ export default {
exportOperationLog: "Export Operation Log",
setAccountBlockThreshold: "Set Account Block Threshold",
setAccountDefaultBlockThreshold: "Set Default Account Block Threshold",
userChangeTenant: "User Change Tenant",
customEvent: "Custom Operation Event",
},
operationDetails: {
Expand Down Expand Up @@ -1172,6 +1194,7 @@ export default {
setAccountBlockThreshold: "Set the block threshold of account {} to {}",
setAccountDefaultBlockThreshold: "Set the default block threshold of accounts in Tenant {} to {}",
unsetAccountBlockThreshold: "Reset the block threshold of account {} to default",
userChangeTenant: "User {} changes from tenant {} to tenant {}",
},
},
userRoles: {
Expand Down
23 changes: 23 additions & 0 deletions apps/mis-web/src/i18n/zh_cn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,13 +293,18 @@ export default {
success:"修改成功",
fail:"修改失败",
changePassword:"修改密码",
changeTenant: "修改租户",
},
createTenantForm:{
prompt:"请输入租户名并为其创建一个新用户作为该租户的管理员",
adminInfo:"管理员信息",
userEmail:"用户邮箱",
userPassword:"用户密码",
confirmPassword:"确认密码",
userType: "用户类型",
newUser: "新用户",
existingUser: "已有用户",
createTenantWarningInfo: "请确保该用户已经没有任何关联账户",
},
ImportUsersTable:{
selectAccount:"请选择账户!",
Expand Down Expand Up @@ -327,6 +332,19 @@ export default {
accountNotFound:"账户未找到",
chargeFinish:"充值完成!",
},
changeTenantModal: {
modifyTenant: "修改租户",
newTenant: "新租户",
originalTenant: "原租户",
userName: "用户姓名",
userId: "用户 ID",
newTenantNameRequired: "请输入新租户",
userNotFound: "用户不存在",
tenantNotFound: "租户不存在",
userStillMaintainsAccountRelationship: "该用户仍然含有账户关系",
userAlreadyExistInThisTenant: "用户已经存在于该租户中",
createTenantWarningInfo: "请确保该用户已经没有任何关联账户",
},
},
commonComponent:{
exportFileModal: {
Expand Down Expand Up @@ -930,6 +948,9 @@ export default {
createTenantFailMessage: "创建租户失败",
createTenant: "创建租户",
unavailable:"本功能在当前配置下不可用",
userNotFound: "此用户不存在",
tenantExist: "租户已存在",
userStillMaintainsAccountRelationship: "该用户仍然含有账户关系",
},
},
systemDebug: {
Expand Down Expand Up @@ -1099,6 +1120,7 @@ export default {
exportOperationLog: "导出操作日志",
setAccountBlockThreshold: "设置账户封锁阈值",
setAccountDefaultBlockThreshold: "设置账户默认封锁阈值",
userChangeTenant: "用户切换租户",
customEvent: "自定义操作行为",
},
operationDetails: {
Expand Down Expand Up @@ -1173,6 +1195,7 @@ export default {
setAccountBlockThreshold: "设置账户{}的封锁阈值为{}",
setAccountDefaultBlockThreshold: "设置租户{}的默认账户封锁阈值为{}",
unsetAccountBlockThreshold: "账户{}恢复使用默认封锁阈值",
userChangeTenant: "用户{}切换租户,从租户{}切换到租户{}",
},
},
userRoles: {
Expand Down
8 changes: 8 additions & 0 deletions apps/mis-web/src/models/operationLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const OperationType: OperationTypeEnum = {
exportOperationLog: "exportOperationLog",
setAccountBlockThreshold: "setAccountBlockThreshold",
setAccountDefaultBlockThreshold: "setAccountDefaultBlockThreshold",
userChangeTenant: "userChangeTenant",
customEvent: "customEvent",
};

Expand Down Expand Up @@ -182,6 +183,7 @@ export const getOperationTypeTexts = (t: OperationTextsTransType): { [key in Lib
exportOperationLog: t(pTypes("exportOperationLog")),
setAccountBlockThreshold: t(pTypes("setAccountBlockThreshold")),
setAccountDefaultBlockThreshold: t(pTypes("setAccountDefaultBlockThreshold")),
userChangeTenant: t(pTypes("userChangeTenant")),
customEvent: t(pTypes("customEvent")),
};

Expand Down Expand Up @@ -245,6 +247,7 @@ export const OperationCodeMap: { [key in LibOperationType]: string } = {
exportChargeRecord: "040305",
exportPayRecord: "040306",
exportOperationLog: "040307",
userChangeTenant: "040308",
customEvent: "050001",
};

Expand Down Expand Up @@ -436,6 +439,11 @@ export const getOperationDetail = (
return t(pDetails("setAccountDefaultBlockThreshold"),
[operationEvent[logEvent].tenantName,
nullableMoneyToString(operationEvent[logEvent].thresholdAmount)]);
case "userChangeTenant":
return t(pDetails("userChangeTenant"),
[operationEvent[logEvent].userId,
operationEvent[logEvent].previousTenantName,
operationEvent[logEvent].newTenantName]);
case "customEvent":
const c = operationEvent[logEvent]?.content;
return getI18nCurrentText(c, languageId);
Expand Down
16 changes: 13 additions & 3 deletions apps/mis-web/src/pageComponents/admin/AllUsersTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import { MAX_EXPORT_COUNT, urlToExport } from "src/pageComponents/file/apis";
import { GetAllUsersSchema } from "src/pages/api/admin/getAllUsers";
import { User } from "src/stores/UserStore";

import { ChangeTenantModalLink } from "./ChangeTenantModal";

interface FilterForm {
idOrName: string | undefined;
}
Expand Down Expand Up @@ -259,7 +261,7 @@ const UserInfoTable: React.FC<UserInfoTableProps> = ({
onChange: (page, pageSize) => setPageInfo({ page, pageSize }),
} : false}
onChange={handleTableChange}
scroll={{ x: data?.platformUsers?.length ? 1200 : true }}
scroll={{ x: true }}
>
<Table.Column<PlatformUserInfo>
dataIndex="userId"
Expand Down Expand Up @@ -301,8 +303,8 @@ const UserInfoTable: React.FC<UserInfoTableProps> = ({
/>

<Table.Column<PlatformUserInfo>
dataIndex="changePassword"
width="7.5%"
dataIndex="operation"
width="10%"
fixed="right"
title={t(pCommon("operation"))}
render={(_, r) => (
Expand All @@ -323,6 +325,14 @@ const UserInfoTable: React.FC<UserInfoTableProps> = ({
>
{t(p("changePassword"))}
</ChangePasswordModalLink>
<ChangeTenantModalLink
tenantName={r.tenantName}
name={r.name}
userId={r.userId}
reload={reload}
>
{t(p("changeTenant"))}
</ChangeTenantModalLink>
</Space>
)}
/>
Expand Down

0 comments on commit a097dd1

Please sign in to comment.