fix(security): harden uploads and wecom binding
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user