Files
service-finder/plans/logic_spec_identity_preserving_soft_delete.md

6.4 KiB

🏗️ Logic Spec: Identity-Preserving Soft Delete & Asset Validation

1. Modul Célja és Masterbook 2 Illeszkedés

Mérföldkő: Identity & Security Hardening
Masterbook 2 Referencia: docs/v201/05_AUTH_AND_IDENTITY_SPEC.md (Soft Delete / Anonymization szekció)
Masterbook 2 Referencia: docs/v201/18_ASSET_AND_FLEET_SPECIFICATION.md (Asset specifikáció)

Jelenlegi állapot a Masterbook szerint:

  • A Soft Delete már létezik, de hiányos: nincs deleted_at timestamp, nincs token invalidáció, nem védi a Person rekordot.
  • Az Asset modellben nincs CheckConstraint a VIN/Rendszám "Vagy-Vagy" szabályhoz.
  • A Pydantic sémákban nincs model_validator a kötelező azonosító validációhoz.

2. Adatmodell változások

2.1 Asset CheckConstraint (backend/app/models/vehicle/asset.py)

Jelenlegi állapot: A Asset osztály __table_args__-ja csak a sémát adja meg:

__table_args__ = {"schema": "vehicle"}

Módosítás: Bővítsük ki a __table_args__-t egy CheckConstraint-tel:

__table_args__ = (
    CheckConstraint(
        "vin IS NOT NULL OR license_plate IS NOT NULL",
        name="ck_asset_vin_or_plate_required"
    ),
    {"schema": "vehicle"}
)

Import: A CheckConstraint már elérhető az SQLAlchemy importok között? Ellenőrizendő: jelenleg csak UniqueConstraint van importálva. Ki kell egészíteni:

from sqlalchemy import String, Boolean, DateTime, ForeignKey, Numeric, text, Text, UniqueConstraint, CheckConstraint, BigInteger, Integer, Float

2.2 deleted_at oszlop a User modellhez (backend/app/models/identity/identity.py)

Jelenlegi állapot: A User osztályban már van is_deleted: Mapped[bool] (149. sor), de hiányzik a deleted_at.

Módosítás: Add hozzá a deleted_at mezőt a User osztályhoz a created_at után:

# === SOFT DELETE ===
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)

2.3 deleted_at oszlop a Person modellhez (opcionális, struktúra miatt)

Jelenlegi állapot: A Person osztályban nincs deleted_at.

Módosítás: Add hozzá a struktúra konzisztencia miatt:

# === SOFT DELETE (structure only - never delete Person data) ===
deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)

3. Pydantic Séma Módosítások

3.1 AssetCreate séma (backend/app/schemas/asset.py)

Jelenlegi állapot: A license_plate kötelező (Field(...)), a vin opcionális.

Módosítás: Mindkét mező legyen Optional[str] = None:

license_plate: Optional[str] = Field(None, min_length=2, max_length=20, description="Rendszám")
vin: Optional[str] = Field(None, min_length=1, max_length=50, description="VIN szám (opcionális)")

Validátor: Adj hozzá egy @model_validator (Pydantic V2 mód) vagy @root_validator (Pydantic V1 mód) metódust, ami:

  1. Üres string ("") átalakítása None-ra mindkét mezőnél
  2. Ha mindkettő None, dobjon ValueError-t (422-es HTTP válasz)

3.2 AssetUpdate séma (backend/app/schemas/asset.py)

Jelenlegi állapot: A license_plate és vin már Optional[str] = None.

Módosítás: Adj hozzá egy @model_validator-t vagy @root_validator-t, ami csak akkor dob hibát, ha a felhasználó explicitly None-ra akarja állítani mindkettőt.


4. Backend Service Logika: Person-Preserving Soft Delete

4.1 AuthService.soft_delete_user módosítása (backend/app/services/auth_service.py)

Jelenlegi kód: soft_delete_user

Módosítások sorrendben:

  1. deleted_at beállítás:

    user.deleted_at = datetime.now(timezone.utc)
    
  2. E-mail átírás (már létezik, de pontosítva):

    old_email = user.email
    timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')
    user.email = f"deleted_{user.id}_{timestamp}_{old_email}"
    
  3. Person rekord érintetlenül hagyása (CRITICAL - maradjon ahogy van, NE módosítsuk)

  4. Token invalidáció: Töröljük az összes aktív Refresh Token-t

  5. Naplózás bővítése deleted_at-tal


5. API Végpont: DELETE /api/v1/users/me

5.1 Új endpoint (backend/app/api/v1/endpoints/users.py)

@router.delete("/me", status_code=200)
async def delete_my_account(
    reason: Optional[str] = Body(None, description="Törlés oka (opcionális)"),
    db: AsyncSession = Depends(get_db),
    current_user: User = Depends(get_current_user),
):
    """
    Saját fiók soft-delete.
    - Anonimizálja az e-mail címet
    - is_active = False, is_deleted = True, deleted_at = timestamp
    - Person rekordot NEM bántja
    - Érvényteleníti az összes refresh token-t
    - Wallet és Gamification adatokat megőrzi (inaktív user miatt freeze)
    """
    success = await AuthService.soft_delete_user(
        db=db,
        user_id=current_user.id,
        reason=reason or "user_requested_self_delete",
        actor_id=current_user.id
    )
    
    if not success:
        raise HTTPException(
            status_code=400,
            detail="A felhasználó már törölve van."
        )
    
    return {"status": "ok", "message": "Fiók sikeresen törölve."}

6. Adatbázis Szinkronizáció

A módosítások után futtatni kell a sync_engine-t:

docker exec -it sf_api python -m app.scripts.sync_engine

7. Tesztterv

7.1 Asset validáció tesztelése

  1. Hozz létre járművet csak VIN-nel → sikeres
  2. Hozz létre járművet csak rendszámmal → sikeres
  3. Hozz létre járművet mindkettővel → sikeres
  4. Hozz létre járművet egyik nélkül sem → 422-es hiba

7.2 Soft Delete tesztelése

  1. Hozz létre usert, authentikálj
  2. DELETE /api/v1/users/me hívás
  3. Ellenőrizd: User.is_deleted = True, User.is_active = False, User.deleted_at beállítva
  4. Ellenőrizd: Person rekord adatai változatlanok, Person.is_active változatlan
  5. Ellenőrizd: Refresh token törölve (régi session nem működik)