Audit-log
Hvad logges, hvorfor det logges, og hvor længe det gemmes.
Audit-loggen er Ressourcifys svar på "hvem gjorde hvad hvornår?". Hver
mutation til kritiske entiteter giver en uforanderlig række i AuditLog.
Hvad logges
Alle handlinger med actor er kandidat — i praksis skrives audit ved alle
CREATE/UPDATE/DELETE på følgende entiteter:
| Entitet | Hvorfor logges |
|---|---|
User | Rolle- og status-ændringer påvirker adgang |
UserRole | Adgangskontrol — kritisk for compliance |
Project | Hvem ændrede projekt-data hvornår |
Assignment | Allokeringer er kontraktlige forpligtelser |
Leave | Fravær-godkendelser sporbarhed |
ForecastPlan | Planlægnings-ændringer over tid |
EntraConfig | SSO-konfigurations-ændringer |
LeaveType, ForecastSize | Konfigurationsdata påvirker beregninger |
Login og logout logges også (AuditAction.LOGIN, LOGOUT), samt
eksport-handlinger (EXPORT).
Felter
| Felt | Eksempel | Note |
|---|---|---|
organizationId | UUID | Tenant-isolation |
performedBy | UUID? | null ved system-handlinger (autoprune, cron) |
action | UPDATE | Enum: CREATE/UPDATE/DELETE/LOGIN/LOGOUT/EXPORT |
entityType | "Assignment" | Modelnavn (string, ikke FK) |
entityId | UUID | ID på påvirket række |
oldValues | {value: 50} | Snapshot før (kun ved UPDATE/DELETE) |
newValues | {value: 75} | Snapshot efter (kun ved CREATE/UPDATE) |
createdAt | timestamp | UTC |
Audit-flow
Audit-skrivning sker i samme transaktion som mutationen — enten lykkes begge eller ingen. Hvis audit-INSERT fejler, rulles mutationen tilbage.
Hvad logges ikke
- Reads — for støjende; ville fylde 99% af loggen
- Bulk-batches — én linje per genereret/slettet Assignment, ikke én per batch
- Auto-prunet data — efter retention slettes rækken; vi logger ikke "vi slettede audit-linjer"
- Følsomme felter —
User.passwordHash,EntraConfig.clientSecretEncryptedredactes til"<redacted>"ioldValues/newValues
Retention
Audit-loggen er ikke uendelig. Hver organisation har en
retention-periode (standard 365 dage), som kan ændres af ORG_ADMIN:
Indstillinger → Sikkerhed → Audit-retention — vælg mellem 90, 180, 365, 730 eller 1825 dage.
Autoprune-jobbet
Én gang om dagen pr. organisation kører runAuditAutoprune():
- Læs
lastRunDatefraAppConfig - Hvis allerede kørt i dag → spring over
- Skriv
todaytillastRunDate(optimistisk lock) - Beregn cutoff =
now − retentionDays DELETE FROM AuditLog WHERE createdAt < cutoff
Idempotent: kører jobbet to gange samme dag, går runde 2 ud uden at slette noget.
Når en audit-linje først er autoprunet, kan den ikke genskabes. Eksportér til CSV før retention-grænsen hvis du har behov for længere opbevaring (fx revisions-formål).
Tilgang til audit
| Handling | Krævet rolle |
|---|---|
| Læs audit-side | ORG_ADMIN |
| Eksportér til CSV | ORG_ADMIN |
| Ændre retention | ORG_ADMIN |
| Slet manuelt | Ikke muligt — kun via autoprune |
DEPT_ADMIN og COORDINATOR har ingen adgang til audit-loggen, heller
ikke for egen afdeling. Begrundelsen er compliance: kun én rolle skal kunne
se hele billedet.
Hvor finder du audit-siden
I sidemenuen under Org & System → Audit.
Filtre:
- Periode (default seneste 30 dage)
- Entity-type (User, Assignment, …)
- Performer (drop-down med brugere)
- Action (CREATE/UPDATE/DELETE/…)
Klik på en række for at se oldValues og newValues som JSON-diff.
Eksport til CSV
På audit-siden: filter → Eksportér CSV. Den downloades med felterne:
Eksport-handlingen logger selv et EXPORT-audit-event — så
"hvem eksporterede hele audit-loggen?" kan også besvares.
Hvor logikken bor
| Sti | Funktion |
|---|---|
lib/api/audit.ts | writeAudit() — skriver én række |
lib/audit/autoprune.ts | runAuditAutoprune() — daglig oprydning |
lib/audit/config.server.ts | Læser per-org retention fra AppConfig |
app/api/v1/admin/audit/route.ts | Liste-endpoint med filtre |