umbau 1.0

This commit is contained in:
2026-09-02 20:30:44 +02:00
parent afe6719f51
commit 5c95b21be7
77 changed files with 10733 additions and 1914 deletions

View File

@ -29,7 +29,6 @@
<button type="button" class="tab-btn" data-tab="credentials">Zugangsdaten</button>
<button type="button" class="tab-btn" data-tab="roles">Rollen</button>
<button type="button" class="tab-btn" data-tab="tokens">API-Tokens</button>
<button type="button" class="tab-btn hidden" data-tab="tenants" id="tenants-tab-btn">Mandanten</button>
<button type="button" class="tab-btn hidden" data-tab="sessions" id="sessions-tab-btn">Sessions</button>
<button type="button" class="tab-btn hidden" data-tab="connlog" id="connlog-tab-btn">Verbindungslog</button>
<button type="button" class="tab-btn" data-tab="audit">Audit-Log</button>
@ -63,7 +62,7 @@
<thead>
<tr>
<th>ID</th><th>Benutzername</th><th>Admin</th><th>Aktiv</th>
<th>TOTP</th><th>Mandant</th><th>Erstellt</th><th></th>
<th>TOTP</th><th>Erstellt</th><th></th>
</tr>
</thead>
<tbody></tbody>
@ -104,10 +103,6 @@
<label for="gc-description">Beschreibung (optional)</label>
<input type="text" id="gc-description" maxlength="1024">
</div>
<div id="gc-tenant-box">
<label for="gc-tenant">Mandant</label>
<select id="gc-tenant" required></select>
</div>
<button type="submit">Anlegen</button>
</form>
</div>
@ -117,7 +112,7 @@
<p class="hint">Rollen fuer eine Gruppe (= Verbindungen mit der ganzen Gruppe teilen) werden im Tab "Rollen" vergeben.</p>
<div class="table-wrap">
<table class="data-table" id="groups-table">
<thead><tr><th>ID</th><th>Name</th><th>Beschreibung</th><th>Mandant</th><th>Mitglieder</th><th></th></tr></thead>
<thead><tr><th>ID</th><th>Name</th><th>Beschreibung</th><th>Mitglieder</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
@ -170,16 +165,12 @@
<label for="hgc-description">Beschreibung (optional)</label>
<input type="text" id="hgc-description" maxlength="1024">
</div>
<div id="hgc-tenant-box">
<label for="hgc-tenant">Mandant</label>
<select id="hgc-tenant" required></select>
</div>
<button type="submit">Anlegen</button>
</form>
<h3>Hostgruppen</h3>
<div class="table-wrap">
<table class="data-table" id="hostgroups-table">
<thead><tr><th>ID</th><th>Name</th><th>Beschreibung</th><th>Mandant</th><th></th></tr></thead>
<thead><tr><th>ID</th><th>Name</th><th>Beschreibung</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
@ -251,7 +242,7 @@
<div class="table-wrap">
<table class="data-table" id="hosts-table">
<thead>
<tr><th>ID</th><th>Hostname</th><th>Adresse</th><th>Protokoll</th><th>OS</th><th>Hostgruppe</th><th>Mandant</th><th>Status</th><th></th></tr>
<tr><th>ID</th><th>Hostname</th><th>Adresse</th><th>Protokoll</th><th>OS</th><th>Hostgruppe</th><th>Status</th><th></th></tr>
</thead>
<tbody></tbody>
</table>
@ -361,10 +352,6 @@
<option value="ca-cert">ca-cert</option>
</select>
</div>
<div id="skc-tenant-box">
<label for="skc-tenant">Mandant</label>
<select id="skc-tenant" required></select>
</div>
<div class="full-width">
<div class="ft-btn-row">
<button type="button" class="btn-secondary btn-small" id="skc-generate-btn">Schluessel automatisch generieren</button>
@ -389,7 +376,7 @@
<h3>Vorhandene SSH-Keys</h3>
<div class="table-wrap">
<table class="data-table" id="ssh-keys-table">
<thead><tr><th>ID</th><th>Label</th><th>Benutzer</th><th>Typ</th><th>Mandant</th><th>Passphrase</th><th>Erstellt</th><th>Zuletzt ersetzt</th><th></th></tr></thead>
<thead><tr><th>ID</th><th>Label</th><th>Benutzer</th><th>Typ</th><th>Passphrase</th><th>Erstellt</th><th>Zuletzt ersetzt</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
@ -463,10 +450,6 @@
<label for="rcc-domain">Domaene (optional)</label>
<input type="text" id="rcc-domain" maxlength="128" autocomplete="off">
</div>
<div id="rcc-tenant-box">
<label for="rcc-tenant">Mandant</label>
<select id="rcc-tenant" required></select>
</div>
<div class="full-width">
<label for="rcc-password">Passwort</label>
<input type="text" id="rcc-password" required maxlength="512">
@ -477,7 +460,7 @@
<h3>Vorhandene RDP-Zugangsdaten</h3>
<div class="table-wrap">
<table class="data-table" id="rdp-creds-table">
<thead><tr><th>ID</th><th>Label</th><th>Benutzer</th><th>Domaene</th><th>Mandant</th><th>Zugewiesene Server</th><th>Zuletzt rotiert</th><th></th></tr></thead>
<thead><tr><th>ID</th><th>Label</th><th>Benutzer</th><th>Domaene</th><th>Zugewiesene Server</th><th>Zuletzt rotiert</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
@ -515,30 +498,11 @@
<!-- ================= Rollen ================= -->
<div class="tab-panel hidden" id="tab-roles">
<div class="panel">
<h2>Rolle(n) an Benutzer vergeben</h2>
<p class="hint">Mehrfachauswahl moeglich -- alle ausgewaehlten Rollen werden in einem Schritt vergeben.</p>
<form id="role-grant-form" class="form-grid">
<div>
<label for="rg-user">Benutzer</label>
<select id="rg-user" required></select>
</div>
<div>
<label for="rg-hostgroup">Hostgruppe</label>
<select id="rg-hostgroup" required></select>
</div>
<div class="full-width">
<label>Rollen</label>
<div class="scope-grid" id="rg-role-grid"></div>
</div>
<div>
<label for="rg-expires">Ablaufdatum (optional, ISO)</label>
<input type="text" id="rg-expires" placeholder="2026-12-31T00:00:00.000Z" maxlength="40">
</div>
<button type="submit">Vergeben</button>
</form>
<h2>Effektive Rollen je Benutzer</h2>
<p class="hint">Direktvergabe an einzelne Benutzer ist entfallen (Teil D) -- diese Tabelle ist eine ABGELEITETE, nicht editierbare Sicht: sie zeigt, welche Rolle ein Benutzer ueber welche Benutzergruppe auf welcher Hostgruppe aktuell haelt. Aendern/Entziehen laeuft ausschliesslich ueber die Gruppenvergabe unten -- steht ein Benutzer hier ueber mehr als eine Gruppe mit derselben Rolle/Hostgruppe, erscheinen entsprechend mehrere Zeilen.</p>
<div class="table-wrap">
<table class="data-table" id="role-grants-table">
<thead><tr><th>Benutzer</th><th>Hostgruppe</th><th>Rolle</th><th>Ablauf</th><th></th></tr></thead>
<thead><tr><th>Benutzer</th><th>Hostgruppe</th><th>Rolle</th><th>Gruppe</th><th>Ablauf</th></tr></thead>
<tbody></tbody>
</table>
</div>
@ -573,13 +537,47 @@
</table>
</div>
</div>
<div class="panel">
<h2>Zugangsdaten fuer Gruppe freigeben (Achse B)</h2>
<p class="hint">Bestimmt WOMIT sich eine Gruppe anmelden darf -- unabhaengig davon, WORAUF sie laut Rollen oben zugreifen darf. Eine Verbindung kommt nur zustande, wenn BEIDES zutrifft: die Rolle (ssh_connect/rdp_connect) auf der Hostgruppe UND eine hier freigegebene, am Host haengende Zugangsdaten-Ressource.</p>
<form id="group-credential-grant-form" class="form-grid">
<div>
<label for="gcg-group">Benutzergruppe</label>
<select id="gcg-group" required></select>
</div>
<div>
<label for="gcg-kind">Art</label>
<select id="gcg-kind" required>
<option value="ssh_key">SSH-Schluessel</option>
<option value="rdp_credential">RDP-Zugangsdaten</option>
<option value="ssh_password_credential">SSH-Passwort</option>
</select>
</div>
<div>
<label for="gcg-credential">Zugangsdatensatz</label>
<select id="gcg-credential" required></select>
</div>
<div>
<label for="gcg-expires">Ablaufdatum (optional, ISO)</label>
<input type="text" id="gcg-expires" placeholder="2026-12-31T00:00:00.000Z" maxlength="40">
</div>
<button type="submit">Freigeben</button>
</form>
<div class="table-wrap">
<table class="data-table" id="group-credential-grants-table">
<thead><tr><th>Gruppe</th><th>Art</th><th>Zugangsdatensatz</th><th>Ablauf</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<!-- ================= API-Tokens ================= -->
<div class="tab-panel hidden" id="tab-tokens">
<div class="panel">
<h2>Neues API-Token erstellen</h2>
<p class="hint">Tokens wirken NUR auf die Verwaltungs-API (Benutzer/Hosts/Rollen/...), nie auf SSH-/RDP-Sitzungen selbst. "rw" schliesst "ro" auf derselben Ressource automatisch mit ein. Jedes Token gehoert genau einem Mandanten.</p>
<p class="hint">Tokens wirken NUR auf die Verwaltungs-API (Benutzer/Hosts/Rollen/...), nie auf SSH-/RDP-Sitzungen selbst. "rw" schliesst "ro" auf derselben Ressource automatisch mit ein.</p>
<form id="token-create-form" class="form-grid">
<div>
<label for="tc-label">Bezeichnung</label>
@ -589,10 +587,6 @@
<label for="tc-user">Fuer Benutzer</label>
<select id="tc-user" required></select>
</div>
<div id="tc-tenant-box">
<label for="tc-tenant">Mandant</label>
<select id="tc-tenant" required></select>
</div>
<div>
<label for="tc-expires">Ablaufdatum (optional, ISO)</label>
<input type="text" id="tc-expires" placeholder="2026-12-31T00:00:00.000Z" maxlength="40">
@ -611,7 +605,7 @@
<div class="table-wrap">
<table class="data-table" id="tokens-table">
<thead>
<tr><th>ID</th><th>Bezeichnung</th><th>Benutzer</th><th>Praefix</th><th>Mandant</th><th>Scopes</th><th>Zuletzt genutzt</th><th>Status</th><th></th></tr>
<tr><th>ID</th><th>Bezeichnung</th><th>Benutzer</th><th>Praefix</th><th>Scopes</th><th>Zuletzt genutzt</th><th>Status</th><th></th></tr>
</thead>
<tbody></tbody>
</table>
@ -619,65 +613,6 @@
</div>
</div>
<!-- ================= Mandanten (nur Super-Admin) ================= -->
<div class="tab-panel hidden" id="tab-tenants">
<div class="panel">
<h2>Neuen Mandanten anlegen</h2>
<form id="tenant-create-form" class="form-grid">
<div>
<label for="tnc-name">Name</label>
<input type="text" id="tnc-name" required maxlength="128">
</div>
<div>
<label for="tnc-description">Beschreibung (optional)</label>
<input type="text" id="tnc-description" maxlength="1024">
</div>
<button type="submit">Anlegen</button>
</form>
<h3>Mandanten</h3>
<div class="table-wrap">
<table class="data-table" id="tenants-table">
<thead><tr><th>ID</th><th>Name</th><th>Beschreibung</th><th>Aktiv</th><th>Hostgruppen</th><th>Benutzergruppen</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="panel hidden" id="tenant-edit-panel">
<h2>Mandant bearbeiten: <span id="tenant-edit-name"></span></h2>
<form id="tenant-edit-form" class="form-grid">
<div>
<label for="tne-name">Name</label>
<input type="text" id="tne-name" required maxlength="128">
</div>
<div>
<label for="tne-description">Beschreibung</label>
<input type="text" id="tne-description" maxlength="1024">
</div>
<div class="checkbox-row"><input type="checkbox" id="tne-active"><label for="tne-active">Aktiv</label></div>
<button type="submit">Speichern</button>
</form>
</div>
<div class="panel hidden" id="tenant-admins-panel">
<h2>Mandanten-Admins von <span id="tenant-admins-name"></span></h2>
<div class="table-wrap">
<table class="data-table" id="tenant-admins-table">
<thead><tr><th>User-ID</th><th>Benutzername</th><th>Seit</th><th></th></tr></thead>
<tbody></tbody>
</table>
</div>
<h3>Mandanten-Admin hinzufuegen</h3>
<form id="tenant-admin-add-form" class="form-grid">
<div>
<label for="ta-user-select">Benutzer</label>
<select id="ta-user-select" required></select>
</div>
<button type="submit">Hinzufuegen</button>
</form>
</div>
</div>
<!-- ================= Sessions (nur Super-Admin) ================= -->
<div class="tab-panel hidden" id="tab-sessions">
<div class="panel">

