255 lines
31 KiB
Markdown
255 lines
31 KiB
Markdown
# 红娘角色权限差距审计(ZC-MM-001)
|
||||
|
|
|
|||
|
|
> **审计基准**:《善缘丹阳 V1.1 红娘角色权限正式决策与开发规则》V1.1(DEC-MATCHMAKER-001~006 全部 LOCKED,A1/B1 已确认)
|
|||
|
|
> **审计日期**:2026-10-09
|
|||
|
|
> **审计执行**:架构师(高见远)· software-danyang-mm 团队 · 只读走查,未修改任何业务代码
|
|||
|
|
> **审计方式**:backend matchmaker/referral/audit/security/auth 模块、Flyway V001~V052、admin-web 红娘相关页面、现有测试与治理文档。所有结论均给出文件路径+行号或 SQL/代码证据。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. 审计范围与方法
|
|||
|
|
|
|||
|
|
- 后端:`backend/src/main/java/com/danyang/match/platform/` 下 `matchmaker/`(controller 2、service 2、entity 3、mapper 3)、`referral/`(controller 1、service 1)、`audit/`、`shared/security/`、`auth/`、`counseling/`(对照 INV-012)、`review/`(对照敏感访问日志范式)。
|
|||
|
|
- 数据库:`backend/src/main/resources/db/migration/` V001~V052 全量中 matchmaker 相关 8 个迁移(V001/V002/V009/V019/V021/V037/V038/V044/V052)。
|
|||
|
|
- 前端:`admin-web/src/modules/matchmaker/MatchmakerManagementView.vue`、`modules/referral/ReferralManagementView.vue`、`router/index.ts`、`layouts/MainLayout.vue`。
|
|||
|
|
- 测试:`backend/src/test` 中 matchmaker/referral/权限相关 6 个测试类。
|
|||
|
|
- 治理文档:`docs/governance/decision-register.md`、`open-questions.md`、`docs/PENDING_DECISIONS.md`。
|
|||
|
|
- 全局检索:`matchmaker_assignments`、`ServiceAssignment`、`matchmaker_leader`、`ContactAuthorization` 等关键词。
|
|||
|
|
|
|||
|
|
状态标注:【已实现】/【部分实现】/【缺失】/【无法确定】。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 现状总览
|
|||
|
|
|
|||
|
|
**一句话结论**:当前仓库的"红娘"只是一个挂在 `matchmaker_services` 服务单上的 `assigned_operator_id` 工单负责人概念——没有独立的服务对象分配关系、没有红娘组长角色、没有统一的数据范围/字段级隐私策略层;工单维度上已做过多轮越权修补(推荐/转介/详情归属校验),但**认领、跟进、完结等写接口仍缺归属校验,未分配队列与全局统计对全体红娘可见,审计与权限回收存在系统性缺口**。A1 核心未建模,B1 现状上"侥幸安全"(接口本就不返回联系方式)而非"设计安全"。
|
|||
|
|
|
|||
|
|
### 决策要求 vs 现状对照表
|
|||
|
|
|
|||
|
|
| # | 决策要求 | 现状 | 状态 | 关键证据 |
|
|||
|
|
|---|---------|------|------|---------|
|
|||
|
|
| 1 | DEC-002/A1:服务对象由平台授权人员或红娘组长分配,普通红娘仅访问本人有效分配对象 | 分配仅体现在 `matchmaker_services.assigned_operator_id`(服务单指派),无独立分配实体、无 assigned_by/理由/有效期/结束语义;`/pending` 全局队列对任何 `matchmaker.view` 持有者可见 | **缺失(核心)** | V009 L5-20;`MatchmakerOperatorController` L32-36;`MatchmakerServiceMapper.findSubmitted()` |
|
|||
|
|
| 2 | DEC-002 规则2:禁止普通红娘认领未分配/他人对象 | `POST /operator/matchmaker/services/{id}/claim` 任何 `matchmaker.service` 持有者可对任何 assigned 服务接单,无归属校验 | **缺失** | `MatchmakerOperatorController` L49-56;`MatchmakerServiceService.claimService` L109-122 |
|
|||
|
|
| 3 | DEC-002 规则3/6:分配、转分配、撤销、结束留痕;状态机覆盖 | 仅有 assign/complete/cancel(cancel 无 HTTP 入口,死代码);无转分配、无撤销、无结束原因;分配人(assigned_by)不记录 | **缺失** | `MatchmakerServiceService` L87-104、L229-237;V009 L11-14 |
|
|||
|
|
| 4 | DEC-003/B1:默认平台内联系,不暴露完整手机号/微信号/详细地址 | 红娘侧 DTO/VO 与前端页面均无 phone/wechat/address/idNumber 字段;但也**不存在**任何联系授权机制(无表、无策略类) | **部分实现(结果安全、机制缺失)** | `MatchmakerServiceService.getServiceDetail` L329-348;`ReferralService` 全文;全局检索 ContactAuthorization 无结果 |
|
|||
|
|
| 5 | DEC-004:红娘不得代替用户 Like/Pass/确认开启聊天/接受活动意向 | 设计与实现均贯彻"推荐≠Like":推荐仅建 `pending_view` 记录,Like/Pass 由用户本人端点(UserPrincipal+`findByIdAndUser` 归属校验)触发;转介确认须用户本人 | **已实现** | `MatchmakerService` L97-142、L213-258;`ReferralService.confirmReferral` L139-184;测试 `MatchmakerReferralPhase13Test` L132-296 |
|
|||
|
|
| 6 | DEC-005:最小必要 + 数据范围 + 字段隐私 + 有效分配 | 无 Policy 层;数据范围校验散落且口径不一:recommend/转介有归属校验,claim/followup/complete 无;`matchmaker.assign` 与 `matchmaker.service` 语义即"工单指派/工单执行",不是"分配服务对象" | **缺失** | `MatchmakerServiceService` L142-151(有)vs L109-122、L186-203、L208-224(无) |
|
|||
|
|
| 7 | DEC-006:全程留痕(分配、敏感查看、越权拒绝等) | 有 audit 基础设施且授权拒绝有全局监听;但 claim 不审计、敏感详情查看不记 SensitiveAccessLog、模块内归属拒绝不走 DataScopeGuard、分配审计不记 assigned_by/理由 | **部分实现** | `AuthorizationAuditListener` L25-49;`DataScopeGuard` 使用方仅 activity/counseling/service_appointment;`MatchmakerServiceService.saveAuditLog` L395-403(before/after 恒 null) |
|
|||
|
|
| 8 | 功能权限矩阵:红娘无全站检索/导出/敏感字段 | matchmaker 角色仅授 3 权限(view/service/assign),无 user.view、无 data.export;全仓未发现导出端点 | **已实现(现状合规)** | V052 L24-28;grep `data.export` 使用方为 0 |
|
|||
|
|
| 9 | 红娘组长角色 | 全仓(迁移+代码+前端)无 matchmaker_leader/team_lead | **缺失** | grep 无结果 |
|
|||
|
|
| 10 | 测试基线 MM-AUTH/CONTACT/ACTION/PRIVACY/AUDIT | 仅有"红娘不代替用户操作"(MM-ACTION 方向)与状态白名单守卫测试;无数据范围/联系方式/审计/生命周期场景测试 | **缺失** | `MatchmakerReferralPhase13Test`、`MatchmakerServiceTest`(仅常量白名单校验) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 分项审计
|
|||
|
|
|
|||
|
|
### 2.1 当前红娘角色及权限实现
|
|||
|
|
|
|||
|
|
**角色与权限定义**【部分实现】:
|
|||
|
|
- `matchmaker` 角色定义于 V001:`('00000000-0000-0000-0000-000000000005', 'matchmaker', '公益红娘', '提供人工婚恋服务', TRUE)`(V001 L145)。
|
|||
|
|
- 三条红娘权限定义于 V001 L161-163:`matchmaker.view`(020)、`matchmaker.assign`(021)、`matchmaker.service`(022)。
|
|||
|
|
- **角色-权限授权缺口曾真实存在**:V052 迁移注释自证——"创建红娘运营账号后发现该角色在 role_permissions 中零条授权,登录后台 403"(V052 L4-12);V052 仅补授 3 条权限(L24-28)。
|
|||
|
|
- 其余角色授权同样残缺:V044 注释承认"permissions 共 48 条,role_permissions 里 super_admin 只有 18 条;matchmaker 角色为 0 条"(V044 L5-11),V044 只回补了 super_admin,**platform_admin/organization_admin 等角色的红娘权限授予状态无法从迁移中确认**【无法确定,需查实际库】。注意 `PermissionMatrixTest` L57-59 期望 platform_admin 拥有 matchmaker.view/assign/service,但迁移中未见对应 INSERT【测试期望与 DB 授权可能漂移】。
|
|||
|
|
- **红娘组长角色不存在**:迁移与代码 grep `leader|team_lead` 均无结果。
|
|||
|
|
|
|||
|
|
**后端接口面**【部分实现】:
|
|||
|
|
- 运营端 `MatchmakerOperatorController`(`/operator/matchmaker`,类级 `@PreAuthorize("hasAuthority('matchmaker.view')")` L19):dashboard(L25)、pending(L32)、assign(L38,`matchmaker.assign`)、claim(L49,`matchmaker.service`)、my-services(L58)、recommendations(L69)、services/{id} 详情(L75)、recommend(L87)、followup(L99)、complete(L112)。
|
|||
|
|
- 用户端 `MatchmakerController`(`/matchmaker`):提交申请(L48-58)、我的服务(L60)、推荐列表(L67)、viewed/accept/pass(L73-96)。
|
|||
|
|
- 转介 `ReferralController`(`/referrals`):service-station(L33)/activity(L53) 均 `matchmaker.service`;confirm(L70)/my-history(L85) 用户本人;operator/history(L99) 按 `matchmaker.service`。
|
|||
|
|
|
|||
|
|
**关键发现:`matchmaker.assign` 的语义**——它是"把服务工单指派给某红娘"(`assignService(serviceId, operatorId, organizationId)`,`MatchmakerServiceService` L87-104),即**工单分派**而非决策文档意义上的"服务对象分配"(对用户建立服务关系)。当前系统里"服务对象"就是服务单上的 `user_id`,分配关系与工单合一,无法表达"同一用户多次/多任务分配、分配历史、到期、撤销"。
|
|||
|
|
|
|||
|
|
**已知实现缺陷(非权限但影响验收)**:
|
|||
|
|
- `countInProgressByOperator` 统计 `status='processing'`(`MatchmakerServiceMapper` L28-30),而 DB CHECK 白名单只有 `in_service`(V009 L30-32),工作台 inProgress 恒为 0。
|
|||
|
|
- `cancelService`(`MatchmakerServiceService` L229-237)无任何 Controller 入口,死代码。
|
|||
|
|
|
|||
|
|
### 2.2 服务对象分配建模
|
|||
|
|
|
|||
|
|
**结论:未建模**【缺失】。
|
|||
|
|
- 全仓(backend main + admin-web src + 全部迁移)grep `matchmaker_assignments|service_assignment|ServiceAssignment` 无结果。
|
|||
|
|
- 唯一的"分配"载体是 `matchmaker_services.assigned_operator_id`(V009 L12),字段只有 `assigned_operator_id / assigned_at / organization_id`,**没有** assigned_by、assignment_reason、assignment_scope、expires_at、ended_at、end_reason、status(分配关系状态与服务单状态混用)。
|
|||
|
|
- `/operator/matchmaker/my-services` 返回该红娘名下全部服务含历史(`MatchmakerServiceMapper.findByOperatorId` L36-38,`MatchmakerServiceService.getMyServices` L68-82),即"我的服务对象 = 工单负责人",非独立分配关系。
|
|||
|
|
- **认领入口存在**:`POST /services/{id}/claim` 仅需 `matchmaker.service`(`MatchmakerOperatorController` L49-56),`claimService` 只校验状态=assigned,不校验该单是否分配给本人,也不把 assigned_operator_id 改成自己(`MatchmakerServiceService` L109-122)——任何红娘可以把任何红娘的 assigned 单推进到 in_service。直接违反 DEC-002 规则 2(不能自行认领)。
|
|||
|
|
- **转分配/撤销/结束**:无 transfer/revoke 接口;`cancelService` 无入口;`completeService` 无归属校验(L208-224)。
|
|||
|
|
|
|||
|
|
### 2.3 列表/搜索/详情/导出数据范围一致性
|
|||
|
|
|
|||
|
|
| 接口 | 数据范围 | 判定 |
|
|||
|
|
|------|---------|------|
|
|||
|
|
| `GET /pending`(L32-36 → `findSubmitted` Mapper L17-18) | **全局**:任何 `matchmaker.view` 持有者可见全库 submitted 申请,含申请人 userId + 需求描述(Service L245-258) | ❌ 违反 DEC-002 规则1(不能浏览未分配对象库)、DEC-005 |
|
|||
|
|
| `GET /dashboard`(L25-30 → L260-276) | pendingCount/todayCount/completedCount 为**全局统计**,inProgress 按本人 | ❌ 信息泄露(全局量级)+ 状态口径 bug |
|
|||
|
|
| `GET /my-services`(L58-63 → `findByOperatorId`) | 按本人过滤 | ✅ |
|
|||
|
|
| `GET /recommendations`(L69-73 → `findByOperatorId`) | 按本人过滤 | ✅ |
|
|||
|
|
| `GET /services/{id}` 详情(L75-85 → `getServiceDetail` L301-351) | 负责人→完整(含 userProfile 8 字段 + followup 正文);非负责人→**仍返回**状态、assignedOperatorId、以及**该服务的推荐列表(含他人服务的 targetUserId 与推荐理由)**(L317-327 对所有调用者装配) | ⚠️ 部分修复:比 Phase 13 D4 前好,但非负责人仍可探测任意 serviceId 的存在性与他人推荐内容(违反"响应不得泄露目标用户是否存在");推荐理由是红娘对候选对象的自由文本,可能含隐私 |
|
|||
|
|
| `POST /services/{id}/recommend`(L87-97 → L142-181) | 归属校验(L148-151)+ 不能推给申请人(L153-155) | ✅(但推荐对象 targetUserId 由红娘手输,无候选来源,见 2.4) |
|
|||
|
|
| `POST /followup`(L99-110 → L186-203) | **无归属校验**:任何 `matchmaker.service` 持有者可对任意 serviceId 写跟进正文 | ❌ 违反 DEC-005;跟进正文为自由文本敏感写面 |
|
|||
|
|
| `POST /complete`(L112-118 → L208-224) | **无归属校验** | ❌ |
|
|||
|
|
| `POST /claim`(L49-56 → L109-122) | **无归属校验** | ❌(见 2.2) |
|
|||
|
|
| 转介 `POST /referrals/*` | `assertMatchmakerServesUser`(`ReferralService` L45-52 → `findActiveByOperatorAndUser` Mapper L48-54)要求存在本人名下、该用户的进行中服务 | ✅(Phase 13 D5 修复,注释自证修复前可代任意用户创建报名/预约) |
|
|||
|
|
| 导出 | 全仓未发现任何导出端点(grep `data.export|export` 在 main 源码无结果) | ✅ 现状无导出面 |
|
|||
|
|
|
|||
|
|
按 userId/查询参数越权形态:matchmaker 角色无 `user.view`(V052 仅 3 权限),无法触达用户管理接口,全站用户检索面关闭;但上述服务单维度参数越权真实存在。
|
|||
|
|
|
|||
|
|
### 2.4 联系方式暴露(B1)
|
|||
|
|
|
|||
|
|
**现状结果层面安全**【已实现(被动)】:
|
|||
|
|
- 红娘侧所有返回 DTO 均为手工 Map 装配:服务详情 userProfile 仅 displayName/gender/age/regionName/educationCode/occupationCode/maritalStatus(`MatchmakerServiceService` L333-343),无手机号/微信号/详细地址/身份证。
|
|||
|
|
- 前端 `MatchmakerManagementView.vue` 表格列仅 userId/serviceType/description/status/createdAt(L7-25)与推荐 userId/targetUserId/reason/status(L29-39);`ReferralManagementView.vue` 无联系方式字段。grep phone/mobile/wechat/身份证/地址 在两个 view 中均无命中。
|
|||
|
|
- 转介活动时的 `emergencyContactPhoneEnc` 是**红娘录入**的紧急联系人信息(`ReferralController` L62、`ReferralService` L111-112),非读取用户联系方式。
|
|||
|
|
- 无导出接口可绕道获取联系方式(2.3)。
|
|||
|
|
|
|||
|
|
**机制层面缺失**【缺失】:
|
|||
|
|
- 全仓无 ContactAuthorization/contact_authorization/线下联系 相关表、实体、策略(grep 无结果)——DEC-003 要求的"电话/线下联系须专项授权"完全没有机制承载,目前是"没有功能所以没有漏洞"。若后续有人补电话/线下功能,无任何护栏。
|
|||
|
|
- 平台内联系渠道:现有通知仅是单向 notification(`notificationService.send/sendAsync`),无红娘↔用户平台内会话通道;B1 的"平台内服务申请/回应"仅覆盖转介确认流。
|
|||
|
|
|
|||
|
|
**功能缺口**:推荐弹窗要求红娘手输 targetUserId(前端 L62"输入被推荐对象的用户ID")——红娘既无全站检索权(正确),也没有任何"从用户已提供的择偶需求/公开池中获取候选"的受控途径,主流程实际不可用【部分实现】。
|
|||
|
|
|
|||
|
|
### 2.5 红娘代替用户操作(DEC-004)
|
|||
|
|
|
|||
|
|
**未发现代替用户决策的接口**【已实现】:
|
|||
|
|
- 红娘推荐只建 `MatchmakerRecommendation(status=pending_view)`,明确注释"这不是替用户 Like!""红娘点创建 Match → 不允许!"(`MatchmakerService` L97-105),并且有归属校验(L117-121)。
|
|||
|
|
- Like/Pass 仅由用户本人端点触发:`POST /matchmaker/recommendations/{id}/accept|pass`(`MatchmakerController` L81-96)→ `acceptRecommendation` 用 `findByIdAndUser(recommendationId, userId)` 归属校验(`MatchmakerService` L213-222),Pass 同理(L246-255)。
|
|||
|
|
- 红娘无法以用户身份调用:用户端点走 `UserPrincipal`,运营端走 `OperatorPrincipal`(两套 principal 体系,`shared/security/principal/`),且测试锁定"红娘无法用别人的用户身份接受推荐"(`MatchmakerReferralPhase13Test` L158-171)。
|
|||
|
|
- 转介确认(活动/预约)须用户本人 `confirmReferral`,校验 `userId.equals(appt.getUserId())`(`ReferralService` L145-146、L166-167),非本人确认被拒并有测试(`MatchmakerReferralPhase13Test` L255-269)。
|
|||
|
|
- 无修改用户择偶条件、同意公开资料的红娘入口(matchmaker 角色无 user 相关权限)。
|
|||
|
|
- `matchmaker.assign` 的实际用途 = 指派工单给红娘,**不是**"代替分配对象做操作";其风险在于指派时 `organizationId` 参数恒传 null(Controller L46 传 `null`),且前端弹窗是硬编码假数据(见 2.8)。
|
|||
|
|
|
|||
|
|
⚠️ 唯一灰色点:转介会**代替用户创建** `activity_registration(status=pending)` / `service_appointment(status=submitted)` 记录(`ReferralService` L74、L114),但带 `source=matchmaker_referral` 标记且必须用户本人确认后生效(`needsUserConfirmation=true`),语义上是"代填草稿、用户确认",与 DEC-004 底线可兼容,但建议在 ZC-MM-004 复核其与"代填关键个人信息"边界的表述。
|
|||
|
|
|
|||
|
|
### 2.6 审计日志覆盖(DEC-006)
|
|||
|
|
|
|||
|
|
**基础设施**【已实现】:
|
|||
|
|
- `audit_logs` 表 + `AuditLog` 实体(operatorId/organizationId/action/resourceType/resourceId/beforeData/afterData/ip/userAgent)。
|
|||
|
|
- `sensitive_access_logs` + `SensitiveAccessLog`(operatorId/userId/dataType/reason/accessTarget/accessResult/ip)。
|
|||
|
|
- `AuthorizationAuditListener`(L25-49):`@PreAuthorize` 拒绝自动写 `AUTHORIZATION_DENIED`(含 IP/UA)→ **@PreAuthorize 层的越权拒绝已覆盖**。
|
|||
|
|
- `DataScopeGuard.deny`(L21-36):写 `DATA_SCOPE_DENIED` 后抛 403。
|
|||
|
|
|
|||
|
|
**matchmaker 模块实际覆盖**【部分实现,缺口明确】:
|
|||
|
|
|
|||
|
|
| 操作 | 审计现状 |
|
|||
|
|
|------|---------|
|
|||
|
|
| 分配 assign | ✅ `MATCHMAKER_ASSIGN`(L102),但 beforeData/afterData 恒 null(saveAuditLog L395-403),**不记录分配人以外信息(谁指派=principal.id 即 assignOperatorId=被指派红娘,见 Controller L44-46 的参数命名混乱:body.operatorId 是"被分配红娘",操作者是 principal,审计里存的是前者)** |
|
|||
|
|
| 认领 claim | ❌ 无审计(L109-122 无 saveAuditLog) |
|
|||
|
|
| 推荐 recommend | ✅ `MATCHMAKER_RECOMMEND`(L174) |
|
|||
|
|
| 跟进 followup | ✅ `MATCHMAKER_FOLLOWUP`(L201) |
|
|||
|
|
| 完成 complete | ✅ `MATCHMAKER_COMPLETE` 但 operatorId 传 null(L222)——审计丢失操作者 |
|
|||
|
|
| 取消 cancel | ❌ 死代码且无审计 |
|
|||
|
|
| 查看敏感详情 getServiceDetail | ❌ 不写 SensitiveAccessLog、不调 recordSensitiveAccess(对照:ReviewService L470-477 访问本地资格佐证时写 SensitiveAccessLog;AuditLogService.recordSensitiveAccess L74-88 已有现成方法) |
|
|||
|
|
| 模块内归属拒绝(recommend/followup 的 IllegalStateException、ReferralService 的 IllegalStateException) | ❌ 不走 DataScopeGuard,不产生 DATA_SCOPE_DENIED;仅裸抛异常 → **服务层越权拒绝不落审计**,违反 MM-AUDIT-002 |
|
|||
|
|
| 缺失字段 | 审计无 purpose_code/service_assignment_id/actor_role_snapshot/request_trace_id(DEC-006 第八节字段要求),AuditLog 实体不含这些列 |
|
|||
|
|
|
|||
|
|
### 2.7 可复用模块
|
|||
|
|
|
|||
|
|
| 组件 | 位置 | 与 A1/B1 的关系 |
|
|||
|
|
|------|------|----------------|
|
|||
|
|
| **CounselingService 的 INV-012 判定链路** | `counseling/service/CounselingService.java` L181-200(assign 用条件更新 assignIfPending 防并发)、assertAssigned 模式(L205/232/242 各写接口前置)、`GET /counseling/services/operator/assigned`(Controller L141-145 → assignedTo) | **与 A1 最接近**:"只有被分配人可见/可操作" 的完整范式,含权限矩阵注释(L38-48 定义 6 种角色的列表/脱敏/全文视图)。红娘"本人有效分配对象"可直接套此模式 |
|
|||
|
|
| **DataScopeGuard** | `shared/security/DataScopeGuard.java` L21-36 | 拒绝+审计一体,activity/counseling/service_appointment 已用;红娘模块的归属拒绝应统一接入 |
|
|||
|
|
| **AuthorizationAuditListener** | `shared/security/AuthorizationAuditListener.java` | @PreAuthorize 拒绝自动审计,MM-AUDIT-002 的 403 层已覆盖 |
|
|||
|
|
| **AuditLogService.recordSensitiveAccess + SensitiveAccessLog** | `audit/service/AuditLogService.java` L74-88;使用范例 `review/service/ReviewService.java` L470-477 | 受限字段/资料查看审计现成方法(matchmaker 详情接口应调用) |
|
|||
|
|
| **MatchmakerServiceMapper.findActiveByOperatorAndUser** | `matchmaker/mapper/MatchmakerServiceMapper.java` L48-54 | 现成的"本人名下+该用户+有效状态"判定 SQL,是未来 ServiceAssignmentPolicy 判定的雏形(状态集含 submitted 待收紧) |
|
|||
|
|
| **OperatorAuthService 权限装载** | `auth/service/OperatorAuthService.java` L98-108(权限集合仅登录时装载进 JWT claims) | 权限事实来源;同时是权限回收的**风险点**(见 P1-7) |
|
|||
|
|
| 用户端归属校验范式 | `MatchmakerRecommendationMapper.findByIdAndUser` + `MatchmakerService.acceptRecommendation` L213-222 | "资源归属=当前主体"校验样板 |
|
|||
|
|
|
|||
|
|
### 2.8 前端现状
|
|||
|
|
|
|||
|
|
- `MatchmakerManagementView.vue`:服务请求队列 tab 对所有 `matchmaker.view` 用户展示全库 pending(userId+需求说明,L7-25);**分配弹窗红娘下拉是硬编码假数据** `operator_wang/operator_chen`(L47-50)——分配功能前端不可真实使用;推荐弹窗需手输 targetUserId(L62)。
|
|||
|
|
- 路由/菜单:`router/index.ts` L40-41、`MainLayout.vue` L177-178 权限码已与后端对齐(matchmaker.view / matchmaker.service,V052 注释所述错配已修复)。
|
|||
|
|
- `ReferralManagementView.vue` L35 已改走 `/referrals/operator/history`(契约缺口已修)。
|
|||
|
|
|
|||
|
|
### 2.9 治理文档现状
|
|||
|
|
|
|||
|
|
- `docs/governance/decision-register.md`:grep `DEC-MATCHMAKER|matchmaker` 无结果【缺失】。
|
|||
|
|
- `docs/governance/open-questions.md`:无 matchmaker/电话/线下联系条目【缺失】。
|
|||
|
|
- `docs/PENDING_DECISIONS.md`:无 A1/B1/matchmaker 条目【缺失】。
|
|||
|
|
- `docs/audits/` 目录不存在(本报告即首次建立)。
|
|||
|
|
|
|||
|
|
### 2.10 现有测试
|
|||
|
|
|
|||
|
|
| 测试 | 覆盖 | 缺口 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `MatchmakerReferralPhase13Test` | MM-ACTION 方向较完整:推荐不产生 Like/Match(L132-141)、只有用户能 Like(L143)、红娘不能代用户接受(L158-171)、转介归属校验(L288-296)、用户本人确认(L225-269) | 不含数据范围/联系方式/审计断言 |
|
|||
|
|
| `MatchmakerServiceTest` | 仅校验常量与 DB CHECK 白名单一致(L94-177),**纯守卫测试,无权限断言** | — |
|
|||
|
|
| `PermissionMatrixTest` | 静态 Map 自断言(L25-224 + L229 起),**不连接 DB/迁移文件**,且期望值(matchmaker 有 user.view=true,L119)与 V052 实际授权(无 user.view)漂移 | 矩阵不校验真实授权,是"看起来有保护"的假阳性 |
|
|||
|
|
| `OperatorEndpointPermissionCoverageTest` | 机械守卫:确保运营端 Controller 方法有 @PreAuthorize(L10、L43) | 只查注解存在性,不校验权限码正确性 |
|
|||
|
|
| SecurityTests / V11InvariantsTest | 通用授权拒绝、INV 守卫 | 无 MM-AUTH/CONTACT/PRIVACY/AUDIT/LIFECYCLE 专属场景 |
|
|||
|
|
|
|||
|
|
**测试基线覆盖判定**:MM-ACTION ≈ 部分覆盖;MM-AUTH/MM-CONTACT/MM-PRIVACY/MM-AUDIT/MM-LIFECYCLE ≈ 全部缺失。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 可复用模块清单
|
|||
|
|
|
|||
|
|
(按建议采用优先级)
|
|||
|
|
1. **CounselingService INV-012 范式**(`CounselingService` L181-200 条件更新分配、assertAssigned 写接口前置、Controller L141-145 assigned 列表)——A1"本人有效分配对象"的直接模板。
|
|||
|
|
2. **DataScopeGuard**(`shared/security/DataScopeGuard.java`)——归属拒绝统一走 deny()=审计+403。
|
|||
|
|
3. **AuthorizationAuditListener**——已有,无需新建。
|
|||
|
|
4. **AuditLogService.recordSensitiveAccess + SensitiveAccessLog**(ReviewService L470-477 为使用范例)——受限字段查看审计。
|
|||
|
|
5. **MatchmakerServiceMapper.findActiveByOperatorAndUser**——分配有效性判定 SQL 雏形。
|
|||
|
|
6. **OperatorAuthService/JwtAuthFilter 权限装载链**——若做权限回收需改造此链路(JWT claims 快照问题)。
|
|||
|
|
7. 无 MatchmakerPolicy/PrivacyPolicy/ContactAuthorizationPolicy 任何现存实现——需新建。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 差距与风险清单(按优先级)
|
|||
|
|
|
|||
|
|
### P0(违反 LOCKED 决策,阻断 DoD)
|
|||
|
|
|
|||
|
|
| # | 差距 | 违反 | 影响 | 证据 |
|
|||
|
|
|---|------|------|------|------|
|
|||
|
|
| P0-1 | 服务对象分配未建模:无 matchmaker_assignments/独立分配实体,分配=工单指派且无 assigned_by/理由/有效期/结束语义/分配历史 | DEC-002 规则6/7、5.1 概念模型 | A1 无法落地;分配不可追溯、不可回收、不可过期 | V009 L5-20;全仓 grep 无 assignment 实体 |
|
|||
|
|
| P0-2 | 普通红娘认领入口开放且无归属校验:claim 任意 assigned 单;followup/complete 对任意 serviceId 可写/可完结 | DEC-002 规则1/2;DEC-005;MM-AUTH-004 | 任何红娘可干预他人服务对象、写入跟进内容、变更服务状态 | MatchmakerOperatorController L49-56、L99-118;MatchmakerServiceService L109-122、L186-203、L208-224 |
|
|||
|
|
| P0-3 | 未分配申请队列+全局统计对全体红娘可见(含申请人 userId 与诉求描述) | DEC-002 规则1;DEC-005;MM-AUTH-002/PRIVACY-001 精神 | 普通红娘可浏览"待分配用户库"挑选对象,正面违反 A1 | MatchmakerOperatorController L32-36;MatchmakerServiceService L245-258、L260-276;Mapper findSubmitted L17-18 |
|
|||
|
|
| P0-4 | 无红娘组长角色与团队管理范围概念,assign 权限无范围约束(谁能 assign 只有权限码、无组织/团队边界;organizationId 恒传 null) | DEC-002 规则4/5/8 | 分配权无法收敛到"平台授权人员或组长";跨团队数据范围无依据 | MatchmakerOperatorController L38-47(organizationId=null);全仓无 leader 角色 |
|
|||
|
|
| P0-5 | 服务层归属/范围拒绝不产生审计(不走 DataScopeGuard),claim 无审计、complete 审计丢操作者、分配审计字段残缺 | DEC-006;MM-AUDIT-001/002 | 分配与越权链条不可追溯 | MatchmakerServiceService L102/L109-122/L222/L395-403;DataScopeGuard 使用方不含 matchmaker |
|
|||
|
|
|
|||
|
|
### P1(安全机制缺口,DoD 必补)
|
|||
|
|
|
|||
|
|
| # | 差距 | 违反 | 影响 | 证据 |
|
|||
|
|
|---|------|------|------|------|
|
|||
|
|
| P1-6 | 非负责人可读任意服务单脱敏详情+他人推荐列表(targetUserId、推荐理由),泄露存在性与他人服务内容 | DEC-005(响应不得泄露目标用户是否存在);MM-PRIVACY-001 | 横向探测面 | MatchmakerServiceService L301-351(L317-327 对非 owner 装配 recommendations) |
|
|||
|
|
| P1-7 | 运营权限是登录时 JWT claims 快照(JwtAuthFilter L90-99 直接取 claims),停用/调岗/权限回收后旧 token 有效期内仍放行 | DEC-005/006(权限及时回收);MM-LIFECYCLE-001 | 权限回收延迟=token 剩余有效期 | OperatorAuthService L98-108;JwtAuthFilter L90-99 |
|
|||
|
|
| P1-8 | 受限资料查看无 SensitiveAccessLog(服务详情 userProfile 属受限资料但零记录) | DEC-006;MM-AUDIT 方向 | 敏感读取不可溯源 | MatchmakerServiceService L329-348;对照 ReviewService L470-477 |
|
|||
|
|
| P1-9 | 无统一 Policy 层(MatchmakerPolicy/DataScopePolicy/PrivacyPolicy),范围校验散落且口径不一 | DEC-005 判定链要求 | 后续每加一个接口都可能漏校验(现状已证明) | §2.3 表格 |
|
|||
|
|
| P1-10 | B1 联系授权机制完全缺失(无表无策略无审批流),当前安全是"功能不存在"而非"有护栏" | DEC-003 规则7 | 一旦有人新增电话/线下功能即裸奔;DoD "无授权电话/线下联系被拒绝" 无承载 | grep ContactAuthorization 无结果 |
|
|||
|
|
| P1-11 | 测试基线缺口:MM-AUTH/CONTACT/PRIVACY/AUDIT/LIFECYCLE 无场景测试;PermissionMatrixTest 与真实授权漂移且自引用 | 决策文档第十节 | 回归无保护网 | §2.10 |
|
|||
|
|
|
|||
|
|
### P2(质量/体验/治理)
|
|||
|
|
|
|||
|
|
| # | 差距 | 影响 | 证据 |
|
|||
|
|
|---|------|------|------|
|
|||
|
|
| P2-12 | 分配弹窗硬编码假红娘(operator_wang/chen),分配功能前端不可用 | 主流程断裂 | MatchmakerManagementView.vue L47-50 |
|
|||
|
|
| P2-13 | 推荐对象需手输 userId,无受控候选来源,推荐主流程实际不可用 | 业务不可用(但不可用方向是安全的) | 同上 L62 |
|
|||
|
|
| P2-14 | dashboard inProgress 统计用 'processing' 与 DB 白名单 'in_service' 不符,恒为 0 | 数据失真 | MatchmakerServiceMapper L28-30 vs V009 L30-32 |
|
|||
|
|
| P2-15 | cancelService 死代码;matchmaker.assign 的 organizationId 参数恒 null | 代码卫生 | MatchmakerServiceService L229-237;Controller L46 |
|
|||
|
|
| P2-16 | 治理文档无 DEC-MATCHMAKER/A1/B1 条目;docs/audits 目录此前不存在 | 治理不同步(ZC-MM-007 范围) | §2.9 |
|
|||
|
|
| P2-17 | PermissionMatrixTest 期望 platform_admin/reviewer 等角色矩阵,但迁移未定义这些角色的 matchmaker 授权,实际库状态无法确定 | 授权事实不清 | V052 仅授 role 005;V044 仅补 super_admin |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 仍需后续确认的实现细节(对应决策文档第十一节,仓库现状)
|
|||
|
|
|
|||
|
|
| 待确认项 | 仓库现状 |
|
|||
|
|
|---------|---------|
|
|||
|
|
| 电话/线下联系例外批准岗位 | 无任何授权机制/审批流/岗位模型(grep 无结果);需产品确认后从零建 |
|
|||
|
|
| 例外授权是否需用户单独同意及记录方式 | 无载体;`activity_registration`/`service_appointment` 已有 `user_confirmed_at`+`source=matchmaker_referral` 的用户确认范式(ReferralService L151-152)可作参考 |
|
|||
|
|
| 临时授权有效期限 | 无 expires_at/有效期字段于任何红娘相关表(matchmaker_services 也没有);`matchmaker_recommendations` 无过期机制 |
|
|||
|
|
| 审计日志/服务摘要保留周期 | 迁移中未见 audit_logs 的保留/清理策略;account 模块有 DataRetentionService(`account/service/DataRetentionService.java`)可参考但未覆盖审计日志【保留周期无法确定】 |
|
|||
|
|
| 红娘组长可分配的组织/团队边界 | `organizations` 表与 `matchmaker_services.organization_id` 存在(V009 L11),operator 与组织的关联关系存在于 operator 体系,但"团队/红娘组"概念不存在,边界配置无从谈起 |
|
|||
|
|
| platform_admin/organization_admin 的 matchmaker 权限实际授权 | 迁移不可证【无法确定,需查演示库】 |
|
|||
|
|
| 转介代建 pending 报名/预约与"代填关键信息"边界 | 见 §2.5 灰色点,建议 ZC-MM-004 复核时与产品确认表述 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 对 ZC-MM-002~007 实施顺序的建议(仅顺序与依赖)
|
|||
|
|
|
|||
|
|
1. **ZC-MM-002(A1 分配)先行且作为一切的地基**:必须先落 `matchmaker_assignments` 独立分配关系(含 assigned_by/reason/status/expires/ended)+ 红娘组长角色 + 分配状态机。理由:P0-1~P0-4 全部依赖此建模;后续所有数据范围判定都以"有效分配"为事实源。
|
|||
|
|
2. **ZC-MM-005(统一数据范围与字段隐私)紧跟 002**:把 claim/followup/complete/pending/dashboard/详情统一接入范围判定(复用 CounselingService INV-012 + DataScopeGuard)。依赖 002 的分配事实源;不先做会继续在旧工单模型上打补丁。
|
|||
|
|
3. **ZC-MM-003(B1 联系规则)与 004(禁止代操作复核)可并行**:003 当前主要是"补护栏+固化现状"(关闭暴露面+建立联系授权的拒绝默认),004 主要是复核与测试锁定(现状已基本合规)。两者都不依赖 002 的表结构,但 003 的"平台内服务通知渠道"若要做成会话类功能,建议在 005 稳定后做。
|
|||
|
|
4. **ZC-MM-006(审计与权限回收)在 002/005 之后收口**:分配/转交审计、SensitiveAccessLog 接入、模块内拒绝走 DataScopeGuard 都依赖 002 的新实体与 005 的判定点;JWT 权限回收(P1-7)涉及 auth 链路改造,放在业务语义稳定之后风险最小。
|
|||
|
|
5. **ZC-MM-007(治理文档)最后**:登记 DEC-MATCHMAKER-001~006 与 A1/B1、open-questions(第十一节五项)、本报告归档于 docs/audits。
|
|||
|
|
6. **贯穿性建议**:每阶段同步补 MM-AUTH/CONTACT/ACTION/PRIVACY/AUDIT/LIFECYCLE 测试基线(当前几乎为零),并把 PermissionMatrixTest 从静态自引用改为对迁移/真实授权的校验——否则 P1-11 的假阳性会掩盖回归。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
*(审计完毕。本报告由架构师产出、主理人汇编落盘。后续实施需 Owner 授权后按 ZC-MM-002~007 分阶段进行。)*
|