Remove legacy cookie auth and Person-based recipe helper; spec updated

This commit is contained in:
jableader 2025-11-01 21:21:53 +11:00
parent 42715c9fa6
commit 93b9869735
4 changed files with 3 additions and 44 deletions

View file

@ -3,11 +3,10 @@ from __future__ import annotations
from typing import AsyncGenerator, Optional from typing import AsyncGenerator, Optional
import aiosqlite import aiosqlite
from fastapi import Cookie, Depends, HTTPException, Request from fastapi import Depends, HTTPException, Request
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
import db import db
import persons
from common import ProblemDetails from common import ProblemDetails
from settings import settings from settings import settings
from users.models import User from users.models import User
@ -45,34 +44,6 @@ async def get_db() -> AsyncGenerator[aiosqlite.Connection, None]:
await sql_db.close() await sql_db.close()
async def cookie_person(
user_id: int = Cookie(..., alias="user_id"),
conn: aiosqlite.Connection = Depends(get_db),
) -> persons.Person:
"""Return the authenticated user from the user_id cookie or raise 401.
When the cookie is missing, FastAPI will raise 422 (validation error).
"""
person = await persons.get_by_id(conn, user_id)
if not person:
raise HTTPException(status_code=401, detail="Unauthorized")
return person
async def cookie_person_optional(
user_id: Optional[int] = Cookie(default=None, alias="user_id"),
conn: aiosqlite.Connection = Depends(get_db),
) -> Optional[persons.Person]:
"""Return the authenticated user if cookie present; otherwise None.
Use for endpoints that want to return 401 for missing auth themselves.
"""
if user_id is None:
return None
person = await persons.get_by_id(conn, user_id)
return person
def error_response(request: Optional[Request], status_code: int, message: str) -> JSONResponse: def error_response(request: Optional[Request], status_code: int, message: str) -> JSONResponse:
body = ProblemDetails( body = ProblemDetails(
title=message, title=message,

View file

@ -149,7 +149,7 @@ Refactor the backend from a single-tenant architecture to a robust, multi-tenant
- `POST /api/v1/auth/logout`: clears refresh cookie. After logout, subsequent `POST /api/v1/auth/refresh` returns 401. - `POST /api/v1/auth/logout`: clears refresh cookie. After logout, subsequent `POST /api/v1/auth/refresh` returns 401.
Notes: Notes:
- `get_current_user` (JWT) is used by protected endpoints (households, shopping purchase, etc.). Legacy `cookie_person` remains only for historical v1 module references and will be removed with full v2 completion. - `get_current_user` (JWT) is used by protected endpoints (households, shopping purchase, etc.). Legacy cookie-based helpers have been removed from the app surface.
- RFC7807 error semantics and the existing OpenAPI augmentation are preserved. The special-case 401 mapping for POST shopping is obsolete under JWT. - RFC7807 error semantics and the existing OpenAPI augmentation are preserved. The special-case 401 mapping for POST shopping is obsolete under JWT.
### 3.2. Household & Tenancy API (current) ### 3.2. Household & Tenancy API (current)
@ -296,7 +296,7 @@ Remaining work (prioritized cleanup to final state):
1. Remove the `persons/` package and all code references across domains (recipes, meals, shopping). Replace `Person` with `users`/HouseholdMember everywhere: 1. Remove the `persons/` package and all code references across domains (recipes, meals, shopping). Replace `Person` with `users`/HouseholdMember everywhere:
- Code hotspots today: `recipes/models.py` (imports Person), `recipes/repository.py` (FKs, hide_recipe signature), `meals/models.py` (participants as List[Person]), `api/shopping_models.py` and `shopping/models.py` (ShoppingList.purchased_by typed as Person), and `shopping/repository.py` (FKs to Person). - Code hotspots today: `recipes/models.py` (imports Person), `recipes/repository.py` (FKs, hide_recipe signature), `meals/models.py` (participants as List[Person]), `api/shopping_models.py` and `shopping/models.py` (ShoppingList.purchased_by typed as Person), and `shopping/repository.py` (FKs to Person).
- Replace internal usages with `user_id`/`MemberRef` where outward, and update repository DDL to stop referencing `Person`. - Replace internal usages with `user_id`/`MemberRef` where outward, and update repository DDL to stop referencing `Person`.
- Remove `api.deps.cookie_person` once no tests or code depend on it. - Removed `api.deps.cookie_person` and `cookie_person_optional`; v1 tests referencing them remain skipped and will be deleted or ported.
- Ensure no API module performs direct SQL; all persistence must flow through repositories (enforced during cleanup). - Ensure no API module performs direct SQL; all persistence must flow through repositories (enforced during cleanup).
2. Recipes outward schema: DONE for `createdById`/`createdBy` and `hiddenById`/`hiddenBy` (MemberRef on delete). If/when we expose hide actor on other flows, keep MemberRef. 2. Recipes outward schema: DONE for `createdById`/`createdBy` and `hiddenById`/`hiddenBy` (MemberRef on delete). If/when we expose hide actor on other flows, keep MemberRef.
3. Shopping outward DTOs: DONE — `ShoppingListOut.purchasedBy` is a MemberRef; mapping added. Ensure clients shift to `displayName`. 3. Shopping outward DTOs: DONE — `ShoppingListOut.purchasedBy` is a MemberRef; mapping added. Ensure clients shift to `displayName`.

View file

@ -17,7 +17,6 @@ from recipes.repository import (
get_all as get_all, get_all as get_all,
get_all_paged as get_all_paged, get_all_paged as get_all_paged,
get_all_paged_scoped as get_all_paged_scoped, get_all_paged_scoped as get_all_paged_scoped,
hide_recipe as hide_recipe,
insert_recipe as insert_recipe, insert_recipe as insert_recipe,
insert_recipe_scoped as insert_recipe_scoped, insert_recipe_scoped as insert_recipe_scoped,
load_recipe_ingredients as load_recipe_ingredients, load_recipe_ingredients as load_recipe_ingredients,

View file

@ -74,17 +74,6 @@ async def insert_recipe_scoped(conn, recipe: Recipe, household_id: int):
recipe.id = cursor.lastrowid recipe.id = cursor.lastrowid
async def hide_recipe(conn, recipe_id: int, person):
await conn.execute(
"""
UPDATE Recipe
SET date_hidden = ?, hidden_by_id = ?
WHERE id = ?
""",
(datetime.datetime.now().astimezone().isoformat(), person.id, recipe_id),
)
async def hide_recipe_scoped(conn, recipe_id: int, household_id: int) -> bool: async def hide_recipe_scoped(conn, recipe_id: int, household_id: int) -> bool:
"""Soft-delete a recipe by household for v2. """Soft-delete a recipe by household for v2.