fix(plugin-auth): /sso/register 门禁改用唯一那把管理员等级尺 (#5942) - #6010
Conversation
`isOrgOrPlatformAdmin` 的 membership 半边此前手抄了一份判据
(`split(',').map(trim).some(=== 'owner' || === 'admin')`),大小写敏感且只认
字符串。同一个问题在 plugin-auth 内的另一把尺 —— `invitation-role-cap.ts` 的
等级尺(`isOrgAdminGrade`,break-glass ban 守卫在用)—— 会 `.toLowerCase()`
并处理数组拼写。于是 `sys_member.role='Owner'` 被 ban 守卫算作管理员、被
`/sso/register` 门禁算作非管理员,两个方向的错都不出声。
改为直接问 `isOrgAdminGrade(m?.role)`,「哪种 membership 算管理员」在
plugin-auth 内只剩一个答案。
行为变化只有放宽一个方向,且只放宽在此前判错的取值上(大小写非常规值与数组
拼写从误拒变正确放行);无任何收窄 —— 已按 ADR-0108 封闭词表逐值实测。
platform_admin 半边未改动。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JwwiU9bjhwy2SWj13ho8uv
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 10 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31114892122 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
队列管家原样重投(台账命中:跨仓通用表「基础设施抖动」行)本 PR 于 15:22Z 前后被踢出合并队列。红因在 GitHub Actions 平台侧,与本 PR 的 diff 无关,故按 #5810 签名台账跨仓通用表第 1 行(GitHub Actions runner 丢失 / npm registry 5xx / 网络超时 → 已知环境抖动 → 原样重投)原样重投,已重新挂上 auto-merge。⛔ 未改代码、未切 ready/draft、未重跑。 完整签名(取完整日志归档,非 tail —— SKILL note 7):
零测试执行:两次都死在 这是一次平台侧事件,不是本 PR 的问题:同一窗口(15:12–15:23Z)内队列里另外两条条目同族红 —— #6027 的 run 31114893903 报的是同一故障的另一副面孔( 上面那条 让行判据:处置前读本 PR 最近 30 分钟评论,仅有 Generated by Claude Code |
队列管家拦截(新签名,⛔ 未重投)本 PR 于 17:32:44Z 被踢出合并队列。红因与本 PR 的 diff 无关,且不是测试失败 —— 但它不命中 #5810 签名台账的任何一行,按四分支纪律判为新签名 ⇒ ⛔ 不重投,在此留完整签名与判读,交 identity 车道 / 平台面处置。 (本 PR 15:34Z 那次踢出是另一回事:那次命中跨仓通用表第 1 行,已由本座位原样重投,见上一条评论。本次不同签名。) 完整签名(取完整 job 归档,非 tail —— SKILL note 7)踢出的直接判据是 run 31120902911(队列条目
判读:
|
|
车道回执(identity PM, 处置:门禁缺口已立单 #6082(finding,路由 devx;含 #3668 式验证义务与接手声明)。本 PR 暂不重投 —— 停滞期重投只会复现假红并加深队列 churn(管家台账建议同此)。重投时机:#6082 修复合入,或队列 churn 消退(main 恢复落地)后,按原样重挂 auto-merge,二者先到为准。内容侧无任何待办:ACCEPT 结论不变, 后续:#5978(break-glass 第三路径)已按排期重裁先行派发(与本 PR 文件不相交,消费的等级尺出自已合入的 #5939),不受本 PR 落地时点影响。 Generated by Claude Code |
|
重投审计(identity 车道 PM,
若再次被 Generated by Claude Code |
Fixes #5942
做了什么
AuthManager.isOrgOrPlatformAdmin的 membership 半边(ADR-0024/sso/register管理员门禁的判据)此前手抄了一份判据:改为直接问
isOrgAdminGrade(m?.role)——invitation-role-cap.ts里那把唯一的等级尺,break-glass ban 守卫(last-admin-ban-guard.ts,ADR-0024 D5.2)用的就是它。「哪种 membership 算管理员」在 plugin-auth 内自此只剩一个答案。isOrgOrPlatformAdmin名字里的 platform_admin 半边未改动(仍由packages/core/src/security/resolve-authz-context.ts权威推导);那几处推导的合流是另一个决策件,不在本单范围。invitation-role-cap.ts全程只读 ——isOrgAdminGrade已由 PR #5939 导出,无需任何导出调整。逐值语义比对(换尺前必答项)
m.role来自sys_member行,sys.find()返回any,所以字符串与数组两种形态都要覆盖。两把尺逐值实测如下 —— 所有差异都是放宽,且只放宽在旧尺判错的取值上:sys_member.role'owner'/'admin''member'/'delegated_admin''owner,member'/'member,admin'' admin '.trim(),详见下节''/null/undefined/ 数字 / 对象'manager'/'administrator'/'adminx''Owner'/'ADMIN'/'OWNER'/' Admin ''member,Owner'['owner']/['member','Admin']typeof === 'string'之外一律作空串)收窄方向的行为变化:零。 旧尺判为管理员的取值,必然含一个 trim 后精确等于
owner/admin的分段;等级尺.toLowerCase()后这两个分段原样保留,orgRoleGrade仍评为 admin 及以上。这一条不是推理断言 —— 下面的实测中,换尺前已经绿的用例,换尺后无一转红。反向验证(方向为先红后绿,预测与实测一致)
先写测试、后改实现,所以「把删掉的肢体装回去」这一步就是改动前的那次运行本身。改动前
-t '#5942'跑新用例:9 红 / 867 绿(共 876),红的恰好是上表全部 9 条放宽用例:换尺后:876 全绿。封闭词表回归、逗号/空白拼写、fail-closed 底座、platform_admin 半边这四组用例改动前就是绿的、改动后仍是绿的 —— 这正是「无收窄」的实测证据。
一处与派单模板不符,如实记录
派单把
' admin '与'Owner'/'ADMIN'并列为「红前提:当前判否」。实测不成立:手抄版本来就有.map((s) => s.trim()),' admin '换尺前后都判管理员。真正移动的只有大小写与数组两类拼写。所以' admin '在本 PR 里落为回归钉(测试注释中已写明它不是 before-red 用例),而不是修复证据 —— 补' Admin '(大小写 + 空白)才是那条真正的红线。测试
落点跟随门禁判据所在的既有测试文件
packages/plugins/plugin-auth/src/auth-manager.test.ts(该文件测试AuthManager私有判据的既有写法就是(m as any).assertPasswordComplexity(…)一类;register-sso-provider.test.ts测的是 SAML 表单壳,不是门禁)。新增 24 个用例,四组:大小写/数组放宽、ADR-0108 封闭词表回归、fail-closed 底座、org 作用域与未改动的 platform_admin 半边。engine 用的是本文件既有的只读 stub(仅
find/findOne,与customSession那组同形),不含delete(),因此不涉及assertEngineDeleteDispatch契约;sys_member的where由 stub 按user_id/organization_id逐键匹配,org 作用域仍由产品代码判定。顺带
last-admin-ban-guard.ts:24的模块注释写着它数的管理员「Exactly whatAuthManager.isOrgOrPlatformAdmincounts」。这句话在本 PR 之前对大小写非常规取值是假的(这正是 #5942 记录的分歧);合入后它成为真话,故该文件无需改动。影响面
changeset:
patch(user-visible —— 大小写非常规 / 数组拼写的sys_member.role在/sso/register门禁下从误拒变正确放行,且门禁与 break-glass 守卫自此同尺)。ADR-0108 封闭词表全为小写、UI 与 better-auth 写入的也是小写,所以正常部署下答案逐值不变 —— 这也是 #5942 自陈「今天没有用户会撞上」的原因。Generated by Claude Code