From 57aa0bc5be0d2bad2bf9e7626a72f60bc9679e4d Mon Sep 17 00:00:00 2001 From: Deploymentcenter Bot Date: Sun, 30 Aug 2026 14:04:02 +0200 Subject: [PATCH] feat(tokens): Master-Tokens nachtraeglich bearbeiten Bisher erzwang jede Korrektur ein neues Token: ein Schreibfehler im Namen, ein falsch gewaehlter Identitaetstyp, ein nachtraeglich gebrauchtes Recht. Das ist nicht nur laestig, sondern gefaehrlich - der neue Wert muss auf alle Maschinen verteilt werden, und am Ende laeuft das alte weiter, weil eine vergessen wurde. AENDERBAR ist alles ausser dem Tokenwert selbst, der Nummer, dem Typ und dem Anlagedatum: Name, Identitaetstyp, freie Identitaet, Projekt, Lizenzschluessel, Umgebung, Ablaufdatum und die Rechte. Der Wert bleibt unberuehrt, weil er es muss - er liegt nur als Hash vor und steht auf fremden Maschinen. Ein "geaenderter" Wert waere ein neues Token mit alter Nummer. Genau diese Zusage haelt ein eigener Testfall fest: derselbe rohe Token muss nach der Aenderung weiter funktionieren. - TokenManager::updateMasterToken() mit zwei Schranken: leerer Name und leere Rechteliste werden abgewiesen. Ein Token ohne Rechte sieht gueltig aus und laesst jeden Aufruf mit "unauthorized" scheitern, ohne dass der Grund sichtbar waere. - Was sich geaendert hat, geht ins Log. Eine stille Rechteaenderung an einem Token ist genau das, was man spaeter rekonstruieren will. - Dialog in der Token-Verwaltung, erreichbar ueber den Stift in der Zeile. Zeigt Nummer und Hinweis, dass der Wert unveraendert bleibt. - Haengen Sub-Tokens daran, sagt der Dialog, dass eine Einschraenkung NICHT rueckwirkend auf sie durchschlaegt - sie haben ihre Rechte beim Ausstellen bekommen. Wer die Kinder beschneiden will, widerruft und stellt neu aus. $scopeChoices ist aus dem Anlegeformular in den Datenteil gewandert. Beide Formulare lesen jetzt dieselbe Liste - eine an zwei Stellen gefuehrte Liste haette genau den Fehler wiederholt, der eben erst behoben wurde: Die Supervisor-Rechte fehlten monatelang im Formular, obwohl TokenManager sie kennt. Testlauf 136 Faelle. Da die Bearbeitung bewusst keine API hat - ein Token soll seine eigenen Rechte nicht erweitern koennen - fuehrt der Weg dorthin ueber testctl.php?action=update_master_token, wie schon beim Werkbank-Token. Co-Authored-By: Claude Opus 5 --- docs/README.md | 2 +- public/index.php | 246 ++++++++++++++++++++++++++++++++++---- src/Core/TokenManager.php | 105 ++++++++++++++++ tests/e2e/suite_kern.py | 84 +++++++++++++ tests/server/testctl.php | 40 ++++++- 5 files changed, 455 insertions(+), 22 deletions(-) diff --git a/docs/README.md b/docs/README.md index dbfae3e..af6529c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,7 +15,7 @@ Monitoring und einen Bugtracker, den Coding-Agenten selbständig bedienen. | **[Änderungen](../public/docs/changelog.json)** | Was sich je Fassung geändert hat und was zu tun ist. Öffentlich unter `/docs/changelog.php`, maschinenlesbar über `GET /api/updateservice/v1/changelog?since=X` | | **[Release-Anleitung für Agenten](../public/docs/release.md)** | Ein Projekt veröffentlichungsfähig machen: Vorlage kopieren, konfigurieren, ausliefern | | **[Linux-Testumgebung](./TESTUMGEBUNG.md)** | **Die geteilte Test-VM.** Wie ein Agent dort selbständig testet: DEV-Lizenz holen, Auslieferung prüfen, Befunde melden — mit `dc-testkit` | -| **[Testinstanz & automatisierter Testlauf](./TESTINSTANZ.md)** | **`dc-dev.mhdf.de` — die zweite Installation zum Kaputtmachen.** 129 Fälle gegen API, Rechte, Migrationen und Zugangssperre (`tests/e2e/run.py`). **`scripts/deploy.py` führt sie vor jedem Produktiv-Upload aus** und bricht bei Fehlschlägen ab (`--ohne-test` umgeht das) | +| **[Testinstanz & automatisierter Testlauf](./TESTINSTANZ.md)** | **`dc-dev.mhdf.de` — die zweite Installation zum Kaputtmachen.** 136 Fälle gegen API, Rechte, Migrationen und Zugangssperre (`tests/e2e/run.py`). **`scripts/deploy.py` führt sie vor jedem Produktiv-Upload aus** und bricht bei Fehlschlägen ab (`--ohne-test` umgeht das) | | **[Agenten anbinden](./AGENTEN_ANBINDEN.md)** | **Was einmalig je Projekt zu tun ist**, damit ein Agent Bugtracker, Roadmap und MCP sinnvoll benutzt: Token, Adapter, Bausteine, Roadmap-Übernahme — mit Prüffragen am Ende | | [Agent-Prompt-Vorlage](./AGENT_PROMPT_TEMPLATE.md) | Die Textbausteine selbst. **A** Grundhaltung (immer), **B** mit MCP, **C** ohne MCP, **D** Test-VM — nicht alles einkopieren | | [Agenten-Handbuch](../public/docs/bugtracker.md) | Vollständige Beschreibung des Bugtracker-Workflows, öffentlich unter `/docs/` | diff --git a/public/index.php b/public/index.php index 7b38b8b..b2e5042 100644 --- a/public/index.php +++ b/public/index.php @@ -959,6 +959,39 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { dc_redirect('#tab-tokens'); } + case 'edit_master_token': { + $tokenId = trim((string)($_POST['token_id'] ?? '')); + $scopes = $_POST['scopes'] ?? []; + + try { + $ok = (new CoreTokenManager($pdo))->updateMasterToken($tokenId, [ + 'name' => $_POST['name'] ?? '', + 'project_slug' => $_POST['project_slug'] ?? '', + 'license_key' => $_POST['license_key'] ?? '', + 'owner_type' => $_POST['owner_type'] ?? '', + 'owner_identity' => $_POST['owner_identity'] ?? '', + 'environment' => $_POST['environment'] ?? '', + // Das Datumsfeld liefert nur den Tag; das Token soll + // an diesem Tag noch gelten und erst danach ablaufen. + 'expires_at' => !empty($_POST['expires_at']) + ? $_POST['expires_at'] . ' 23:59:59' : '', + 'scopes' => is_array($scopes) ? $scopes : [], + ]); + } catch (\InvalidArgumentException $e) { + dc_flash($e->getMessage(), 'danger'); + dc_redirect('#tab-tokens'); + } + + dc_flash( + $ok + ? 'Token-Angaben gespeichert. Der Tokenwert selbst ist unveraendert — ' + . 'auf den Maschinen muss nichts ausgetauscht werden.' + : 'Master-Token nicht gefunden.', + $ok ? 'success' : 'danger' + ); + dc_redirect('#tab-tokens'); + } + case 'revoke_core_token': { $tokenId = trim((string)($_POST['token_id'] ?? '')); if ($tokenId !== '') { @@ -2085,6 +2118,33 @@ function rmBadge(string $status): string return '' . $marker . ' ' . e($label) . ''; } +// Die auswaehlbaren Rechte - EINE Liste fuer Anlegen und Bearbeiten. +// +// Vorher stand sie im Anlegeformular. Genau deshalb fehlten die +// Supervisor-Rechte monatelang: Wer eine Liste an zwei Stellen fuehrt, +// pflegt eine davon nicht. Ein zweites Formular waere die zweite Stelle +// gewesen. +$scopeChoices = [ + '*' => ['⚠️ Alle Rechte', 'Uneingeschränkt — auch Releases veröffentlichen und Tokens ausstellen. Nicht an einen Agenten geben.'], + 'bugtracker:manage' => ['⚙️ Bugtracker verwalten', 'Übernehmen, kommentieren, schließen — schließt Lesen und Melden ein'], + 'bugtracker:report' => ['🐛 Bugtracker melden', 'Nur Einträge anlegen — schließt Lesen ein'], + 'bugtracker:read' => ['👁️ Bugtracker lesen', 'Nur Abfragen'], + 'watchdog:ping' => ['🛡️ Watchdog Heartbeat', 'Statusmeldungen senden'], + 'watchdog:read' => ['📊 Watchdog lesen', 'Monitore und Ereignisse abfragen'], + // Die Supervisor-Rechte fehlten hier, obwohl TokenManager + // sie seit 2.6.0 kennt. Folge: Ein Werkbank-Token liess + // sich ueber die Oberflaeche gar nicht vollstaendig + // ausstellen - dc_findings und dc_metrics im MCP-Adapter + // liefen ins unauthorized, und der Grund war nirgends zu + // sehen. + 'supervisor:read' => ['🔎 Befunde lesen', 'Supervisor-Befunde abfragen — braucht ein Werkbank-Token für dc_findings'], + 'supervisor:manage' => ['🔬 Befunde einstufen', 'Befunde abhaken und bewerten — schließt Lesen ein'], + 'supervisor:report' => ['📡 Befunde melden', 'Nur einliefern, etwa aus einem Prüfwerkzeug — schließt Lesen ein'], + 'updateservice:read' => ['📦 Updates prüfen', 'Auf neue Releases prüfen'], + 'updateservice:publish' => ['🚀 Releases veröffentlichen', 'Neue Releases einstellen — schließt Prüfen ein'], + 'license:dev' => ['🧪 DEV-Lizenz abholen', 'Befristeten Testschlüssel selbst ausstellen — nur für Werkbank-Token auf Testmaschinen'], +]; + // Agenten-Uebersicht: wer bearbeitet gerade was $agentWorkload = $pdo->query(' SELECT claimed_by, @@ -5303,6 +5363,19 @@ Antwort:
+
@@ -5399,6 +5472,113 @@ Antwort:
+ + + +

➕ Neuen Token erstellen

@@ -5470,26 +5650,6 @@ Antwort:

['⚠️ Alle Rechte', 'Uneingeschränkt — auch Releases veröffentlichen und Tokens ausstellen. Nicht an einen Agenten geben.'], - 'bugtracker:manage' => ['⚙️ Bugtracker verwalten', 'Übernehmen, kommentieren, schließen — schließt Lesen und Melden ein'], - 'bugtracker:report' => ['🐛 Bugtracker melden', 'Nur Einträge anlegen — schließt Lesen ein'], - 'bugtracker:read' => ['👁️ Bugtracker lesen', 'Nur Abfragen'], - 'watchdog:ping' => ['🛡️ Watchdog Heartbeat', 'Statusmeldungen senden'], - 'watchdog:read' => ['📊 Watchdog lesen', 'Monitore und Ereignisse abfragen'], - // Die Supervisor-Rechte fehlten hier, obwohl TokenManager - // sie seit 2.6.0 kennt. Folge: Ein Werkbank-Token liess - // sich ueber die Oberflaeche gar nicht vollstaendig - // ausstellen - dc_findings und dc_metrics im MCP-Adapter - // liefen ins unauthorized, und der Grund war nirgends zu - // sehen. - 'supervisor:read' => ['🔎 Befunde lesen', 'Supervisor-Befunde abfragen — braucht ein Werkbank-Token für dc_findings'], - 'supervisor:manage' => ['🔬 Befunde einstufen', 'Befunde abhaken und bewerten — schließt Lesen ein'], - 'supervisor:report' => ['📡 Befunde melden', 'Nur einliefern, etwa aus einem Prüfwerkzeug — schließt Lesen ein'], - 'updateservice:read' => ['📦 Updates prüfen', 'Auf neue Releases prüfen'], - 'updateservice:publish' => ['🚀 Releases veröffentlichen', 'Neue Releases einstellen — schließt Prüfen ein'], - 'license:dev' => ['🧪 DEV-Lizenz abholen', 'Befristeten Testschlüssel selbst ausstellen — nur für Werkbank-Token auf Testmaschinen'], - ]; foreach ($scopeChoices as $value => $labels): ?>