feat(api): enforce ownership across health routines and profile flows
This commit is contained in:
parent
cd8e39939a
commit
ffa3b71309
14 changed files with 1225 additions and 206 deletions
100
backend/tests/test_tenancy_domains.py
Normal file
100
backend/tests/test_tenancy_domains.py
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from uuid import uuid4
|
||||
|
||||
from innercontext.api.auth_deps import get_current_user
|
||||
from innercontext.auth import CurrentUser, IdentityData, TokenClaims
|
||||
from innercontext.models import Role
|
||||
from innercontext.models.ai_log import AICallLog
|
||||
from main import app
|
||||
|
||||
|
||||
def _user(subject: str, *, role: Role = Role.MEMBER) -> CurrentUser:
|
||||
claims = TokenClaims(
|
||||
issuer="https://auth.test",
|
||||
subject=subject,
|
||||
audience=("innercontext-web",),
|
||||
expires_at=datetime.now(UTC) + timedelta(hours=1),
|
||||
raw_claims={"iss": "https://auth.test", "sub": subject},
|
||||
)
|
||||
return CurrentUser(
|
||||
user_id=uuid4(),
|
||||
role=role,
|
||||
identity=IdentityData.from_claims(claims),
|
||||
claims=claims,
|
||||
)
|
||||
|
||||
|
||||
def _set_current_user(user: CurrentUser) -> None:
|
||||
app.dependency_overrides[get_current_user] = lambda: user
|
||||
|
||||
|
||||
def test_profile_health_routines_skincare_ai_logs_are_user_scoped_by_default(
|
||||
client, session
|
||||
):
|
||||
owner = _user("owner")
|
||||
intruder = _user("intruder")
|
||||
|
||||
_set_current_user(owner)
|
||||
profile = client.patch(
|
||||
"/profile", json={"birth_date": "1991-01-15", "sex_at_birth": "male"}
|
||||
)
|
||||
medication = client.post(
|
||||
"/health/medications", json={"kind": "prescription", "product_name": "Owner Rx"}
|
||||
)
|
||||
routine = client.post(
|
||||
"/routines", json={"routine_date": "2026-03-01", "part_of_day": "am"}
|
||||
)
|
||||
snapshot = client.post("/skincare", json={"snapshot_date": "2026-03-01"})
|
||||
log = AICallLog(endpoint="routines/suggest", model="gemini-3-flash-preview")
|
||||
log.user_id = owner.user_id
|
||||
session.add(log)
|
||||
session.commit()
|
||||
session.refresh(log)
|
||||
|
||||
assert profile.status_code == 200
|
||||
assert medication.status_code == 201
|
||||
assert routine.status_code == 201
|
||||
assert snapshot.status_code == 201
|
||||
|
||||
medication_id = medication.json()["record_id"]
|
||||
routine_id = routine.json()["id"]
|
||||
snapshot_id = snapshot.json()["id"]
|
||||
|
||||
_set_current_user(intruder)
|
||||
assert client.get("/profile").json() is None
|
||||
assert client.get("/health/medications").json() == []
|
||||
assert client.get("/routines").json() == []
|
||||
assert client.get("/skincare").json() == []
|
||||
assert client.get("/ai-logs").json() == []
|
||||
|
||||
assert client.get(f"/health/medications/{medication_id}").status_code == 404
|
||||
assert client.get(f"/routines/{routine_id}").status_code == 404
|
||||
assert client.get(f"/skincare/{snapshot_id}").status_code == 404
|
||||
assert client.get(f"/ai-logs/{log.id}").status_code == 404
|
||||
|
||||
|
||||
def test_health_admin_override_requires_explicit_user_id(client):
|
||||
owner = _user("owner")
|
||||
admin = _user("admin", role=Role.ADMIN)
|
||||
|
||||
_set_current_user(owner)
|
||||
created = client.post(
|
||||
"/health/lab-results",
|
||||
json={
|
||||
"collected_at": "2026-03-01T00:00:00",
|
||||
"test_code": "718-7",
|
||||
"test_name_original": "Hemoglobin",
|
||||
},
|
||||
)
|
||||
assert created.status_code == 201
|
||||
|
||||
_set_current_user(admin)
|
||||
default_scope = client.get("/health/lab-results")
|
||||
assert default_scope.status_code == 200
|
||||
assert default_scope.json()["items"] == []
|
||||
|
||||
overridden = client.get(f"/health/lab-results?user_id={owner.user_id}")
|
||||
assert overridden.status_code == 200
|
||||
assert len(overridden.json()["items"]) == 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue