外观
2026-09-20 迭代 — 验收用例(TC)
依据 PRD 原文 与 技术方案。PRD 定义产品行为,技术方案定义实现契约; 本文只把两者转成可重复执行、可留证据的判定条件,不另行扩大范围。 本文与 PRD、技术方案一同评审;预跑与证据归档由 T01(lamolabs-docs#77) 执行, 预跑完成后在 §8 回填结果与证据链接。
1. 执行规则
- 编号含义:
P= 端到端主路径,B= 边界与安全,N= 非功能硬阈值,MTG= 上会用例子集。 编号一经发布不得复用或重排,后续任务的「测试追踪」按本编号引用。 - 会上只执行 §3 的 8 条,每条判定时间不超过 3 分钟;其余用例在 2026-09-19 预跑阶段完成。
- 一条用例的全部判定条件均满足才算通过;部分满足、无证据或只能口头说明都记为不通过。
- 因环境、资产或评审结论缺失而无法执行时记为阻塞,不允许把"未测"写成"通过"。
- 涉及时间的用例以服务端时间为准;毫秒级阈值(
N01/N03)用服务端毫秒时间戳差计算,不用人工秒表。 - 三项硬阈值必须实测取数,不得估算;数据由预跑阶段采集,会上只复核证据。
- 鉴权判定类用例必须同时断言
allow与denyReason两个字段,只看allow不算通过。 - 允许用缩短周期的验收配置(如
PERM_RECONCILE_INTERVAL_SECONDS=10)模拟兜底场景,但必须同时证明生产默认值(300)仍正确配置。
1.1 本期冻结的验收基线
| 项目 | 验收值 |
|---|---|
| 鉴权判定顺序 | ① 主体在职 → ② 显式拒绝 → ③ 权限点/命名空间/组织状态 → ④ 直接授权 → ⑤ 角色路径;任一前置失败即拒,不进后续 |
| 拒绝原因枚举 | subject_inactive / deny_hit / pp_invalid / no_grant / exception,SDK 原样透传 |
| 鉴权性能 | p99 < 10ms(服务端计时;压测数据集 100 主体 × 50 角色 × 500 权限点,连续 1000 次混合请求) |
| 判定超时 | PERM_CHECK_STATEMENT_TIMEOUT_MS=200,超时按 exception 拒绝 |
| 组合深度上限 | PERM_COMPOSITION_MAX_DEPTH=5,创建时强拒不截断 |
| 定时兜底周期 | PERM_RECONCILE_INTERVAL_SECONDS=300(≤5min) |
| SDK 连接层兜底 | 连接错误/超时/非 2xx/畸形响应 → {allow:false, denyReason:'exception'},不抛异常;SDK 超时 PERM_CHECK_TIMEOUT_MS=2000 |
| 单有效行 | UNIQUE(subject_type, external_id) WHERE active;epoch 首建 0,停用不递增,启用/调岗新建行递增 |
| 通用 deny 清除 | 作废旧有效行(停用/调岗)同事务清除该用户通用权限显式拒绝,删除快照进 audit_outbox |
| 审计事件 | 4 类(grant_change/check_deny/pp_change/role_change)100% 产生 outbox 行;只记 deny 不记 allow;deny 类异步投递不阻塞主流程 |
| 不可变字段 | 权限点 scope/org_id、角色 org_id、app 主体 bound_namespace_id:应用层错误码 IMMUTABLE_FIELD + DB 触发器双层拒改 |
| 错误码 | 以技术方案 §9.1 错误码表为准,用例断言具体 code 而非只看 HTTP 状态 |
2. 环境前置
nexo-perm-api(test 环境,3002)+nexo_perm库迁移与初始化脚本已执行,超管(PERM_SUPERADMIN_USER_ID)可登录管理台nexo-perm-console已部署并经壳(nexo-app)入口可达;独立访问方式亦可登录nexo-accounttest 实例可用(供主体创建校验与状态通知联动);具备可自由停用/启用的测试账号 ≥3 个- 造数与压测脚本随证据归档到
evidence/scripts/,结构化取数落evidence/logs/,管理台截图落evidence/console/ - 只读
psql访问nexo_perm(断言 subjects.epoch、outbox、inbox 用)
3. 上会用例(MTG 子集,每条 ≤3 分钟)
P01 P03 P05 B01 B03 B06 B09 N01 共 8 条;其余在 09-19 预跑完成,会上只复核证据。
4. 主路径用例(P01—P10)
P01 业务方接入全链路(组织→命名空间→权限点→角色→授权→鉴权)
- 前置条件:超管已登录管理台;
nexo-account有 ACTIVE 测试账号u_t1。 - 操作步骤:① 建组织
org-a;② 建命名空间ns-task与 app 主体(绑ns-task);③ 在ns-task注册组织权限点task.close(scope=org,绑org-a);④ 建角色任务操作员(org-a)并关联task.close;⑤ 建 user 主体u_t1(主组织 org-a)并分配角色;⑥ 以 app 主体身份调/internal/check {userId:'u_t1', permissionPoint:'task.close'}。 - 预期结果:⑥ 返回
{allow:true}(无denyReason字段);①-⑤ 每步写操作在audit_outbox各有对应事件行(pp_change/role_change/grant_change);subjects中u_t1行epoch=0, active=true。 - 证据采集:各步请求/响应 JSON;
audit_outbox按dedupe_key的psql断言输出;check 响应原文。 - 对应:PRD FR-1/2/3/5/6/7 · 上会
MTG-01
P02 直接授权链路与有效期到期失效
- 前置条件:P01 环境;
u_t1未经角色持有ns-task下另一权限点task.export。 - 操作步骤:① 注册
task.export(org 权限,org-a);② 给u_t1直接授权task.export,expiresAt= now+7d;③ check → 记录;④psql将该行expires_at改为 now-1s;⑤ 再 check。 - 预期结果:③
{allow:true}(④ 直接授权命中,不经角色);⑤{allow:false, denyReason:'no_grant'}(过期行在判定中不生效,无需物理清理)。 - 证据采集:两次 check 响应;
direct_grants行前后对比。 - 对应:PRD FR-6/7 · 总 PRD §4.4
P03 显式拒绝优先于放行
- 前置条件:P01 完成(
u_t1经角色已有task.close)。 - 操作步骤:① check 确认 allow;② 给
u_t1设置task.close显式拒绝;③ check;④ 解除显式拒绝;⑤ check。 - 预期结果:③
{allow:false, denyReason:'deny_hit'}(角色路径存在仍拒);⑤ 恢复{allow:true};②④ 各产生grant_changeoutbox 行,其中解除事件为异步投递口径(delivered_at可后置但行必存在)。 - 证据采集:三次 check 响应;outbox 两行断言。
- 对应:PRD FR-6/7 · 上会
MTG-02
P04 角色组合动态聚合与实时生效
- 前置条件:org-a 下有角色
R-base(关联task.close)与空角色R-comp。 - 操作步骤:①
R-comp组合R-base;② 给u_t2(org-a)分配R-comp;③ checktask.close;④ 给R-base新关联task.export;⑤ 立即 checktask.export(不做任何刷新/等待);⑥ 调GET /api/roles/{R-comp}/effective-permissions。 - 预期结果:③⑤ 均
{allow:true}(来源变更自动跟随,实时生效);⑥ 聚合预览含两个权限点且via:'composed'标注来源R-base,与判定结果一致。 - 证据采集:check 响应(带服务端时间戳证明④⑤间隔 <5s);聚合预览响应。
- 对应:PRD FR-4 · 总 PRD §4.2
P05 账号停用联动(通知路径)
- 前置条件:
u_t1为 ACTIVE 且 P03 后持有task.close权限与一条通用权限显式拒绝(先建通用权限点platform.demo并对u_t1设 deny)。 - 操作步骤:① 记录
subjects当前行(id/epoch);② 在nexo-account停用u_t1;③ 等通知消费(≤5s)后 checktask.close;④psql断言主体行与explicit_denies。 - 预期结果:③
{allow:false, denyReason:'subject_inactive'};④ 原行active=false且 epoch 不变、无新行;u_t1的通用权限 deny 行已删除(org 权限 deny 保留);outbox 含 deny 清除快照事件与联动告警事件。 - 证据采集:停用前后
subjects/explicit_denies对比;check 响应;inbox 行(event_type='disabled', processed_at非空)。 - 对应:PRD FR-9 · 上会
MTG-03
P06 重新启用(复职)走新建行
- 前置条件:P05 后
u_t1处于失活。 - 操作步骤:① 在
nexo-account重新启用u_t1;② 消费后psql查subjects全历史;③ checktask.close。 - 预期结果:② 出现新行
active=true, epoch=旧+1,旧行保持active=false;③{allow:false, denyReason:'no_grant'}——旧行上的角色关联不复活,需重新分配(重新分配后 check 恢复 allow,作为附加断言)。 - 证据采集:
subjects历史行输出;两次 check 响应。 - 对应:PRD FR-9 · 总 PRD §4.7
P07 主组织变更(调岗)后旧组织权限失效
- 前置条件:
u_t3(org-a)经角色持有 org-a 组织权限点,且有一条通用权限显式拒绝;组织org-b已建。 - 操作步骤:① 触发
org_changed通知(orgId=org-b);② 消费后psql断言;③ check 旧 org-a 组织权限点。 - 预期结果:② 新行
org_id=org-b, epoch+1,旧行失活;通用 deny 已清除;③{allow:false}(no_grant:新有效行组织与 pp 归属不符,且旧行角色关联已脱钩)。 - 证据采集:
subjects历史、explicit_denies前后、check 响应。 - 对应:PRD FR-9 · 总 PRD §4.1/§4.7
P08 权限点下线与同名重建(激活原实体)
- 前置条件:
task.close上有一条对u_t2的显式拒绝。 - 操作步骤:① 下线
task.close;②u_t2check;③ 同名重新激活(POST .../activate);④u_t2check。 - 预期结果:②
{allow:false, denyReason:'pp_invalid'};③ 为同一实体(id 不变)翻回 active;④{allow:false, denyReason:'deny_hit'}——原显式拒绝保留永效、继承命中;outbox 含两条pp_change。 - 证据采集:权限点 id 前后一致的断言;两次 check 响应。
- 对应:PRD FR-2 · 总 PRD §4.3
P09 硬删除连删与同名重建(全新实体)
- 前置条件:
task.export下存在显式拒绝 1 条、直接授权 1 条、角色关联 1 条。 - 操作步骤:① 调 impact 接口记录 N/M/K;② 带
confirmCode硬删;③psql断言三关联表;④ 同名重新注册task.export;⑤ 原被 deny 用户 check。 - 预期结果:① N/M/K 与实际一致(1/1/1);③ 三表对该 pp 零残留(CASCADE);④ 新实体(新 id);⑤
{allow:false, denyReason:'no_grant'}——旧 deny 不继承(不是 deny_hit);outbox 含 deny 快照事件(异步口径)。 - 证据采集:impact 响应;删除前后三表计数;新旧 id 对比;check 响应。
- 对应:PRD FR-2 · 总 PRD §4.3
P10 管理台主流程走查(F02—F08)
- 前置条件:test 环境管理台可达;壳内与独立访问均已登录。
- 操作步骤:按 F02 组织/命名空间 → F03 权限点 → F04 角色与组合 → F05 授权 → F06 主体 → F08 我的权限顺序,每页完成一次主操作闭环;构造一次后端业务拒绝(如跨组织分配)观察提示。
- 预期结果:每页主操作成功且列表即时刷新;业务拒绝显示明确中文提示(非透传英文 code/500);F08 页面无任何写操作入口;壳内切换到 IM 再切回状态不丢。
- 证据采集:每页操作后截图(
evidence/console/);拒绝提示截图。 - 对应:PRD F 系列任务 · F07 壳挂载
5. 边界与安全用例(B01—B14)
B01 判定顺序短路(第一步优先)
- 前置:失活主体
u_x同时存在:显式拒绝 1 条 + 未过期直接授权 1 条。 - 步骤:check 该用户任一相关权限点。
- 预期:
{allow:false, denyReason:'subject_inactive'}——不是deny_hit(证明①先于②执行)。 - 证据:check 响应 +
subjects/explicit_denies/direct_grants三表状态断言。 - 对应:总 PRD §4.5 · 上会
MTG-04
B02 组合成环三型强拒
- 步骤:分别尝试建:①
A组合A(自引用);② 已有A→B再建B→A;③ 已有A→B→C再建C→A。 - 预期:三次均 HTTP 4xx,
code='COMPOSITION_CYCLE';role_compositions无新增行;outbox 各有role_changerejected 事件。 - 证据:三次响应 + 表计数 + outbox 断言。
- 对应:PRD FR-4
B03 深度上限强拒(不截断)
- 前置:
PERM_COMPOSITION_MAX_DEPTH=5;已构造深度恰为 5 的链R1→R2→R3→R4→R5,R5关联权限点pp_deep,用户经R1可判pp_deepallow(先断言此点)。 - 步骤:尝试建
R0组合R1(使深度变 6)。 - 预期:创建被拒
code='COMPOSITION_DEPTH_EXCEEDED';既有 5 层链判定不受影响(pp_deep仍 allow)——证明"强拒新建"而非"截断存量"。 - 证据:拒绝响应;前后两次
pp_deepcheck。 - 对应:PRD FR-4 · 上会
MTG-05
B04 同组织校验矩阵(四拒一放)
- 步骤:分别尝试:① org-b 用户分配 org-a 角色;② org-b 角色关联 org-a 组织权限点;③ org-b 角色组合 org-a 角色;④ org-b 用户直接授权 org-a 组织权限点;⑤ org-b 角色关联通用权限点。
- 预期:①-④ 均拒
code='CROSS_ORG';⑤ 成功(通用权限跨组织可关联)。 - 证据:五次响应汇总表。
- 对应:PRD FR-3/4/6 · 总 PRD §5.3
B05 不可变字段双层拒改
- 步骤:① API 层分别 PATCH 权限点 scope、角色 org_id、app 主体 bound_namespace_id;②
psql直连各执行一条对应 UPDATE。 - 预期:① 三次均
code='IMMUTABLE_FIELD';② 三条 UPDATE 均被触发器RAISE EXCEPTION 'IMMUTABLE_FIELD'拒绝——应用层与 DB 层双层防线各自独立成立。 - 证据:API 响应 + psql 报错原文。
- 对应:design §7.2
B06 跨业务方探测防护(命名空间推导覆盖自报)
- 前置:业务方 A 的 app 主体(绑
ns-a);ns-b下有权限点b.secret,某用户在 B 侧对其有授权。 - 步骤:以 A 的 HMAC 身份调 check,
permissionPoint:'b.secret'(载荷无命名空间字段,无从自报;如实现允许多余字段则附带伪造namespace:'ns-b'再试一次)。 - 预期:
{allow:false, denyReason:'pp_invalid'}——按 A 的 bound_namespace 推导查(ns-a, 'b.secret')不存在;伪造字段被忽略;outbox 有check_deny事件。 - 证据:check 响应;outbox 事件行。
- 对应:总 PRD §4.5 · 上会
MTG-06
B07 组织停用联动禁用角色
- 前置:org-c 下 3 个 active 角色,用户
u_c经其一持有组织权限点c.op。 - 步骤:① 停用 org-c(记录响应
disabledRoleCount);②psql断言角色状态;③u_ccheckc.op。 - 预期:①
disabledRoleCount=3;② 三角色同事务全部disabled(中途构造失败场景则整体回滚,可用事务注入验证一次);③{allow:false, denyReason:'pp_invalid'}(组织停用在③步拒)。 - 证据:响应、角色状态、check 响应。
- 对应:PRD FR-1 · 总 PRD §4.5 第三步
B08 命名空间停用全局拒绝
- 步骤:停用
ns-task后对其下任意权限点 check。 - 预期:
{allow:false, denyReason:'pp_invalid'}。 - 证据:check 响应。
- 对应:总 PRD §4.5 第三步
B09 fail-closed:断库与超时(服务端 + SDK 双层)
- 步骤:① 暂停
nexo_perm容器后经 SDK 调checkPermission;② 恢复库,注入慢查询(pg_sleep于判定路径或将PERM_CHECK_STATEMENT_TIMEOUT_MS调至 1ms)再调;③ 全程观察 SDK 是否抛异常。 - 预期:① SDK 返回
{allow:false, denyReason:'exception'}不抛异常;② 服务端按exception拒绝(statement_timeout 生效);③ 业务方进程无未捕获异常;恢复正常配置后 check 回归 allow。 - 证据:SDK 调用日志;服务端错误日志(脱敏);恢复后 check 响应。
- 对应:PRD FR-7 / B12 · 总 PRD §5.2 · 上会
MTG-07
B10 定时兜底双方向防错
- 前置:验收配置
PERM_RECONCILE_INTERVAL_SECONDS=10(同时截图生产配置=300)。 - 步骤:复活方向——
psql将在职用户u_r的 perm 主体行手工置active=false(模拟漏通知),等兜底发现;在兜底核对完成后、消费前(暂停消费循环制造窗口),把nexo-account侧u_r停用;恢复消费。失活方向——构造核对快照后该用户被enabled事件替换新行的时序(先采快照、后投 enabled 事件、再放行兜底校正消费)。 - 预期:复活方向:消费时复查 account 发现已 DISABLED → 跳过复活,
u_r保持失活(无新行);失活方向:epoch 与快照不一致 → 跳过校正,新行保持 active。两方向均在日志留痕 skip 原因。 - 证据:inbox 行(
reconcile_fix与 skip 日志);subjects历史断言。 - 对应:PRD FR-9 · 总 PRD §4.7
B11 状态通知幂等
- 步骤:同一
eventId的disabled通知连投 3 次;随后同一eventId的enabled通知连投 3 次。 - 预期:inbox 仅各存 1 行;
disabled仅一次翻转(epoch 不变),enabled仅新建 1 行(epoch 恰 +1,无 +2/+3);三次 HTTP 响应均 202。 - 证据:inbox 计数;
subjects历史行。 - 对应:design §12
B12 平台接口自身鉴权与鸡生蛋闭环
- 步骤:① 无
platform.*权限的普通用户调建组织接口;② 禁用某 app 主体后以其身份调 check;③ 以超管(仅初始化数据)完成:建组织管理员角色 → 分配给普通用户 → 该用户成功执行本组织管理操作、且对他组织操作被拒。 - 预期:①
code='FORBIDDEN'附denyReason:'no_grant';②FORBIDDEN附subject_inactive;③ 闭环成立,行权范围过滤生效(本组织成功/他组织 FORBIDDEN)。 - 证据:各步响应;超管初始化数据 psql 断言。
- 对应:PRD FR-8 · design §13
B13 硬删除影响面与双重确认
- 步骤:① impact 接口返回与 psql 实数对比;② 管理台硬删弹窗:不输入 confirmCode 直接提交;③ 输错 code 提交;④ 输对提交。
- 预期:① N/M/K 完全一致;②③ 提交按钮不可用/被拒;④ 成功。前端展示含"同名重建为全新实体,不继承旧显式拒绝"提示文案。
- 证据:impact 响应与 psql 对比;三种状态截图。
- 对应:PRD FR-2 / F03 · 上会(并入 P09 复核)
B14 失活主体授权拦截
- 步骤:对失活用户分别尝试:分配角色、直接授权、设置显式拒绝、重复创建主体。
- 预期:前三者
code='SUBJECT_INACTIVE';重复创建code='SUBJECT_EXISTS'(失活行禁翻活,提示走状态联动)。 - 证据:四次响应。
- 对应:PRD FR-5/6 · 总 PRD §4.7
6. 非功能硬阈值(N01—N03)
N01 鉴权延迟 p99 < 10ms
- 取数方式:造数脚本生成 100 主体 × 50 角色(含 3-5 层组合链)× 500 权限点;压测脚本连续 1000 次 check,请求构成 allow:五类 deny ≈ 6:4 混合(各 deny 类型均覆盖);服务端毫秒时间戳差计 p50/p95/p99,脚本与原始数据归档。
- 阈值:p99 < 10ms(记录 p50/p95 供基线回归)。
- 对应:PRD §6 · 上会
MTG-08
N02 停用回收时效
- 取数方式:
nexo-account停用操作时间戳 → perm 主体行updated_at(通知路径);另测通知被人为丢弃场景下由兜底闭合的时延(验收配置 10s,按比例换算生产 300s 上界成立)。 - 阈值:通知路径联动生效 ≤ 1s;丢通知场景 ≤ 兜底周期上界。
- 对应:PRD FR-9 · 总 PRD §3.1
N03 deny 审计不阻塞判定
- 取数方式:在投递 worker 注入 2s 延迟(或暂停 worker 使 outbox 积压),以 N01 同口径重跑 1000 次 check,对比 p99。
- 阈值:与 N01 基线差异 < 1ms;积压期间 check 全部正常返回。
- 对应:PRD FR-10 · 总 PRD §4.6
7. 证据目录约定
evidence/scripts/:造数、压测、时序构造(B10)脚本,可重复执行evidence/logs/:结构化取数(JSON),文件名按用例编号(如n01.json、b10-reconcile.json)evidence/console/:管理台截图,文件名按页面/用例(如p10-f03-harddelete-confirm.png)- 敏感信息脱敏:HMAC 密钥不入库,token 只留前 8 位
8. 预跑结果(T01 于 09-19 回填)
预跑报告见 tc-evidence-preflight。预跑时 27 条:通过 22 / 不通过 1 / 阻塞 4。 后续复验:
B09已解除(修复1b3c25b,复验证据evidence/logs/b09-failclosed-reverify.json)。 2026-09-20 管理台上生产后补跑P10与B13的界面部分,两条均通过(证据在evidence/console/)。 现为 通过 25 / 不通过 0 / 阻塞 2(B10、N02的兜底方向,两者同因:需真实nexo-accounttest 实例)。 下表B09行同时保留预跑原始结论与复验结果。 判定环境为集成分支integration/2026-09-20(真实服务 3002 + 真实nexo_perm库), 判定接口与状态通知走真实 HTTP + 真实 HMAC,管理接口走真实 Bearer JWT(经nexo-auth登录链路);nexo-account以 stub 扮演(仅影响 §2.2 所列面)。环境不允许的部分一律记为阻塞,未把「未测」写成「通过」。
| 编号 | 结果 | 证据 | 备注 |
|---|---|---|---|
P01 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json、evidence/logs/e2e-integration-2026-09-20.txt | ①–⑤ 真实 HTTP 写链路;⑥ {allow:true};⑦ 三类审计事件落 audit_outbox;⑧ epoch=0, active=true。账号校验经 stub |
P02 | 通过 | evidence/logs/t01-cases.json | 未过期 → allow;expires_at 改到过去 → no_grant 且行仍在(无需物理清理) |
P03 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json | 设 deny → deny_hit;解除 → 恢复 allow,explicit_denies 无残留 |
P04 | 通过 | evidence/logs/p04-effective-permissions.json | 组合链 allow;来源新关联后 10ms 即时生效;聚合预览 via='composed' |
P05 | 通过 | evidence/logs/p05-p06-p07.json | subject_inactive、epoch 不变无新行、通用 deny 清除 / org deny 保留、inbox 已处理。账号侧发事件由脚本扮演 |
P06 | 通过 | evidence/logs/p05-p06-p07.json | 新建行 epoch+1、旧行失活;旧角色不复活 → no_grant;重新分配后恢复 allow |
P07 | 通过 | evidence/logs/p05-p06-p07.json | 新行 org_id=org-b, epoch+1、旧行失活、通用 deny 清除;查旧组织权限点被拒。账号侧发事件由脚本扮演 |
P08 | 通过 | evidence/logs/t01-cases.json | 下线 → pp_invalid(无 deny 用户);activate 回原实体(id 不变);原 deny 永效 → deny_hit。用例问题见报告 §4.4 |
P09 | 通过 | evidence/logs/p09-impact.json | impact 1/1/1 与实际一致;硬删 204 且三关联表零残留;同名重建新 id、旧 deny 不继承 → no_grant |
P10 | 通过(含 1 项未执行步骤) | evidence/console/f02-org-created.jpg、f02-namespace-created.jpg、f03-permission-point-registered.jpg、f04-composition-prefilled.jpg | 09-20 于生产管理台(nexo-perm-console.pages.dev → nexo-perm.snailuu.cn,perm-api 0.1.3-alpha.1)以平台管理员走查六页,每页主操作闭环成功且列表即时刷新:F02 建组织/命名空间、F03 注册组织权限点(scope 联动出「所属组织」必填、「范围一经声明不可改」提示在位)、F04 建两角色并组合(编辑器正确回填已有组合,依赖新增的 GET /api/roles/:id/compositions,见 nexo-perm-api#29)、F05 权限全景 8 点带「角色路径」来源、F06 建 app 主体(「bound_namespace 一经创建不可变」提示在位)、F08 只读视图无任何写入口。「构造跨组织分配观察拒绝提示」这一步未执行:管理台在选择阶段即按组织过滤,跨组织角色不出现在下拉中,UI 上无法构造该拒绝——属前端防呆,非缺陷,但该步骤需改写,见下方「用例问题」 |
B01 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json | 失活 + deny + 未过期直授 → subject_inactive(①先于②) |
B02 | 通过 | evidence/logs/t01-cases.json | 自引用 / 两节点 / 三节点回路三型均 COMPOSITION_CYCLE,无新增行 |
B03 | 通过 | evidence/logs/t01-cases.json | 5 层链 allow;建第 6 层强拒 COMPOSITION_DEPTH_EXCEEDED;存量链仍 allow(强拒非截断) |
B04 | 通过 | evidence/logs/b04-matrix.json | 四拒一放:分配角色 / 关联组织权限点 / 组合 / 直接授权 → CROSS_ORG;通用权限点 → 成功 |
B05 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json | 角色 orgId、app boundNamespaceId 经 HTTP 改 → IMMUTABLE_FIELD;权限点无 PATCH 端点(404)。DB 触发器层见报告 §4.3 |
B06 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json | 跨业务方探测 → pp_invalid(按 X-Nexo-App 推导命名空间);伪造字段被 strip;check_deny 审计行存在 |
B07 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json | 停用组织 → disabledRoleCount=3、三角色全 disabled;其下权限点判定 → pp_invalid |
B08 | 通过 | evidence/logs/t01-cases.json | 停用前 allow(反证非恒拒)→ 停用 ns 后 pp_invalid → 恢复后 allow |
B09 | 通过(复验;预跑为不通过) | evidence/logs/b09-failclosed-reverify.json(复验);evidence/logs/b09-failclosed.json、evidence/logs/b09-crash.log(预跑失败原始记录) | 预跑:停库 / statement_timeout=1ms 时服务进程直接退出(Unhandled 'error' event on BoundPool,src/db/index.ts 缺 pool.on('error')),未返回 {allow:false, denyReason:'exception'};期望「HTTP 200 + exception」,实际「连接被拒、/health 不可达」。复验(修复 1b3c25b 后):基线 / 停库 / 暂停容器 / 恢复 / statement_timeout=1ms 五档全通过,SDK 全程不抛异常,服务进程 restarts=0。附带发现:describeError 想单独带出 SQLSTATE 但 drizzle 把 code 包在 .cause,日志里 57014 从未出现(P3 可观测性,见复验证据 extraFinding) |
B10 | 阻塞 | evidence/logs/p01-p03-b01-b05-b13.json(部分) | 已证生产默认周期 = 300s。双方向时序未测:需要真实账号中心可变在职状态作核对对端,固定状态 stub 构造不出「核对快照后账号侧变化」。解除:提供可用 nexo-account test 实例(可变状态测试账号 ≥3) |
B11 | 通过 | evidence/logs/b11-idempotency.json | 同 eventId 的 disabled 连投 3 次:inbox 仅 1 行、响应均 202、仅一次翻转;enabled 连投 3 次:仅新建 1 行、epoch 恰 +1 |
B12 | 通过 | evidence/logs/b12-closure.json | 无平台授权时建组织 → 403 FORBIDDEN;对照:恢复后同一请求 201;超管建组织管理员角色 → 分配成功。「第二个真实用户」维度受本地账号限制(报告 §4.5.2),以主体停用隔离 |
B13 | 通过 | evidence/logs/p01-p03-b01-b05-b13.json(API 部分)、evidence/console/b13-confirm-empty.jpg、b13-confirm-mismatch.jpg | API 侧通过:impact 计数与库内实数一致、confirmCode 错 → VALIDATION_FAILED。管理台三态 09-20 补齐:① 未输确认码 → 提交按钮置灰;② 输错 → 仍置灰并给出中文提示「请输入完全一致的权限点标识:」+ 该权限点 code;③ 输对 → 删除成功、列表即时刷新。弹窗含 impact 计数(N/M/K)与要求文案「同名重建为全新实体,不继承旧显式拒绝」 |
B14 | 通过 | evidence/logs/t01-cases.json | 失活主体:分配角色 / 直接授权 / 设显式拒绝 → SUBJECT_INACTIVE;重复建主体 → SUBJECT_EXISTS |
N01 | 通过 | evidence/logs/n01-t01-rerun.json | p99 2.711ms(阈值 10ms,余量 3.7×);p50 1.228 / p95 1.885;期望不符 0/1000。B08 原读数 p99 2.248ms 同档 |
N02 | 阻塞 | evidence/logs/n02-latency.json(通知路径) | 通知路径 最大 14ms ≤ 1s,通过;丢通知 → 兜底闭合方向未测(同 B10:需真实 account 状态作核对对端)。解除:同 B10,并把 PERM_RECONCILE_INTERVAL_SECONDS 调到验收值归档原始时间戳 |
N03 | 通过 | evidence/logs/n03-audit-backlog.json | 积压态(check_deny 新增 36 行未投递)p99 2.424ms vs N01 基线 2.711ms,差 −0.287ms < 1ms;积压期间 1000/1000 正常返回 |