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
+29 -18
View File
@@ -1,30 +1,41 @@
import time
import uuid
from datetime import datetime, timedelta, timezone
import httpx
from sqlalchemy import delete, update
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
from app.models.wecom_bind_token import WecomBindToken
# In-memory bind token store (TTL 600s). Replace with Redis if scaling to multiple workers.
_bind_tokens: dict[str, tuple[str, float]] = {} # token → (wecom_userid, expires_at)
def store_bind_token(wecom_userid: str, ttl: int = 600) -> str:
"""Store a bind token → wecom_userid mapping. Returns the token."""
async def store_bind_token(db: AsyncSession, wecom_userid: str, ttl: int = 600) -> str:
"""Persist a one-time bind token so it works across application workers."""
token = uuid.uuid4().hex
_bind_tokens[token] = (wecom_userid, time.time() + ttl)
# Cleanup expired tokens
now = time.time()
for k in list(_bind_tokens):
if _bind_tokens[k][1] < now:
del _bind_tokens[k]
now = datetime.now(timezone.utc)
await db.execute(delete(WecomBindToken).where(WecomBindToken.expires_at < now))
db.add(WecomBindToken(
token=token,
wecom_userid=wecom_userid,
expires_at=now + timedelta(seconds=ttl),
))
await db.commit()
return token
def consume_bind_token(token: str) -> str | None:
"""Lookup and consume a bind token. Returns wecom_userid or None."""
entry = _bind_tokens.pop(token, None)
if entry and entry[1] > time.time():
return entry[0]
return None
async def consume_bind_token(db: AsyncSession, token: str) -> str | None:
"""Atomically consume an unexpired binding token and return its WeCom user ID."""
now = datetime.now(timezone.utc)
result = await db.execute(
update(WecomBindToken)
.where(
WecomBindToken.token == token,
WecomBindToken.consumed_at.is_(None),
WecomBindToken.expires_at > now,
)
.values(consumed_at=now)
.returning(WecomBindToken.wecom_userid)
)
await db.commit()
return result.scalar_one_or_none()
class WecomClient: