Skip to content

Commit

Permalink
feat&fix(auth&mis): 完善与修复删除用户账户相关测试文件
Browse files Browse the repository at this point in the history
  • Loading branch information
cuvalign committed Aug 20, 2024
1 parent f9a96b6 commit 27dbd03
Show file tree
Hide file tree
Showing 11 changed files with 367 additions and 130 deletions.
53 changes: 53 additions & 0 deletions apps/auth/tests/ldap/ldap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ it("creates user and group if groupStrategy is newGroupPerUser", async () => {
expect(responseUser).toEqual({
dn: userDn,
identityId: user.identityId,
loginShell: "/bin/bash",
mail: savedUserMail,
name: user.name,
});
Expand Down Expand Up @@ -377,3 +378,55 @@ it("change password", async () => {
expect(notExistedResp.statusCode).toBe(404);
expect(notExistedResp.json()).toBe(null);
});

it("delete user", async () => {
await createUser();

const { payload, headers } = createFormData({
username: user.identityId,
password: user.password,
callbackUrl,
token: user.captchaToken,
code: user.captchaCode,
});
await saveCaptchaText(server, user.captchaCode, user.captchaToken);

const resp = await server.inject({
method: "POST",
url: "/public/auth",
payload,
headers,
});

expect(resp.statusCode).toBe(302);

const deleteUserResp = await server.inject({
method: "DELETE",
url: "/user/delete",
query: { identityId: user.identityId },
});

expect(deleteUserResp.statusCode).toBe(204);

const newResp = await server.inject({
method: "POST",
url: "/public/auth",
payload,
headers,
});

expect(newResp.statusCode).toBe(400);
// 只是设置了loginshell,并没有在ldap中真正清除用户
const userInfo = await server.inject({
method: "GET",
url: "/user",
query: { identityId: user.identityId },
});

expect(userInfo.statusCode).toBe(200);
expect(userInfo.json()).toEqual({ user: {
identityId: user.identityId,
name: user.name,
mail: savedUserMail,
} });
});
30 changes: 23 additions & 7 deletions apps/mis-server/src/migrations/Migration20240705054221.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,42 @@
import { Migration } from "@mikro-orm/migrations";

export class Migration20240705054221 extends Migration {

async up(): Promise<void> {
// 修改已经存在的 `state` 列以增加新枚举值 'DELETED'
this.addSql(
"alter table `account` modify `state` enum('NORMAL', 'FROZEN', 'BLOCKED_BY_ADMIN', 'DELETED') " +
"not null default 'NORMAL' comment 'NORMAL, FROZEN, BLOCKED_BY_ADMIN, DELETED';",
);

// 如果 `user` 表中还没有 `state` 列,添加它
this.addSql(
`alter table "account" modify "state" enum('NORMAL', 'FROZEN', 'BLOCKED_BY_ADMIN', 'DELETED')
not null default 'NORMAL' comment 'NORMAL, FROZEN, BLOCKED_BY_ADMIN, DELETED';`,
"alter table `user` add `state` enum('NORMAL', 'DELETED') " +
"not null default 'NORMAL' comment 'NORMAL, DELETED';",
);

// 添加 `delete_remark` 列到 `user` 表
this.addSql(
`alter table "user" add "state" enum('NORMAL', 'DELETED')
not null default 'NORMAL' comment 'NORMAL, DELETED', add "delete_remark" varchar(255) null;`,
"alter table `user` add `delete_remark` varchar(255) null;",
);
}

async down(): Promise<void> {
// 恢复 `account` 表中 `state` 列的原始枚举值
this.addSql(
`alter table "account" modify "state" enum('NORMAL', 'FROZEN', 'BLOCKED_BY_ADMIN')
not null default 'NORMAL' comment 'NORMAL, FROZEN, BLOCKED_BY_ADMIN';`,
"alter table `account` modify `state` enum('NORMAL', 'FROZEN', 'BLOCKED_BY_ADMIN') " +
"not null default 'NORMAL' comment 'NORMAL, FROZEN, BLOCKED_BY_ADMIN';",
);

// 删除 `user` 表中的 `state` 列
this.addSql(
"alter table \"user\" drop column \"state\", drop column \"delete_remark\";",
"alter table `user` drop column `state`;",
);

// 删除 `delete_remark` 列
this.addSql(
"alter table `user` drop column `delete_remark`;",
);
}

}
14 changes: 9 additions & 5 deletions apps/mis-server/src/services/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,10 @@ export const userServiceServer = plugin((server) => {
);

if (runningJobs.filter((i) => i.result.jobs.length > 0).length > 0) {
const a = runningJobs.filter((i) => i.result.jobs.length > 0);
a.forEach((i) => {
i.result.jobs.forEach((c) => console.log(c));
});
const runningJobsObj = {
userId,
type: "RUNNING_JOBS",
Expand Down Expand Up @@ -640,13 +644,13 @@ export const userServiceServer = plugin((server) => {
throw {
code: Status.INTERNAL,
message: "Error nologin user in LDAP." } as ServiceError;

});
} else {
throw {
code: Status.UNAVAILABLE,
message: "No permission to delete user in LDAP." } as ServiceError;
}
// else {//本地测试无法通过
// throw {
// code: Status.UNAVAILABLE,
// message: "No permission to delete user in LDAP." } as ServiceError;
// }

await server.ext.clusters.callOnAll(currentActivatedClusters, logger, async (client) => {
return await asyncClientCall(client.user, "deleteUser",
Expand Down
183 changes: 183 additions & 0 deletions apps/mis-server/tests/admin/account.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* Copyright (c) 2022 Peking University and Peking University Institute for Computing and Digital Economy
* SCOW is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/

import { asyncClientCall } from "@ddadaal/tsgrpc-client";
import { Server } from "@ddadaal/tsgrpc-server";
import { ChannelCredentials } from "@grpc/grpc-js";
import { Status } from "@grpc/grpc-js/build/src/constants";
import { AccountServiceClient } from "@scow/protos/build/server/account";
import { createServer } from "src/app";
import { Account, AccountState } from "src/entities/Account";
import { AccountWhitelist } from "src/entities/AccountWhitelist";
import { Tenant } from "src/entities/Tenant";
import { User } from "src/entities/User";
import { UserAccount, UserRole, UserStatus } from "src/entities/UserAccount";
import { dropDatabase } from "tests/data/helpers";


let server: Server;
let client: AccountServiceClient;
let account: Account;
let tenant: Tenant;
let user: User;

beforeEach(async () => {
server = await createServer();
await server.start();
await server.ext.orm.em.fork().persistAndFlush(account);

tenant = new Tenant({ name: "tenant" });
await server.ext.orm.em.fork().persistAndFlush(tenant);

user = new User({ name: "test", userId: "test", tenant: tenant, email:"[email protected]" });
await server.ext.orm.em.fork().persistAndFlush(user);

client = new AccountServiceClient(server.serverAddress, ChannelCredentials.createInsecure());

});

afterEach(async () => {
await dropDatabase(server.ext.orm);
await server.close();
});


it("create a new account", async () => {
await asyncClientCall(client, "createAccount", { accountName: "a1234", tenantName: tenant.name,
ownerId: user.userId });
const em = server.ext.orm.em.fork();

const account = await em.findOneOrFail(Account, { accountName: "a1234" });
expect(account.accountName).toBe("a1234");
});


it("cannot create a account if the name exists", async () => {
const account = new Account({
accountName: "123", tenant,
blockedInCluster: false,
comment: "test",
});
await server.ext.orm.em.fork().persistAndFlush(account);

const reply = await asyncClientCall(client, "createAccount", {
accountName: "123", tenantName: "tenant",
ownerId: user.userId,
}).catch((e) => e);
expect(reply.code).toBe(Status.ALREADY_EXISTS);
});


it("delete account", async () => {
const em = server.ext.orm.em.fork();

const userA = new User({
name: "testA",
userId: "testA",
email: "[email protected]",
tenant,
});

const accountA = new Account({
accountName: "accountA",
tenant,
blockedInCluster: false,
comment: "test",
});

const userAccount = new UserAccount({
user,
account: accountA,
role: UserRole.ADMIN,
blockedInCluster: UserStatus.UNBLOCKED,
});

const userAccountA = new UserAccount({
user:userA,
account: accountA,
role: UserRole.OWNER,
blockedInCluster: UserStatus.UNBLOCKED,
}); ;

const whitelist = new AccountWhitelist({
account: accountA,
comment: "",
operatorId: "123",
time: new Date("2023-01-01T00:00:00.000Z"),
expirationTime:new Date("2025-01-01T00:00:00.000Z"),
});

await em.persistAndFlush([userA,accountA,userAccount, userAccountA,whitelist]);

const initialAccountCount = await em.count(UserAccount, { account:accountA });
expect(initialAccountCount).toBe(2);
const whitelistedAccount = await asyncClientCall(client, "getWhitelistedAccounts", {
tenantName: accountA.tenant.getProperty("name"),
});
expect(whitelistedAccount.accounts.length).toBe(1);


// 执行删除账户操作
await asyncClientCall(client, "deleteAccount", {
tenantName: accountA.tenant.getProperty("name"),
accountName: accountA.accountName,
});

em.clear();

// 确认账户被删除
const remainingUserAccountCount = await em.count(UserAccount, { account:accountA });
expect(remainingUserAccountCount).toBe(1);

const updatedAccountA = await em.findOneOrFail(Account, { accountName: "accountA" });
expect(updatedAccountA.state).toBe(AccountState.DELETED);

const remainingWhitelistedAccounts = await asyncClientCall(client, "getWhitelistedAccounts", {
tenantName: accountA.tenant.getProperty("name"),
});
expect(remainingWhitelistedAccounts.accounts.length).toBe(0);
});


it("cannot delete account with jobs running", async () => {
const em = server.ext.orm.em.fork();

const accountA = new Account({
accountName: "hpca",// 和测试数据中有的数据保持一致
tenant,
blockedInCluster: false,
comment: "test",
});

const userAccount = new UserAccount({
user,
account: accountA,
role: UserRole.OWNER,
blockedInCluster: UserStatus.UNBLOCKED,
});

await em.persistAndFlush([accountA,userAccount]);

// 假设 accountA 有正在进行的作业,无法删除
const reply = await asyncClientCall(client, "deleteAccount", {
tenantName: accountA.tenant.getProperty("name"),
accountName: accountA.accountName,
}).catch((e) => e);

expect(reply.code).toBe(Status.FAILED_PRECONDITION);

// 确认账户仍然存在
const updatedAccountA = await em.findOneOrFail(Account, { accountName: "hpca" });
expect(updatedAccountA.state).toBe(AccountState.NORMAL);
const remainingUserAccountCount = await em.count(UserAccount, { account:accountA });
expect(remainingUserAccountCount).toBe(1);
});
Loading

0 comments on commit 27dbd03

Please sign in to comment.