# Permission Module

一個完整的 Go 權限管理模組，提供 JWT 令牌管理、RBAC（基於角色的訪問控制）以及權限樹管理功能。

## 📋 目錄

- [功能特性](#功能特性)
- [架構設計](#架構設計)
- [目錄結構](#目錄結構)
- [快速開始](#快速開始)
- [API 文檔](#api-文檔)
- [配置說明](#配置說明)
- [測試](#測試)
- [最佳實踐](#最佳實踐)

## 🎯 功能特性

### 1. JWT 令牌管理
- ✅ Access Token 與 Refresh Token 機制
- ✅ One-Time Token 支持（一次性令牌）
- ✅ 設備追蹤與管理
- ✅ 令牌黑名單機制
- ✅ 多設備登錄限制
- ✅ 令牌自動過期與刷新

### 2. RBAC 權限控制
- ✅ 層級式權限結構（權限樹）
- ✅ 角色與權限關聯管理
- ✅ 使用者與角色關聯管理
- ✅ 動態權限檢查
- ✅ HTTP API 權限映射
- ✅ 權限繼承（父權限自動包含）

### 3. 資料持久化
- ✅ Redis 令牌存儲與快取
- ✅ MongoDB 權限/角色數據存儲
- ✅ 批量查詢優化（避免 N+1）
- ✅ 軟刪除支持

### 4. 安全特性
- ✅ JWT 簽名驗證
- ✅ 令牌黑名單
- ✅ 設備指紋追蹤
- ✅ 循環依賴檢測
- ✅ 管理員權限特殊處理

## 🏗️ 架構設計

本模組遵循 **Clean Architecture** 設計原則：

```
pkg/permission/
├── domain/          # 領域層（核心業務邏輯）
│   ├── entity/      # 實體定義
│   ├── repository/  # Repository 介面
│   ├── usecase/     # UseCase 介面
│   ├── config/      # 配置定義
│   ├── permission/  # 權限類型定義
│   └── token/       # 令牌類型定義
├── usecase/         # UseCase 實現層
│   ├── token.go                    # 令牌業務邏輯
│   ├── permission_usecase.go       # 權限業務邏輯
│   ├── role_usecase.go             # 角色業務邏輯
│   ├── role_permission_usecase.go  # 角色權限業務邏輯
│   ├── user_role_usecase.go        # 使用者角色業務邏輯
│   └── permission_tree.go          # 權限樹結構
├── repository/      # Repository 實現層（數據訪問）
│   ├── token_model.go         # Redis 令牌存儲
│   ├── permission.go          # MongoDB 權限存儲
│   ├── role.go                # MongoDB 角色存儲
│   ├── role_permission.go     # MongoDB 角色權限存儲
│   └── user_role.go           # MongoDB 使用者角色存儲
└── mock/            # Mock 實現（測試用）
```

### 依賴關係圖

```
┌─────────────────┐
│   HTTP Handler  │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    UseCase      │  ◄─── 業務邏輯層
│  - Token        │
│  - Permission   │
│  - Role         │
│  - UserRole     │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Repository    │  ◄─── 數據訪問層
│  - Redis        │
│  - MongoDB      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  Storage        │  ◄─── 存儲層
│  - Redis        │
│  - MongoDB      │
└─────────────────┘
```

## 📁 目錄結構

### domain/ - 領域層

#### entity/ - 實體定義
- `token.go` - 令牌實體（Token, Ticket, Blacklist）
- `permission.go` - 權限實體
- `role.go` - 角色實體
- `role_permission.go` - 角色權限關聯實體
- `user_role.go` - 使用者角色實體

#### repository/ - Repository 介面
定義數據訪問接口，與具體實現解耦

#### usecase/ - UseCase 介面
定義業務邏輯接口，規範業務操作

#### config/ - 配置定義
- `config.go` - 模組配置結構
- `errors.go` - 配置錯誤定義

#### permission/ - 權限類型
- `types.go` - 權限狀態、類型、集合定義

#### token/ - 令牌類型
- `token_type.go` - 令牌類型常量
- `grant_type.go` - 授權類型定義

### usecase/ - 業務邏輯實現

#### 令牌管理
- `token.go` - JWT 令牌生成、驗證、刷新
- `token_jwt.go` - JWT 編解碼
- `token_claims.go` - JWT Claims 處理

#### RBAC 管理
- `permission_usecase.go` - 權限管理
- `role_usecase.go` - 角色管理
- `role_permission_usecase.go` - 角色權限管理
- `user_role_usecase.go` - 使用者角色管理
- `permission_tree.go` - 權限樹結構與操作

### repository/ - 數據訪問實現

- `token_model.go` - Redis 令牌存儲實現
- `permission.go` - MongoDB 權限存儲實現
- `role.go` - MongoDB 角色存儲實現
- `role_permission.go` - MongoDB 角色權限存儲實現
- `user_role.go` - MongoDB 使用者角色存儲實現

### mock/ - Mock 實現

自動生成的 Mock 實現，用於單元測試

## 🚀 快速開始

### 1. 安裝依賴

```bash
go get backend/pkg/permission
```

### 2. 配置

```go
import (
    "backend/pkg/permission/domain/config"
    "backend/pkg/permission/usecase"
    "backend/pkg/permission/repository"
)

// 配置
cfg := &config.Config{
    Token: config.TokenConfig{
        Secret: "your-secret-key",
        Expired: config.ExpiredConfig{
            Seconds: 900, // 15 分鐘
        },
        RefreshExpires: config.ExpiredConfig{
            Seconds: 604800, // 7 天
        },
        Issuer:             "playone-backend",
        MaxTokensPerUser:   10,
        MaxTokensPerDevice: 5,
    },
    Role: config.RoleConfig{
        AdminRoleUID: "ADMIN",
        UIDPrefix:    "ROLE",
        UIDLength:    10,
    },
}
```

### 3. 初始化 Repository

```go
// Redis 令牌存儲
redisClient := redis.NewClient(&redis.Options{
    Addr: "localhost:6379",
})

tokenRepo := repository.NewTokenRepository(repository.TokenRepositoryParam{
    Redis: redisClient,
})

// MongoDB 存儲
mongoConf := &mongo.Conf{
    Host:     "localhost:27017",
    Database: "permission",
    User:     "admin",
    Password: "password",
}

permRepo := repository.NewPermissionRepository(repository.PermissionRepositoryParam{
    Conf:      mongoConf,
    CacheConf: cache.CacheConf{},
})

roleRepo := repository.NewRoleRepository(repository.RoleRepositoryParam{
    Conf:      mongoConf,
    CacheConf: cache.CacheConf{},
})

rolePermRepo := repository.NewRolePermissionRepository(repository.RolePermissionRepositoryParam{
    Conf:      mongoConf,
    CacheConf: cache.CacheConf{},
})

userRoleRepo := repository.NewUserRoleRepository(repository.UserRoleRepositoryParam{
    Conf:      mongoConf,
    CacheConf: cache.CacheConf{},
})
```

### 4. 初始化 UseCase

```go
// 權限 UseCase
permUC := usecase.NewPermissionUseCase(usecase.PermissionUseCaseParam{
    PermRepo:     permRepo,
    RolePermRepo: rolePermRepo,
    RoleRepo:     roleRepo,
    UserRoleRepo: userRoleRepo,
})

// 角色權限 UseCase
rolePermUC := usecase.NewRolePermissionUseCase(usecase.RolePermissionUseCaseParam{
    PermRepo:     permRepo,
    RolePermRepo: rolePermRepo,
    RoleRepo:     roleRepo,
    UserRoleRepo: userRoleRepo,
    PermUseCase:  permUC,
    AdminRoleUID: cfg.Role.AdminRoleUID,
})

// 角色 UseCase
roleUC := usecase.NewRoleUseCase(usecase.RoleUseCaseParam{
    RoleRepo:        roleRepo,
    UserRoleRepo:    userRoleRepo,
    RolePermUseCase: rolePermUC,
    Config: usecase.RoleUseCaseConfig{
        AdminRoleUID: cfg.Role.AdminRoleUID,
        UIDPrefix:    cfg.Role.UIDPrefix,
        UIDLength:    cfg.Role.UIDLength,
    },
})

// 使用者角色 UseCase
userRoleUC := usecase.NewUserRoleUseCase(usecase.UserRoleUseCaseParam{
    UserRoleRepo: userRoleRepo,
    RoleRepo:     roleRepo,
})

// 令牌 UseCase
tokenUC := usecase.NewTokenUseCase(usecase.TokenUseCaseParam{
    TokenRepo: tokenRepo,
    Config:    cfg,
})
```

## 📚 API 文檔

### 令牌管理 API

#### 生成令牌
```go
req := entity.AuthorizationReq{
    GrantType: token.PasswordCredentials.ToString(),
    Data: map[string]string{
        "uid":  "user123",
        "role": "admin",
    },
    DeviceID: "device123",
}

tokenResp, err := tokenUC.NewToken(ctx, req)
// tokenResp.AccessToken
// tokenResp.RefreshToken
// tokenResp.ExpiresIn
```

#### 刷新令牌
```go
req := entity.RefreshTokenReq{
    RefreshToken: "old-refresh-token",
    DeviceID:     "device123",
}

tokenResp, err := tokenUC.RefreshToken(ctx, req)
```

#### 驗證令牌
```go
isValid := tokenUC.IsAccessTokenValid(ctx, accessToken)
```

#### 取消令牌
```go
err := tokenUC.CancelToken(ctx, tokenID)
```

#### 黑名單令牌
```go
err := tokenUC.BlacklistToken(ctx, entity.BlacklistTokenReq{
    TokenID: tokenID,
    Reason:  "User logout",
})
```

### 權限管理 API

#### 獲取所有權限
```go
permissions, err := permUC.GetAll(ctx)
```

#### 獲取權限樹
```go
tree, err := permUC.GetTree(ctx)
```

#### 根據 HTTP 路徑獲取權限
```go
perm, err := permUC.GetByHTTP(ctx, "/api/users", "GET")
```

#### 展開權限（包含父權限）
```go
perms := permission.Permissions{
    "user.list": permission.Open,
}
expanded, err := permUC.ExpandPermissions(ctx, perms)
// expanded 將包含 "user" 和 "user.list"
```

### 角色管理 API

#### 創建角色
```go
req := usecase.CreateRoleRequest{
    ClientID: 1,
    Name:     "管理員",
    Permissions: permission.Permissions{
        "user.list":   permission.Open,
        "user.create": permission.Open,
    },
}

role, err := roleUC.Create(ctx, req)
```

#### 更新角色
```go
name := "高級管理員"
req := usecase.UpdateRoleRequest{
    Name: &name,
    Permissions: permission.Permissions{
        "user.list":   permission.Open,
        "user.create": permission.Open,
        "user.delete": permission.Open,
    },
}

role, err := roleUC.Update(ctx, "ROLE0000000001", req)
```

#### 刪除角色
```go
err := roleUC.Delete(ctx, "ROLE0000000001")
```

#### 分頁查詢角色
```go
filter := usecase.RoleFilterRequest{
    ClientID: 1,
    Name:     "管理",
}

page, err := roleUC.Page(ctx, filter, 1, 10)
// page.List - 角色列表（含使用者數量）
// page.Total - 總數
```

### 角色權限管理 API

#### 獲取角色權限
```go
perms, err := rolePermUC.GetByRoleUID(ctx, "ROLE0000000001")
```

#### 獲取使用者權限
```go
userPerms, err := rolePermUC.GetByUserUID(ctx, "user123")
// userPerms.RoleUID
// userPerms.RoleName
// userPerms.Permissions
```

#### 更新角色權限
```go
perms := permission.Permissions{
    "user.list":   permission.Open,
    "user.create": permission.Open,
}

err := rolePermUC.UpdateRolePermissions(ctx, "ROLE0000000001", perms)
```

#### 檢查權限
```go
result, err := rolePermUC.CheckPermission(ctx, "ROLE0000000001", "/api/users", "GET")
// result.Allowed - 是否允許
// result.PermissionName - 權限名稱
// result.PlainCode - 是否有 plain_code 權限
```

### 使用者角色管理 API

#### 指派角色給使用者
```go
req := usecase.AssignRoleRequest{
    UserUID: "user123",
    RoleUID: "ROLE0000000001",
    Brand:   "brand1",
}

userRole, err := userRoleUC.Assign(ctx, req)
```

#### 更新使用者角色
```go
userRole, err := userRoleUC.Update(ctx, "user123", "ROLE0000000002")
```

#### 移除使用者角色
```go
err := userRoleUC.Remove(ctx, "user123")
```

#### 獲取角色的所有使用者
```go
userRoles, err := userRoleUC.GetByRole(ctx, "ROLE0000000001")
```

## ⚙️ 配置說明

### Token 配置
```yaml
token:
  secret: "your-jwt-secret-key"           # JWT 簽名密鑰
  expired:
    seconds: 900                           # Access Token 過期時間（秒）
  refresh_expires:
    seconds: 604800                        # Refresh Token 過期時間（秒）
  issuer: "playone-backend"               # 發行者
  max_tokens_per_user: 10                 # 每個使用者最大令牌數
  max_tokens_per_device: 5                # 每個設備最大令牌數
  enable_device_tracking: true            # 是否啟用設備追蹤
```

### Role 配置
```yaml
role:
  admin_role_uid: "ADMIN"                 # 管理員角色 UID
  uid_prefix: "ROLE"                      # 角色 UID 前綴
  uid_length: 10                          # UID 數字長度
```

### RBAC 配置
```yaml
rbac:
  enable_cache: true                      # 是否啟用快取
  cache_ttl: 3600                         # 快取過期時間（秒）
```

## 🧪 測試

### 運行所有測試
```bash
go test ./pkg/permission/... -v
```

### 運行特定測試
```bash
# 令牌測試
go test ./pkg/permission/usecase -run TestToken -v

# 權限樹測試
go test ./pkg/permission/usecase -run TestPermissionTree -v

# Repository 測試
go test ./pkg/permission/repository -v
```

### 測試覆蓋率
```bash
go test ./pkg/permission/... -cover
```

### 生成覆蓋率報告
```bash
go test ./pkg/permission/... -coverprofile=coverage.out
go tool cover -html=coverage.out -o coverage.html
```

## 💡 最佳實踐

### 1. 令牌管理

#### ✅ 推薦做法
```go
// 使用 Refresh Token 自動刷新
if tokenUC.IsAccessTokenValid(ctx, accessToken) {
    // 使用令牌
} else {
    // 嘗試刷新
    newToken, err := tokenUC.RefreshToken(ctx, refreshReq)
    if err != nil {
        // 要求重新登錄
    }
}
```

#### ❌ 避免做法
```go
// 不要在每次請求都生成新令牌
// 不要將令牌存儲在不安全的地方
// 不要在客戶端解析 Refresh Token
```

### 2. 權限檢查

#### ✅ 推薦做法
```go
// 使用權限檢查中間件
func AuthMiddleware(rolePermUC usecase.RolePermissionUseCase) gin.HandlerFunc {
    return func(c *gin.Context) {
        roleUID := c.GetString("role_uid")
        result, err := rolePermUC.CheckPermission(
            c.Request.Context(),
            roleUID,
            c.Request.URL.Path,
            c.Request.Method,
        )
        
        if err != nil || !result.Allowed {
            c.AbortWithStatus(http.StatusForbidden)
            return
        }
        
        c.Next()
    }
}
```

#### ❌ 避免做法
```go
// 不要在每個 Handler 中重複權限檢查代碼
// 不要硬編碼權限名稱
// 不要跳過權限檢查
```

### 3. 權限設計

#### ✅ 推薦做法
```go
// 使用層級式權限結構
// 例如：
// - user
//   - user.list
//   - user.create
//   - user.update
//   - user.delete

// 父權限會自動包含，只需設置子權限即可
perms := permission.Permissions{
    "user.list": permission.Open,
}
// 展開後會自動包含 "user"
```

#### ❌ 避免做法
```go
// 不要創建過深的權限層級（建議 ≤ 3 層）
// 不要使用過於細緻的權限粒度
// 不要創建循環依賴的權限
```

### 4. 角色管理

#### ✅ 推薦做法
```go
// 使用預定義角色
const (
    RoleAdmin     = "ADMIN"
    RoleManager   = "MANAGER"
    RoleEmployee  = "EMPLOYEE"
)

// 定期檢查無使用者的角色
roles, _ := roleUC.Page(ctx, filter, 1, 100)
for _, role := range roles.List {
    if role.UserCount == 0 {
        // 考慮刪除或停用
    }
}
```

#### ❌ 避免做法
```go
// 不要創建過多的角色
// 不要刪除有使用者的角色
// 不要頻繁修改角色權限
```

## 🔐 安全建議

1. **JWT 密鑰管理**
   - 使用強密鑰（至少 32 字元）
   - 定期輪換密鑰
   - 不要將密鑰硬編碼在代碼中

2. **令牌過期設置**
   - Access Token: 15 分鐘 - 1 小時
   - Refresh Token: 7 - 30 天
   - One-Time Token: 5 - 10 分鐘

3. **設備追蹤**
   - 啟用設備指紋追蹤
   - 限制每個設備的令牌數量
   - 檢測異常登錄行為

4. **權限檢查**
   - 在所有 API 端點進行權限檢查
   - 使用白名單而非黑名單
   - 記錄權限拒絕事件

## 📝 資料庫設計

### MongoDB Collections

#### permissions
```json
{
  "_id": ObjectId,
  "parent_id": ObjectId,
  "name": "user.list",
  "http_method": "GET",
  "http_path": "/api/users",
  "status": 1,
  "type": 1,
  "create_time": 1234567890,
  "update_time": 1234567890
}
```

#### roles
```json
{
  "_id": ObjectId,
  "client_id": 1,
  "uid": "ROLE0000000001",
  "name": "管理員",
  "status": 1,
  "create_time": 1234567890,
  "update_time": 1234567890
}
```

#### role_permissions
```json
{
  "_id": ObjectId,
  "role_id": ObjectId,
  "permission_id": ObjectId,
  "create_time": 1234567890,
  "update_time": 1234567890
}
```

#### user_roles
```json
{
  "_id": ObjectId,
  "brand": "brand1",
  "uid": "user123",
  "role_id": "ROLE0000000001",
  "status": 1,
  "create_time": 1234567890,
  "update_time": 1234567890
}
```

### Redis Keys

```
permission:access_token:{token_id}         # Access Token
permission:refresh_token:{token_id}        # Refresh Token
permission:device_token:{device_id}        # Device Token List
permission:uid_token:{uid}                 # User Token List
permission:ticket:{ticket}                 # One-Time Token
permission:blacklist:{token_id}            # Token Blacklist
```