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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions