You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/doc/permission_design.md

12 KiB

VBase 权限模型设计文档

核心原则

  1. Policy策略:定义"什么权限可以访问哪些接口"
  2. Role角色Policy 的集合,简化授权
  3. 授权关系:用户通过 Role 或直接与 Policy 关联,并携带数据范围

表结构关系

┌─────────────────┐     ┌──────────────────┐     ┌─────────────────┐
│     Policy      │◄────┤   PolicyRoute    │     │      Role       │
│  (策略定义)      │     │  (策略路由绑定)   │     │   (角色定义)     │
└────────┬────────┘     └──────────────────┘     └────────┬────────┘
         │                                               │
         │ Many-to-Many                                  │ Many-to-Many
         │                                               │
         ▼                                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                    UserPermission (用户授权)                      │
│  ├─ user_id: 用户ID                                              │
│  ├─ policy_code: 策略代码 (或直接关联 Policy)                      │
│  ├─ role_id: 角色ID (可选,与 policy_code 二选一)                  │
│  ├─ resource: 资源类型 (project/org/...)                         │
│  ├─ resource_id: 具体资源ID (proj-123, * 表示所有)                  │
│  ├─ scope: 权限范围 (owner/admin/member)                         │
│  └─ expire_at: 过期时间 (可选)                                    │
└─────────────────────────────────────────────────────────────────┘

详细表设计

1. Policy策略表

定义权限的本质:对某资源可以执行什么操作

字段 类型 说明
id string UUID
code string 唯一标识,如 project:read, project:write, project:admin
resource string 资源类型:user, org, project, *(所有)
action string 操作类型:create, read, update, delete, list, *, admin
effect string allow / deny
scope string 作用域:platform(平台级) / org(组织级)
description string 描述

示例数据:

-- 项目相关权限
('p1', 'project:create', 'project', 'create', 'allow', 'org', '创建项目')
('p2', 'project:read',   'project', 'read',   'allow', 'org', '查看项目')
('p3', 'project:write',  'project', 'write',  'allow', 'org', '编辑项目(包含read)')
('p4', 'project:delete', 'project', 'delete', 'allow', 'org', '删除项目')
('p5', 'project:admin',  'project', 'admin',  'allow', 'org', '项目管理员(包含所有)')

-- 通配权限
('p9', 'project:*',      'project', '*',      'allow', 'org', '所有项目权限')
('p0', '*:*',            '*',       '*',      'allow', 'platform', '超级管理员')

层级关系:

project:admin > project:write > project:read
    │              │              └─ project:create, project:delete
    └──────────────┴──────────────────────────────────────────────

2. PolicyRoute策略路由绑定表

Policy 与具体接口的绑定关系。一个 Policy 可以绑定多个路由。

字段 类型 说明
id string UUID
policy_id string 关联 Policy.id
domain string 应用域,如 vbase, myapp
prefix string 路由前缀,如 /api/v1
pattern string 路由模式,如 /projects, /projects/{id}
method string HTTP 方法:GET, POST, *, ...
resource_id_param string 从 URL 提取资源ID的参数名id
description string 接口描述

示例数据:

-- project:read 权限可以访问以下接口
('r1', 'p2', 'myapp', '/api/v1', '/projects',           'GET',    '',    '项目列表')
('r2', 'p2', 'myapp', '/api/v1', '/projects/{id}',      'GET',    'id',  '项目详情')
('r3', 'p2', 'myapp', '/api/v1', '/projects/{id}/logs', 'GET',    'id',  '项目日志')

-- project:write 权限
('r4', 'p3', 'myapp', '/api/v1', '/projects/{id}',      'PATCH',  'id',  '更新项目')
('r5', 'p3', 'myapp', '/api/v1', '/projects/{id}/env',  'PUT',    'id',  '更新环境变量')

-- project:create 权限
('r6', 'p1', 'myapp', '/api/v1', '/projects',           'POST',   '',    '创建项目')

-- project:admin 权限(包含所有)
('r7', 'p5', 'myapp', '/api/v1', '/projects/{id}/members', 'GET',  'id',  '成员管理')
('r8', 'p5', 'myapp', '/api/v1', '/projects/{id}/settings', '*', 'id',  '设置管理')

3. Role角色表

Policy 的集合,用于批量授权。

字段 类型 说明
id string UUID
org_id string 组织ID系统角色为空
code string 角色代码:admin, developer, viewer, owner
name string 角色名称
policy_codes string 关联的 Policy code 列表,逗号分隔
is_system bool 是否系统预设角色

示例数据:

-- 系统预设角色
('r1', '', 'owner',      '所有者',      'project:admin,org:admin,*:*', true)
('r2', '', 'admin',      '管理员',      'project:write,org:read,user:read', true)
('r3', '', 'developer',  '开发者',      'project:write,resource:read', true)
('r4', '', 'viewer',     '只读用户',    'project:read', true)

