Audit-Log + Brute-Force-Schutz + unlimited_budget + User-Delete-Fix
- Schema-Migration: ON DELETE SET NULL fuer incidents.created_by, magic_links.user_id, network_analyses.created_by (behebt 500er beim User-Loeschen). Neue Spalte licenses.unlimited_budget. Neue Tabellen portal_audit_log, portal_login_attempts. - Audit-Log: alle CREATE/UPDATE/DELETE auf Org/User/Lizenz/Quelle + Login-Events werden mit before/after-Diff in portal_audit_log geschrieben. - Brute-Force-Schutz: 5 Fehlversuche pro IP+Username/15min -> 429 mit Retry-After. - Token-Budget: expliziter Schalter unlimited_budget pro Lizenz. UI zeigt ehrlich >100%-Verbrauch (kein Math.min mehr) und ungebremste Anzeige bei unlimited. - Neuer Audit-Log Tab mit Filter (Aktion/Ressource/Admin/Zeitraum) und Pagination.
Dieser Commit ist enthalten in:
@@ -31,6 +31,7 @@
|
||||
<button class="nav-tab" data-section="orgs">Organisationen</button>
|
||||
<button class="nav-tab" data-section="licenses">Lizenzen</button>
|
||||
<button class="nav-tab" data-section="sources">Quellen</button>
|
||||
<button class="nav-tab" data-section="audit">Audit-Log</button>
|
||||
</nav>
|
||||
|
||||
<!-- Dashboard Section -->
|
||||
@@ -224,6 +225,12 @@
|
||||
<div class="card" style="margin-top:12px;">
|
||||
<div class="card-body">
|
||||
<form id="tokenBudgetForm" style="display:grid; grid-template-columns:1fr 1fr; gap:12px;">
|
||||
<div style="grid-column:1/-1; padding:8px 12px; background:var(--bg-tertiary,#0f172a); border-radius:6px; border:1px solid var(--border);">
|
||||
<label style="display:flex; align-items:center; gap:8px; cursor:pointer; margin:0;">
|
||||
<input type="checkbox" id="editUnlimitedBudget" onchange="onUnlimitedToggle()">
|
||||
<span><strong>Unbegrenztes Budget</strong> — Verbrauch wird trotzdem getrackt, aber kein Limit/Hard-Stop</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="editCreditsTotal">Credits-Kontingent</label>
|
||||
<input type="number" id="editCreditsTotal" placeholder="z.B. 600000">
|
||||
@@ -392,6 +399,48 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Audit-Log Section -->
|
||||
<div class="section" id="sec-audit">
|
||||
<div class="action-bar" style="flex-wrap:wrap;gap:8px;">
|
||||
<div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||
<select class="filter-select" id="auditFilterAction">
|
||||
<option value="">Alle Aktionen</option>
|
||||
</select>
|
||||
<select class="filter-select" id="auditFilterResource">
|
||||
<option value="">Alle Ressourcen</option>
|
||||
</select>
|
||||
<select class="filter-select" id="auditFilterAdmin">
|
||||
<option value="">Alle Admins</option>
|
||||
</select>
|
||||
<input type="date" class="filter-select" id="auditFilterFrom" title="Von (Datum)">
|
||||
<input type="date" class="filter-select" id="auditFilterTo" title="Bis (Datum)">
|
||||
<button class="btn btn-secondary btn-small" id="auditFilterReset">Filter zuruecksetzen</button>
|
||||
<span class="text-secondary" id="auditCount"></span>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:8px;">
|
||||
<button class="btn btn-secondary btn-small" id="auditPrevBtn" disabled>← Zurueck</button>
|
||||
<button class="btn btn-secondary btn-small" id="auditNextBtn" disabled>Weiter →</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:170px;">Zeitpunkt</th>
|
||||
<th style="width:120px;">Admin</th>
|
||||
<th style="width:140px;">IP</th>
|
||||
<th style="width:140px;">Aktion</th>
|
||||
<th>Ressource</th>
|
||||
<th style="width:50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="auditTable"><tr><td colspan="6" class="text-muted">Lade...</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Modal: New Organization -->
|
||||
@@ -478,6 +527,12 @@
|
||||
<div class="text-muted mt-8" style="font-size: 12px;">Trial: Standard 14 Tage, Jahreslizenz: Standard 365 Tage</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="background:var(--bg-tertiary,#0f172a); padding:8px 12px; border-radius:6px; border:1px solid var(--border);">
|
||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer;margin:0;">
|
||||
<input type="checkbox" id="newLicUnlimited" onchange="onNewLicUnlimitedToggle()">
|
||||
<span><strong>Unbegrenztes Budget</strong> — getrackt, aber kein Hard-Stop</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="newLicCreditsTotal">Credits-Kontingent</label>
|
||||
<input type="number" id="newLicCreditsTotal" placeholder="z.B. 600000">
|
||||
@@ -626,5 +681,6 @@
|
||||
<script src="/static/js/app.js"></script>
|
||||
<script src="/static/js/sources.js"></script>
|
||||
<script src="/static/js/source-health.js"></script>
|
||||
<script src="/static/js/audit.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren