Add 5 plans

This commit is contained in:
2026-06-24 15:30:47 +02:00
parent 5348bedb86
commit d97d8c6cf4
5 changed files with 644 additions and 0 deletions
+162
View File
@@ -0,0 +1,162 @@
# Plan 4 — Security
**Items covered:** #1 (no authentication), #11 (no rate limiting), #14 (unbounded limit parameter)
---
## Context
You are working on a FastAPI backend for a baby monitor / sleep tracker app. The codebase lives under `app/`. This plan adds three layers of security enforcement. Do them in order — auth first, then rate limiting, then the parameter bound. After each change, re-read the modified files to verify correctness, and run the tests.
---
## Tasks
### 1. Add API key authentication to all endpoints (`app/`)
#### Step 1 — Read the relevant files first
Read `app/config.py`, `app/main.py`, and all router files under `app/routers/` before making any changes. Understand how the app is structured and how dependencies are currently shared.
#### Step 2 — Add `API_KEY` to settings
In `app/config.py`, add an `API_KEY` field to the `Settings` model:
```python
API_KEY: str = ""
```
The empty default means auth is effectively disabled unless the env var is set. This allows existing tests to keep working without modification (see Step 5 for the alternative if you prefer strict-by-default).
#### Step 3 — Create the auth dependency
Create a new file `app/dependencies.py`:
```python
from fastapi import Security, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.config import settings
bearer = HTTPBearer(auto_error=False)
def verify_api_key(creds: HTTPAuthorizationCredentials = Security(bearer)):
if not settings.API_KEY:
# API_KEY not configured — auth is disabled (development mode)
return
if creds is None or creds.credentials != settings.API_KEY:
raise HTTPException(status_code=401, detail="Unauthorized")
```
#### Step 4 — Apply the dependency to all routers
In each file under `app/routers/`, add `verify_api_key` as a dependency on the router so every endpoint in that file is protected:
```python
from fastapi import APIRouter, Depends
from app.dependencies import verify_api_key
router = APIRouter(dependencies=[Depends(verify_api_key)])
```
Apply this to every router file. Do not add it per-endpoint — the router-level dependency is cleaner and ensures nothing is accidentally missed.
#### Step 5 — Update the tests
Any test that calls a protected endpoint now needs an `Authorization: Bearer <key>` header — OR the `API_KEY` setting can be left empty (the default), which disables the check. Verify which approach your test setup uses. If tests use a test client with a settings override, ensure `API_KEY` is either empty (disables check) or consistently set to a test value and the header is passed.
Run the tests:
```bash
pytest
```
Fix any 401 failures before proceeding.
---
### 2. Add rate limiting (`app/main.py` and `app/routers/`)
#### Step 1 — Install slowapi
```bash
uv add slowapi
```
Or add `slowapi` to `pyproject.toml` dependencies and run `uv sync`.
#### Step 2 — Configure the limiter in `main.py`
```python
from slowapi import Limiter, _rate_limit_exceeded_handler
from slowapi.util import get_remote_address
from slowapi.errors import RateLimitExceeded
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
```
#### Step 3 — Apply limits to endpoints
Add rate limit decorators to the endpoints most at risk:
- All write endpoints (`POST`, `PUT`, `PATCH`, `DELETE`): `@limiter.limit("10/minute")`
- The timetable endpoint (expensive): `@limiter.limit("30/minute")`
- Other read endpoints: `@limiter.limit("60/minute")`
Example:
```python
from app.main import limiter
from fastapi import Request
@router.post("/sessions")
@limiter.limit("10/minute")
async def create_session(request: Request, ...):
...
```
Note: `slowapi` requires the `request: Request` parameter to be present in the function signature for the decorator to work. Add it to any endpoint that doesn't already have it. It does not need to be used inside the function body.
#### Step 4 — Verify
Run the tests:
```bash
pytest
```
Rate limiting may interfere with tests that call endpoints in a tight loop. If so, configure the limiter to be disabled in the test environment (e.g. check for a `TESTING=true` env var and set `enabled=False` on the limiter).
---
### 3. Bound the `limit` query parameter (`app/routers/sessions.py`)
#### Step 1 — Read the file first
Find the `get_sessions` endpoint (or equivalent) and locate the `limit` parameter.
#### Step 2 — Add the bound
Replace the bare `limit: int = 200` with a constrained `Query`:
```python
from fastapi import Query
async def get_sessions(
limit: int = Query(default=200, ge=1, le=1000),
...
):
```
This rejects any `limit` value below 1 or above 1000 with a FastAPI 422 validation error automatically — no extra code needed.
#### Step 3 — Verify
Run the full test suite one final time:
```bash
pytest
```
All tests must pass before declaring this plan complete. Do not move on to Plan 5 until the suite is green.