背景
ADR-0092(docs/adr/0092-sys-user-profile-field-delegation.md,经 #2787 + #2808 落地)的实现工作第一阶段。这是关键路径:既是放开 sys_user 档案编辑的前置,也在堵一个现存安全洞 —— admin_full_access(通配符、无 RLS)今天可经通用 data API 裸写全部 managedBy: 'better-auth' 表的任意列(裸插 sys_member 自授成员身份、裸改 sys_api_key、裸删 sys_session),全部绕过 better-auth 副作用。
范围(ADR-0092 D2 / D3 / D6)
D2 — 通用守卫(plugin-auth,kernel:ready 注册,模式同 SCIM provenance hook)
before{Insert,Update,Delete} 引擎 hook,按 schema registry 的 managedBy === 'better-auth' 标志判定适用对象(不硬编码表清单;default-permission-sets.ts 的 BETTER_AUTH_MANAGED_OBJECTS 保持现状 = 权限集种子)。
- 仅拦截用户上下文写入(context 带真实用户且非
isSystem);better-auth adapter(无 context)、插件 SYSTEM_CTX、import 引擎调用直通。
- default-deny:用户上下文的 insert / delete,以及无白名单注册对象的 update,一律
FORBIDDEN 级报错,错误信息要指路("use Invite / Create User / better-auth API")。
- 唯一开口:
registerManagedUpdateWhitelist(object, fields) registry,beforeUpdate 据此剥离名单外字段;剥空则抛错(响亮失败,非静默 no-op)。首条目:sys_user → SYS_USER_PROFILE_EDIT_FIELDS。
- 覆盖单 id 与
options.multi 两种 update 形态。
D3 — 白名单同源模块
- 新模块(如
sys-user-writable-fields.ts):SYS_USER_PROFILE_EDIT_FIELDS = {name, image};SYS_USER_IMPORT_UPDATE_FIELDS = {...PROFILE, phone_number, role}(spread 派生,subset-by-construction)。
admin-import-users.ts 的私有 UPDATE_ALLOWED_FIELDS 替换为共享常量。
D6 — 会话缓存一致性
afterUpdate hook(object: 'sys_user'):用户上下文写入改动 Tier-1 字段后,令该用户在 secondary storage 的会话快照失效(具体键由 auth manager 负责);未接 secondary storage 时 no-op。
测试矩阵
合并前硬性验证(ADR Risks 第 1 条)
default-deny 最大风险是砸中未知的合法用户上下文写路径。合并前必须:
- 全量扫描仓库内对身份表的
engine.update/insert/delete 调用点,逐一确认上下文类型;
- dogfood 跑通:登录、邀请、建用户(含临时密码)、set-password、ban/unban/unlock、组织/团队生命周期、import-users、SSO/SCIM(若可用)。
发现的合法路径不得反射性加白名单:真属内部写的改 system context,属管理操作的走/新建专用端点。
交付物
守卫 + registry + 共享模块 + import 重构 + 上述测试 + changeset(功能变更)。
相关
背景
ADR-0092(
docs/adr/0092-sys-user-profile-field-delegation.md,经 #2787 + #2808 落地)的实现工作第一阶段。这是关键路径:既是放开sys_user档案编辑的前置,也在堵一个现存安全洞 ——admin_full_access(通配符、无 RLS)今天可经通用 data API 裸写全部managedBy: 'better-auth'表的任意列(裸插sys_member自授成员身份、裸改sys_api_key、裸删sys_session),全部绕过 better-auth 副作用。范围(ADR-0092 D2 / D3 / D6)
D2 — 通用守卫(plugin-auth,
kernel:ready注册,模式同 SCIM provenance hook)before{Insert,Update,Delete}引擎 hook,按 schema registry 的managedBy === 'better-auth'标志判定适用对象(不硬编码表清单;default-permission-sets.ts的BETTER_AUTH_MANAGED_OBJECTS保持现状 = 权限集种子)。isSystem);better-auth adapter(无 context)、插件SYSTEM_CTX、import 引擎调用直通。FORBIDDEN级报错,错误信息要指路("use Invite / Create User / better-auth API")。registerManagedUpdateWhitelist(object, fields)registry,beforeUpdate据此剥离名单外字段;剥空则抛错(响亮失败,非静默 no-op)。首条目:sys_user → SYS_USER_PROFILE_EDIT_FIELDS。options.multi两种 update 形态。D3 — 白名单同源模块
sys-user-writable-fields.ts):SYS_USER_PROFILE_EDIT_FIELDS = {name, image};SYS_USER_IMPORT_UPDATE_FIELDS = {...PROFILE, phone_number, role}(spread 派生,subset-by-construction)。admin-import-users.ts的私有UPDATE_ALLOWED_FIELDS替换为共享常量。D6 — 会话缓存一致性
afterUpdatehook(object: 'sys_user'):用户上下文写入改动 Tier-1 字段后,令该用户在 secondary storage 的会话快照失效(具体键由 auth manager 负责);未接 secondary storage 时 no-op。测试矩阵
sys_userupdate:名单外字段被剥离、纯名单外整包抛错、{name, image}通过options.multi批量 update 同样被过滤SYSTEM_CTX直通 + 共享常量约束字段)合并前硬性验证(ADR Risks 第 1 条)
default-deny 最大风险是砸中未知的合法用户上下文写路径。合并前必须:
engine.update/insert/delete调用点,逐一确认上下文类型;发现的合法路径不得反射性加白名单:真属内部写的改 system context,属管理操作的走/新建专用端点。
交付物
守卫 + registry + 共享模块 + import 重构 + 上述测试 + changeset(功能变更)。
相关