Files
012-kaopeilian/backend/app/schemas/user.py
yuliang_guo 79b55cfd12
All checks were successful
continuous-integration/drone/push Build is passing
fix: 修复权限提升漏洞和添加安全头
安全修复:
- 创建 UserSelfUpdate schema,禁止用户修改自己的 role 和 is_active
- /users/me 端点现在使用 UserSelfUpdate 而非 UserUpdate

安全增强:
- 添加 SecurityHeadersMiddleware 中间件
- X-Content-Type-Options: nosniff
- X-Frame-Options: DENY
- X-XSS-Protection: 1; mode=block
- Referrer-Policy: strict-origin-when-cross-origin
- Permissions-Policy: 禁用敏感功能
- Cache-Control: API响应不缓存
2026-01-31 10:57:41 +08:00

168 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
用户相关 Schema
"""
from datetime import datetime
from typing import List, Optional
from pydantic import EmailStr, Field, field_validator
from .base import BaseSchema
class UserBase(BaseSchema):
"""用户基础信息"""
username: str = Field(..., min_length=3, max_length=50)
email: Optional[EmailStr] = None
phone: Optional[str] = Field(None, pattern=r"^1[3-9]\d{9}$")
full_name: Optional[str] = Field(None, max_length=100)
avatar_url: Optional[str] = None
bio: Optional[str] = None
role: str = Field(default="trainee", pattern="^(admin|manager|trainee)$")
gender: Optional[str] = Field(None, pattern="^(male|female)$")
school: Optional[str] = Field(None, max_length=100)
major: Optional[str] = Field(None, max_length=100)
class UserCreate(UserBase):
"""创建用户"""
password: str = Field(..., min_length=6, max_length=100)
@field_validator("password")
def validate_password(cls, v):
if len(v) < 6:
raise ValueError("密码长度至少为6位")
return v
class UserUpdate(BaseSchema):
"""更新用户(管理员使用)"""
email: Optional[EmailStr] = None
phone: Optional[str] = Field(None, pattern=r"^1[3-9]\d{9}$")
full_name: Optional[str] = Field(None, max_length=100)
avatar_url: Optional[str] = None
bio: Optional[str] = None
role: Optional[str] = Field(None, pattern="^(admin|manager|trainee)$")
is_active: Optional[bool] = None
gender: Optional[str] = Field(None, pattern="^(male|female)$")
school: Optional[str] = Field(None, max_length=100)
major: Optional[str] = Field(None, max_length=100)
class UserSelfUpdate(BaseSchema):
"""用户自己更新个人信息不允许修改role和is_active"""
email: Optional[EmailStr] = None
phone: Optional[str] = Field(None, pattern=r"^1[3-9]\d{9}$")
full_name: Optional[str] = Field(None, max_length=100)
avatar_url: Optional[str] = None
bio: Optional[str] = None
gender: Optional[str] = Field(None, pattern="^(male|female)$")
school: Optional[str] = Field(None, max_length=100)
major: Optional[str] = Field(None, max_length=100)
class UserPasswordUpdate(BaseSchema):
"""更新密码"""
old_password: str
new_password: str = Field(..., min_length=6, max_length=100)
class UserInDBBase(UserBase):
"""数据库中的用户基础信息"""
id: int
is_active: bool
is_verified: bool
created_at: datetime
updated_at: datetime
last_login_at: Optional[datetime] = None
class User(UserInDBBase):
"""用户信息(不含敏感数据)"""
teams: List["TeamBasic"] = []
class UserWithPassword(UserInDBBase):
"""用户信息(含密码)"""
hashed_password: str
# Team Schemas
class TeamBase(BaseSchema):
"""团队基础信息"""
name: str = Field(..., min_length=2, max_length=100)
code: str = Field(..., min_length=2, max_length=50)
description: Optional[str] = None
team_type: str = Field(
default="department", pattern="^(department|project|study_group)$"
)
class TeamCreate(TeamBase):
"""创建团队"""
leader_id: Optional[int] = None
parent_id: Optional[int] = None
class TeamUpdate(BaseSchema):
"""更新团队"""
name: Optional[str] = Field(None, min_length=2, max_length=100)
description: Optional[str] = None
leader_id: Optional[int] = None
is_active: Optional[bool] = None
class TeamBasic(BaseSchema):
"""团队基本信息"""
id: int
name: str
code: str
team_type: str
class Team(TeamBase):
"""团队完整信息"""
id: int
is_active: bool
leader_id: Optional[int] = None
parent_id: Optional[int] = None
created_at: datetime
updated_at: datetime
member_count: Optional[int] = 0
class TeamWithMembers(Team):
"""团队信息(含成员)"""
members: List[User] = []
leader: Optional[User] = None
# 避免循环引用
UserBase.model_rebuild()
User.model_rebuild()
Team.model_rebuild()
# Filter schemas
class UserFilter(BaseSchema):
"""用户筛选条件"""
role: Optional[str] = Field(None, pattern="^(admin|manager|trainee)$")
is_active: Optional[bool] = None
team_id: Optional[int] = None
keyword: Optional[str] = None # 搜索用户名、邮箱、姓名