Skip to main content

Schema Ownership

Ownership 描述“资源归谁管理、谁可以默认访问、谁创建/修改/删除了资源”。它是资源权限、分享、孤儿对象处理、审计的基础。

created_by vs owner

column语义是否用于权限
created_by_id谁创建了资源
updated_by_id谁最后修改审计
deleted_by_id谁删除审计
owner_id资源当前所有者可以用于权限
owner_typeowner 类型可以用于权限
owner_user_idowner 为 user 时的生成/冗余字段方便查询/授权
owner_team_idowner 为 team 时的生成/冗余字段方便查询/授权
warning

不要把 created_by 直接等同于 owner。资源可能由管理员代建、从外部导入、转移所有权,或者由系统创建。

owner 模型

单一用户 owner

简单系统可以只用:

owner_user_id text

适合:个人资源、个人 token、个人文档。

多态 owner

如果资源可能归属于用户、团队、组织、部门:

owner_type text,
owner_id text

可选 generated column:

owner_user_id text generated always as (
case owner_type when 'User' then owner_id end
) stored,
owner_team_id text generated always as (
case owner_type when 'Team' then owner_id end
) stored

PG 也可以用 expression index:

create index idx_resource_owner_user
on resource ((case when owner_type = 'User' then owner_id end));

系统资源

系统内置资源可以使用:

owner_type = 'System'
owner_id = 'system'

owner_* 为空,并通过:

visibility = system

表达。

visibility

常见可见性:

value说明
privateowner 可见/可用
shared通过 grant/share 表授权
teamowner team 或当前团队可见
organization组织内可见
global登录用户可见/可用
public匿名可见,谨慎使用
system系统内置,通常 admin 管理

建议字段:

visibility text not null default 'private'

如果可见性有原因或审批:

visibility_changed_at timestamptz,
visibility_changed_by_id text,
visibility_reason text

共享授权

owner/visibility 只能表达默认规则。复杂分享应使用 grant 表:

create table resource_grant (
id text primary key,
resource_type text not null,
resource_id text not null,
principal_type text not null, -- user | team | group | role | token
principal_id text not null,
action text not null,
effect text not null default 'allow',
created_by_id text,
created_at timestamptz not null default now(),
deleted_at timestamptz
);

如果系统已有通用 auth_permission_grant,优先使用通用表,避免每类资源都建一套 grant。

组织/租户

多租户系统通常需要:

tenant_id text not null

tenant 与 owner 不等价:

  • tenant_id:数据隔离边界。
  • owner_*:资源归属和默认授权。
  • created_by_*:审计。

孤儿对象

用户/团队删除后,资源可能变成孤儿。常见策略:

  • 转给系统 owner。
  • 转给管理员或团队 owner。
  • 标记 state = 'orphaned'
  • 保留 owner id 但 user 不存在,用于审计。

不要在无策略时随用户删除级联删除重要资源。

示例

create table resource (
id text primary key,
name text not null unique,
title text,

owner_type text,
owner_id text,
owner_user_id text generated always as (
case owner_type when 'User' then owner_id end
) stored,
owner_team_id text generated always as (
case owner_type when 'Team' then owner_id end
) stored,
visibility text not null default 'private',

created_by_user_id text,
updated_by_user_id text,
deleted_by_user_id text,
created_at timestamptz not null default now(),
updated_at timestamptz not null default now(),
deleted_at timestamptz
);

反例

  • 只有 created_by,没有 owner,导致无法转移所有权。
  • owner_id 没有 owner_type,未来无法支持团队/组织。
  • visibility = public 但资源包含 secret 或敏感配置。
  • 资源表和授权表分别在不同数据库,导致鉴权跨库。

既有表改造顺序

给已有资源表补 ownership/auditor 字段时,建议分阶段:

  1. 先补审计字段created_by_*updated_by_*deleted_by_*。如果已有 created_by / updated_by,先明确语义,不必一次性改名。
  2. 再补 owner 字段:简单场景先加 owner_user_id;需要团队/组织时再用 owner_type + owner_id
  3. 补 visibility:默认用 private / global / system,历史公共配置可回填为 systemglobal
  4. 补索引owner_user_idvisibilitycreated_by_*updated_by_*deleted_at
  5. 服务写入当前用户:Create 写 created_byowner_user_id;Update 写 updated_by;Delete 写 deleted_bydeleted_at
  6. 最后接授权逻辑:list 过滤、get/use/manage 检查、grant/share。

历史数据回填建议:

owner_user_id = null
visibility = system 或 global
created_by_user_id = null

不要为了回填而伪造创建人;未知就保持 null,并用 visibility 表达默认可见范围。

实践参考

accountleadfileflowpreference 等表使用了同一套 owner 模式:

owner_id text,
owner_type text,
owner_user_id text generated always as (
case owner_type when 'User' then owner_id end
) stored,
owner_team_id text generated always as (
case owner_type when 'Team' then owner_id end
) stored

并通过 generated column 对 users / team 建外键。详见 Schema Wode Practice

相关