diff --git a/doc/README.md b/doc/README.md index 0ee6fe8..9b27b22 100644 --- a/doc/README.md +++ b/doc/README.md @@ -7,9 +7,40 @@ | 文档 | 说明 | |------|------| | [API 文档](./api.md) | 完整的 API 接口文档,包含请求/响应格式 | -| [架构设计](./architecture.md) | 系统架构、模块设计、数据模型 | +| [架构设计](./architecture.md) | 系统架构、模块设计、数据模型、BaaS 资源管理 | | [部署指南](./deployment.md) | 环境配置、Docker 部署、运维指南 | -| [权限系统](./permission.md) | RBAC+ABAC 权限模型使用指南 | +| [权限系统](./permission.md) | RBAC+ABAC 权限模型、三级权限体系使用指南 | + +## 项目定位:可私有化部署的 IAM + BaaS 产品 + +VBase 是一个 **产品化的 IAM + BaaS 平台**,你可以将其**售卖/部署**给终端客户: + +### 三层级架构 + +| 层级 | 角色 | 说明 | +|------|------|------| +| **平台层** | 平台运营商(你) | 管理 VBase 产品,提供部署和运维服务 | +| **实例层** | 客户(2C/2B/混合) | 独立部署 VBase 实例,完全隔离 | +| **业务层** | 终端用户 | 使用客户的产品,与你无直接关系 | + +### 两种交付模式 + +1. **托管部署**:你在自己的云基础设施上为客户部署 VBase 实例,负责运维 +2. **客户自托管**:客户在自己的环境中部署,数据完全自主可控 + +### 客户类型 + +- **2C 产品公司**(社交 App、电商平台):用 VBase 管理用户注册、登录、个人数据 +- **2B 产品公司**(SaaS、企业软件):用 VBase 做多租户隔离、组织架构、权限管理 +- **混合产品公司**(电商平台、O2O):同时管理 C 端用户和 B 端企业 + +## 核心特性 + +- **项目-资源隔离**: 多项目数据隔离,支持个人/团队场景 +- **三级权限体系**: 平台级 → 项目级 → 资源级 +- **RBAC + ABAC**: 角色与策略结合的细粒度权限控制 +- **Redis 缓存**: 权限缓存 1 分钟 TTL,平衡性能与实时性 +- **资源级权限**: 数据库、存储、函数、API 密钥的细粒度控制 ## 快速开始 diff --git a/doc/architecture.md b/doc/architecture.md index 25049df..80cb3e7 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -4,13 +4,47 @@ VBase 是一个类似 **Supabase** 的 **Backend-as-a-Service (BaaS)** 平台,为个人开发者(2C)和团队/企业(2B)提供后端资源托管和权限管理服务。采用 Go 语言开发,基于 Vigo 框架构建。 -### 1.1 产品定位 +### 1.1 产品定位:可私有化部署的 IAM + BaaS 平台 -| 用户类型 | 使用场景 | 功能需求 | -|----------|----------|----------| -| **个人开发者 (2C)** | 创建个人项目,管理应用后端资源 | 项目管理、资源隔离、API 访问 | -| **团队/初创 (2B)** | 团队协作开发,共享项目资源 | 成员管理、角色权限、组织隔离 | -| **企业客户 (2B)** | 多项目/多团队管理,合规要求 | 组织架构、细粒度权限、审计日志 | +VBase 是一个**产品化**的解决方案,你可以将 VBase **售卖/部署**给终端客户,客户在自己的环境中独立运行 VBase 实例,完全隔离地管理自己的业务。 + +#### 三层级架构模型 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Layer 1: VBase 平台运营层 (你的 SaaS 平台) │ +│ - 你是 VBase 产品的提供商 │ +│ - 功能:实例管理、计费、监控、版本分发 │ +│ - 部署模式:托管部署 or 客户自托管 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ 部署/交付 +┌─────────────────────────────────────────────────────────────┐ +│ Layer 2: VBase 实例层 (客户的独立部署) │ +│ - 每个客户拥有一个完全独立的 VBase 实例 │ +│ - 独立数据库、独立用户体系、独立配置 │ +│ - 客户可以是:2C 产品 / 2B 产品 / 混合产品 │ +│ - 管理员:客户的技术负责人/运维 │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ 使用 +┌─────────────────────────────────────────────────────────────┐ +│ Layer 3: 客户业务层 (与你无关) │ +│ - 客户用 VBase 管理自己的终端用户和业务项目 │ +│ ├─ 2C 场景:App 用户注册登录、个人数据管理 │ +│ ├─ 2B 场景:企业内部系统、团队协作 │ +│ └─ 混合场景:既有终端用户又有内部团队 │ +│ - 用户数据完全属于客户,与你无关 │ +└─────────────────────────────────────────────────────────────┘ +``` + +#### 目标客户类型 + +| 客户类型 | 场景说明 | VBase 提供的价值 | +|----------|----------|-----------------| +| **2C 产品公司** | 社交 App、电商平台、内容社区等 | 用户认证、个人数据管理、权限控制 | +| **2B 产品公司** | SaaS 软件、企业管理系统 | 多租户隔离、组织架构、RBAC 权限 | +| **混合产品公司** | 既有 C 端用户又有 B 端管理 | 统一身份管理、分级权限控制 | ### 1.2 核心特性 @@ -35,16 +69,28 @@ VBase 是一个类似 **Supabase** 的 **Backend-as-a-Service (BaaS)** 平台, ### 1.4 概念说明 -**项目 (Project/Org)**: 资源管理的基本单元 -- 个人开发者创建的项目 = 个人项目 -- 团队创建的项目 = 团队项目 -- 企业可以创建多个项目,也可以使用组织层级管理 +#### 三层级角色体系 -**用户 (User)**: 平台注册用户 +| 层级 | 角色 | 说明 | 权限范围 | +|------|------|------|----------| +| **平台层** | 平台管理员 | VBase 产品提供商(你) | 管理所有 VBase 实例 | +| **实例层** | 实例管理员 | 客户的技术负责人 | 管理自己的 VBase 实例配置 | +| **业务层** | 终端用户 | 客户的 C 端用户或 B 端成员 | 使用客户的产品功能 | + +#### VBase 实例内部概念 + +在一个独立的 VBase 实例内部,包含以下核心概念: + +**用户 (User)**: VBase 实例中的注册用户 - 可以创建多个项目 - 可以被邀请加入其他项目 - 在不同项目中可以有不同的角色 +**项目/组织 (Project/Org)**: 资源管理的基本单元 +- 对 2C 场景:对应一个 App 或产品 +- 对 2B 场景:对应一个企业或部门 +- 企业可以创建多个项目,也可以使用组织层级管理 + **资源 (Resource)**: 项目内的后端资源 - 数据库、存储、函数、API 等 - 属于特定项目 @@ -115,7 +161,99 @@ VBase 是一个类似 **Supabase** 的 **Backend-as-a-Service (BaaS)** 平台, --- -## 3. 目录结构 +## 3. 部署模式:VBase 作为产品交付 + +VBase 设计为可私有化部署的产品,支持两种交付模式: + +### 3.1 模式一:托管部署(你帮客户部署运维) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 你的云基础设施 │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ 客户A实例 │ │ 客户B实例 │ │ 客户C实例 │ │ +│ │ (2C产品) │ │ (2B产品) │ │ (混合产品) │ │ +│ │ │ │ │ │ │ │ +│ │ DB: A_db │ │ DB: B_db │ │ DB: C_db │ │ +│ │ Redis: A_1 │ │ Redis: B_1 │ │ Redis: C_1 │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +**特点:** +- 你在自己的云基础设施上为客户部署独立的 VBase 实例 +- 每个实例完全隔离(独立数据库、独立 Redis) +- 你负责运维,客户只管使用 +- 可按照实例数量、资源使用量计费 + +**适用客户:** 没有运维能力的小团队,希望快速使用 + +### 3.2 模式二:客户自托管(客户自己部署) + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 客户 A 的基础设施 │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ VBase 实例 │ │ +│ │ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ 用户认证 │ │ 权限管理 │ │ │ +│ │ │ (2C用户) │ │ (2B组织) │ │ │ +│ │ └──────────────┘ └──────────────┘ │ │ +│ │ │ │ +│ │ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ 数据库资源 │ │ 存储资源 │ │ │ +│ │ └──────────────┘ └──────────────┘ │ │ +│ └─────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────┘ +``` + +**特点:** +- 客户在自己的环境中部署 VBase(云服务器、私有云、K8s) +- 数据完全属于客户,与你无关 +- 客户自己运维,或者购买你的运维支持服务 +- 通常按许可(License)或订阅模式收费 + +**适用客户:** 有数据合规要求的企业,有自己运维能力的团队 + +### 3.3 两种模式的对比 + +| 维度 | 托管部署 | 客户自托管 | +|------|----------|------------| +| **部署位置** | 你的云基础设施 | 客户的基础设施 | +| **数据归属** | 客户拥有,你代管 | 客户完全拥有 | +| **运维责任** | 你负责 | 客户负责 | +| **计费模式** | 按用量/实例数 | 按 License/订阅 | +| **定制化** | 受限 | 完全可定制 | +| **网络要求** | 需要公网访问 | 可内网部署 | + +### 3.4 关键设计:完全隔离 + +无论哪种模式,**每个 VBase 实例都是完全独立的**: + +```go +// 实例级别的完全隔离 +// - 独立的数据库连接 +// - 独立的 Redis 命名空间 +// - 独立的 JWT 密钥 +// - 独立的配置 + +type VBaseInstance struct { + InstanceID string // 实例唯一标识 + DatabaseDSN string // 独立数据库 + RedisPrefix string // Redis 前缀隔离 + JWTSecret string // 独立 JWT 密钥 + Config InstanceConfig +} +``` + +这种设计确保: +1. 客户 A 的数据与客户 B 完全隔离 +2. 一个实例的故障不影响其他实例 +3. 客户可以独立升级、备份、迁移自己的实例 + +--- + +## 4. 目录结构 ``` vbase/ @@ -318,6 +456,95 @@ VBase 中的 "Org" 概念灵活适配不同场景: └── 项目3 (level=2, Org) ``` +### 4.4 典型客户场景示例 + +以下示例展示 VBase 作为产品交付的完整链路: + +#### 示例1: 2C 产品公司(社交 App) + +``` +你(VBase 提供商) +│ +├─ 售卖 VBase 实例给「社交App公司」 +│ +▼ +客户:社交App公司(2C产品) +│ +├─ 部署 VBase 实例到自己的服务器(自托管模式) +│ +├─ 技术负责人配置 VBase: +│ ├─ 开启 App 用户注册/登录 +│ ├─ 配置数据库存储用户资料 +│ └─ 设置存储桶保存用户头像 +│ +▼ +终端用户:社交 App 的普通用户 + ├─ 注册账号(存储在 VBase) + ├─ 登录获取 JWT Token + ├─ 上传头像到 VBase 存储 + └─ 查询好友列表(VBase 数据库) +``` + +**关键点:** +- 社交App公司的用户数据完全在自己的 VBase 实例中 +- 你作为 VBase 提供商,接触不到终端用户数据 +- 社交App公司按需购买 License 或订阅服务 + +#### 示例2: 2B 产品公司(SaaS 软件) + +``` +你(VBase 提供商) +│ +├─ 托管部署 VBase 实例给「企业SaaS公司」 +│ (部署在你的云服务器上) +│ +▼ +客户:企业SaaS公司(2B产品) +│ +├─ 使用 VBase 管理自己的企业客户 +│ ├─ 每个企业客户 = VBase 中的一个 Org +│ ├─ 企业内的部门 = Org 下的子组织 +│ └─ 企业员工 = OrgMember +│ +▼ +终端用户:SaaS 软件的企业用户 + ├─ 企业 A 的员工使用 VBase 登录 SaaS + ├─ 按角色分配权限(管理员/普通员工) + └─ 企业间的数据通过 VBase 完全隔离 +``` + +**关键点:** +- SaaS 公司用 VBase 做租户隔离(多租户架构) +- 你帮 SaaS 公司运维 VBase 实例,按实例数/用户量收费 +- SaaS 公司的终端企业用户与你无直接关系 + +#### 示例3: 混合产品公司(2C + 2B) + +``` +你(VBase 提供商) +│ +├─ 售卖两个 VBase 实例给「电商平台公司」 +│ (一个给 C 端,一个给 B 端管理后台) +│ +▼ +客户:电商平台公司(混合产品) +│ +├─ 实例1:C 端用户系统 +│ ├─ 消费者注册/登录 +│ ├─ 购物车、订单数据 +│ └─ 用户画像存储 +│ +└─ 实例2:B 端商家管理系统 + ├─ 商家入驻/审核 + ├─ 商家员工权限管理 + └─ 运营数据分析 +``` + +**关键点:** +- 同一客户的不同业务线可以用独立 VBase 实例 +- 完全物理隔离,符合数据合规要求 +- 你提供统一的管理控制台给客户技术团队 + #### 数据隔离 - **项目隔离**: 每个 Org 是独立的资源边界 @@ -325,7 +552,53 @@ VBase 中的 "Org" 概念灵活适配不同场景: - **资源归属**: 所有资源通过 `org_id` 归属到具体项目 - **跨项目访问**: 需要显式授权,默认隔离 -### 4.4 OAuth2.0 服务端 +### 4.5 BaaS 资源管理模块 + +作为 Supabase-like BaaS 平台,VBase 提供以下后端资源管理能力: + +#### 资源类型 + +| 资源类型 | 说明 | 典型操作 | +|----------|------|----------| +| **Database** | 数据库表、集合 | 创建表、查询、修改结构、删除表 | +| **Storage** | 文件存储 | 上传、下载、删除、管理存储桶 | +| **Function** | 边缘函数/云函数 | 部署、调用、更新、删除 | +| **API Key** | 访问密钥 | 创建、删除、刷新、权限绑定 | + +#### 资源权限模型 + +``` +项目 (Org) +├── 资源组 (Resource Group) +│ ├── 数据库资源 +│ │ ├── table_1 (owner: user_a) +│ │ └── table_2 (owner: user_b) +│ ├── 存储资源 +│ │ ├── bucket_images (public) +│ │ └── bucket_private (private) +│ └── 函数资源 +│ ├── function_api (callable) +│ └── function_cron (internal) +``` + +#### 资源访问控制 + +```go +// 资源操作前检查权限 +func AccessResource(userID, orgID, resourceType, action string) error { + checker := service.NewPermissionChecker() + result, err := checker.Check(userID, orgID, resourceType, action, nil) + if err != nil || !result.Allowed { + return vigo.ErrForbidden.WithString(result.Reason) + } + + // 额外检查资源级别的细粒度权限 + // 例如:只能修改自己创建的表 + return nil +} +``` + +### 4.5 OAuth2.0 服务端 #### 支持的授权流程 diff --git a/doc/permission.md b/doc/permission.md index 9d8f334..e6f13db 100644 --- a/doc/permission.md +++ b/doc/permission.md @@ -161,79 +161,105 @@ type Role struct { |------|------|----------| | "" | 无条件,始终匹配 | 通用权限 | | "owner" | 资源所有者 | 只能操作自己的数据 | -| "org_member" | 组织成员 | 组织内数据访问 | -| "org_owner" | 组织所有者 | 所有者权限判断 | +| "org_member" | 项目成员 | 项目内数据访问 | +| "org_owner" | 项目所有者 | 项目所有者权限判断 | --- ## 3. 权限检查流程 -### 3.1 流程图 - -``` -┌─────────────────┐ -│ 请求进入 │ -│ user, org, │ -│ resource, action│ -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ 命中 ┌─────────────────┐ -│ 检查缓存 │────────────→│ 返回缓存结果 │ -│ Redis │ │ allow/deny │ -└────────┬────────┘ └─────────────────┘ - │ 未命中 - ▼ -┌─────────────────┐ -│ 1. 检查组织所有者 │────────┐ -└────────┬────────┘ │ - │ 是 │ - ▼ │ - ┌──────────┐ │ - │ 返回 Allow│────────────┘ - └──────────┘ - │ 否 - ▼ -┌─────────────────┐ -│ 2. 获取用户角色 │ -│ 从 OrgMember │ -│ 获取 role_ids │ -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 3. 获取角色策略 │ -│ 解析所有策略 │ -│ 去重合并 │ -└────────┬────────┘ - │ - ▼ -┌─────────────────┐ -│ 4. 检查 Deny 策略│────────┐ -│ 高优先级优先 │ │ -└────────┬────────┘ │ - │ 匹配 Deny │ - ▼ │ - ┌──────────┐ │ - │返回 Deny │────────────┘ - │ 缓存结果 │ - └──────────┘ - │ 无 Deny - ▼ -┌─────────────────┐ -│ 5. 检查 Allow 策略│────────┐ -└────────┬────────┘ │ - │ 匹配 Allow │ - ▼ │ - ┌──────────┐ │ - │返回 Allow│────────────┘ - │ 缓存结果 │ - └──────────┘ - │ 无 Allow - ▼ -┌─────────────────┐ -│ 6. 默认拒绝 │────────→ 返回 Deny -└─────────────────┘ 缓存结果 +### 3.1 权限检查流程 + +根据是否有项目上下文 (`org_id`),权限检查分为两种方式: + +#### 流程 A: 平台级权限检查 (无项目上下文) + +``` +┌─────────────────────────────┐ +│ 请求进入 (无 org_id) │ +│ user, resource, action │ +└────────────┬────────────────┘ + │ + ▼ +┌─────────────────────────────┐ 命中 ┌─────────────────┐ +│ 检查缓存 │────────────→│ 返回缓存结果 │ +└────────────┬────────────────┘ └─────────────────┘ + │ 未命中 + ▼ +┌─────────────────────────────┐ +│ 1. 检查是否是资源所有者 │ +│ owner_id == user_id │ +└────────────┬────────────────┘ + │ 是 + ▼ + ┌──────────┐ + │ 返回 Allow│ + └──────────┘ + │ 否 + ▼ +┌─────────────────────────────┐ +│ 2. 加载平台级默认策略 │ +│ personal:* 策略 │ +└────────────┬────────────────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 3. 评估策略 (Deny优先) │ +└────────────┬────────────────┘ + │ + ┌─────┴─────┐ + ▼ ▼ + ┌───────┐ ┌───────┐ + │ Deny │ │ Allow │ + └───────┘ └───────┘ +``` + +#### 流程 B: 项目级权限检查 (有项目上下文) + +``` +┌─────────────────────────────┐ +│ 请求进入 (有 org_id) │ +│ user, org_id, resource... │ +└────────────┬────────────────┘ + │ + ▼ +┌─────────────────────────────┐ 命中 ┌─────────────────┐ +│ 检查缓存 │────────────→│ 返回缓存结果 │ +└────────────┬────────────────┘ └─────────────────┘ + │ 未命中 + ▼ +┌─────────────────────────────┐ +│ 1. 检查是否是项目所有者 │ +│ org.owner_id == user_id │────────┐ +└────────────┬────────────────┘ │ + │ 是 │ + ▼ │ + ┌──────────┐ │ + │ 返回 Allow│───────────────────┘ + └──────────┘ + │ 否 + ▼ +┌─────────────────────────────┐ +│ 2. 获取用户在项目的角色 │ +│ 从 OrgMember 表查询 │ +└────────────┬────────────────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 3. 获取角色关联的策略 │ +└────────────┬────────────────┘ + │ + ▼ +┌─────────────────────────────┐ +│ 4. 评估策略 (Deny优先) │ +│ Deny策略 → Allow策略 │ +└────────────┬────────────────┘ + │ + ┌─────┴─────┐ + ▼ ▼ + ┌───────┐ ┌───────┐ + │ Deny │ │ Allow │ + └───────┘ └───────┘ ``` ### 3.2 决策规则 @@ -247,63 +273,134 @@ type Role struct { ## 4. 使用指南 -### 4.1 检查权限 +### 4.1 平台级权限检查 + +**场景**:用户管理自己的账号、创建新项目 + +```go +// 检查用户是否可以更新自己的信息 +// orgID 传空字符串,表示平台级检查 +result, err := checker.Check(userID, "", "user", "update", map[string]any{ + "owner_id": userID, // 传入自己的ID +}) +// 结果:允许(满足 owner 条件) + +// 检查用户是否可以创建新项目 +result, err := checker.Check(userID, "", "org", "create", nil) +// 结果:允许(平台级默认策略允许创建项目) + +// 检查用户是否可以更新其他用户信息 +result, err := checker.Check(userID, "", "user", "update", map[string]any{ + "owner_id": otherUserID, // 传入他人ID +}) +// 结果:拒绝(不满足 owner 条件) +``` + +### 4.2 项目级权限检查 -**代码示例:** +**场景**:在项目内操作资源 ```go -import "github.com/veypi/vbase/internal/service" +// 检查用户在项目内是否有管理员权限 +result, err := checker.Check(userID, "project-123", "member", "invite", nil) +// 结果取决于用户在 project-123 中的角色 + +// 项目所有者检查(快速路径) +// 如果 userID == org.OwnerID,直接返回允许 + +// 普通成员检查 +// 1. 查询 OrgMember 获取角色 +// 2. 查询 Role 获取策略 +// 3. 评估策略 +``` + +### 4.3 完整示例:更新用户信息 -func SomeHandler(x *vigo.X) error { +```go +func UpdateUser(x *vigo.X, req *UpdateRequest) error { userID := middleware.CurrentUser(x) - orgID := middleware.CurrentOrg(x) + targetUserID := req.UserID checker := service.NewPermissionChecker() - result, err := checker.Check(userID, orgID, "user", "update", map[string]any{ - "owner_id": targetUserID, + + // 场景1: 更新自己的信息(平台级) + if targetUserID == userID { + result, err := checker.Check(userID, "", "user", "update", map[string]any{ + "owner_id": userID, + }) + if err != nil || !result.Allowed { + return vigo.ErrForbidden.WithString(result.Reason) + } + // 执行更新... + return nil + } + + // 场景2: 管理员更新其他用户信息(需要平台级管理员权限) + result, err := checker.Check(userID, "", "user", "update", nil) + if err != nil || !result.Allowed { + return vigo.ErrForbidden.WithString("only admin can update other users") + } + // 执行更新... + return nil +} +``` + +### 4.4 完整示例:项目内操作 + +```go +func DeleteProjectResource(x *vigo.X, req *DeleteResourceRequest) error { + userID := middleware.CurrentUser(x) + orgID := middleware.CurrentOrg(x) // 从请求头 X-Org-ID 获取 + + if orgID == "" { + return vigo.ErrArgInvalid.WithString("org_id required") + } + + checker := service.NewPermissionChecker() + + // 检查用户在项目内是否有删除资源的权限 + result, err := checker.Check(userID, orgID, "resource", "delete", map[string]any{ + "resource_id": req.ResourceID, }) if err != nil || !result.Allowed { return vigo.ErrForbidden.WithString(result.Reason) } - // 继续处理... + // 执行删除... + return nil } ``` -### 4.2 创建自定义策略 +### 4.5 创建自定义策略 ```go +// 项目级策略(需要关联到具体项目) policy := &model.Policy{ - Code: "custom:resource:action", - Name: "自定义策略", - Description: "允许特定用户操作", - Resource: "resource_name", - Action: "action_name", // create/read/update/delete + Code: "custom:db:admin", + Name: "数据库管理员", + Description: "管理项目数据库", + Resource: "database", + Action: "*", // 所有操作 Effect: model.EffectAllow, - Condition: "owner", // owner/org_member/空 - Priority: 50, + Condition: "", // 无条件限制 + Scope: "project", // 项目级策略 + OrgID: "project-123", // 所属项目 } - model.DB.Create(policy) -``` -### 4.3 创建角色并授权 - -```go -// 创建角色 +// 将策略添加到角色 role := &model.Role{ - OrgID: orgID, - Name: "部门管理员", - Description: "管理部门成员", - PolicyIDs: "policy1,policy2,policy3", + OrgID: "project-123", + Name: "DBAdmin", + PolicyIDs: "custom:db:admin", } model.DB.Create(role) -// 分配给用户 +// 分配角色给用户 member := &model.OrgMember{ - OrgID: orgID, - UserID: userID, + OrgID: "project-123", + UserID: "user-456", RoleIDs: role.ID, } model.DB.Create(member) @@ -381,19 +478,102 @@ Condition: "org_member" SysPolicyRoleManage = "sys:role:manage" Resource: "role" Action: "*" -Condition: "" +Condition: "admin" // 读取策略 SysPolicyPolicyRead = "sys:policy:read" Resource: "policy" Action: "read" -Condition: "" +Condition: "org_member" // 管理策略 SysPolicyPolicyManage = "sys:policy:manage" Resource: "policy" Action: "*" -Condition: "" +Condition: "admin" +``` + +### 5.5 资源级策略(BaaS 核心资源) + +**数据库资源 (database)** + +```go +// 查询数据库 +SysPolicyDBRead = "sys:db:read" +Resource: "database" +Action: "read" +Condition: "org_member" + +// 管理数据库(创建表、修改结构) +SysPolicyDBManage = "sys:db:manage" +Resource: "database" +Action: "*" +Condition: "developer" +``` + +**存储资源 (storage)** + +```go +// 读取存储文件 +SysPolicyStorageRead = "sys:storage:read" +Resource: "storage" +Action: "read" +Condition: "org_member" + +// 上传/删除文件 +SysPolicyStorageWrite = "sys:storage:write" +Resource: "storage" +Action: "create,update,delete" +Condition: "developer" + +// 管理存储桶 +SysPolicyStorageAdmin = "sys:storage:admin" +Resource: "storage" +Action: "*" +Condition: "admin" +``` + +**函数资源 (function)** + +```go +// 调用函数 +SysPolicyFunctionCall = "sys:function:call" +Resource: "function" +Action: "call" +Condition: "org_member" + +// 管理函数(部署、删除) +SysPolicyFunctionManage = "sys:function:manage" +Resource: "function" +Action: "create,update,delete" +Condition: "developer" +``` + +**API 密钥管理 (apikey)** + +```go +// 读取 API 密钥(只能读取自己创建的密钥) +SysPolicyAPIKeyRead = "sys:apikey:read" +Resource: "apikey" +Action: "read" +Condition: "owner" + +// 管理 API 密钥 +SysPolicyAPIKeyManage = "sys:apikey:manage" +Resource: "apikey" +Action: "*" +Condition: "developer" +``` + +### 5.6 平台超级管理员策略 + +```go +// 超级管理员 - 拥有所有权限 +SysPolicySuperAdmin = "sys:super:admin" +Resource: "*" +Action: "*" +Condition: "is_super_admin" +Priority: 999 ``` --- @@ -440,18 +620,84 @@ service.ClearOrgPermissionCache(orgID) 2. **职责分离**: 敏感操作需要多个角色共同完成 3. **策略命名规范**: `{resource}:{action}:{condition}` -### 7.2 角色设计 +### 7.2 BaaS 平台角色设计 +VBase 作为 Supabase-like BaaS 平台,角色设计覆盖 **2C个人开发者** 和 **2B团队/企业** 场景: + +#### 2C 个人开发者场景 + +``` +个人项目角色体系: +├── owner (项目所有者) +│ └── 全部权限(可以删除项目) +└── developer (开发者) + └── 读写资源、调用 API ``` -组织层级角色体系: + +个人开发者通常自己就是项目所有者,不需要复杂的角色体系。 + +#### 2B 团队协作场景 + +``` +团队项目角色体系: ├── owner (所有者) -│ └── 全部权限 +│ └── 全部权限、可删除项目、管理账单 ├── admin (管理员) -│ └── 管理成员、角色、策略 -├── manager (部门经理) -│ └── 管理部门成员 -└── member (普通成员) - └── 基础访问权限 +│ └── 管理成员、角色、资源设置 +├── developer (开发者) +│ ├── 数据库: 创建表、查询、修改结构 +│ ├── 存储: 上传/下载文件、管理存储桶 +│ ├── 函数: 部署、调用、删除函数 +│ └── API密钥: 创建、管理 +├── viewer (访客/只读) +│ └── 查看数据、只读访问 +└── auditor (审计员 - 可选) + └── 查看日志、审计记录 +``` + +#### 角色权限对照表 + +| 角色 | 成员管理 | 资源管理 | 数据读写 | API密钥 | 项目设置 | +|------|----------|----------|----------|---------|----------| +| **owner** | ✓ | ✓ | ✓ | ✓ | ✓ | +| **admin** | ✓ | ✓ | ✓ | ✓ | ✗ | +| **developer** | ✗ | ✓ | ✓ | ✓ | ✗ | +| **viewer** | ✗ | ✗ | 只读 | ✗ | ✗ | + +#### 系统角色配置示例 + +```go +// Owner 角色 - 绑定所有权限 +var RoleOwner = model.Role{ + Name: "owner", + Description: "项目所有者", + PolicyIDs: "sys:org:admin,sys:member:manage,sys:role:manage,sys:policy:manage", + IsSystem: true, +} + +// Admin 角色 - 管理权限 +var RoleAdmin = model.Role{ + Name: "admin", + Description: "项目管理员", + PolicyIDs: "sys:member:manage,sys:role:read,sys:db:manage,sys:storage:admin,sys:function:manage", + IsSystem: true, +} + +// Developer 角色 - 开发权限 +var RoleDeveloper = model.Role{ + Name: "developer", + Description: "开发者", + PolicyIDs: "sys:db:manage,sys:storage:write,sys:function:manage,sys:apikey:manage", + IsSystem: true, +} + +// Viewer 角色 - 只读权限 +var RoleViewer = model.Role{ + Name: "viewer", + Description: "访客", + PolicyIDs: "sys:org:read,sys:db:read,sys:storage:read,sys:function:call", + IsSystem: true, +} ``` ### 7.3 权限检查位置 @@ -478,7 +724,79 @@ func UpdateUser(userID string, data map[string]any) error { } ``` -### 7.4 权限测试 +### 7.4 BaaS 平台权限场景示例 + +**场景1: 个人开发者创建项目** + +```go +// 小明是个人开发者,注册后创建项目 +// 1. 创建项目(平台级权限检查) +result, _ := checker.Check("xiaoming_user_id", "", "org", "create", nil) +// 结果: 允许(personal:org:create 策略) + +// 2. 小明成为项目所有者 +// org.owner_id = "xiaoming_user_id" + +// 3. 小明在项目内操作数据库(项目级权限) +result, _ = checker.Check("xiaoming_user_id", "my-project", "database", "create", nil) +// 结果: 允许(小明是项目所有者) +``` + +**场景2: 团队协作开发** + +```go +// CEO 创建 WebApp 项目,邀请 CTO 为管理员,员工为开发者 +// - CEO: owner +// - CTO: admin +// - 员工A: developer +// - 外包人员: viewer + +// CTO 邀请成员(有 member:manage 权限) +result, _ := checker.Check("cto_user_id", "webapp-project", "member", "create", nil) +// 结果: 允许(admin 角色有 member:manage 策略) + +// 员工A 创建数据库表(有 db:manage 权限) +result, _ = checker.Check("employee_a_id", "webapp-project", "database", "create", nil) +// 结果: 允许(developer 角色有 db:manage 策略) + +// 外包人员 尝试删除表(viewer 没有 delete 权限) +result, _ = checker.Check("contractor_id", "webapp-project", "database", "delete", nil) +// 结果: 拒绝(viewer 只有 db:read 权限) +``` + +**场景3: API 密钥访问控制** + +```go +// 开发者创建 API 密钥用于后端服务 +// 只能查看自己创建的密钥 + +result, _ := checker.Check("developer_id", "project-123", "apikey", "read", map[string]any{ + "owner_id": "developer_id", +}) +// 结果: 允许(满足 owner 条件) + +// 尝试查看他人创建的密钥 +result, _ = checker.Check("developer_id", "project-123", "apikey", "read", map[string]any{ + "owner_id": "other_developer_id", +}) +// 结果: 拒绝(不满足 owner 条件) +``` + +**场景4: 跨项目资源访问** + +```go +// 用户小明在 project-A 是开发者,在 project-B 是访客 + +// 在 project-A 上传文件 +result, _ := checker.Check("xiaoming_id", "project-A", "storage", "create", nil) +// 结果: 允许(developer 角色有 storage:write 权限) + +// 在 project-B 上传文件 +result, _ = checker.Check("xiaoming_id", "project-B", "storage", "create", nil) +// 结果: 拒绝(viewer 角色只有 storage:read 权限) +``` + +### 7.5 权限测试 ```go func TestPermissionCheck(t *testing.T) { @@ -531,11 +849,20 @@ func TestPermissionCheck(t *testing.T) { **检查步骤:** +**平台级权限检查失败(org_id 为空):** + 1. 确认用户已登录 -2. 确认用户是组织成员 -3. 检查用户角色 -4. 检查角色绑定的策略 -5. 检查策略的 Effect 和 Condition +2. 确认操作的是自己的资源(检查 owner_id 条件) +3. 检查用户是否有平台级管理员权限 +4. 检查默认个人策略是否生效 + +**项目级权限检查失败(有 org_id):** + +1. 确认用户是项目成员(OrgMember 状态为 active) +2. 检查用户角色是否正确分配 +3. 检查角色绑定的策略 +4. 检查策略的 Effect 和 Condition +5. 确认资源所属的项目是否正确 **调试日志:** @@ -584,18 +911,84 @@ func (pc *PermissionChecker) evaluateCondition(p *model.Policy, userID, orgID st } ``` -### 9.2 添加资源类型 +### 9.2 添加 BaaS 资源类型 + +**步骤1: 定义资源常量** ```go // 在 policy.go 中添加资源常量 const ( - ResourceUser = "user" - ResourceOrg = "org" - ResourceMember = "member" - ResourceCustom = "custom_resource" // 新增 + ResourceUser = "user" + ResourceOrg = "org" + ResourceMember = "member" + ResourceDatabase = "database" + ResourceStorage = "storage" + ResourceFunction = "function" + ResourceAPIKey = "apikey" + ResourceCustom = "custom_resource" // 新增资源类型 ) ``` +**步骤2: 创建资源级策略** + +```go +// 为新增资源创建策略 +func (pc *PermissionChecker) getCustomResourcePolicies() []model.Policy { + return []model.Policy{ + { + Code: "custom:read", + Name: "读取自定义资源", + Resource: ResourceCustom, + Action: "read", + Effect: model.EffectAllow, + Condition: "org_member", + }, + { + Code: "custom:manage", + Name: "管理自定义资源", + Resource: ResourceCustom, + Action: "create,update,delete", + Effect: model.EffectAllow, + Condition: "developer", + }, + } +} +``` + +**步骤3: 在角色中使用** + +```go +// 创建新角色使用自定义策略 +customRole := &model.Role{ + OrgID: orgID, + Name: "custom_manager", + Description: "自定义资源管理员", + PolicyIDs: "custom:read,custom:manage", + Scope: "resource", +} +``` + +### 9.3 多租户资源隔离 + +在 BaaS 平台中,资源需要严格的项目隔离: + +```go +// 查询时自动添加 org_id 过滤 +func ListResources(userID, orgID string) ([]Resource, error) { + // 检查用户在项目中的权限 + checker := service.NewPermissionChecker() + result, err := checker.Check(userID, orgID, "custom_resource", "read", nil) + if err != nil || !result.Allowed { + return nil, vigo.ErrForbidden + } + + // 查询该项目的资源(自动隔离) + var resources []Resource + err = model.DB.Where("org_id = ?", orgID).Find(&resources).Error + return resources, err +} +``` + --- ## 10. 参考