-- 组织自定义角色
('r5', 'org-123', 'pm', '项目经理', 'project:admin', false)

4. UserPermission用户授权表

核心授权表,记录"谁对什么资源有什么权限"。

字段 类型 说明
id string UUID
user_id string 用户ID
org_id string 组织ID可选
policy_code string 策略代码(如 project:read
resource string 资源类型(如 project
resource_id string 具体资源ID(如 proj-123*
granted_by string 授权来源:role(通过角色) / direct(直接授权)
source_id string 来源IDRole ID 或留空)
expire_at timestamp 过期时间(可选,永久有效为空)
created_at timestamp 创建时间

关键设计:

  • resource_id = * 表示对该类型所有资源的权限
  • 数据级权限通过具体的 resource_id 实现
  • 用户最终权限 = 直接授权 + 角色授权 的并集

示例数据:

-- 场景:用户 A
-- 1. 是项目 1 的 admin直接授权
('up1', 'user-a', 'org-1', 'project:admin', 'project', 'proj-1', 'direct', '', null)

-- 2. 是项目 2 的只读成员(直接授权)
('up2', 'user-a', 'org-1', 'project:read', 'project', 'proj-2', 'direct', '', null)

-- 3. 通过 developer 角色获得对所有项目的 write 权限
('up3', 'user-a', 'org-1', 'project:write', 'project', '*', 'role', 'role-dev-id', null)

-- 4. 临时授权3天后过期
('up4', 'user-a', 'org-1', 'project:admin', 'project', 'proj-3', 'direct', '', '2025-02-20 00:00:00')

权限检查流程

请求: GET /api/v1/projects/proj-123/settings
      Header: X-Org-ID: org-1

      │
      ▼
┌─────────────────────────────────────┐
│ 1. 路由匹配                          │
│    domain=myapp + method=GET +      │
│    pattern=/projects/{id}/settings   │
│                                     │
│    找到: PolicyRoute.r8             │
│    对应: Policy.p5 (project:admin)  │
│    提取: resource_id = "proj-123"   │
└─────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────┐
│ 2. 获取用户权限列表                   │
│    SELECT * FROM user_permissions   │
│    WHERE user_id = 'user-a'         │
│      AND resource = 'project'       │
│      AND (resource_id = 'proj-123'  │
│           OR resource_id = '*')     │
└─────────────────────────────────────┘
      │
      ▼
┌─────────────────────────────────────┐
│ 3. 权限层级匹配                        │
│    需要的权限: project:admin          │
│                                     │
│    用户有的权限:                      │
│    - project:admin (proj-1) ❌       │
│    - project:read (proj-2)   ❌       │
│    - project:write (*)       ❌       │
│      (write < admin)                │
│                                     │
│    结果: 拒绝 ❌                     │
└─────────────────────────────────────┘
      │
      ▼
拒绝,返回 X-Required-URL: /apply-perm?resource=project&action=admin&id=proj-123

场景覆盖

场景1用户是项目 owner拥有所有权限

INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
VALUES ('user-a', 'project:admin', 'project', 'proj-1')
-- 自动包含 project:read/write/delete/create

场景2平台管理员拥有所有资源的所有权限

-- 方案一:通配符
INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
VALUES ('admin', '*:*', '*', '*')

-- 方案二:绑定 owner 角色
-- Role.owner 包含 policy_codes: '*:*'

场景3组织管理员拥有组织内所有项目权限

-- 组织 admin 角色包含 project:*
-- 在组织上下文内生效
INSERT INTO user_permissions (user_id, org_id, policy_code, resource, resource_id, granted_by)
VALUES ('user-a', 'org-1', 'project:*', 'project', '*', 'role')

场景4用户加入多个项目权限不同

-- 项目1: admin
INSERT INTO user_permissions VALUES ('u1', 'project:admin', 'project', 'proj-1', ...)

-- 项目2: read
INSERT INTO user_permissions VALUES ('u1', 'project:read', 'project', 'proj-2', ...)

-- 项目3: write
INSERT INTO user_permissions VALUES ('u1', 'project:write', 'project', 'proj-3', ...)

与旧方案对比

维度 旧方案 (RoutePolicy) 新方案 (Policy + UserPermission)
核心思想 接口白名单 资源操作权限
配置粒度 每个接口单独配置 按 Resource + Action 配置
数据级权限 难实现 通过 resource_id 字段
通配授权 不支持 支持 *
层级权限 admin > write > read
新增接口成本 高(需给所有角色加策略) 低(绑定到 Policy 即可)
角色数量 多(每项目配角色) 少(复用系统角色 + 数据范围)

请确认此设计后,我开始修改代码。