fix(security): harden uploads and wecom binding

This commit is contained in:
2026-07-28 12:18:45 +08:00
parent 29e4e4a804
commit 6b43b77c3b
14 changed files with 185 additions and 84 deletions
+13 -18
View File
@@ -9,8 +9,9 @@ from fastapi.responses import PlainTextResponse
from pydantic import BaseModel
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from app.config import settings
from app.database import get_db
from app.database import async_session, get_db
from app.middleware.auth import get_current_user, require_director
from app.models.user import User
from app.services.wecom import wecom_client, store_bind_token, consume_bind_token
@@ -116,21 +117,20 @@ async def wecom_callback_event(request: Request):
# Triggers for binding
if (msg_type == "text" and content == "绑定") or (msg_type == "event" and event == "click" and event_key == "BIND_ACCOUNT"):
_log.info("wecom bind triggered")
_handle_bind_request(from_user)
await _handle_bind_request(from_user)
return PlainTextResponse(content="success")
_log.info("wecom message ignored (no matching action)")
return PlainTextResponse(content="success")
def _handle_bind_request(wecom_userid: str):
async def _handle_bind_request(wecom_userid: str):
"""Generate bind token, store it, and push a bind link to the user."""
if not wecom_userid:
return
import asyncio
bind_token = store_bind_token(wecom_userid)
async with async_session() as db:
bind_token = await store_bind_token(db, wecom_userid)
content = (
f"【账号绑定】\n\n"
@@ -139,16 +139,7 @@ def _handle_bind_request(wecom_userid: str):
f"绑定后可使用企微一键登录,并接收填报提醒通知。"
)
try:
# Must run async in sync context
loop = asyncio.get_event_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(
wecom_client.send_text_message([wecom_userid], content)
)
await wecom_client.send_text_message([wecom_userid], content)
# ── Bind confirmation (JWT-protected) ──
@@ -163,7 +154,7 @@ async def bind_confirm(
if not data.token:
raise HTTPException(status_code=400, detail="token 不能为空")
wecom_userid = consume_bind_token(data.token)
wecom_userid = await consume_bind_token(db, data.token)
if not wecom_userid:
raise HTTPException(status_code=400, detail="绑定链接已过期或无效,请重新在企微发送「绑定」")
@@ -181,7 +172,11 @@ async def bind_confirm(
)
user = result.scalar_one()
user.wecom_userid = wecom_userid
await db.commit()
try:
await db.commit()
except IntegrityError as exc:
await db.rollback()
raise HTTPException(status_code=409, detail="该企业微信已绑定其他账号") from exc
return {"code": 200, "msg": "绑定成功", "wecom_userid": wecom_userid}