All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m17s
- 合并 customer_account 和 shop_account 路由到统一的 account 接口 - 新增统一认证接口 (auth handler) - 实现越权防护中间件和权限检查工具函数 - 新增操作审计日志模型和服务 - 更新数据库迁移 (版本 39: account_operation_log 表) - 补充集成测试覆盖权限检查和审计日志场景
128 lines
6.2 KiB
Markdown
128 lines
6.2 KiB
Markdown
# 账号管理权限检查规格
|
||
|
||
## ADDED Requirements
|
||
|
||
### Requirement: 三层越权防护架构
|
||
系统 SHALL 实现三层越权防护机制,确保账号管理操作的安全性。
|
||
|
||
#### Scenario: 路由层中间件拦截企业账号
|
||
- **WHEN** 企业账号(user_type=4)访问账号管理接口(/api/admin/accounts/*)
|
||
- **THEN** 中间件应返回 403 错误:"无权限访问账号管理功能"
|
||
|
||
#### Scenario: Service层权限检查成功
|
||
- **WHEN** 代理账号创建自己店铺的账号
|
||
- **THEN** CanManageShop 检查应通过,账号创建成功
|
||
|
||
#### Scenario: GORM层自动过滤生效
|
||
- **WHEN** 代理账号查询账号列表
|
||
- **THEN** GORM Callback 应自动添加 `shop_id IN (当前店铺+下级店铺)` 过滤条件
|
||
|
||
### Requirement: 代理账号只能管理自己店铺及下级店铺的账号
|
||
系统 SHALL 验证代理账号对目标店铺的管理权限,禁止跨店铺越权操作。
|
||
|
||
#### Scenario: 代理创建自己店铺的账号成功
|
||
- **WHEN** 代理账号(shop_id=100)创建 shop_id=100 的账号
|
||
- **THEN** 权限检查通过,账号创建成功
|
||
|
||
#### Scenario: 代理创建下级店铺的账号成功
|
||
- **WHEN** 代理账号(shop_id=100,下级:101,102)创建 shop_id=101 的账号
|
||
- **THEN** GetSubordinateShopIDs 返回 [100,101,102],权限检查通过
|
||
|
||
#### Scenario: 代理创建其他店铺的账号失败
|
||
- **WHEN** 代理账号(shop_id=100)创建 shop_id=200 的账号
|
||
- **THEN** CanManageShop 返回错误:"无权限管理该店铺的账号",创建失败
|
||
|
||
#### Scenario: 代理创建平台账号失败
|
||
- **WHEN** 代理账号尝试创建 user_type=2 的平台账号
|
||
- **THEN** Service 层检查返回错误:"无权限创建平台账号",创建失败
|
||
|
||
### Requirement: 平台账号和超级管理员可以管理所有账号
|
||
系统 SHALL 允许平台账号和超级管理员跳过所有权限检查,管理所有账号。
|
||
|
||
#### Scenario: 平台账号创建任意类型账号
|
||
- **WHEN** 平台账号(user_type=2)创建代理账号(user_type=3, shop_id=100)
|
||
- **THEN** 权限检查跳过,账号创建成功
|
||
|
||
#### Scenario: 超级管理员创建任意类型账号
|
||
- **WHEN** 超级管理员(user_type=1)创建任意类型账号
|
||
- **THEN** 权限检查跳过,账号创建成功
|
||
|
||
#### Scenario: 平台账号查询所有账号
|
||
- **WHEN** 平台账号调用账号列表接口
|
||
- **THEN** GORM Callback 跳过过滤,返回所有账号
|
||
|
||
### Requirement: 企业账号禁止访问账号管理接口
|
||
系统 SHALL 禁止企业账号访问所有账号管理接口。
|
||
|
||
#### Scenario: 企业账号创建账号失败(路由层拦截)
|
||
- **WHEN** 企业账号(user_type=4)调用 POST /api/admin/accounts/enterprise
|
||
- **THEN** 路由层中间件返回 403 错误:"无权限访问账号管理功能"
|
||
|
||
#### Scenario: 企业账号更新账号失败(Service层拦截)
|
||
- **WHEN** 企业账号绕过路由层,直接调用 AccountService.Update
|
||
- **THEN** Service 层返回 403 错误:"企业账号不允许更新账号"
|
||
|
||
### Requirement: 统一错误返回防止信息泄露
|
||
系统 SHALL 在越权访问时统一返回模糊错误消息,防止攻击者判断资源是否存在。
|
||
|
||
#### Scenario: 查询不存在的账号返回模糊错误
|
||
- **WHEN** 用户查询不存在的账号 ID
|
||
- **THEN** 返回 403 错误:"无权限操作该资源或资源不存在"
|
||
|
||
#### Scenario: 查询越权的账号返回相同错误
|
||
- **WHEN** 代理账号(shop_id=100)查询 shop_id=200 的账号
|
||
- **THEN** 返回 403 错误:"无权限操作该资源或资源不存在"(与不存在的错误消息相同)
|
||
|
||
### Requirement: CanManageShop 权限检查函数
|
||
系统 SHALL 提供 CanManageShop 函数验证用户对目标店铺的管理权限。
|
||
|
||
#### Scenario: 验证代理对自己店铺的权限
|
||
- **WHEN** 调用 CanManageShop(ctx, 100, shopStore) 且当前用户 shop_id=100
|
||
- **THEN** 返回 nil(有权限)
|
||
|
||
#### Scenario: 验证代理对下级店铺的权限
|
||
- **WHEN** 调用 CanManageShop(ctx, 101, shopStore) 且当前用户 shop_id=100,下级包含 101
|
||
- **THEN** GetSubordinateShopIDs 返回 [100,101,102],返回 nil(有权限)
|
||
|
||
#### Scenario: 验证代理对其他店铺的权限失败
|
||
- **WHEN** 调用 CanManageShop(ctx, 200, shopStore) 且当前用户 shop_id=100
|
||
- **THEN** 返回错误:"无权限管理该店铺的账号"
|
||
|
||
#### Scenario: 验证平台账号自动通过
|
||
- **WHEN** 调用 CanManageShop(ctx, 200, shopStore) 且当前用户 user_type=2(平台)
|
||
- **THEN** 不调用 GetSubordinateShopIDs,直接返回 nil(有权限)
|
||
|
||
### Requirement: CanManageEnterprise 权限检查函数
|
||
系统 SHALL 提供 CanManageEnterprise 函数验证用户对目标企业的管理权限。
|
||
|
||
#### Scenario: 验证平台账号管理任意企业
|
||
- **WHEN** 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且当前用户 user_type=2
|
||
- **THEN** 返回 nil(有权限)
|
||
|
||
#### Scenario: 验证代理对归属企业的权限
|
||
- **WHEN** 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=100,当前用户 shop_id=100
|
||
- **THEN** 返回 nil(有权限)
|
||
|
||
#### Scenario: 验证代理对下级店铺企业的权限
|
||
- **WHEN** 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=101,当前用户 shop_id=100,下级包含 101
|
||
- **THEN** 返回 nil(有权限)
|
||
|
||
#### Scenario: 验证代理对其他店铺企业的权限失败
|
||
- **WHEN** 调用 CanManageEnterprise(ctx, 50, enterpriseStore, shopStore) 且企业 owner_shop_id=200,当前用户 shop_id=100
|
||
- **THEN** 返回错误:"无权限管理该企业的账号"
|
||
|
||
### Requirement: 权限检查性能优化
|
||
系统 SHALL 使用 Redis 缓存优化权限检查性能,确保 API 响应时间 < 200ms。
|
||
|
||
#### Scenario: GetSubordinateShopIDs 命中缓存
|
||
- **WHEN** 调用 GetSubordinateShopIDs(ctx, 100) 且缓存存在
|
||
- **THEN** 从 Redis 读取缓存,不查询数据库,耗时 < 5ms
|
||
|
||
#### Scenario: GetSubordinateShopIDs 缓存未命中
|
||
- **WHEN** 调用 GetSubordinateShopIDs(ctx, 100) 且缓存不存在
|
||
- **THEN** 递归查询数据库,写入 Redis 缓存(30分钟),返回结果
|
||
|
||
#### Scenario: 权限检查总耗时 < 10ms
|
||
- **WHEN** 执行完整权限检查(包含 GetSubordinateShopIDs)
|
||
- **THEN** 总耗时 < 10ms(缓存命中时 < 5ms)
|