Vier Faelle, die genau die Fehler wiederfinden wuerden, um die es in 2.7.1 ging: - whoami nennt Bindung und Rechte, und zwar dieselben, die provisioniert wurden - whoami gibt den Klartextwert des Tokens nicht heraus. Eine Auskunft ueber ein Token darf das Token nicht weiterreichen - whoami ohne Token antwortet 401 - Health meldet dieselbe Fassung, die changelog.json fuehrt. Das ist der Fall, der #346 nicht wiederkommen laesst: Ein fester Wert in der Konfiguration faellt sonst niemandem auf, weil die Datei gitignored ist Ausserdem: Wird der Shared Key an whoami geschickt, sagt der Endpunkt das jetzt (not_a_token) statt "Token unbekannt". Der Shared Key ist kein Token - er hat weder Bindung noch Rechteliste, und ohne den Hinweis sucht der Aufrufer den Fehler bei seinem Token. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
84 lines
3.5 KiB
PHP
84 lines
3.5 KiB
PHP
<?php
|
|
|
|
/**
|
|
* GET /api/tokens/v1/whoami
|
|
*
|
|
* Auskunft ueber das eigene Token: Besitzerart, Projektbindung, Rechte,
|
|
* Umgebung, Frist.
|
|
*
|
|
* Warum es das gibt: Bis hierher konnte ein Aufrufer nur ausprobieren, was
|
|
* sein Token darf - und das geht nicht ohne Nebenwirkung. "Darf ich
|
|
* bugtracker:manage?" haette geheissen, ein Item zu uebernehmen; "habe ich
|
|
* license:dev?" haette einen Schluessel ausgestellt. Also meldete
|
|
* dc-testkit doctor nur "gueltig - der Bugtracker antwortet" und lief mit
|
|
* einem Laufzeit-Token glatt durch, dem beides fehlte. Der Lauf scheiterte
|
|
* dann beim ersten Schritt, der die Rechte wirklich brauchte.
|
|
*
|
|
* Ausgeliefert wird ausschliesslich, was der Aufrufer ohnehin besitzt. Der
|
|
* Klartextwert des Tokens ist nicht dabei.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../../../src/bootstrap.php';
|
|
|
|
use Deploymentcenter\Core\Config;
|
|
use Deploymentcenter\Core\Db;
|
|
use Deploymentcenter\Core\Http;
|
|
use Deploymentcenter\Core\TokenManager;
|
|
|
|
Http::beginJson(['GET', 'OPTIONS'], true);
|
|
|
|
$presented = Http::bearerToken();
|
|
if ($presented === null) {
|
|
Http::fail(401, 'unauthorized', 'Kein Token uebergeben. Erwartet als Authorization: Bearer <token> oder X-Agent-Token.');
|
|
}
|
|
|
|
$db = Db::init();
|
|
$manager = new TokenManager($db);
|
|
|
|
// Ohne geforderten Scope: die Frage lautet "wer bin ich", nicht "darf ich
|
|
// etwas". Widerruf, kaskadierender Widerruf und Ablauf werden trotzdem
|
|
// geprueft - ein entwertetes Token bekommt hier keine Auskunft.
|
|
$token = $manager->validateToken($presented, null);
|
|
|
|
if ($token === null) {
|
|
// Der haeufigste Irrtum an dieser Stelle: Es wurde der Shared Key
|
|
// geschickt. Der ist kein Token - er hat weder Bindung noch Rechteliste,
|
|
// und die Frage "wer bin ich" hat fuer ihn keine Antwort. Ohne diesen
|
|
// Hinweis sucht der Aufrufer den Fehler bei seinem Token.
|
|
$sharedKey = (string)Config::get('security.shared_key', '');
|
|
if ($sharedKey !== '' && hash_equals($sharedKey, $presented)) {
|
|
Http::fail(
|
|
401,
|
|
'not_a_token',
|
|
'Das ist der Shared Key, kein Agenten-Token. Er hat keine Bindung und keine '
|
|
. 'Rechteliste - dieser Endpunkt kann darueber nichts sagen.'
|
|
);
|
|
}
|
|
|
|
Http::fail(401, 'invalid_token', 'Token unbekannt, widerrufen oder abgelaufen.');
|
|
}
|
|
|
|
$scopes = TokenManager::decodeScopes($token['scopes']);
|
|
$isWorkbench = ($token['owner_type'] ?? null) === 'dev_agent';
|
|
|
|
Http::ok([
|
|
'token_id' => (string)$token['token_id'],
|
|
'name' => (string)$token['name'],
|
|
'type' => (string)$token['type'],
|
|
'owner_type' => (string)$token['owner_type'],
|
|
'owner_identity' => $token['owner_identity'] !== null ? (string)$token['owner_identity'] : null,
|
|
// Werkbank-Token duerfen in fremde Projekte melden, Laufzeit-Token nicht.
|
|
// Der Unterschied entscheidet ueber Fremdmeldungen und ist deshalb der
|
|
// erste Wert, den ein Werkzeug pruefen sollte.
|
|
'is_workbench' => $isWorkbench,
|
|
'project_slug' => $token['project_slug'] !== null ? (string)$token['project_slug'] : null,
|
|
'environment' => (string)$token['environment'],
|
|
'scopes' => $scopes,
|
|
'created_at' => $token['created_at'] !== null ? (string)$token['created_at'] : null,
|
|
'last_used_at' => $token['last_used_at'] !== null ? (string)$token['last_used_at'] : null,
|
|
'expires_at' => $token['expires_at'] !== null ? (string)$token['expires_at'] : null,
|
|
'parent_token_id' => $token['parent_token_id'] !== null ? (string)$token['parent_token_id'] : null,
|
|
]);
|