View File

@ -7,18 +7,19 @@
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<div class="session-shell" id="session-shell" data-host-id="{{ host_id }}">
<div class="session-toolbar">
<button id="fullscreen-btn">Vollbild</button>
<button id="ctrlaltdel-btn">Strg+Alt+Entf</button>
<span class="spacer"></span>
<span class="status" id="status">Verbinde ...</span>
<button id="exit-btn" class="btn-danger btn-small" title="Sitzung beenden und Tab schliessen">Exit</button>
</div>
<div id="rdp-display"></div>
</div>
<!--
F1 (Umsetzungsauftrag_Sonnet5.md Teil F.3.1): diese Seite baut das
Sitzungs-DOM nicht mehr selbst -- static/js/rdp.js erzeugt beim Laden
GENAU EINE RdpSession-Instanz und haengt deren komplettes Markup
(Toolbar, Anzeigeflaeche, Fehler-Overlay) hier ein. Duenner Rahmen um
denselben Baustein, den auch eine kuenftige Arbeitsflaeche (/workspace,
Teil F.3, spaetere Stufe) verwendet -- keine zweite Implementierung
(F.2, letzter Absatz).
-->
<div id="session-container" data-host-id="{{ host_id }}"></div>
<script src="/static/js/vendor/guacamole-common.js"></script>
<script src="/static/js/session-keepalive.js"></script>
<script src="/static/js/rdp.js"></script>
</body>
</html>

