7.4 KiB
运营敏感数据临时访问开发说明
对应接口契约:OpenAPI 0.55.0
对应数据库迁移:V27
适用范围:指定客户组织内、指定单次通话的限时字幕读取与原录音播放
1. 目标与边界
运营账号默认只能在诊断工作台查看设备、通话、供应商任务、错误码和配置版本等脱敏状态,不能通过家庭角色或运营角色直接读取字幕正文或取得录音地址。确需排障时,申请人必须选择单次通话、最小能力、明确理由和到期时间,由另一名审批人员决定;批准后也只能由原申请人在有效期内使用。每次成功读取和每次被拒绝的尝试均独立记录,不把字幕或录音地址写入审计正文。
能力只有两种:
TRANSCRIPT_READ:读取指定通话已经落库的最终话轮;RECORDING_PLAY:查看该通话已验证录音分片,并为单个分片现场签发5分钟播放地址。
单次申请至少5分钟、最长4小时。申请不支持全租户、全设备、全长者或长期通配范围,不提供录音下载能力。录音播放除临时批准外,仍要求原始录音存储授权当前有效。
2. 角色与权限
| 角色 | 申请 | 查看队列 | 审批 | 使用批准权限 |
|---|---|---|---|---|
ops_viewer |
是 | 仅本人 | 否 | 仅本人已批准申请 |
ops_operator |
是 | 仅本人 | 否 | 仅本人已批准申请 |
ops_approver |
是 | 当前客户组织 | 是,禁止自批 | 仅本人已批准申请 |
ops_admin |
是 | 当前客户组织 | 是,禁止自批 | 仅本人已批准申请 |
审批角色不能借用申请人的批准,也不能批准自己提交的申请。账号被停用或平台角色退回customer后,即使申请尚未到期也无法继续使用。
3. 业务流程
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. 验证
$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、临时单通话和另一用户的待审批申请夹具,验证未登录深链在认证后恢复到联调诊断、创建申请的空理由/能力/有效期门禁,以及批准和拒绝时申请上下文、版本、依据、最小能力复核与取消清理。脚本要求桌面页面无横向溢出、控制台错误或失败请求,并明确拦截、断言敏感访问创建和审批请求均为零,不实际提交或审批,不替代上面的双账号服务集成验证。