Add slowapi-based rate limiting: 5/min on password login, 10/min on WebAuthn login. Includes shared rate limiter reset fixture for tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
import re
|
|
from collections.abc import AsyncIterator
|
|
|
|
import pytest
|
|
from httpx import ASGITransport, AsyncClient
|
|
|
|
from porchlight.app import create_app
|
|
from porchlight.config import Settings
|
|
from porchlight.rate_limit import limiter
|
|
|
|
|
|
@pytest.fixture
|
|
def settings() -> Settings:
|
|
return Settings(issuer="http://localhost:8000", sqlite_path=":memory:", session_https_only=False)
|
|
|
|
|
|
@pytest.fixture
|
|
async def client(settings: Settings) -> AsyncIterator[AsyncClient]:
|
|
app = create_app(settings)
|
|
transport = ASGITransport(app=app)
|
|
async with app.router.lifespan_context(app), AsyncClient(transport=transport, base_url=settings.issuer) as ac:
|
|
yield ac
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_rate_limiter() -> None:
|
|
"""Reset the rate limiter storage before each test."""
|
|
limiter.reset()
|
|
|
|
|
|
async def get_csrf_token(client: AsyncClient) -> str:
|
|
"""Get a CSRF token by visiting the login page.
|
|
|
|
Returns the token string. The session cookie is automatically stored
|
|
in the client's cookie jar (httpx.AsyncClient persists cookies).
|
|
"""
|
|
resp = await client.get("/login")
|
|
match = re.search(r'name="csrf-token" content="([^"]+)"', resp.text)
|
|
assert match, "CSRF meta tag not found in page"
|
|
return match.group(1)
|