# Auth 权限系统设计

---

## 一、权限码层级

### 1.1 层级定义

```
层级从 1 开始计数：
- 奇数层（第1、3、5层）：资源类型
- 偶数层（第2、4、6层）：实例
```

### 1.2 示例

```
app                      → 第1层 (奇数) - 资源类型
app:vbase                → 第2层 (偶数) - 实例
app:vbase:role           → 第3层 (奇数) - 资源类型
app:vbase:role:admin     → 第4层 (偶数) - 实例
```

---

## 二、权限等级

### 2.1 奇数层（资源类型）

| level | 二进制 | 含义 |
| ----- | ------ | -------------------- |
| 0     | 000    | 无权限 |
| 1     | 001    | 可创建该类型的子资源 |

### 2.2 偶数层（实例）

| level | 二进制 | 含义 |
| ----- | ------ | ---------------------- |
| 0     | 000    | 无权限 |
| 2     | 010    | 读取 |
| 4     | 100    | 写入（修改，不能删除） |
| 6     | 110    | 读写（读取+修改） |
| 7     | 111    | 管理员（完全控制：读写+删除+授权） |

---

## 三、检查规则

### 3.1 层级与权限对应

| 权限 | level | 检查层级 | 说明 |
|------|-------|----------|------|
| 创建 | 1 | 奇数层 | 检查资源类型层 |
| 读取 | 2 | 偶数层 | 检查实例层 |
| 写入 | 4 | 偶数层 | 检查实例层 |
| 读写 | 6 | 偶数层 | 检查实例层 |
| 管理 | 7 | 偶数层 | 检查实例层 |

### 3.2 具体规则

```
创建资源 (level 1)
  → 检查当前 permissionID 对应的奇数层
  → 例: "app:{appID}:role" 检查 "app:{appID}:role" 层

读取/更新/删除资源 (level 2,4,6,7)
  → 检查当前 permissionID 对应的偶数层
  → 如无权限，递归向上检查父实例层
  → 注意：只有 Level 7 (管理员) 权限才会向下继承，Level 2,4,6 不会继承
  → 例: "app:{appID}:role:{roleID}" 先检查实例层，再检查 "app:{appID}"
```

---

## 四、权限流程示例

### 场景一：用户 A 创建应用

```
1. 用户A创建应用 "VBase"
2. 自动创建权限:
   - PermissionID: "app:vbase"
   - Level: 7 (创建者完全控制)
```

### 场景二：用户 A 邀请用户 B 加入应用

```
1. 用户A授予用户B: app:vbase level 2 (读)
2. 用户B权限表:
   - app:vbase level 2
3. 用户B可执行:
   - ✓ 读取 vbase 信息
   - ✗ 修改/删除
```

### 场景三：用户 B 创建角色

```
前置: 用户B有 app:vbase level 2 (读)，需要额外授权

1. 用户A授予用户B: app:vbase:role level 1 (创建角色)
2. 用户B创建角色 "Editor"
3. 自动创建权限:
   - PermissionID: "app:vbase:role:editor"
   - Level: 7
```

---

## 五、Auth 接口设计

```go
package auth

import (
	"context"
	"github.com/veypi/vigo"
)

// ========== 权限等级 ==========

const (
	LevelNone      = 0
	LevelCreate    = 1 // 001 创建 (检查奇数层)
	LevelRead      = 2 // 010 读取 (检查偶数层)
	LevelWrite     = 4 // 100 写入 (检查偶数层)
	LevelReadWrite = 6 // 110 读写 (检查偶数层)
	LevelAdmin     = 7 // 111 管理员 (完全控制)
)

// PermFunc 权限检查函数类型
type PermFunc func(x *vigo.X) error

// Auth 权限管理接口
type Auth interface {
	// ========== 上下文 ==========

	// UserID 获取当前用户ID
	UserID(x *vigo.X) string

	// ========== 登录检查 ==========

	// Login 检查用户是否登录
	Login() PermFunc

	// ========== 权限检查 ==========

	// Perm 检查权限
	// code: 权限码，支持动态解析
	//   - 固定写法: "app:vbase"
	//   - 动态解析: "app:{appID}" 从 path 获取
	//               "app:{appID@query}" 从 query 获取
	//               "app:{appID@header}" 从 header 获取
	//               "app:{appID@ctx}" 从 ctx 获取
	// level: 需要的权限等级
	Perm(code string, level int) PermFunc

	// ========== 快捷方法 ==========

	// PermCreate 检查创建权限 (level 1，检查奇数层)
	PermCreate(code string) PermFunc

	// PermRead 检查读取权限 (level 2，检查偶数层)
	PermRead(code string) PermFunc

	// PermWrite 检查更新权限 (level 4，检查偶数层)
	PermWrite(code string) PermFunc

	// PermAdmin 检查管理员权限 (level 7，检查偶数层)
	PermAdmin(code string) PermFunc

	// ========== 权限授予（业务调用） ==========

	// Grant 授予权限
	// 在创建资源、被授权等业务逻辑中调用
	// permissionID: 权限码，如 "app:vbase"
	// level: 权限等级
	Grant(ctx context.Context, userID, permissionID string, level int) error

	// Revoke 撤销权限
	Revoke(ctx context.Context, userID, permissionID string) error

	// ========== 权限查询 ==========

	// Check 检查权限 不支持动态解析
	// permissionID: 完整的权限码，如 "app:vbase"
	Check(ctx context.Context, userID, permissionID string, level int) bool

	// ========== 资源列表查询 ==========

	// ListResources 查询用户在特定资源类型下的详细权限信息
	// 用于解决 "查询我有权限的 app 列表" 等场景
	// userID: 用户ID
	// resourceType: 资源类型 (奇数层)，如 "app" 或 "app:{appID}:role"
	// 返回: map[实例ID]权限等级 (如 {"vbase": 2, "other": 7})
	ListResources(ctx context.Context, userID, resourceType string) (map[string]int, error)

	// ListUsers 查询特定资源的所有协作者及其权限
	// 用于解决 "查看这个应用有哪些成员" 等场景
	// permissionID: 资源实例权限码，如 "app:vbase"
	// 返回: map[用户ID]权限等级 (如 {"user1": 2, "user2": 7})
	ListUsers(ctx context.Context, permissionID string) (map[string]int, error)
}

// ========== 数据结构 ==========

// Permission 用户权限
type Permission struct {
	ID            string `json:"id"`
	UserID        string `json:"user_id"`
	RoleID        string `json:"role_id"`
	PermissionID  string `json:"permission_id"`
	Level         int    `json:"level"`
	CreatedAt     int64  `json:"created_at"`
	UpdatedAt     int64  `json:"updated_at"`
}
```