View File

@ -8,47 +8,20 @@
<link rel="stylesheet" href="/static/js/vendor/xterm.css">
</head>
<body>
<div class="session-shell" id="session-shell" data-host-id="{{ host_id }}">
<div class="session-toolbar">
<button id="fullscreen-btn">Vollbild</button>
<button id="filetransfer-btn">Dateitransfer</button>
<span class="spacer"></span>
<span class="status" id="status">Verbinde ...</span>
<button id="exit-btn" class="btn-danger btn-small" title="Sitzung beenden und Tab schliessen">Exit</button>
</div>
<div id="terminal"></div>
</div>
<div class="ft-overlay hidden" id="ft-overlay">
<div class="ft-panel">
<h2>Dateitransfer
<button type="button" class="btn-secondary btn-small" id="ft-close-btn">Schliessen</button>
</h2>
<h3>Hochladen</h3>
<label for="ft-upload-remote-path">Zielpfad auf dem Server</label>
<input type="text" id="ft-upload-remote-path" placeholder="/tmp/datei.txt">
<input type="file" id="ft-upload-file-input" class="hidden">
<div class="ft-btn-row">
<button type="button" class="btn-secondary btn-small" id="ft-upload-pick-btn">Datei auswaehlen</button>
<span class="hint" id="ft-upload-filename">Keine Datei ausgewaehlt</span>
</div>
<button type="button" class="btn-small" id="ft-upload-submit-btn">Hochladen</button>
<h3>Herunterladen</h3>
<label for="ft-download-remote-path">Pfad auf dem Server</label>
<input type="text" id="ft-download-remote-path" placeholder="/etc/hosts">
<button type="button" class="btn-small" id="ft-download-submit-btn">Herunterladen</button>
<div class="error" id="ft-status"></div>
<h3>Letzte Transfers</h3>
<div class="ft-list" id="ft-list"></div>
</div>
</div>
<!--
F1 (Umsetzungsauftrag_Sonnet5.md Teil F.3.1): diese Seite baut das
Sitzungs-DOM nicht mehr selbst -- static/js/terminal.js erzeugt beim
Laden GENAU EINE SshTerminalSession-Instanz und haengt deren komplettes
Markup (Toolbar, Terminal, Dateitransfer-Panel) hier ein. Duenner
Rahmen um denselben Baustein, den auch eine kuenftige Arbeitsflaeche
(/workspace, Teil F.3, spaetere Stufe) verwendet -- keine zweite
Implementierung (F.2, letzter Absatz).
-->
<div id="session-container" data-host-id="{{ host_id }}"></div>
<script src="/static/js/vendor/xterm.js"></script>
<script src="/static/js/vendor/xterm-addon-fit.js"></script>
<script src="/static/js/session-keepalive.js"></script>
<script src="/static/js/terminal.js"></script>
</body>
</html>

