完成五人团队正式内用最小收口(07-18-small-team-internal-readiness)
这个提交包含在:
@@ -30,7 +30,7 @@ async function buildApp({ routeOptions = {}, employee } = {}) {
|
||||
{
|
||||
id: "actor-1",
|
||||
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
||||
permissionCodes: ["system.users"],
|
||||
permissionCodes: ["system.accounts.manage"],
|
||||
};
|
||||
|
||||
app.decorate("prisma", {
|
||||
@@ -53,6 +53,13 @@ async function buildApp({ routeOptions = {}, employee } = {}) {
|
||||
const permissionCodes = requestEmployee.permissionCodes ?? [];
|
||||
return permissionCodes.includes("*") || permissionCodes.includes(permissionCode);
|
||||
});
|
||||
app.decorate("assertPermission", (requestEmployee, permissionCode) => {
|
||||
const permissionCodes = requestEmployee.permissionCodes ?? [];
|
||||
if (!permissionCodes.includes("*") && !permissionCodes.includes(permissionCode)) {
|
||||
throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403 });
|
||||
}
|
||||
return permissionCode;
|
||||
});
|
||||
app.decorate("config", {});
|
||||
|
||||
await app.register(usersRoutes, { prefix: "/api", ...routeOptions });
|
||||
@@ -193,4 +200,72 @@ describe("usersRoutes list query", () => {
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("returns a minimal directory DTO and does not select or search contact fields", async () => {
|
||||
const { app, findMany } = await buildApp({
|
||||
routeOptions: {
|
||||
basePath: "/employees",
|
||||
allowDepartmentFilter: true,
|
||||
directoryOnly: true,
|
||||
managementPermissionCode: "system.users",
|
||||
listPermissionCodes: ["expense.view_own"],
|
||||
},
|
||||
employee: {
|
||||
id: "viewer-1",
|
||||
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
||||
permissionCodes: ["expense.view_own"],
|
||||
},
|
||||
});
|
||||
|
||||
const response = await app.inject({ method: "GET", url: "/api/employees?q=alice" });
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.json().data.items[0]).toEqual({
|
||||
id: "11111111-1111-4111-8111-111111111111",
|
||||
employeeNo: "E001",
|
||||
name: "Alice",
|
||||
title: "员工",
|
||||
status: "active",
|
||||
department: null,
|
||||
avatarUrl: null,
|
||||
});
|
||||
const query = findMany.mock.calls[0][0];
|
||||
expect(query.select).toEqual({
|
||||
id: true,
|
||||
employee_no: true,
|
||||
name: true,
|
||||
title: true,
|
||||
status: true,
|
||||
avatar_path: true,
|
||||
department: { select: { id: true, name: true } },
|
||||
});
|
||||
const searchFields = query.where.AND[0].OR.map((item) => Object.keys(item)[0]);
|
||||
expect(searchFields).toEqual(["employee_no", "name"]);
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it("rejects management scope before directory queries without the personnel permission", async () => {
|
||||
const { app, findMany, count } = await buildApp({
|
||||
routeOptions: {
|
||||
basePath: "/employees",
|
||||
allowDepartmentFilter: true,
|
||||
directoryOnly: true,
|
||||
managementPermissionCode: "system.users",
|
||||
listPermissionCodes: ["expense.view_own"],
|
||||
},
|
||||
employee: {
|
||||
id: "system-admin-1",
|
||||
department_id: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
||||
permissionCodes: ["expense.view_own", "system.accounts.manage"],
|
||||
},
|
||||
});
|
||||
|
||||
const response = await app.inject({ method: "GET", url: "/api/employees?scope=management" });
|
||||
|
||||
expect(response.statusCode).toBe(403);
|
||||
expect(findMany).not.toHaveBeenCalled();
|
||||
expect(count).not.toHaveBeenCalled();
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户