Initial commit: CI Agent competitive-intelligence monitoring app
FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.base import ensure_aware_utc
|
||||
from app.models.refresh_token import RefreshToken
|
||||
|
||||
|
||||
class RefreshTokenRepository:
|
||||
def __init__(self, db: AsyncSession) -> None:
|
||||
self.db = db
|
||||
|
||||
async def create(
|
||||
self, *, user_id: uuid.UUID, token_hash: str, expires_at: datetime
|
||||
) -> RefreshToken:
|
||||
record = RefreshToken(user_id=user_id, token_hash=token_hash, expires_at=expires_at)
|
||||
self.db.add(record)
|
||||
await self.db.flush()
|
||||
return record
|
||||
|
||||
async def get_valid_by_hash(self, token_hash: str) -> RefreshToken | None:
|
||||
result = await self.db.execute(
|
||||
select(RefreshToken).where(RefreshToken.token_hash == token_hash)
|
||||
)
|
||||
record = result.scalar_one_or_none()
|
||||
if record is None:
|
||||
return None
|
||||
if record.revoked_at is not None:
|
||||
return None
|
||||
if ensure_aware_utc(record.expires_at) < datetime.now(UTC):
|
||||
return None
|
||||
return record
|
||||
|
||||
async def revoke(self, record: RefreshToken) -> None:
|
||||
record.revoked_at = datetime.now(UTC)
|
||||
await self.db.flush()
|
||||
|
||||
async def revoke_all_for_user(self, user_id: uuid.UUID) -> None:
|
||||
"""Invalidates every active session for this user - used after a
|
||||
password reset, since an attacker who had a valid refresh token
|
||||
shouldn't stay logged in past the password change that locked them
|
||||
out going forward."""
|
||||
result = await self.db.execute(
|
||||
select(RefreshToken).where(
|
||||
RefreshToken.user_id == user_id, RefreshToken.revoked_at.is_(None)
|
||||
)
|
||||
)
|
||||
now = datetime.now(UTC)
|
||||
for record in result.scalars().all():
|
||||
record.revoked_at = now
|
||||
await self.db.flush()
|
||||
Reference in New Issue
Block a user