diff --git a/.superpowers/sdd/2026-10-25-v12-permission-scope-dashboard-remediation/task-1-report.md b/.superpowers/sdd/2026-10-25-v12-permission-scope-dashboard-remediation/task-1-report.md new file mode 100644 index 0000000..fe9aafe --- /dev/null +++ b/.superpowers/sdd/2026-10-25-v12-permission-scope-dashboard-remediation/task-1-report.md @@ -0,0 +1,32 @@ +# Task 1 完成报告:角色权限与范围基线 + +## 变更 + +- 新增 `docs/v12/V1.2-角色权限与前端入口矩阵.md`。 + - 冻结 14 个正式角色的数据范围、工作台入口、允许操作和禁止操作。 + - 明确普通红娘为 SELF、红娘组长为 TEAM、平台/超级管理员为 ALL。 + - 明确五类工作台业务卡片的查看及审核、待办分配、确认、完成权限,同时保留红娘不得代用户 Like/Pass、不得查看身份证原始佐证和咨询敏感备注等边界。 + - 说明 V070/V071 是历史收敛迁移,不能覆盖 2026-10-25 最新裁决,后续通过幂等迁移恢复正式最小权限。 +- 修改 `docs/governance/decision-register.md`,登记 V070/V071 冲突裁决。 +- 新增 `backend/src/test/java/com/danyang/match/platform/deployment/PermissionBaselineContractTest.java`,锁定角色、范围、操作、敏感权限和历史迁移裁决。 + +## 测试 + +命令: + +```text +mvn -B -f backend/pom.xml -s backend/maven-settings.xml -Dtest=PermissionBaselineContractTest test +``` + +结果: + +```text +Tests run: 3, Failures: 0, Errors: 0, Skipped: 0 +BUILD SUCCESS +``` + +## 证据边界与风险 + +- 本任务未修改 V070/V071 历史迁移,也未直接修改数据库;活跃 PostgreSQL 角色权限/版本查询留给活栈验收任务。 +- 矩阵锁定的是产品权限与数据范围契约,实际恢复权限需要后续 Task 5 的幂等迁移并通过干净数据库和活栈验证。 +- 未运行全量 Maven、前端构建或 E2E;这些属于后续任务门禁。 diff --git a/backend/src/test/java/com/danyang/match/platform/deployment/PermissionBaselineContractTest.java b/backend/src/test/java/com/danyang/match/platform/deployment/PermissionBaselineContractTest.java new file mode 100644 index 0000000..ef1bc60 --- /dev/null +++ b/backend/src/test/java/com/danyang/match/platform/deployment/PermissionBaselineContractTest.java @@ -0,0 +1,69 @@ +package com.danyang.match.platform.deployment; + +import org.junit.jupiter.api.Test; + +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + +/** Locks the V1.2 role/scope baseline before implementation tasks consume it. */ +class PermissionBaselineContractTest { + + private static final Path ROOT = Path.of("."); + private static final Path MATRIX = ROOT.resolve("docs/v12/V1.2-角色权限与前端入口矩阵.md"); + private static final Path DECISIONS = ROOT.resolve("docs/governance/decision-register.md"); + private static final Path V070 = ROOT.resolve("backend/src/main/resources/db/migration/V070__remove_matchmaker_cross_domain_permissions.sql"); + private static final Path V071 = ROOT.resolve("backend/src/main/resources/db/migration/V071__remove_matchmaker_review_permissions.sql"); + + @Test + void matrixFreezesRolesScopesMenusAndForbiddenOperations() throws Exception { + String matrix = Files.readString(MATRIX); + for (String role : new String[]{"user", "matchmaker", "matchmaker_leader", "platform_admin", "super_admin", + "reviewer", "activity_manager", "risk_operator", "organization_admin", "counselor", + "counseling_manager", "service_station_operator", "content_editor", "data_viewer"}) { + assertTrue(matrix.contains("`" + role + "`"), "矩阵缺少角色: " + role); + } + assertAll( + () -> assertTrue(matrix.contains("本人有效 `matchmaker_assignments`")), + () -> assertTrue(matrix.contains("本人所属组织内")), + () -> assertTrue(matrix.contains("平台范围")), + () -> assertTrue(matrix.contains("review.process")), + () -> assertTrue(matrix.contains("counseling.service.assign/process")), + () -> assertTrue(matrix.contains("service_appointment.assign/confirm/complete")), + () -> assertTrue(matrix.contains("activity.review_registration")), + () -> assertTrue(matrix.contains("counseling.note.view")), + () -> assertTrue(matrix.contains("代用户决策")), + () -> assertTrue(matrix.contains("userId")), + () -> assertTrue(matrix.contains("403")) + ); + } + + @Test + void historicalRevocationsAreExplicitlySupersededByTheDecision() throws Exception { + String matrix = Files.readString(MATRIX); + String decisions = Files.readString(DECISIONS); + String v070 = Files.readString(V070); + String v071 = Files.readString(V071); + assertAll( + () -> assertTrue(matrix.contains("V070、V071 是在本需求确认前形成的收敛迁移")), + () -> assertTrue(matrix.contains("不得通过修改历史迁移文件解决冲突")), + () -> assertTrue(decisions.contains("V1.2 工作台权限冲突裁决(2026-10-25)")), + () -> assertTrue(v070.contains("DELETE FROM role_permissions")), + () -> assertTrue(v071.contains("DELETE FROM role_permissions")) + ); + } + + @Test + void matchmakerBaselineDoesNotGrantSensitiveOrUserDecisionPermissions() throws Exception { + String matrix = Files.readString(MATRIX); + assertAll( + () -> assertTrue(matrix.contains("`matchmaker.assign`")), + () -> assertTrue(matrix.contains("`counseling.note.view`")), + () -> assertTrue(matrix.contains("资格原始佐证")), + () -> assertTrue(matrix.contains("Like/Pass")), + () -> assertTrue(matrix.contains("未发布资料 revision 不得进入展示、推荐、匹配、红娘服务或统计")) + ); + assertFalse(matrix.contains("matchmaker` | 平台范围"), "普通红娘不能被定义为平台范围"); + } +} diff --git a/docs/governance/decision-register.md b/docs/governance/decision-register.md index fa40353..c5a236b 100644 --- a/docs/governance/decision-register.md +++ b/docs/governance/decision-register.md @@ -66,3 +66,4 @@ --- *最后更新: 2026-10-11 — 登记 DEC-MATCHMAKER-003-B1 受控联系例外通道立项(V056 / 提交 d3735e3,QA PASS 0 P0/P1);OQ-MATCHMAKER-003 转 IN_PROGRESS,剩余 LEGAL_VERIFY 终审项与审批双岗收敛见 open-questions OQ-MATCHMAKER-003/009,参数 canonical key 见 config-register `matchmaker.contact.*`。此前:2026-10-10 登记红娘角色权限专项 DEC-MATCHMAKER-001~006(ZC-MM-001~007 全链路);2026-09-20 登记 15 项已确认决策。* +| V1.2 工作台权限冲突裁决(2026-10-25) | 普通红娘与红娘组长均可查看五类工作台业务卡片,并在本人/本组织范围内执行审核、业务待办分配、确认、完成;不得跨组织、查看敏感原始佐证/咨询备注或代替用户作出 Like/Pass 等意思表示 | LOCKED | V068/V069 的授权意图按最新需求保留;V070/V071 仅作为历史收敛迁移,不得覆盖本裁决;后续幂等迁移恢复最小权限 | diff --git a/docs/v12/V1.2-角色权限与前端入口矩阵.md b/docs/v12/V1.2-角色权限与前端入口矩阵.md new file mode 100644 index 0000000..b1bfbe8 --- /dev/null +++ b/docs/v12/V1.2-角色权限与前端入口矩阵.md @@ -0,0 +1,64 @@ +# V1.2 角色权限与前端入口矩阵 + +> 生效基线:2026-10-25。本文是 V1.2 Task 1 的唯一角色、权限范围和入口基线。 +> 角色权限由服务端认证主体推导;前端菜单只展示服务端已授予的 capability,不能扩大权限。 + +## 1. 范围与冲突裁决 + +- 普通红娘(`matchmaker`)只能访问本人有效分配的服务对象;红娘组长(`matchmaker_leader`)只能访问本人所属组织/组;`platform_admin`、`super_admin` 才能使用平台范围。 +- 普通红娘和红娘组长均可查看工作台五类业务卡片并处理其授权范围内的审核、分配、确认、完成待办。这里的“分配”是业务待办/服务记录分配,不等于跨组织分配用户,也不等于代替用户 Like、Pass、活动意向、开启聊天或修改资料。 +- 红娘不得查看身份证原始佐证、手机号完整值、咨询敏感备注;这些权限仍由专门权限和审计控制。 +- V070、V071 是在本需求确认前形成的收敛迁移,删除了 V068/V069 所需的工作台操作权限。它们保留为历史迁移,不代表最终权限基线;干净数据库执行到最新版本后,必须由后续幂等迁移按本矩阵恢复已确认的最小权限。不得通过修改历史迁移文件解决冲突。 + +## 2. 数据范围 + +| 角色 | 服务对象/看板范围 | 组织要求 | +|---|---|---| +| `user` | 仅本人用户数据 | 无运营菜单 | +| `matchmaker` | 本人有效 `matchmaker_assignments` | 必须有有效分配;请求体 `userId`、`matchmakerId`、`teamId` 不改变范围 | +| `matchmaker_leader` | 本人所属组织内的有效分配及组内服务数据 | 无组织归属必须拒绝或进入明确异常态;不得把 NULL 组织合并为一组 | +| `platform_admin` | 全平台 | 后端仍执行敏感字段权限和审计 | +| `super_admin` | 全平台 | 全权限仅限系统治理,仍保留审计 | +| `reviewer` | 被授权的资料/资格审核数据 | 不得读取咨询正文、私人聊天 | +| `activity_manager` | 活动业务数据 | 不得读取与活动无关的用户敏感数据 | +| `risk_operator` | 风控、举报及其必要关联数据 | 不得浏览全量婚恋档案 | +| `organization_admin` | 本组织被授权的运营数据 | 不得跨组织;不得隐式获得平台范围 | +| `counselor` | 明确分配给本人的咨询服务 | 只能 Assigned Service | +| `counseling_manager` | 咨询服务管理范围 | 可按专门权限查看敏感咨询备注并留痕 | +| `service_station_operator` | 已授权服务站的现场预约/任务 | 不得浏览全用户库 | +| `content_editor` | 内容、课堂、反诈文章 | 零用户婚恋数据权限 | +| `data_viewer` | 已授权的统计读模型 | 不得进入用户详情和运营写操作 | + +## 3. 工作台入口与操作 + +六项指标固定为:`pending_reviews`、`matchmaker_todos`、`counseling_todos`、`station_appointments`、`effective_matches`、`activity_registrations`。其中前五类业务卡片(资料审核/红娘服务/咨询服务/服务站预约/有效匹配)和活动报名卡片均使用同一服务端 scope builder;聚合与详情不得接受替代主体或组织参数。 + +| 角色 | 可见入口 | 允许的工作台操作 | 明确禁止 | +|---|---|---|---| +| `matchmaker` | 红娘工作台;本人范围内六卡片 | `review.process`、`local_eligibility.review`、`counseling.service.assign/process`、`counseling.note.create`、`service_appointment.assign/confirm/complete`、`activity.review_registration`;红娘待办可 `matchmaker.service` | `matchmaker.assign`、跨组织/他人对象、`counseling.note.view`、资格原始佐证、代用户决策 | +| `matchmaker_leader` | 红娘工作台;本组织六卡片;已授权组内管理入口 | 上述工作台操作;另可按明确授权执行 `matchmaker.assign`/`matchmaker.team.assign`,仅限本组织 | 跨组织分配/建档、全平台统计、敏感原始佐证、咨询备注全文、代用户决策 | +| `platform_admin` | 全部运营菜单与工作台 | 平台范围内的审核、分配、确认、完成及角色/组织管理 | 越过审计或绕过状态门禁 | +| `super_admin` | 全部系统菜单与工作台 | 同 `platform_admin`,另负责系统治理 | 仍不得绕过审计 | +| `reviewer` | 资料审核 | 资料审核/本地资格审核 | 红娘、咨询、预约、活动管理 | +| `activity_manager` | 公益活动 | 活动审核、报名、签到及活动流程 | 咨询正文、红娘分配 | +| `risk_operator` | 风控/举报 | 风险处置及必要审核 | 全量用户浏览、红娘服务操作 | +| `organization_admin` | 本组织授权运营入口 | 本组织内经授予的管理操作 | 跨组织和平台范围 | +| `counselor` | 我的咨询服务 | 被分配咨询的处理、摘要记录 | 分配他人服务、查看未分配服务、红娘数据 | +| `counseling_manager` | 咨询管理 | 咨询分配、处理、必要备注查看 | 红娘/活动/预约无关操作 | +| `service_station_operator` | 我的服务站预约 | 本站预约确认、完成 | 全用户库、咨询正文、红娘分配 | +| `content_editor` | 内容管理 | 内容创建、编辑、发布、下架 | 任何用户婚恋数据和工作台 | +| `data_viewer` | 统计只读入口(如已授权) | 统计读取 | 所有业务写操作和用户详情 | + +## 4. 前端入口规则 + +1. 菜单和卡片由服务端 capability 显示;`403`、真实 `0`、查询错误必须分别呈现。 +2. 无 capability 的入口不显示,也不能通过直接 URL 进入;后端仍必须再次鉴权。 +3. 管理员可查看工作台统计,但独立“公益红娘/红娘服务”菜单是否显示仍按具体权限,不因统计权限自动出现。 +4. 卡片点击详情只传分页和时间筛选;不得传入或信任 `userId`、`matchmakerId`、`teamId`、角色或 scope 字段。 + +## 5. 敏感边界与不变量 + +- `counseling.note.view`、`local_eligibility.evidence.view`、完整手机号和身份证原始佐证不因工作台权限自动授予。 +- 红娘主体调用用户 Like/Pass、活动意向、开启聊天、修改择偶条件、同意公开资料等端点必须被认证层拒绝。 +- 未发布资料 revision 不得进入展示、推荐、匹配、红娘服务或统计。 +- 任何越权拒绝、敏感访问和角色/组织变更都必须写审计事件。