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

286 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 | 描述 |
**示例数据:**
```sql
-- 项目相关权限
('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 | 接口描述 |
**示例数据:**
```sql
-- 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 | 是否系统预设角色 |
**示例数据:**
```sql
-- 系统预设角色
('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` 实现
- 用户最终权限 = 直接授权 + 角色授权 的并集
**示例数据:**
```sql
-- 场景:用户 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拥有所有权限
```sql
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平台管理员拥有所有资源的所有权限
```sql
-- 方案一:通配符
INSERT INTO user_permissions (user_id, policy_code, resource, resource_id)
VALUES ('admin', '*:*', '*', '*')
-- 方案二:绑定 owner 角色
-- Role.owner 包含 policy_codes: '*:*'
```
### 场景3组织管理员拥有组织内所有项目权限
```sql
-- 组织 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用户加入多个项目权限不同
```sql
-- 项目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 即可)|
| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围)|
---
请确认此设计后,我开始修改代码。