- Löschfristen (Migration 0020): pro Mandant einstellbar statt fest im Regelwerk, da die DSGVO selbst keine festen Fristen nennt (Art. 5 Abs. 1 lit. e). Jede Firma wird mit risikogestaffelten Vorschlagswerten vorbelegt, loeschfrist_max_tage wird jetzt tatsächlich hart gegen Werkzeuge gefiltert (schließt rules/OPEN.md Punkt 4). - Neues internal/mail-Paket (SMTP-Versand + Test-Doppel) und darauf aufbauend Passwort-Zurücksetzen (Migration 0019) - bisher nur als Absicht in der Rollentabelle genannt, nie gebaut. - dpf_zertifiziert für alle 20 Katalogeinträge gegen das offizielle DPF-Register recherchiert und in der Produktions-DB aktualisiert (12 zertifiziert, 5 recherchiert-nicht-gefunden, 3 nicht anwendbar). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
70 lines
2.6 KiB
Go
70 lines
2.6 KiB
Go
// Löschfristen je Datenklasse (Migration 0020) — schließt die in
|
|
// rules/OPEN.md, Punkt 4 dokumentierte Lücke: loeschfrist_max_tage war
|
|
// bisher nur als Anforderung "vorhanden", ohne Tageswerte, und wurde
|
|
// deshalb nicht hart gefiltert. Die konkreten Tageswerte sind KEINE
|
|
// gesetzliche Vorgabe (die DSGVO nennt keine festen Fristen, nur den
|
|
// Grundsatz "so lange wie für den Zweck nötig", Art. 5 Abs. 1 lit. e) —
|
|
// deshalb pro Mandant einstellbar statt hartkodiert, vom
|
|
// Datenschutzbeauftragten hinterlegt. Bei Firmenanlage werden
|
|
// risikogestaffelte Vorschlagswerte vorbelegt (siehe
|
|
// internal/web/loeschfrist_handlers.go), frei editierbar.
|
|
package store
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type LoeschfristEinstellung struct {
|
|
ID string
|
|
AccountID string
|
|
DatenklasseID string
|
|
MaxTage int
|
|
UpdatedAt time.Time
|
|
}
|
|
|
|
// UpsertLoeschfristEinstellung legt eine Einstellung an oder
|
|
// aktualisiert sie — ein Mandant hat höchstens eine Frist je
|
|
// Datenklasse (UNIQUE-Constraint).
|
|
func (s *Store) UpsertLoeschfristEinstellung(ctx context.Context, accountID, datenklasseID string, maxTage int) (LoeschfristEinstellung, error) {
|
|
var e LoeschfristEinstellung
|
|
err := s.Pool.QueryRow(ctx, `
|
|
INSERT INTO loeschfrist_einstellung (account_id, datenklasse_id, max_tage)
|
|
VALUES ($1, $2, $3)
|
|
ON CONFLICT (account_id, datenklasse_id)
|
|
DO UPDATE SET max_tage = $3, updated_at = now()
|
|
RETURNING id, account_id, datenklasse_id, max_tage, updated_at
|
|
`, accountID, datenklasseID, maxTage).Scan(&e.ID, &e.AccountID, &e.DatenklasseID, &e.MaxTage, &e.UpdatedAt)
|
|
if err != nil {
|
|
return LoeschfristEinstellung{}, fmt.Errorf("store: upsert loeschfrist einstellung: %w", err)
|
|
}
|
|
return e, nil
|
|
}
|
|
|
|
// ListLoeschfristEinstellungenForAccount liefert alle konfigurierten
|
|
// Fristen eines Mandanten.
|
|
func (s *Store) ListLoeschfristEinstellungenForAccount(ctx context.Context, accountID string) ([]LoeschfristEinstellung, error) {
|
|
rows, err := s.Pool.Query(ctx, `
|
|
SELECT id, account_id, datenklasse_id, max_tage, updated_at
|
|
FROM loeschfrist_einstellung WHERE account_id = $1 ORDER BY datenklasse_id
|
|
`, accountID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("store: list loeschfrist einstellungen: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []LoeschfristEinstellung
|
|
for rows.Next() {
|
|
var e LoeschfristEinstellung
|
|
if err := rows.Scan(&e.ID, &e.AccountID, &e.DatenklasseID, &e.MaxTage, &e.UpdatedAt); err != nil {
|
|
return nil, fmt.Errorf("store: scan loeschfrist einstellung: %w", err)
|
|
}
|
|
out = append(out, e)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("store: list loeschfrist einstellungen: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|