forked from EduCraft/curriculum-project-hub
fix(filelib): 授权表与侧栏展示 displayName 而非裸 userId
GrantDto 加 principalName:USER → User.displayName,GROUP → MemberGroup.name, 取不到行(用户/组已删)时回落为 principalId,与 /database/api/me 同一回落语义。 解析走批量 helper(两条 IN 查询,非 N+1),listGrants/putGrants/forceAdjustGrants 三个出口共用,保证 GET 与 PUT 响应同形状。组不按 archivedAt 过滤 —— 已归档组的 历史授权仍需显示名字,否则管理员无法辨认后收回。 principalName 是纯展示字段;写路径仍只认 principalId,不得据此做授权判断。 前端: - GrantsPanel 主体列由裸 id 改为展示名,id 移入 title 供排查;收回确认框同步。 - LibraryView 侧栏身份区改用 $me.displayName(/me 早已返回,此前未消费)。 - types.ts 去掉重复声明的 Grant 与无引用的 GroupSearchResult。 集成测试断言三种情形(displayName / 组名 / 已删主体回落)。
This commit is contained in:
@@ -150,6 +150,42 @@ describe("filelib http · 8.1 授权矩阵", () => {
|
||||
expect(revoke.statusCode).toBe(403);
|
||||
expect(revoke.json().error.code).toBe("cannot_touch_creator");
|
||||
});
|
||||
|
||||
it("grants 返回 principalName:USER→displayName / GROUP→组名;取不到行时回落为 id", async () => {
|
||||
const rootId = await createRoot(ADMIN_COOKIE());
|
||||
const group = await prisma.memberGroup.create({ data: { name: "物理组" } });
|
||||
|
||||
const put = await app.inject({
|
||||
method: "PUT", url: `/database/api/nodes/${rootId}/grants`,
|
||||
headers: { cookie: ADMIN_COOKIE() },
|
||||
payload: {
|
||||
grants: [
|
||||
{ principalType: "USER", principalId: "u_alice", role: "EDIT" },
|
||||
{ principalType: "GROUP", principalId: group.id, role: "VIEW" },
|
||||
// 数据库里没有对应 User 行(已删/脏数据)→ 回落为 principalId
|
||||
{ principalType: "USER", principalId: "u_ghost", role: "VIEW" },
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(put.statusCode).toBe(200);
|
||||
|
||||
const list = await app.inject({
|
||||
method: "GET", url: `/database/api/nodes/${rootId}/grants`,
|
||||
headers: { cookie: ADMIN_COOKIE() },
|
||||
});
|
||||
expect(list.statusCode).toBe(200);
|
||||
const byPrincipal = new Map<string, string>(
|
||||
list.json().grants.map((g: { principalId: string; principalName: string }) => [g.principalId, g.principalName]),
|
||||
);
|
||||
expect(byPrincipal.get("u_alice")).toBe("Alice");
|
||||
expect(byPrincipal.get(group.id)).toBe("物理组");
|
||||
expect(byPrincipal.get("u_ghost")).toBe("u_ghost");
|
||||
// 创建者授权也要解析出名字
|
||||
const creator = list.json().grants.find((g: { isCreatorGrant: boolean }) => g.isCreatorGrant);
|
||||
expect(creator.principalName).toBe("Admin");
|
||||
// PUT 响应与 GET 同形状
|
||||
expect(put.json().grants.every((g: { principalName?: string }) => typeof g.principalName === "string")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("filelib http · 文件冲突流", () => {
|
||||
|
||||
Reference in New Issue
Block a user