Files
story/docs/ops-sensitive-access.md
T

100 lines
7.4 KiB
Markdown

# 运营敏感数据临时访问开发说明
> 对应接口契约:OpenAPI 0.55.0
> 对应数据库迁移:V27
> 适用范围:指定客户组织内、指定单次通话的限时字幕读取与原录音播放
## 1. 目标与边界
运营账号默认只能在诊断工作台查看设备、通话、供应商任务、错误码和配置版本等脱敏状态,不能通过家庭角色或运营角色直接读取字幕正文或取得录音地址。确需排障时,申请人必须选择单次通话、最小能力、明确理由和到期时间,由另一名审批人员决定;批准后也只能由原申请人在有效期内使用。每次成功读取和每次被拒绝的尝试均独立记录,不把字幕或录音地址写入审计正文。
能力只有两种:
- `TRANSCRIPT_READ`:读取指定通话已经落库的最终话轮;
- `RECORDING_PLAY`:查看该通话已验证录音分片,并为单个分片现场签发5分钟播放地址。
单次申请至少5分钟、最长4小时。申请不支持全租户、全设备、全长者或长期通配范围,不提供录音下载能力。录音播放除临时批准外,仍要求原始录音存储授权当前有效。
## 2. 角色与权限
| 角色 | 申请 | 查看队列 | 审批 | 使用批准权限 |
|---|---:|---:|---:|---:|
| `ops_viewer` | 是 | 仅本人 | 否 | 仅本人已批准申请 |
| `ops_operator` | 是 | 仅本人 | 否 | 仅本人已批准申请 |
| `ops_approver` | 是 | 当前客户组织 | 是,禁止自批 | 仅本人已批准申请 |
| `ops_admin` | 是 | 当前客户组织 | 是,禁止自批 | 仅本人已批准申请 |
审批角色不能借用申请人的批准,也不能批准自己提交的申请。账号被停用或平台角色退回`customer`后,即使申请尚未到期也无法继续使用。
## 3. 业务流程
```mermaid
sequenceDiagram
participant R as 申请人
participant API as 运营API
participant A as 另一名审批人
participant DB as PostgreSQL
participant S as 私有对象存储
R->>API: 通话+能力+理由+到期时间+幂等键
API->>DB: 固定租户和通话,写PENDING申请与审计
A->>API: APPROVE/REJECT+审批理由
API->>DB: 校验非本人、未到期、仍为PENDING
API->>DB: 写决定、审批人、时间与审计
R->>API: 申请编号+指定通话读取字幕
API->>DB: 再校验申请人/租户/通话/能力/状态/有效期
API->>DB: 写SUCCESS或DENIED逐次访问日志
API-->>R: 最终字幕及批准范围内的录音分片编号
R->>API: 申请编号+单个分片播放
API->>DB: 再校验RECORDING_PLAY和原始录音授权
API->>S: 现场签发5分钟只读播放地址
API->>DB: 写逐次访问日志与审计
API-->>R: 短期播放地址
```
到期判断发生在列表刷新和每次实际访问时。到期申请会进入`EXPIRED`,此后不再返回正文或签发地址;服务不依赖前端计时器实现安全边界。
### 3.1 页面申请流程
联调诊断工作台不使用浏览器弹窗采集申请信息。运营人员先通过精确编号定位一条通话,再在该诊断结果下打开页面内“临时敏感访问申请”表单。表单固定绑定当前通话,不能改成租户、设备或长者范围;页面同时明确提示“提交申请不等于获得访问权限”,必须等待另一名审批人处理。
- “读取本次通话最终字幕”是本页面排障流程的必选能力,默认选中且不可取消;
- “同时申请本次通话原录音播放”由申请人按实际需要选取,不勾选时不申请录音能力;
- 排障理由必填、最长500字符,空理由不能提交;
- 有效期只能选择15分钟、1小时或4小时,服务端仍执行最长4小时校验;
- 取消申请会关闭表单并清空当前草稿,不创建申请;
- 页面不提供录音下载选项,批准后的播放仍按单分片签发5分钟地址。
审批角色从待审批卡片打开页面内审批区,不再使用浏览器输入弹窗。审批区冻结申请公开编号、版本、申请人、通话、能力、到期时间和原始理由,避免审批过程中列表刷新或选择变化造成目标漂移。批准与拒绝依据均必填、最长500字符:批准在填写依据后还必须确认申请人不是本人,并已复核单次通话、最小能力和到期时间;拒绝需要说明能力范围、理由或有效期不满足要求的具体原因。取消或刷新申请列表会清除审批草稿,不调用审批接口。页面隐藏本人申请的审批按钮,服务端仍会再次校验禁止自批、审批角色、`PENDING`状态、租户、版本和到期时间。
## 4. 接口设计
所有接口要求OIDC Bearer令牌和`X-Tenant-Id`。创建与审批还要求`Idempotency-Key`;敏感读取响应统一`Cache-Control: no-store`。
| 方法与路径 | 说明 |
|---|---|
| `GET /api/v1/ops/sensitive-access-requests` | 本人申请或审批队列 |
| `POST /api/v1/ops/sensitive-access-requests` | 创建单通话、限能力、限时申请 |
| `POST /api/v1/ops/sensitive-access-requests/{id}/review` | 另一名审批人批准或拒绝 |
| `GET /api/v1/ops/sensitive-conversations/{id}` | 使用`X-Ops-Access-Grant`读取最终字幕 |
| `POST /api/v1/ops/sensitive-media/{id}/play` | 使用批准申请签发单分片5分钟播放地址 |
稳定错误包括`TEMPORARY_ACCESS_DENIED`、`TEMPORARY_ACCESS_STATE_CONFLICT`、`RESOURCE_NOT_FOUND`和幂等冲突。缺少申请编号、能力不匹配、审批未完成、申请人与调用人不同、到期、跨租户或原始录音授权失效均不会降级放行。
## 5. 数据库设计
`ops_sensitive_access_request`保存申请公开编号、租户、通话、申请人、规范化能力数组、理由、申请到期时间、状态、审批人、审批理由、审批时间和版本。部分唯一索引保证同一申请人对同一通话最多只有一个`PENDING/APPROVED`申请。
`ops_sensitive_access_log`追加保存申请、调用人、通话、可选录音分片、能力、`SUCCESS/DENIED`结果、稳定拒绝原因、接口请求关联编号和时间,不保存字幕正文、对象路径或播放地址。通用`audit_event`另保存申请、审批和逐次访问动作,支持按请求编号还原操作链。
## 6. 验证
```powershell
$env:TEST_DATABASE_URL='postgres://voice:voice_dev_only@localhost:55432/voice_platform?sslmode=disable'
go test ./internal/opsaccess -count=1 -cover
pwsh -NoProfile -File .\scripts\smoke-ops-sensitive-access.ps1
```
服务集成测试覆盖申请、自批拒绝、错误使用人拒绝、另一人审批、字幕读取、录音短链、原申请到期和成功/拒绝日志。HTTP烟测使用本地真实OIDC管理员与审批账号,验证双账号审批和逐次审计后精确清理夹具。生产验收仍需用实际客服组织、值班审批账号、真实录音对象和审计留存策略复测。
页面改动采用轻量可用性检查:`test/e2e/visual-ops-sensitive-access-request.mjs`使用真实本地OIDC、临时单通话和另一用户的待审批申请夹具,验证未登录深链在认证后恢复到联调诊断、创建申请的空理由/能力/有效期门禁,以及批准和拒绝时申请上下文、版本、依据、最小能力复核与取消清理。脚本要求桌面页面无横向溢出、控制台错误或失败请求,并明确拦截、断言敏感访问创建和审批请求均为零,不实际提交或审批,不替代上面的双账号服务集成验证。