背景
浏览器验证 ADR-0092 D4(#2817 / PR #2832 + objectui #2395 已合并)时发现:sys_user 的标准编辑表单在 Setup 里以 seeded 平台管理员身份打开时,所有字段仍不可编辑 。
根因(权威数据)
GET /api/v1/auth/me/permissions(seeded admin)对 sys_user 返回 allowEdit:false,而通配符 * 返回 allowEdit:true。console 的字段级安全(FLS)据此正确禁用表单 —— 这不是 objectui 渲染 bug(objectui #2395 已修一刀切禁用),而是有效权限 本身为 deny。
seeded admin 同时持有两个权限集:
admin_full_access:仅通配符 *: { allowEdit:true },无 sys_user 专项。
organization_admin:含 denyWritesOnManagedObjects() → 显式 sys_user: { allowEdit:false }。
显式 per-object deny 胜过 wildcard allow (narrowing 语义),故合成结果 sys_user.allowEdit:false。
纯 admin_full_access 持有者(无 org_admin):sys_user 落到通配符 → allowEdit:true → 编辑表单可用 。
同时是 org owner 的平台管理员 (seeded dev-admin 即此情形,org owner 自动获授 organization_admin):被显式 deny 挡住。
影响
ADR-0092 D5 假设"平台管理员(admin_full_access)本就过了权限层"。该假设对单独 admin_full_access 成立,但对"平台管理员 ∩ 组织所有者"不成立 —— 而 dev seed 与不少真实部署里两者恰好重合。结果:D4 的编辑 affordance 对这类主体"可见但不可用"。
决策选项(安全敏感,需 ADR 级定夺)
接受现状 :D4 面向纯平台管理员;org-owner-兼-平台管理员的编辑归 D5 已明确 defer 的"org-admin-scoped profile editing"后续。文档说明即可。
admin_full_access 增设显式 sys_user: { allowEdit:true, allowRead:true } (仅档案语义,凭据仍靠守卫 feat(auth): 身份表写守卫 — managedBy:'better-auth' 引擎级强制(ADR-0092 D2/D3/D6,closes #2816) #2828 拦列),让其在合成中盖过 org_admin 的 managed-deny。需确认合成里"同优先级显式 allow vs 显式 deny"的胜负规则。
调整合成语义 :平台管理员(持 manage_users / 无 RLS 的 admin_full_access)的 wildcard 不被 org 级 managed-deny 收窄。影响面最大,谨慎。
服务端守卫 #2828 已确保:即便放开 allowEdit,用户上下文对 sys_user 也只能改 {name, image},凭据/系统列仍被剥离。故选项 2/3 的实际写入面仍受控。
相关
ADR-0092 D4/D5 · #2817 / PR #2832 (元数据,draft)· #2828 (守卫,已合并)· objectui #2395 (表单渲染器,已合并)· #2833
背景
浏览器验证 ADR-0092 D4(#2817 / PR #2832 + objectui #2395 已合并)时发现:
sys_user的标准编辑表单在 Setup 里以 seeded 平台管理员身份打开时,所有字段仍不可编辑。根因(权威数据)
GET /api/v1/auth/me/permissions(seeded admin)对sys_user返回allowEdit:false,而通配符*返回allowEdit:true。console 的字段级安全(FLS)据此正确禁用表单 —— 这不是 objectui 渲染 bug(objectui #2395 已修一刀切禁用),而是有效权限本身为 deny。seeded admin 同时持有两个权限集:
admin_full_access:仅通配符*: { allowEdit:true },无sys_user专项。organization_admin:含denyWritesOnManagedObjects()→ 显式sys_user: { allowEdit:false }。显式 per-object deny 胜过 wildcard allow(narrowing 语义),故合成结果
sys_user.allowEdit:false。admin_full_access持有者(无 org_admin):sys_user落到通配符 →allowEdit:true→ 编辑表单可用。影响
ADR-0092 D5 假设"平台管理员(admin_full_access)本就过了权限层"。该假设对单独 admin_full_access 成立,但对"平台管理员 ∩ 组织所有者"不成立 —— 而 dev seed 与不少真实部署里两者恰好重合。结果:D4 的编辑 affordance 对这类主体"可见但不可用"。
决策选项(安全敏感,需 ADR 级定夺)
sys_user: { allowEdit:true, allowRead:true }(仅档案语义,凭据仍靠守卫 feat(auth): 身份表写守卫 — managedBy:'better-auth' 引擎级强制(ADR-0092 D2/D3/D6,closes #2816) #2828 拦列),让其在合成中盖过 org_admin 的 managed-deny。需确认合成里"同优先级显式 allow vs 显式 deny"的胜负规则。服务端守卫 #2828 已确保:即便放开 allowEdit,用户上下文对
sys_user也只能改{name, image},凭据/系统列仍被剥离。故选项 2/3 的实际写入面仍受控。相关
ADR-0092 D4/D5 · #2817 / PR #2832(元数据,draft)· #2828(守卫,已合并)· objectui #2395(表单渲染器,已合并)· #2833