feat(filelib): 项目级授权恒生效,移除独立权限开关(ADR-0030)

- permission.ts: effectiveRole 删除 D11 冻结分支,输入不再含开关字段
- treeService: 停止读 FileLibProjectSettings;建项目不再写默认行
- grantService/routes: 删 setIndependentPermission 与 PUT 路由;节点详情 DTO 去掉 independentPermission
- filelib-web: 概览 tab 移除开关;NodeDetail 类型同步
- 测试: 单测/集成改为断言恒生效语义;ADR-0030 废除契约 D11/P5
- FileLibProjectSettings 表保留(存量行忽略,不再读写),审计词表保留历史读取
This commit is contained in:
ymy
2026-07-30 22:27:45 +08:00
parent c72f8c7050
commit 39a2be6347
11 changed files with 83 additions and 155 deletions
+4 -9
View File
@@ -80,20 +80,15 @@ describe("treeService · 创建规则", () => {
});
});
describe("treeService · D11 独立权限开关", () => {
it("关闭时项目级非创建者 grant 冻结,创建者仍 MANAGE", async () => {
describe("treeService · 项目级 grant 恒生效(ADR-0030)", () => {
it("项目级非创建者 grant 创建即生效,创建者仍 MANAGE", async () => {
const project = await createNode(deps(), ADMIN, {
parentId: null, kind: "PROJECT", name: "TH-141",
grants: [{ principalType: "USER", principalId: "u_alice", role: "EDIT" }],
});
await expect(getEffectiveRole(deps(), ALICE, project.id))
.rejects.toMatchObject({ statusCode: 404 }); // 冻结 = 无权限 = D8 不可见
// 无开关、无冻结:alice 的项目级 EDIT 立即可见。
expect(await getEffectiveRole(deps(), ALICE, project.id)).toBe("EDIT");
expect(await getEffectiveRole(deps(), ADMIN, project.id)).toBe("MANAGE");
await prisma.fileLibProjectSettings.update({
where: { nodeId: project.id },
data: { independentPermissionsEnabled: true },
});
expect(await getEffectiveRole(deps(), ALICE, project.id)).toBe("EDIT"); // 恢复
});
});
+11 -17
View File
@@ -1,6 +1,6 @@
/**
* 纯权限 reducer 单测(契约 P6 / D11 / 2.3)。
* 矩阵覆盖:个人/Group/祖先继承/max 取最高/不降权/空权限/toggle 冻结;
* 纯权限 reducer 单测(契约 P6 / 2.3)。
* 矩阵覆盖:个人/Group/祖先继承/max 取最高/不降权/空权限;
* 外加确定性随机化不变量(单调性:任何可用 grant 都不超过 effective)。
*/
import { describe, expect, it } from "vitest";
@@ -10,7 +10,6 @@ const base: EffectiveRoleInput = {
nodeId: "N",
nodeKind: "FOLDER",
ancestorIds: ["A", "R"], // N ⊂ A ⊂ R
independentPermissionsEnabled: false,
userId: "u1",
groupIds: ["g1"],
grants: [],
@@ -75,33 +74,28 @@ describe("effectiveRole · 契约 P6 矩阵", () => {
});
});
describe("effectiveRole · D11 独立权限开关", () => {
describe("effectiveRole · 项目级 grant 恒生效(ADR-0030)", () => {
const project: EffectiveRoleInput = { ...base, nodeKind: "PROJECT", nodeId: "P" };
it("开关关闭:项目级非创建者 grant 冻结", () => {
it("项目级非创建者 grant 直接参与(无开关、无冻结)", () => {
const grants = [grant({ nodeId: "P", role: "EDIT" })];
expect(effectiveRole({ ...project, grants })).toBeNull();
expect(effectiveRole({ ...project, grants })).toBe("EDIT");
});
it("开关关闭:创建者 grant 生效", () => {
it("创建者 grant 照常生效", () => {
const grants = [grant({ nodeId: "P", role: "MANAGE", isCreatorGrant: true })];
expect(effectiveRole({ ...project, grants })).toBe("MANAGE");
});
it("开关关闭:祖先链 grant 不受影响", () => {
it("项目级与祖先链 grant 同取 max", () => {
const grants = [
grant({ nodeId: "P", role: "MANAGE" }), // 冻结
grant({ nodeId: "A", role: "VIEW" }), // 生效
grant({ nodeId: "P", role: "VIEW" }),
grant({ nodeId: "A", role: "EDIT" }),
];
expect(effectiveRole({ ...project, grants })).toBe("VIEW");
expect(effectiveRole({ ...project, grants })).toBe("EDIT");
});
it("开关开启:项目级 grant 恢复参与", () => {
const grants = [grant({ nodeId: "P", role: "EDIT" })];
expect(effectiveRole({ ...project, independentPermissionsEnabled: true, grants })).toBe("EDIT");
});
it("文件夹忽略开关(self grant 照常参与)", () => {
it("文件夹与项目语义一致(self grant 照常参与)", () => {
const grants = [grant({ nodeId: "N", role: "EDIT" })];
expect(effectiveRole({ ...base, grants })).toBe("EDIT");
});