248 lines
9.4 KiB
Python
248 lines
9.4 KiB
Python
|
|
import secrets
|
|||
|
|
from datetime import datetime, timedelta
|
|||
|
|
from uuid import uuid4
|
|||
|
|
|
|||
|
|
from fastapi import APIRouter, Depends
|
|||
|
|
from fastapi.responses import JSONResponse
|
|||
|
|
from sqlalchemy import select
|
|||
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
|
|
|||
|
|
from loveace.config.logger import LoggerMixin
|
|||
|
|
from loveace.database.auth.register import InviteCode as InviteCodeDB
|
|||
|
|
from loveace.database.auth.register import RegisterCoolDown
|
|||
|
|
from loveace.database.auth.token import AuthMEToken
|
|||
|
|
from loveace.database.auth.user import ACEUser
|
|||
|
|
from loveace.database.creator import get_db_session
|
|||
|
|
from loveace.router.dependencies.logger import no_user_logger_mixin
|
|||
|
|
from loveace.router.endpoint.auth.model.register import (
|
|||
|
|
InviteCodeRequest,
|
|||
|
|
InviteCodeResponse,
|
|||
|
|
InviteErrorToCode,
|
|||
|
|
RegisterErrorToCode,
|
|||
|
|
RegisterRequest,
|
|||
|
|
RegisterResponse,
|
|||
|
|
)
|
|||
|
|
from loveace.router.schemas.uniresponse import UniResponseModel
|
|||
|
|
from loveace.service.remote.aufe import AUFEService
|
|||
|
|
from loveace.service.remote.aufe.depends import get_aufe_service
|
|||
|
|
from loveace.utils.rsa import RSAUtils
|
|||
|
|
|
|||
|
|
register_router = APIRouter(prefix="/register")
|
|||
|
|
|
|||
|
|
|
|||
|
|
temp_tokens = []
|
|||
|
|
|
|||
|
|
rsa_util = RSAUtils.get_or_create_rsa_utils()
|
|||
|
|
|
|||
|
|
|
|||
|
|
@register_router.post(
|
|||
|
|
"/invite",
|
|||
|
|
response_model=UniResponseModel[InviteCodeResponse],
|
|||
|
|
responses=InviteErrorToCode.gen_code_table(),
|
|||
|
|
summary="邀请码验证",
|
|||
|
|
)
|
|||
|
|
async def register(
|
|||
|
|
invite_code: InviteCodeRequest,
|
|||
|
|
db: AsyncSession = Depends(get_db_session),
|
|||
|
|
logger: LoggerMixin = Depends(no_user_logger_mixin),
|
|||
|
|
) -> UniResponseModel[InviteCodeResponse] | JSONResponse:
|
|||
|
|
"""
|
|||
|
|
验证邀请码并返回临时 Token
|
|||
|
|
|
|||
|
|
✅ 功能特性:
|
|||
|
|
- 验证邀请码的有效性
|
|||
|
|
- 生成临时 Token 用于后续注册步骤
|
|||
|
|
- 邀请码一次性使用
|
|||
|
|
|
|||
|
|
💡 使用场景:
|
|||
|
|
- 用户注册流程的第一步
|
|||
|
|
- 邀请制系统的验证
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
invite_code: 邀请码请求对象
|
|||
|
|
db: 数据库会话
|
|||
|
|
logger: 日志记录器
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
InviteCodeResponse: 包含临时 Token
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
async with db as session:
|
|||
|
|
logger.info(f"邀请码: {invite_code.invite_code}")
|
|||
|
|
invite = select(InviteCodeDB).where(
|
|||
|
|
InviteCodeDB.code == invite_code.invite_code
|
|||
|
|
)
|
|||
|
|
result = await session.execute(invite)
|
|||
|
|
invite_data = result.scalars().first()
|
|||
|
|
if invite_data is None:
|
|||
|
|
logger.info(f"邀请码不存在: {invite_code.invite_code}")
|
|||
|
|
return InviteErrorToCode().invalid_invite_code.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
token = secrets.token_urlsafe(128)
|
|||
|
|
temp_tokens.append(token)
|
|||
|
|
return UniResponseModel[InviteCodeResponse](
|
|||
|
|
success=True,
|
|||
|
|
data=InviteCodeResponse(token=token),
|
|||
|
|
message="邀请码验证成功",
|
|||
|
|
error=None,
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error("邀请码验证失败:")
|
|||
|
|
logger.exception(e)
|
|||
|
|
return InviteErrorToCode().server_error.to_json_response(logger.trace_id)
|
|||
|
|
|
|||
|
|
|
|||
|
|
@register_router.post(
|
|||
|
|
"/next",
|
|||
|
|
response_model=UniResponseModel[RegisterResponse],
|
|||
|
|
responses=RegisterErrorToCode.gen_code_table(),
|
|||
|
|
summary="用户注册",
|
|||
|
|
)
|
|||
|
|
async def register_user(
|
|||
|
|
register_info: RegisterRequest,
|
|||
|
|
db: AsyncSession = Depends(get_db_session),
|
|||
|
|
logger: LoggerMixin = Depends(no_user_logger_mixin),
|
|||
|
|
aufe_service: AUFEService = Depends(get_aufe_service),
|
|||
|
|
) -> UniResponseModel[RegisterResponse] | JSONResponse:
|
|||
|
|
"""
|
|||
|
|
用户注册,验证身份并创建账户
|
|||
|
|
|
|||
|
|
✅ 功能特性:
|
|||
|
|
- 通过 AUFE 服务验证 EC 密码和登录密码
|
|||
|
|
- 验证身份信息的有效性
|
|||
|
|
- 生成 Authme Token 用于登录
|
|||
|
|
|
|||
|
|
⚠️ 限制条件:
|
|||
|
|
- EC 密码或登录密码错误会触发 5 分钟冷却时间
|
|||
|
|
- 用户 ID 不能重复
|
|||
|
|
- 必须提供有效的邀请 Token
|
|||
|
|
|
|||
|
|
💡 使用场景:
|
|||
|
|
- 新用户注册
|
|||
|
|
- 创建学号对应的账户
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
register_info: 包含用户 ID、EC 密码、登录密码和邀请 Token 的注册信息
|
|||
|
|
db: 数据库会话
|
|||
|
|
logger: 日志记录器
|
|||
|
|
aufe_service: AUFE 远程认证服务
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
RegisterResponse: 包含 Authme Token
|
|||
|
|
"""
|
|||
|
|
try:
|
|||
|
|
async with db as session:
|
|||
|
|
# COOLDOWN检查
|
|||
|
|
query = select(RegisterCoolDown).where(
|
|||
|
|
RegisterCoolDown.userid == register_info.userid
|
|||
|
|
)
|
|||
|
|
result = await session.execute(query)
|
|||
|
|
cooldown = result.scalars().first()
|
|||
|
|
if cooldown:
|
|||
|
|
if cooldown.expire_date > datetime.now():
|
|||
|
|
logger.info(f"用户ID注册冷却中: {register_info.userid}")
|
|||
|
|
return RegisterErrorToCode().userid_exists.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
await session.delete(cooldown)
|
|||
|
|
await session.commit()
|
|||
|
|
if register_info.token not in temp_tokens:
|
|||
|
|
logger.info(f"无效的注册Token: {register_info.token}")
|
|||
|
|
return RegisterErrorToCode().invalid_token.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
query = select(ACEUser).where(ACEUser.userid == register_info.userid)
|
|||
|
|
result = await session.execute(query)
|
|||
|
|
user = result.scalars().first()
|
|||
|
|
if user is not None:
|
|||
|
|
logger.info(f"用户ID已存在: {register_info.userid}")
|
|||
|
|
return RegisterErrorToCode().userid_exists.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
# 尝试使用AUFE服务验证EC密码
|
|||
|
|
try:
|
|||
|
|
ec_password = rsa_util.decrypt(register_info.ec_password)
|
|||
|
|
password = rsa_util.decrypt(register_info.password)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.info(f"用户 {register_info.userid} 提供的密码解密失败: {e}")
|
|||
|
|
return RegisterErrorToCode().decrypt_error.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
conn = await aufe_service.get_or_create_connection(
|
|||
|
|
userid=register_info.userid,
|
|||
|
|
ec_password=ec_password,
|
|||
|
|
password=password,
|
|||
|
|
)
|
|||
|
|
ec_login_status = await conn.ec_login()
|
|||
|
|
if not ec_login_status.success:
|
|||
|
|
cooldown_entry = RegisterCoolDown(
|
|||
|
|
userid=register_info.userid,
|
|||
|
|
expire_date=datetime.now() + timedelta(minutes=5),
|
|||
|
|
)
|
|||
|
|
session.add(cooldown_entry)
|
|||
|
|
await session.commit()
|
|||
|
|
if ec_login_status.fail_invalid_credentials:
|
|||
|
|
logger.info(f"EC密码错误: {register_info.userid}")
|
|||
|
|
return RegisterErrorToCode().ec_password_error.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
logger.error(f"AUFE服务异常: {ec_login_status}")
|
|||
|
|
return RegisterErrorToCode().ec_server_error.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
uaap_login_status = await conn.uaap_login()
|
|||
|
|
if not uaap_login_status.success:
|
|||
|
|
cooldown_entry = RegisterCoolDown(
|
|||
|
|
userid=register_info.userid,
|
|||
|
|
expire_date=datetime.now() + timedelta(minutes=5),
|
|||
|
|
)
|
|||
|
|
session.add(cooldown_entry)
|
|||
|
|
await session.commit()
|
|||
|
|
if uaap_login_status.fail_invalid_credentials:
|
|||
|
|
logger.info(f"登录密码错误: {register_info.userid}")
|
|||
|
|
return RegisterErrorToCode().uaap_password_error.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
else:
|
|||
|
|
logger.error(f"AUFE服务异常: {uaap_login_status}")
|
|||
|
|
return RegisterErrorToCode().ec_server_error.to_json_response(
|
|||
|
|
logger.trace_id
|
|||
|
|
)
|
|||
|
|
# 创建新用户
|
|||
|
|
new_user = ACEUser(
|
|||
|
|
userid=register_info.userid,
|
|||
|
|
ec_password=register_info.ec_password,
|
|||
|
|
password=register_info.password,
|
|||
|
|
)
|
|||
|
|
session.add(new_user)
|
|||
|
|
await session.commit()
|
|||
|
|
# 注册成功后删除临时Token
|
|||
|
|
temp_tokens.remove(register_info.token)
|
|||
|
|
# 生成Authme Token
|
|||
|
|
authme_token = secrets.token_urlsafe(128)
|
|||
|
|
new_token = AuthMEToken(
|
|||
|
|
user_id=new_user.userid, token=authme_token, device_id=uuid4().hex
|
|||
|
|
)
|
|||
|
|
session.add(new_token)
|
|||
|
|
await session.commit()
|
|||
|
|
return UniResponseModel[RegisterResponse](
|
|||
|
|
success=True,
|
|||
|
|
data=RegisterResponse(token=authme_token),
|
|||
|
|
message="注册成功",
|
|||
|
|
error=None,
|
|||
|
|
)
|
|||
|
|
except ValueError as ve:
|
|||
|
|
logger.error("用户注册失败: RSA解密错误")
|
|||
|
|
logger.exception(ve)
|
|||
|
|
return RegisterErrorToCode().server_error.to_json_response(
|
|||
|
|
logger.trace_id, "RSA解密错误,请检查授权密文"
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error("用户注册失败:")
|
|||
|
|
logger.exception(e)
|
|||
|
|
return RegisterErrorToCode().server_error.to_json_response(logger.trace_id)
|