Skip to content

架构:把「服务端托管字段」收敛成一个声明式 schema 概念(统一 owner_id / 公开表单托管集 / organization_id 的中间件特例) #3058

Description

@os-zhuang

背景

写路径上「哪些字段由服务端托管、客户端不得自由写」的规则,目前散落成 plugin-security 中间件里的 N 个互相独立的特例,每个都是单独硬编码 + 单独执法:

  1. owner_id(属主锚点,安全:owner_id(属主锚点)客户端可写、服务端无守卫 → 非属主可伪造/转移记录属主 #3004 / PR fix(security): 守卫 owner_id 属主锚点 + bulk 写按属主收敛 (#3004, #2982) #3018) — step 3.5 硬编码字段名 owner_id:insert 空值回填 acting user、伪造/转移/弃权需 allowTransfer/modifyAllRecords;非 scalar 拒绝、原型链防护、数组 change-set fail-closed;级联 set_null 经 __referentialFieldClear 豁免(属主守卫与级联 set_null 冲突:非特权删除 sys_user 时 owner_id 级联置空被 #3004 守卫拦截(级联中途失败) #3023 / PR fix(security): 级联 set_null 豁免属主转移守卫 (#3023) #3048)。
  2. 公开表单托管集(安全:公开表单(publicFormGrant)提交绕过 owner_id 属主守卫 → 匿名可伪造属主 #3022 / PR fix(security): 公开表单不可再伪造服务端托管锚点(owner_id 等)(#3022) #3036) — 新增 @objectstack/spec/securityPUBLIC_FORM_SERVER_MANAGED_FIELDS(id / owner_id / organization_id / tenant_id / 审计列 / is_deleted / deleted_at / __search),在匿名公开表单路径逐行剥离;路由层白名单无条件排除。
  3. organization_id(租户墙,ADR-0095 / security(authz): 多组织下伪造 organization_id 的 insert 可越租户墙 — Layer 0 未门控 insert post-image #2937 / fix(plugin-security): 堵跨租户 UPDATE 写 + org_admin private 对象越墙(security) #2946) — step 3.7 硬编码字段名 organization_id:供给值必须过 Layer 0 租户 CHECK,否则拒绝(防跨租户搬运)。

三处各自定义「托管字段是哪些、怎么执法」,彼此不共享抽象。

问题(为什么这是债)

提议(结构性,需 ADR)

把「服务端托管字段」提升为一个声明式 schema 概念,一处声明、一处执法:

  • packages/spec 的 Field/Object schema 上引入一个声明(草案命名,待 ADR 定稿),例如字段级 systemManaged: { onInsert: 'stampActor' | 'reject' | 'strip', onUpdate: 'reject' | 'strip' | 'immutable', requiresCapability?: string },或对象级的托管字段策略表。
    • owner_idonInsert: stampActor(空值回填)、onUpdate: reject(除非 allowTransfer);
    • 审计列(created_by 等)→ immutable/strip;
    • organization_id → 由租户层(@objectstack/organizations)贡献其托管策略(跨租户 CHECK);
    • 公开表单托管集 → 从同一声明派生(PUBLIC_FORM_SERVER_MANAGED_FIELDS 不再手维护)。
  • 单一执法点:中间件读该声明统一执行(insert 回填/拒绝、update 拒绝/剥离、能力门),已认证与匿名公开表单共用同一派生集合;__referentialFieldClear 这类引擎内部豁免也纳入统一模型。
  • explain 引擎据同一声明解释「为什么这个字段写不进去」。

范围与非目标

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions