Browse Source

📝 docs(prd): add epic-006 for shared-crud data permission enhancement

- add detailed epic document for shared-crud data permission enhancement
- describe existing system context and limitations
- outline enhancement details and architecture design
- define stories for core permission features, advanced features and integration
- specify compatibility requirements and risk mitigation strategies
- include architecture design details and implementation examples
- add testing strategy and story manager handoff instructions
yourname 1 month ago
parent
commit
768ea11d33
1 changed files with 452 additions and 0 deletions
  1. 452 0
      docs/prd/epic-006-shared-crud-data-permission-enhancement.md

+ 452 - 0
docs/prd/epic-006-shared-crud-data-permission-enhancement.md

@@ -0,0 +1,452 @@
+# Epic-006: Shared-CRUD 数据权限控制增强 - Brownfield Enhancement
+
+## Epic Goal
+
+增强 shared-crud 包的数据权限控制能力,支持基于用户ID的数据访问权限控制,使业务模块能够轻松实现"用户只能操作自己的数据"的安全需求,同时保持向后兼容性和配置灵活性。
+
+## Epic Description
+
+### Existing System Context
+
+**Current relevant functionality:**
+- shared-crud 包已提供基础的 CRUD 操作和用户跟踪字段自动填充
+- 现有 `userTracking` 配置支持创建和更新时自动设置用户ID字段
+- 认证系统通过 JWT token 在上下文中提供用户信息
+- 业务模块使用 shared-crud 作为数据访问层基础设施
+
+**Current limitations:**
+- 查询操作没有基于用户ID的数据过滤
+- 删除操作没有权限验证
+- 创建操作没有权限限制(用户可以创建不属于自己的数据)
+- 更新操作没有权限验证(用户可以更新不属于自己的数据)
+- 缺乏统一的数据权限控制配置
+- 业务模块需要手动实现数据权限逻辑
+
+**Technology stack:**
+- Backend: Node.js, TypeScript, Hono, TypeORM, PostgreSQL
+- Authentication: JWT, Redis session management
+- Database: PostgreSQL with user tracking fields
+- Package: shared-crud, shared-types, shared-utils
+
+**Integration points:**
+- 现有认证中间件提供用户上下文
+- 业务模块实体中的用户关联字段(userId, createdBy, updatedBy)
+- 现有的 CRUD 路由和服务层
+- 现有的用户跟踪字段自动填充机制
+
+### Enhancement Details
+
+**What's being added/changed:**
+- 数据权限控制配置选项,支持基于用户ID的数据过滤
+- 查询操作的自动权限过滤
+- 创建操作的权限限制(防止创建不属于自己的数据)
+- 更新操作的权限验证
+- 删除操作的权限验证
+- 管理员权限覆盖机制
+- 向后兼容的权限控制配置
+
+**Enhanced architecture:**
+```
+shared-crud/
+├── src/
+│   ├── services/
+│   │   ├── generic-crud.service.ts (增强权限控制)
+│   │   └── concrete-crud.service.ts
+│   ├── routes/
+│   │   └── generic-crud.routes.ts (增强权限中间件)
+│   ├── types/
+│   │   └── data-permission.types.ts (新增权限类型定义)
+│   └── middleware/
+│       └── data-permission.middleware.ts (新增权限中间件)
+```
+
+**How it integrates:**
+- 扩展现有的 `CrudOptions` 接口,添加数据权限配置
+- 复用现有的用户跟踪字段配置
+- 保持现有 API 不变,新增配置为可选
+- 与现有认证系统无缝集成
+- 提供灵活的权限控制粒度
+
+**Success criteria:**
+- 现有功能不受影响,保持向后兼容
+- 业务模块可以轻松启用数据权限控制
+- 查询、创建、更新、删除操作自动进行权限验证
+- 创建操作防止用户创建不属于自己的数据
+- 提供管理员权限覆盖机制
+- 完整的单元测试和集成测试覆盖
+- 清晰的配置文档和使用示例
+
+## Stories
+
+### 阶段 1: 核心权限控制功能
+1. **Story 1:** 数据权限控制类型定义和配置接口 - 定义权限控制配置类型,扩展 CrudOptions 接口
+2. **Story 2:** 查询操作权限过滤增强 - 在 getList 和 getById 方法中添加用户ID过滤
+3. **Story 3:** 创建操作权限限制增强 - 在 create 方法中验证和限制用户创建权限
+4. **Story 4:** 更新操作权限验证增强 - 在 update 方法中添加权限验证
+5. **Story 5:** 删除操作权限验证增强 - 在 delete 方法中添加权限验证
+6. **Story 6:** 权限控制中间件 - 创建数据权限验证中间件
+
+### 阶段 2: 高级权限功能
+7. **Story 7:** 管理员权限覆盖机制 - 支持管理员查看和操作所有数据
+8. **Story 8:** 多租户数据隔离支持 - 支持基于租户ID的数据隔离
+9. **Story 9:** 自定义权限验证钩子 - 提供自定义权限验证扩展点
+
+### 阶段 3: 集成和测试
+10. **Story 10:** 业务模块集成示例 - 提供用户模块的权限控制集成示例
+11. **Story 11:** 完整测试覆盖 - 单元测试、集成测试和性能测试
+12. **Story 12:** 文档和迁移指南 - 使用文档和现有项目迁移指南
+
+## Compatibility Requirements
+
+- [x] 现有 APIs 保持不变,权限控制为可选配置
+- [x] 现有业务模块无需修改即可继续使用
+- [x] 数据库 schema 保持不变
+- [x] 性能影响最小化,权限过滤使用数据库索引
+- [x] 配置向后兼容,现有 userTracking 配置继续有效
+
+## Risk Mitigation
+
+**Primary Risk:** 权限控制影响现有查询性能
+**Mitigation:** 使用数据库索引优化权限过滤查询,提供性能基准测试
+**Rollback Plan:** 权限控制为可选配置,可随时禁用
+
+**Primary Risk:** 复杂的权限配置影响开发体验
+**Mitigation:** 提供简化的默认配置和清晰的文档
+**Rollback Plan:** 保持现有配置方式不变,新增配置可选
+
+**Primary Risk:** 权限验证逻辑与业务逻辑耦合
+**Mitigation:** 权限控制作为基础设施层,与业务逻辑分离
+**Rollback Plan:** 权限验证可独立禁用
+
+## Definition of Done
+
+- [ ] 阶段 1 stories 完成且验收标准满足
+- [ ] 现有功能通过回归测试验证
+- [ ] 权限控制配置灵活且易于使用
+- [ ] 性能基准测试通过,无明显性能下降
+- [ ] 完整的单元测试和集成测试覆盖
+- [ ] 使用文档和示例代码完整
+- [ ] 向后兼容性验证通过
+
+## 架构设计详情
+
+### 权限控制配置设计
+
+```typescript
+// 扩展现有的 CrudOptions
+export interface DataPermissionOptions {
+  enabled: boolean;
+  userIdField: string; // 用户ID字段名,如 'userId', 'createdBy'
+  adminOverride?: {
+    enabled: boolean;
+    adminRole?: string; // 管理员角色标识
+    userIdField?: string; // 管理员用户ID字段
+  };
+  multiTenant?: {
+    enabled: boolean;
+    tenantIdField: string; // 租户ID字段名
+  };
+  customValidator?: (userId: string | number, entity: any) => Promise<boolean>;
+}
+
+// 扩展 CrudOptions
+export type CrudOptions<T extends ObjectLiteral, ...> = {
+  // ... 现有配置
+  userTracking?: UserTrackingOptions;
+  dataPermission?: DataPermissionOptions; // 新增权限控制配置
+  // ...
+};
+```
+
+### 权限控制实现设计
+
+#### 查询权限过滤
+```typescript
+// 在 getList 方法中添加权限过滤
+async getList(
+  page: number = 1,
+  pageSize: number = 10,
+  keyword?: string,
+  searchFields?: string[],
+  where?: Partial<T>,
+  relations: string[] = [],
+  order: { [P in keyof T]?: 'ASC' | 'DESC' } = {},
+  filters?: { [key: string]: any },
+  userId?: string | number // 新增用户ID参数
+): Promise<[T[], number]> {
+  // 构建基础查询
+  const query = this.repository.createQueryBuilder('entity');
+
+  // 添加权限过滤条件
+  if (this.dataPermissionOptions?.enabled && userId) {
+    const userIdField = this.dataPermissionOptions.userIdField;
+    query.andWhere(`entity.${userIdField} = :userId`, { userId });
+  }
+
+  // ... 现有查询逻辑
+}
+```
+
+#### 创建权限限制
+```typescript
+async create(data: DeepPartial<T>, userId?: string | number): Promise<T> {
+  // 权限验证:防止用户创建不属于自己的数据
+  if (this.dataPermissionOptions?.enabled && userId) {
+    const userIdField = this.dataPermissionOptions.userIdField;
+
+    // 如果数据中已经包含用户ID字段,验证是否与当前用户匹配
+    if (data[userIdField] && data[userIdField] !== userId) {
+      throw new Error('无权创建该资源');
+    }
+  }
+
+  const entityData = { ...data };
+  this.setUserFields(entityData, userId, true);
+
+  // ... 现有创建逻辑
+}
+```
+
+#### 更新权限验证
+```typescript
+async update(id: number, data: Partial<T>, userId?: string | number): Promise<T | null> {
+  // 权限验证
+  if (this.dataPermissionOptions?.enabled && userId) {
+    const entity = await this.getById(id);
+    if (!entity) return null;
+
+    const userIdField = this.dataPermissionOptions.userIdField;
+    const entityUserId = (entity as any)[userIdField];
+
+    if (entityUserId !== userId) {
+      throw new Error('无权更新该资源');
+    }
+  }
+
+  const updateData = { ...data };
+  this.setUserFields(updateData, userId, false);
+
+  // ... 现有更新逻辑
+}
+```
+
+#### 删除权限验证
+```typescript
+async delete(id: number, userId?: string | number): Promise<boolean> {
+  // 权限验证
+  if (this.dataPermissionOptions?.enabled && userId) {
+    const entity = await this.getById(id);
+    if (!entity) return false;
+
+    const userIdField = this.dataPermissionOptions.userIdField;
+    const entityUserId = (entity as any)[userIdField];
+
+    if (entityUserId !== userId) {
+      throw new Error('无权删除该资源');
+    }
+  }
+
+  // 执行删除
+  const result = await this.repository.delete(id);
+  return result.affected === 1;
+}
+```
+
+### 路由层集成设计
+
+#### 权限控制中间件
+```typescript
+export function dataPermissionMiddleware(
+  options: DataPermissionOptions
+): MiddlewareHandler<AuthContext> {
+  return async (c, next) => {
+    if (!options.enabled) {
+      return next();
+    }
+
+    const user = c.get('user');
+    if (!user?.id) {
+      return c.json({ code: 401, message: '未授权访问' }, 401);
+    }
+
+    // 设置用户ID到上下文,供服务层使用
+    c.set('dataPermissionUserId', user.id);
+
+    await next();
+  };
+}
+```
+
+#### 路由配置示例
+```typescript
+// 现有配置方式(保持兼容)
+createCrudRoutes({
+  entity: UserEntity,
+  createSchema: CreateUserSchema,
+  updateSchema: UpdateUserSchema,
+  getSchema: GetUserSchema,
+  listSchema: ListUserSchema,
+  userTracking: { userIdField: 'userId' },
+  // 新增权限控制配置
+  dataPermission: {
+    enabled: true,
+    userIdField: 'userId',
+    adminOverride: {
+      enabled: true,
+      adminRole: 'admin'
+    }
+  }
+});
+```
+
+### 管理员权限覆盖机制
+
+```typescript
+// 在权限验证中添加管理员检查
+private async checkPermission(
+  entity: any,
+  userId: string | number
+): Promise<boolean> {
+  const options = this.dataPermissionOptions;
+  if (!options?.enabled) return true;
+
+  // 管理员权限覆盖
+  if (options.adminOverride?.enabled) {
+    const user = await this.getCurrentUser(userId);
+    if (user?.role === options.adminOverride.adminRole) {
+      return true;
+    }
+  }
+
+  // 普通用户权限验证
+  const userIdField = options.userIdField;
+  const entityUserId = (entity as any)[userIdField];
+  return entityUserId === userId;
+}
+```
+
+## 使用示例
+
+### 基础权限控制配置
+```typescript
+// 用户只能操作自己的数据
+createCrudRoutes({
+  entity: UserProfileEntity,
+  // ... 其他配置
+  userTracking: { userIdField: 'userId' },
+  dataPermission: {
+    enabled: true,
+    userIdField: 'userId'
+  }
+});
+```
+
+### 管理员权限覆盖配置
+```typescript
+// 管理员可以查看所有用户数据
+createCrudRoutes({
+  entity: UserProfileEntity,
+  // ... 其他配置
+  userTracking: { userIdField: 'userId' },
+  dataPermission: {
+    enabled: true,
+    userIdField: 'userId',
+    adminOverride: {
+      enabled: true,
+      adminRole: 'admin'
+    }
+  }
+});
+```
+
+### 多租户数据隔离
+```typescript
+// 支持多租户数据隔离
+createCrudRoutes({
+  entity: TenantDataEntity,
+  // ... 其他配置
+  dataPermission: {
+    enabled: true,
+    userIdField: 'createdBy',
+    multiTenant: {
+      enabled: true,
+      tenantIdField: 'tenantId'
+    }
+  }
+});
+```
+
+### 自定义权限验证
+```typescript
+// 自定义复杂的权限验证逻辑
+createCrudRoutes({
+  entity: ComplexEntity,
+  // ... 其他配置
+  dataPermission: {
+    enabled: true,
+    userIdField: 'ownerId',
+    customValidator: async (userId, entity) => {
+      // 自定义权限验证逻辑
+      return await someComplexPermissionCheck(userId, entity);
+    }
+  }
+});
+```
+
+## 技术实现要点
+
+### 向后兼容性保证
+1. **配置可选**: 数据权限控制为可选配置,默认禁用
+2. **API 不变**: 现有 API 接口和行为保持不变
+3. **性能优化**: 权限过滤使用数据库索引,性能影响最小化
+4. **错误处理**: 权限验证失败返回明确的错误信息
+
+### 安全性考虑
+1. **默认安全**: 启用权限控制后,默认遵循最小权限原则
+2. **输入验证**: 所有用户输入进行严格的验证和转义
+3. **SQL 注入防护**: 使用参数化查询防止 SQL 注入
+4. **错误信息**: 权限错误信息不泄露敏感数据
+
+### 性能优化
+1. **数据库索引**: 确保用户ID字段有合适的索引
+2. **查询优化**: 权限过滤条件与现有查询条件合并
+3. **缓存策略**: 考虑权限验证结果的缓存
+4. **批量操作**: 支持批量操作的权限验证优化
+
+## 测试策略
+
+### 单元测试
+- 权限配置验证测试
+- 查询权限过滤测试
+- 创建权限限制测试
+- 更新权限验证测试
+- 删除权限验证测试
+- 管理员权限覆盖测试
+- 自定义权限验证测试
+
+### 集成测试
+- 完整 CRUD 操作的权限控制测试(创建、查询、更新、删除)
+- 多用户场景的权限隔离测试
+- 创建操作权限限制场景测试
+- 更新操作权限验证场景测试
+- 管理员权限覆盖场景测试
+- 性能基准测试
+
+### 回归测试
+- 现有功能回归测试
+- 向后兼容性验证测试
+- 配置迁移测试
+
+---
+
+## Story Manager Handoff
+
+"请为这个brownfield epic开发详细的用户故事。关键考虑因素:
+
+- 这是一个对现有 shared-crud 包的增强,运行在 Node.js + TypeScript + Hono + TypeORM + PostgreSQL 技术栈上
+- 集成点:现有认证系统、用户跟踪字段、CRUD 服务层、路由配置
+- 需要遵循的现有模式:TypeORM 查询构建、Hono 中间件、配置驱动开发
+- 关键兼容性要求:现有配置和 API 保持不变、性能影响最小化、错误处理一致
+- 每个故事必须包含验证现有功能保持完整的回归测试
+
+该epic应该保持系统完整性,同时实现 shared-crud 包的数据权限控制增强,为业务模块提供安全、灵活的数据访问控制能力。"