---

## 六、使用示例

### 6.1 固定写法

```go
var Router = vigo.NewRouter()

func init() {
	// 创建应用 - 需要系统级 app 权限
	Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)

	// 超级管理员接口
	Router.Get("/admin/users", cfg.Auth.PermAdmin("*"), AdminListUsers)
}
```

### 6.2 动态解析

```go
func init() {
	// 从路径参数获取 appID (默认)
	// GET /apps/{appID}
	Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)

	// 从 query 参数获取
	// GET /apps?appID=xxx
	Router.Get("/apps", cfg.Auth.PermRead("app:{appID@query}"), GetApp)

	// 多层嵌套
	// GET /apps/{appID}/roles/{roleID}
	Router.Get("/apps/{appID}/roles/{roleID}",
		cfg.Auth.PermRead("app:{appID}:role:{roleID}"),
		GetRole,
	)
}
```

### 6.3 完整示例

```go
var Router = vigo.NewRouter().Use(cfg.Auth.Login())

func init() {
	// 创建应用 - 系统级权限
	Router.Post("/apps", cfg.Auth.PermCreate("app"), CreateApp)

	// 列出我的应用 - 只需登录
	Router.Get("/apps", ListMyApps)

	// 应用操作 - 从路径获取
	Router.Get("/apps/{appID}", cfg.Auth.PermRead("app:{appID}"), GetApp)
	Router.Put("/apps/{appID}", cfg.Auth.PermWrite("app:{appID}"), UpdateApp)
	Router.Delete("/apps/{appID}", cfg.Auth.PermAdmin("app:{appID}"), DeleteApp)

	// 角色操作 - 嵌套资源
	Router.Post("/apps/{appID}/roles", cfg.Auth.PermCreate("app:{appID}:role"), CreateRole)
	Router.Get("/apps/{appID}/roles/{roleID}", cfg.Auth.PermRead("app:{appID}:role:{roleID}"), GetRole)
	Router.Put("/apps/{appID}/roles/{roleID}", cfg.Auth.PermWrite("app:{appID}:role:{roleID}"), UpdateRole)
	Router.Delete("/apps/{appID}/roles/{roleID}", cfg.Auth.PermAdmin("app:{appID}:role:{roleID}"), DeleteRole)
}
```

### 6.4 动态解析规则

| 语法 | 来源 | 示例 |
|------|------|------|
| `{key}` | path 参数 | `{appID}` |
| `{key@query}` | query 参数 | `{appID@query}` |
| `{key@header}` | header | `{appID@header}` |
| `{key@ctx}` | context | `{appID@ctx}` |

---

## 七、接口说明

### 7.1 业务调用 vs 管理端

此接口是**业务层**调用，用于：
- 创建资源时自动授予权限
- 业务逻辑中检查权限

**管理端**（如权限管理后台）可以通过直接操作数据库实现批量管理。

### 7.2 Grant 调用时机

```go
// 创建应用时
func CreateApp(x *vigo.X, req *CreateAppReq) (*AppResp, error) {
	app := models.App{Name: req.Name}
	db.Create(&app)

	// 授予创建者完全控制权限
	cfg.Auth.Grant(x.Context(), userID, "app:"+app.ID, auth.LevelAdmin)

	return &AppResp{App: app}, nil
}
```

### 7.3 列表与搜索接口设计

对于资源列表（List）或搜索接口，推荐以下设计模式：

1. **全量管理接口**（如后台管理系统）：
   - 使用 `PermAdmin("*")` 或 `PermAdmin("app:*")`。
   - 这类接口返回所有数据，必须严格控制权限。

2. **用户侧列表/搜索**（如“我的应用”）：
   - **方式一（仅所有者）**：
     - 使用 `Login()` 确保登录。
     - 业务层：`db.Where("owner_id = ?", userID).Find(&apps)`
   - **方式二（协作模式 - 使用 ListResources）**：
     - 调用 Auth 接口获取有权限的 ID 列表。
     - `perms, _ := auth.ListResources(ctx, userID, "app")`
     - `ids := keys(perms)`
     - 业务层：`db.Where("id IN ?", ids).Find(&apps)`
   - **方式三（混合模式）**：
     - 同时查询 owner_id 和 授权列表。
     - `perms, _ := auth.ListResources(...)`
     - `ids := keys(perms)`
     - `db.Where("owner_id = ? OR id IN ?", userID, ids).Find(&apps)`
