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
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).
- 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.
- 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.
- 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).
- 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.
- 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.
- 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
Jelenség
Az admin írásoknak nincs nyoma. A tartalom-entitásokon egyetlen
UpdatedAtvan, ami csak azt mondja meg, hogy valamikor módosult: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
CloudflareAccessMiddlewarevalidálja az Access assertiont, de a benne lévő identitást nem használja fel semmire: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
ContentAudittábla:Id,OccurredAt,Actor(az Access assertion e-mail claimje),EntityType,EntitySlug,Action(Create/Update/Delete),Payload(jsonb, a mentett állapot vagy a diff).CloudflareAccessMiddlewaretegye be a validált token identitását aHttpContext.Items-be (vagy állítsonClaimsPrincipal-t aresult.ClaimsIdentity-ből) — ez aTokenValidationResult-ban már megvan, csak eldobjuk.SaveChangesAsyncinterceptor (EF CoreSaveChangesInterceptor) vagy egy közös admin write helper. A hét admin controller mindegyikébe szórt kód garantáltan elcsúszik.Databájtok soha ne kerüljenek a naplóba).PageViewRetentionService/ContactRetentionServicemintájára kell hozzá egyAudit__RetentionDaysbeállítás és sweep.Actormezője személyes adat, adeploy/RUNBOOK.md-ben szerepeljen, hogy hol tárolódik és meddig.Érintett fájlok
PortfolioCMS.Core/Models/ContentAudit.csPortfolioCMS.Core/Data/AppDbContext.cs,PortfolioCMS.Core/Migrations/— új migrációPortfolioCMS.Core/Middleware/CloudflareAccessMiddleware.csPortfolioCMS.Core/Services/AuditInterceptor.cs,AuditOptions.cs,AuditRetentionService.csPortfolioCMS.Admin/Controllers/— a hét admin controller (ha nem interceptorral megy)portfoliocms.admin.client/src/components/admin/alattdocker-compose.yml,.env.example,deploy/RUNBOOK.md