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

1 week ago
# 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 即可)|
| 角色数量 | 多(每项目配角色) | 少(复用系统角色 + 数据范围)|
---
请确认此设计后,我开始修改代码。