63
templates/workspace.html Normal file
View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Jumphost Arbeitsflaeche</title>
<link rel="stylesheet" href="/static/css/app.css">
<link rel="stylesheet" href="/static/js/vendor/xterm.css">
</head>
<body>
<!--
F3/F4/F5 (Umsetzungsauftrag_Sonnet5.md Teil F.3): dauerhafte Arbeitsflaeche.
static/js/workspace.js baut Seitenleiste, Hostkatalog und die Sitzungen
(ueber SshTerminalSession/RdpSession, denselben Baustein wie
/terminal/{id} bzw. /rdp/{id} aus Stufe F1 -- keine zweite
Implementierung, F.2). #workspace-catalog-view und #workspace-sessions-area
sind feste Rahmen, die per .hidden (app.css) umgeschaltet werden -- die
Sitzungs-Elemente selbst werden beim Umschalten NICHT entfernt, nur
innerhalb von #workspace-sessions-area ein-/ausgeblendet (F4: mehrere
gleichzeitige Sitzungen bleiben im Hintergrund verbunden). F5 (F.3.8):
#workspace-limit-hint zeigt die konfigurierten Obergrenzen (GET
/catalog/session-limits) und deaktiviert "+ Neue Sitzung"/"Verbinden",
bevor der Server eine neue Sitzung mit WS-Code 4429 ablehnen wuerde.
#workspace-attention-summary sammelt Hintergrundsitzungen mit Fehler/
Trennung, damit man sie nicht erst durch Umschalten entdeckt.
-->
<div class="topbar">
<div class="brand">Jumphost -- Arbeitsflaeche</div>
<div>
<span class="hint" id="whoami"></span>
<a href="/dashboard">Dashboard</a>
<button id="logout-btn">Abmelden</button>
</div>
</div>
<div class="workspace-layout">
<div class="workspace-sidebar">
<div class="workspace-sidebar-header">
<h2>Sitzungen</h2>
<p class="hint">
Sitzungen ueberleben keinen Seiten-Reload -- ein Neuladen dieser
Seite beendet alle gerade offenen Sitzungen (F.3.7).
</p>
<p class="hint workspace-limit-hint" id="workspace-limit-hint"></p>
</div>
<div class="workspace-attention-summary hidden" id="workspace-attention-summary"></div>
<div class="workspace-tiles" id="workspace-tiles"></div>
<button type="button" class="workspace-new-btn" id="workspace-new-btn">+ Neue Sitzung</button>
</div>
<div class="workspace-main">
<div class="workspace-catalog-view" id="workspace-catalog-view"></div>
<div class="workspace-sessions-area hidden" id="workspace-sessions-area"></div>
</div>
</div>
<script src="/static/js/vendor/xterm.js"></script>
<script src="/static/js/vendor/xterm-addon-fit.js"></script>
<script src="/static/js/vendor/guacamole-common.js"></script>
<script src="/static/js/session-keepalive.js"></script>
<script src="/static/js/terminal.js"></script>
<script src="/static/js/rdp.js"></script>
<script src="/static/js/workspace.js"></script>
</body>
</html>