Skip to content

docs(adr): ADR-0092 — sys_user 档案字段经引擎 hook 委托,放开 edit affordance(RFC #2784)#2787

Merged
os-zhuang merged 1 commit into
mainfrom
claude/sys-user-field-delegation-4p8l20
Jul 10, 2026
Merged

docs(adr): ADR-0092 — sys_user 档案字段经引擎 hook 委托,放开 edit affordance(RFC #2784)#2787
os-zhuang merged 1 commit into
mainfrom
claude/sys-user-field-delegation-4p8l20

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

概要

针对 RFC #2784 的评估产出:新增 ADR-0092(Proposed),决策是否/如何放开 sys_user 纯档案字段的标准表单编辑。仅文档,不含实现代码;ADR 通过后再立实现 issue。

结论(TL;DR)

放开一条窄口径、服务端强制的档案写路径:

  • D1 字段分层:可编辑档案字段仅 nameimagerole / phone_number / manager_id / ai_access / ban 列归专用管理面;email、凭据、全部 system-managed stamp 列永不直写。phone_number 是登录标识(唯一索引)、manager_id 驱动 own_and_reports RLS 范围,都排除在表单层之外。
  • D2 机制:plugin-auth 注册 beforeUpdate 白名单守卫 hook(fail-closed,仅拦截用户上下文写入;better-auth adapter 与 system 写入不受影响)。不采用仅 UI readonly 的方案 —— 调研发现字段级 readonly 在服务端只是校验跳过而非写入拦截,违反 ADR-0049。附带收益:堵上现存漏洞 —— admin_full_access 今天就能经 data API 裸写 sys_user 任意列(含 email)。
  • D3 单一白名单:新模块导出两级集合,import upsert 的 UPDATE_ALLOWED_FIELDS(PR feat(auth): 管理员直建用户 + 手机号登录 + sys_user 批量导入(#2766 V1/V1.5/V2) #2771)改为 superset-by-construction 复用,杜绝两处漂移。
  • D4 affordance:守卫落地后才在 sys_user 上加 userActions: { edit: true }(create/import/delete 保持关闭);顺序是硬约束(ADR-0049)。
  • D5 权限拓扑不变:member/org-admin 仍 allowEdit: false,标准编辑路径实际只对平台管理员开放;自助改档案继续走 better-auth /update-user(既有 update_my_profile action)。
  • D6 会话一致性afterUpdate hook 令该用户的 secondary-storage 会话快照失效,不把写入委托给 internalAdapter.updateUser(避免 adapter→engine 重入与 API 耦合)。
  • 审计(评估项 4):无需新建 —— plugin-audit 的引擎级 hook 天然捕获该路径,字段级 before/after 快照优于专用端点的显式审计。

主要调研依据

  • packages/spec/src/data/object.zod.tsresolveCrudAffordances / userActions 仅为 UI 提示;sys_user 的 data API 全量开启。
  • packages/objectql/src/engine.ts + validation/record-validator.ts — 静态 readonly 不在服务端拦截写入(仅 readonlyWhen 被 strip)。
  • packages/plugins/plugin-auth/src/objectql-adapter.ts — better-auth 写入本身走 engine.update,守卫必须按调用上下文而非对象区分。
  • packages/plugins/plugin-security/src/objects/default-permission-sets.ts — 现有 deny 拓扑与 sys_user RLS carve-out。
  • packages/plugins/plugin-auth/src/admin-import-users.tsUPDATE_ALLOWED_FIELDS = {name, phone_number, role}(与表单层白名单刻意不同,故取"同源派生"而非"同一份集合")。

后续

ADR 接受后按 Rollout 章节立实现 issue(守卫 hook + 白名单模块 + import 重构 + 测试 → D6 → affordance 翻转 + objectui 验证)。

Closes #2784 需等实现落地,此 PR 仅关联不关闭。

🤖 Generated with Claude Code

https://claude.ai/code/session_01YGAN5VvvBi4YRGwpNT6e21


Generated by Claude Code

…ne hook (#2784)

Evaluation outcome for RFC #2784: open a narrow, server-enforced profile
write path on sys_user instead of keeping name/image edits endpoint-only
or relying on UI readonly flags.

- D1 field tiers: profile-editable (name, image) / admin-surface-only
  (role, phone_number, manager_id, ai_access, ban columns) / never-direct
  (email, credentials, system-managed stamps)
- D2 fail-closed beforeUpdate whitelist guard in plugin-auth, user-context
  writes only (better-auth adapter + system writes bypass); also closes
  the existing admin_full_access raw-write hole on sys_user
- D3 single whitelist module; import upsert's UPDATE_ALLOWED_FIELDS
  becomes a superset-by-construction re-export (anti-drift, PR #2771)
- D4 userActions.edit flip ships only after the guard exists (ADR-0049)
- D5 permission topology unchanged: platform-admin only; self-service
  stays on better-auth /update-user
- D6 afterUpdate hook invalidates cached session snapshots

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YGAN5VvvBi4YRGwpNT6e21
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 10, 2026 12:02pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rfc(identity): 评估 sys_user 档案字段经引擎 hook 委托,放开 edit affordance

2 participants