audit:export gatete nichts - Export hing an audit:read
Bei der Gegenprobe zur neuen Rolle "Gegenbuch" gefunden: Ein Konto mit ausschliesslich audit:read bekam auf GET /audit-logs/export eine 200. Die Berechtigung audit:export stand im Katalog und in der Rollenverwaltung - und wurde nirgends geprueft. Der Unterschied ist nicht kosmetisch. Blaettern zeigt 50 Zeilen; der Export liefert in einem Zug das gesamte Protokoll inklusive changesBefore und changesAfter, also der vollstaendigen Vorher/Nachher-Datensaetze, dazu resourceLabel mit Klartextnamen, IP-Adressen und User-Agents. Live nachgewiesen auf Staging: 43 Eintraege mit gefuellter resourceLabel allein fuer resourceType=Customer. Damit konnte ausgerechnet das Dienstkonto des Gegenbuchs Personendaten exportieren - das Konto, dessen Passwort im Klartext in der .env auf der Notar-Maschine liegt, und dem README und Rollenname "nur Pruefwerte lesen" zusichern. /audit-logs/export verlangt jetzt audit:export. Betroffen ist genau eine Rolle: Gegenbuch, und zwar gewollt. Die DSGVO-Rolle traegt audit:* vollstaendig und behaelt den Export. In der Oberflaeche erscheinen JSON- und CSV-Knopf nur noch mit audit:export - sonst stuenden dort Knoepfe, die zuverlaessig 403 liefern. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '../../context/AuthContext';
|
||||
import { auditLogApi, AuditLogSearchParams, authApi } from '../../services/api';
|
||||
import type { AuditLog, AuditAction, AuditSensitivity } from '../../types';
|
||||
import Card from '../../components/ui/Card';
|
||||
@@ -279,6 +280,10 @@ function DetailModal({ log, onClose }: DetailModalProps) {
|
||||
|
||||
export default function AuditLogs() {
|
||||
const navigate = useNavigate();
|
||||
// Export haengt an `audit:export`, Lesen an `audit:read` – ein Konto darf
|
||||
// blaettern duerfen, ohne das gesamte Protokoll herausziehen zu koennen.
|
||||
// Ohne diese Abfrage stuenden hier Knoepfe, die zuverlaessig 403 liefern.
|
||||
const { hasPermission } = useAuth();
|
||||
const [page, setPage] = useState(1);
|
||||
const [filters, setFilters] = useState<AuditLogSearchParams>({
|
||||
page: 1,
|
||||
@@ -393,14 +398,18 @@ export default function AuditLogs() {
|
||||
<RefreshCw className="w-4 h-4 mr-2" />
|
||||
Aktualisieren
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => handleExport('json')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
JSON
|
||||
</Button>
|
||||
<Button variant="secondary" onClick={() => handleExport('csv')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
CSV
|
||||
</Button>
|
||||
{hasPermission('audit:export') && (
|
||||
<Button variant="secondary" onClick={() => handleExport('json')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
JSON
|
||||
</Button>
|
||||
)}
|
||||
{hasPermission('audit:export') && (
|
||||
<Button variant="secondary" onClick={() => handleExport('csv')}>
|
||||
<Download className="w-4 h-4 mr-2" />
|
||||
CSV
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user