Skip to content

Feat: audit log és verziózás az admin írásokra #76

Description

@Xentinus

Jelenség

Az admin írásoknak nincs nyoma. A tartalom-entitásokon egyetlen UpdatedAt van, ami csak azt mondja meg, hogy valamikor módosult:

/// <summary>Last edit, written by the admin write path. Feeds the sitemap's lastmod.</summary>
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;

Nincs rögzítve, mi változott, és nincs mód visszaállni. Egy véletlenül kitörölt leírás vagy egy elrontott rich text mentés után az egyetlen lehetőség a napi dump visszatöltése (deploy/restore.sh) — vagyis az egész adatbázis, minden más közben történt változással együtt.

A CloudflareAccessMiddleware validálja az Access assertiont, de a benne lévő identitást nem használja fel semmire:

var result = await ValidateAsync(token, context.RequestAborted);
if (!result.IsValid) { ... }
await next(context);

Vagyis a "ki írta" adat rendelkezésre áll (a tokenben benne van az e-mail cím), csak nem kerül ki a middleware-ből. Ma egyszemélyes projekt, de egy API token szivárgása esetén pont ez a napló mondaná meg, mi történt.

Javaslat

  1. ContentAudit tábla: Id, OccurredAt, Actor (az Access assertion e-mail claimje), EntityType, EntitySlug, Action (Create / Update / Delete), Payload (jsonb, a mentett állapot vagy a diff).
  2. A CloudflareAccessMiddleware tegye be a validált token identitását a HttpContext.Items-be (vagy állítson ClaimsPrincipal-t a result.ClaimsIdentity-ből) — ez a TokenValidationResult-ban már megvan, csak eldobjuk.
  3. A naplózás egy helyen legyen, ne minden admin controllerben: SaveChangesAsync interceptor (EF Core SaveChangesInterceptor) vagy egy közös admin write helper. A hét admin controller mindegyikébe szórt kód garantáltan elcsúszik.
  4. Tárolt payload: a teljes állapot a legegyszerűbb és a visszaállításhoz a legjobb; a rich text miatt viszont nagy. Kompromisszum: teljes állapot tartalom-entitásokra, csak metaadat a csatolmányokra (a Data bájtok soha ne kerüljenek a naplóba).
  5. Retention: a napló is növekvő tábla — a PageViewRetentionService / ContactRetentionService mintájára kell hozzá egy Audit__RetentionDays beállítás és sweep.
  6. Admin UI: időrendi lista a dashboardon, elemenként "visszaállítás erre az állapotra" akció. A visszaállítás legyen új mentés (tehát maga is naplózva), ne rejtett írás.
  7. Adatvédelem: a napló Actor mezője személyes adat, a deploy/RUNBOOK.md-ben szerepeljen, hogy hol tárolódik és meddig.

Érintett fájlok

  • új PortfolioCMS.Core/Models/ContentAudit.cs
  • PortfolioCMS.Core/Data/AppDbContext.cs, PortfolioCMS.Core/Migrations/ — új migráció
  • PortfolioCMS.Core/Middleware/CloudflareAccessMiddleware.cs
  • új PortfolioCMS.Core/Services/AuditInterceptor.cs, AuditOptions.cs, AuditRetentionService.cs
  • PortfolioCMS.Admin/Controllers/ — a hét admin controller (ha nem interceptorral megy)
  • új admin panel a portfoliocms.admin.client/src/components/admin/ alatt
  • docker-compose.yml, .env.example, deploy/RUNBOOK.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions