2026-05-11 19:48:18 +05:30
|
|
|
"""Test-only token mint endpoint for the E2E backend entrypoint.
|
|
|
|
|
|
|
|
|
|
Mounted by ``tests/e2e/run_backend.py`` so Playwright can authenticate
|
|
|
|
|
the seeded e2e user without hitting ``/auth/jwt/login`` (rate-limited
|
|
|
|
|
to 5/min/IP in production). NEVER ships to production: this whole
|
|
|
|
|
``tests/`` tree is excluded from the production Docker image by
|
|
|
|
|
``surfsense_backend/.dockerignore``.
|
|
|
|
|
|
|
|
|
|
Authn: shared secret in ``X-E2E-Mint-Secret``. Same value is set on the
|
|
|
|
|
backend container env (``docker/docker-compose.e2e.yml``) and exported
|
|
|
|
|
to the Playwright runner (``.github/workflows/e2e-tests.yml``).
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, FastAPI, Header, HTTPException
|
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
|
|
|
|
|
from app.db import User, async_session_maker
|
|
|
|
|
from app.users import get_jwt_strategy
|
|
|
|
|
|
|
|
|
|
_logger = logging.getLogger("surfsense.e2e.auth_mint")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MintRequest(BaseModel):
|
|
|
|
|
email: str = "e2e-test@surfsense.net"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MintResponse(BaseModel):
|
|
|
|
|
access_token: str
|
|
|
|
|
token_type: str = "bearer"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _expected_secret() -> str:
|
2026-05-12 04:00:04 +05:30
|
|
|
return os.environ.get("E2E_MINT_SECRET", "local-e2e-mint-secret-not-for-production")
|
2026-05-11 19:48:18 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/__e2e__", tags=["__e2e__"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.post("/auth/token", response_model=MintResponse)
|
|
|
|
|
async def mint_test_token(
|
|
|
|
|
body: MintRequest,
|
|
|
|
|
x_e2e_mint_secret: str = Header(..., alias="X-E2E-Mint-Secret"),
|
|
|
|
|
) -> MintResponse:
|
|
|
|
|
if x_e2e_mint_secret != _expected_secret():
|
|
|
|
|
raise HTTPException(status_code=403, detail="invalid e2e mint secret")
|
|
|
|
|
async with async_session_maker() as session:
|
|
|
|
|
result = await session.execute(select(User).where(User.email == body.email))
|
|
|
|
|
user = result.scalar_one_or_none()
|
|
|
|
|
if user is None:
|
|
|
|
|
raise HTTPException(
|
|
|
|
|
status_code=404, detail=f"e2e user {body.email!r} not seeded"
|
|
|
|
|
)
|
|
|
|
|
token = await get_jwt_strategy().write_token(user)
|
|
|
|
|
return MintResponse(access_token=token)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def install(app: FastAPI) -> None:
|
|
|
|
|
"""Mount the test-only mint router onto the given FastAPI app."""
|
|
|
|
|
app.include_router(router)
|
|
|
|
|
_logger.warning("[e2e] mounted POST /__e2e__/auth/token (test-only token mint)")
|