From 3376cf99dc910dc31d3c4c13a0a077d109cbace5 Mon Sep 17 00:00:00 2001 From: sysops Date: Thu, 27 Aug 2026 20:56:48 +0200 Subject: [PATCH] AUD-05: audit-log-registrierung bei archive-retention-engine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit internal/audit/retention.go: RegisterWithArchive meldet audit_log_entry als Objekttyp bei Archives Retention-Engine an (Default-Frist 10 Jahre, GoBD- Buchungsbeleg-Frist, tenant-ueberschreibbar). RetentionRegistrar ist der RET-05-Modul-Adapter-Vertrag, wie Core ihn konsumiert — die eigentliche Implementierung lebt im Archive-Modul. WICHTIGER HINWEIS: Archive (RET-01 Retention-Objektmodell, RET-02 Fristen- Engine, RET-05 Modul-Adapter) existiert zum Zeitpunkt dieser Kachel NICHT als Code — nur als Planung in archive-kanban/. Diese Kachel implementiert ausschliesslich die Core-Seite (Registrierungsaufruf gegen die Schnittstelle) und testet sie gegen einen lokalen Fake, der den RET-05-Vertrag simuliert. Das ist KEIN Ersatz fuer eine echte Integrationspruefung gegen Archive. Core implementiert bewusst keine eigene Loeschlogik fuer Audit-Eintraege (Akzeptanzkriterium 3) — es gibt in diesem Paket keinen Delete-Codepfad ausser dem durch AUD-02 technisch unterbundenen. Pruefungen: 1. Registrierung bei Archive erfolgreich getestet, Objekttyp taucht in Archives Retention-Konfiguration auf — NICHT durchfuehrbar, da Archive nicht existiert. Stattdessen TestRegisterWithArchive_UsesCorrectObjectTypeAndRetention gegen Fake: bestaetigt korrekten Aufruf mit objectType=audit_log_entry, 10 Jahre, tenantOverridable=true. Ausgefuehrt, PASS — aber die eigentliche Pruefung bleibt OFFEN bis Archive RET-05 existiert. 2. Audit-Eintrag mit abgelaufener Frist wird von Archive korrekt als loeschfaellig markiert, Core greift nicht ein — NICHT durchfuehrbar ohne Archive RET-02. Offen. 3. Legal Hold aus Archive verhindert Loeschung trotz abgelaufener Frist — NICHT durchfuehrbar ohne Archive RET-01/02. Offen. Co-Authored-By: Claude Sonnet 5 --- internal/audit/retention.go | 41 ++++++++++++++++++++++++++ internal/audit/retention_test.go | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 internal/audit/retention.go create mode 100644 internal/audit/retention_test.go diff --git a/internal/audit/retention.go b/internal/audit/retention.go new file mode 100644 index 0000000..75e8673 --- /dev/null +++ b/internal/audit/retention.go @@ -0,0 +1,41 @@ +package audit + +import "context" + +// AuditLogObjectType ist der Objekttyp-Bezeichner, unter dem Audit-Log- +// Eintraege bei der Archive-Retention-Engine registriert werden +// (Akzeptanzkriterium 1). +const AuditLogObjectType = "audit_log_entry" + +// DefaultAuditRetentionYears ist die GoBD-Buchungsbeleg-Frist (Akzeptanz- +// kriterium 2) — Default, pro Tenant ueberschreibbar sofern rechtlich +// zulaessig. Die eigentliche Ueberschreibung/Durchsetzung liegt vollstaendig +// bei Archive, siehe AuditRetentionTenantOverridable. +const DefaultAuditRetentionYears = 10 + +// AuditRetentionTenantOverridable erlaubt Archive, die Default-Frist pro +// Tenant zu ueberschreiben — Core trifft dabei keine rechtliche Entscheidung, +// sondern erlaubt Archive lediglich, so eine Entscheidung zuzulassen. +const AuditRetentionTenantOverridable = true + +// RetentionRegistrar ist der Modul-Adapter-Vertrag aus Archive RET-05, wie +// Core ihn konsumiert. Die tatsaechliche Implementierung lebt im +// Archive-Modul (RET-01/RET-02/RET-05) und existiert zum Zeitpunkt dieser +// Kachel noch nicht als Code — Core kennt nur diese Schnittstelle. +// +// WICHTIG: Core implementiert absichtlich KEINE eigene Loeschlogik fuer +// Audit-Eintraege (Akzeptanzkriterium 3). Dieses Paket enthaelt keinen +// Delete-Codepfad fuer audit_events ausser dem durch AUD-02 technisch +// unterbundenen — die tatsaechliche Loeschung/Aufbewahrungssperre erfolgt +// ausschliesslich innerhalb von Archive, ausserhalb dieses Prozesses. +type RetentionRegistrar interface { + RegisterObjectType(ctx context.Context, objectType string, defaultRetentionYears int, tenantOverridable bool) error +} + +// RegisterWithArchive meldet den Audit-Log-Objekttyp bei der Archive- +// Retention-Engine an. Dies ist die EINZIGE Beruehrung dieses Pakets mit +// Retention ueberhaupt — kein zweites, Core-eigenes Retention-System +// (siehe "Bekannte Fehler vermeiden" im AUD-05-Ticket). +func RegisterWithArchive(ctx context.Context, registrar RetentionRegistrar) error { + return registrar.RegisterObjectType(ctx, AuditLogObjectType, DefaultAuditRetentionYears, AuditRetentionTenantOverridable) +} diff --git a/internal/audit/retention_test.go b/internal/audit/retention_test.go new file mode 100644 index 0000000..4e498fc --- /dev/null +++ b/internal/audit/retention_test.go @@ -0,0 +1,49 @@ +package audit + +import ( + "context" + "testing" +) + +// fakeRegistrar simuliert den RET-05-Modul-Adapter-Vertrag, da Archive +// (RET-01/RET-02/RET-05) zum Zeitpunkt dieser Kachel noch nicht als Code +// existiert (nur geplant in archive-kanban). Belegt NUR, dass Core mit den +// richtigen Parametern registriert — ersetzt KEINE Integrationspruefung +// gegen die echte Archive-Engine, siehe Pruefungen-Abschnitt im Commit. +type fakeRegistrar struct { + objectType string + defaultRetentionYears int + tenantOverridable bool + called bool +} + +func (f *fakeRegistrar) RegisterObjectType(ctx context.Context, objectType string, defaultRetentionYears int, tenantOverridable bool) error { + f.called = true + f.objectType = objectType + f.defaultRetentionYears = defaultRetentionYears + f.tenantOverridable = tenantOverridable + return nil +} + +// Akzeptanzkriterium 1 + 2: Registrierung mit korrektem Objekttyp und +// GoBD-Default-Frist von 10 Jahren, tenant-ueberschreibbar. +func TestRegisterWithArchive_UsesCorrectObjectTypeAndRetention(t *testing.T) { + fake := &fakeRegistrar{} + + if err := RegisterWithArchive(context.Background(), fake); err != nil { + t.Fatalf("register: %v", err) + } + + if !fake.called { + t.Fatal("erwartet aufruf von RegisterObjectType") + } + if fake.objectType != AuditLogObjectType { + t.Fatalf("objectType = %q, want %q", fake.objectType, AuditLogObjectType) + } + if fake.defaultRetentionYears != 10 { + t.Fatalf("defaultRetentionYears = %d, want 10 (GoBD-Frist)", fake.defaultRetentionYears) + } + if !fake.tenantOverridable { + t.Fatal("erwartet tenantOverridable = true") + } +}