# 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:** Shared-CRUD 数据权限控制完整实现 - 实现完整的数据权限控制功能,包括: - 数据权限控制类型定义和配置接口 - 查询操作的自动权限过滤(getList 和 getById) - 创建操作的权限限制(防止创建不属于自己的数据) - 更新操作的权限验证 - 删除操作的权限验证 - 权限控制中间件 - 管理员权限覆盖机制 - 完整的单元测试和集成测试 ### 阶段 2: 高级功能和集成 2. **Story 2:** 高级权限功能和业务模块集成 - 实现高级权限功能和集成示例: - 多租户数据隔离支持 - 自定义权限验证钩子 - 业务模块集成示例(用户模块) - 性能优化和基准测试 - 完整的使用文档和迁移指南 ## 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 - [ ] Story 1 完成且验收标准满足 - [ ] 现有功能通过回归测试验证 - [ ] 权限控制配置灵活且易于使用 - [ ] 性能基准测试通过,无明显性能下降 - [ ] 完整的单元测试和集成测试覆盖 - [ ] 使用文档和示例代码完整 - [ ] 向后兼容性验证通过 ## 架构设计详情 ### 权限控制配置设计 ```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; } // 扩展 CrudOptions export type CrudOptions = { // ... 现有配置 userTracking?: UserTrackingOptions; dataPermission?: DataPermissionOptions; // 新增权限控制配置 // ... }; ``` ### 权限控制实现设计 #### 查询权限过滤 ```typescript // 在 getList 方法中添加权限过滤 async getList( page: number = 1, pageSize: number = 10, keyword?: string, searchFields?: string[], where?: Partial, 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, userId?: string | number): Promise { // 权限验证:防止用户创建不属于自己的数据 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, userId?: string | number): Promise { // 权限验证 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 { // 权限验证 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 { 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 { 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 包的数据权限控制增强,为业务模块提供安全、灵活的数据访问控制能力。"