Appearance
08_作业权限系统设计
概述
作业权限系统基于用户角色和学校归属关系,实现了细粒度的权限控制。系统支持四种用户角色:学生、老师、学校管理员和平台管理员。
权限矩阵
| 操作 | 学生 | 老师 | 学校管理员 | 平台管理员 |
|---|---|---|---|---|
| 查看作业详情 | ✅ 仅本班级 | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 提交作业 | ✅ 仅本班级 | ❌ | ❌ | ❌ |
| 创建作业 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 更新作业 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 删除作业 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 查看提交列表 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 评分作业 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 设置发布状态 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
| 生成AI建议 | ❌ | ✅ 仅本班级 | ✅ 仅本学校 | ✅ 所有 |
权限检查逻辑
1. 平台管理员 (PLATFORM_ADMIN)
- 权限范围: 所有学校的作业
- 检查逻辑: 无需额外检查,直接通过
- 使用场景: 系统维护、全局监控、跨学校管理
2. 学校管理员 (SCHOOL_ADMIN)
- 权限范围: 本学校的所有作业
- 检查逻辑: 验证班级是否属于该管理员所在的学校
- 使用场景: 学校内部管理、跨班级作业管理
3. 老师 (TEACHER)
- 权限范围: 自己担任教师的班级作业
- 检查逻辑: 验证用户是否在
class_teachers表中 - 使用场景: 日常教学、作业管理
4. 学生 (STUDENT)
- 权限范围: 自己所在班级的作业
- 检查逻辑: 验证用户是否在
class_members表中 - 使用场景: 查看作业、提交作业
核心权限检查方法
AssignmentService.ensure_user_can_manage_assignment()
python
async def ensure_user_can_manage_assignment(self, user: User, assignment_id: int) -> bool:
"""确保用户有权限管理该作业"""
# 平台管理员可以管理所有作业
if user.role == UserRole.PLATFORM_ADMIN:
return True
# 获取作业信息
assignment = await self.get_assignment(assignment_id)
if not assignment:
return False
# 学校管理员可以管理本学校的作业
if user.role == UserRole.SCHOOL_ADMIN:
class_result = await self.db.execute(
select(Class.school_id).where(Class.id == assignment.class_id)
)
class_school_id = class_result.scalar_one()
return class_school_id == user.school_id
# 老师只能管理自己班级的作业
if user.role == UserRole.TEACHER:
return await self.ensure_teacher_can_manage_class(user.id, assignment.class_id)
return FalseAssignmentService.ensure_user_can_view_assignment()
python
async def ensure_user_can_view_assignment(self, user: User, assignment_id: int) -> bool:
"""确保用户有权限查看该作业"""
# 平台管理员可以查看所有作业
if user.role == UserRole.PLATFORM_ADMIN:
return True
# 获取作业信息
assignment = await self.get_assignment(assignment_id)
if not assignment:
return False
# 学校管理员可以查看本学校的作业
if user.role == UserRole.SCHOOL_ADMIN:
class_result = await self.db.execute(
select(Class.school_id).where(Class.id == assignment.class_id)
)
class_school_id = class_result.scalar_one()
return class_school_id == user.school_id
# 老师只能查看自己班级的作业
if user.role == UserRole.TEACHER:
return await self.ensure_teacher_can_manage_class(user.id, assignment.class_id)
# 学生只能查看自己班级的作业
if user.role == UserRole.STUDENT:
return await self.ensure_student_in_class(user.id, assignment.class_id)
return FalseAPI 端点权限控制
创建作业权限检查
python
@router.post("/assignments", response_model=AssignmentOut, status_code=201)
async def create_assignment(
payload: AssignmentCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_teacher_or_admin),
):
"""创建作业"""
svc = AssignmentService(db)
# 检查用户是否有权限管理该班级的作业
if not await svc.ensure_user_can_manage_assignment(current_user, 0):
# 对于新作业,检查用户是否有权限管理该班级
if current_user.role.value == "teacher":
if not await svc.ensure_teacher_can_manage_class(current_user.id, payload.class_id):
raise HTTPException(status_code=403, detail="No permission to manage this class")
elif current_user.role.value == "school_admin":
# 检查班级是否属于该学校
class_result = await db.execute(
select(Class.school_id).where(Class.id == payload.class_id)
)
class_school_id = class_result.scalar_one()
if class_school_id != current_user.school_id:
raise HTTPException(status_code=403, detail="No permission to manage this class")
# platform_admin 可以管理所有班级
assignment = await svc.create_assignment(payload)
return assignment测试用例
学校管理员权限测试
python
@pytest.mark.asyncio
async def test_create_assignment_as_school_admin():
"""测试学校管理员创建作业"""
# 确保班级属于该学校
assert test_class.school_id == test_school.id
response = await client.post(
"/assignments",
json=assignment_data,
headers={"Authorization": f"Bearer {school_admin_user.id}"}
)
assert response.status_code == 201跨学校权限限制测试
python
@pytest.mark.asyncio
async def test_school_admin_cannot_manage_other_school_class():
"""测试学校管理员无法管理其他学校的班级作业"""
# 确保班级属于其他学校
assert other_class.school_id == other_school.id
assert other_class.school_id != school_admin_user.school_id
response = await client.post(
"/assignments",
json=assignment_data,
headers={"Authorization": f"Bearer {school_admin_user.id}"}
)
assert response.status_code == 403
assert "No permission to manage this class" in response.json()["detail"]安全考虑
- 角色验证: 所有权限检查都基于用户角色和学校归属关系
- 数据隔离: 学校管理员只能访问本学校的数据
- 最小权限原则: 用户只能访问必要的功能和数据
- 审计日志: 所有权限相关的操作都应该记录日志
扩展性
权限系统设计考虑了未来的扩展需求:
- 新角色支持: 可以轻松添加新的用户角色
- 权限组: 可以基于权限组实现更细粒度的控制
- 动态权限: 可以基于业务规则动态调整权限
- 多租户: 支持多租户架构的权限隔离
总结
新的权限系统实现了:
- ✅ 学校管理员可以管理本学校的所有作业
- ✅ 平台管理员可以管理所有学校的作业
- ✅ 老师只能管理自己班级的作业
- ✅ 学生只能访问自己班级的作业
- ✅ 严格的权限边界和数据隔离
- ✅ 完整的测试覆盖
- ✅ 清晰的权限检查逻辑