Files
2026-10-19 18:05:51 +00:00

68 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
/**
* Prüft, wie die Anmeldung der Apps aus den Anfrage-Kopfzeilen gelesen wird. Viele Hoster (Apache mit PHP-FPM, z. B. Plesk)
* reichen "Authorization" nicht an PHP weiter: Dann muss der Ersatz-Header "X-DeskHub-Authorization" genügen.
*/
putenv('DESKHUB_SQLITE_PATH=:memory:');
require_once dirname(__DIR__) . '/api/staff/v1/common.php';
$failures = [];
$check = function (bool $ok, string $what) use (&$failures): void {
if (!$ok) $failures[] = $what;
};
$token = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$other = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$reset = function (array $server): void {
foreach (['HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION', 'HTTP_X_DESKHUB_AUTHORIZATION'] as $k) unset($_SERVER[$k]);
foreach ($server as $k => $v) $_SERVER[$k] = $v;
};
$reset(['HTTP_AUTHORIZATION' => "Bearer {$token}"]);
$check(api_bearer_token() === $token, 'normaler Authorization-Header wird gelesen');
$reset(['REDIRECT_HTTP_AUTHORIZATION' => "Bearer {$token}"]);
$check(api_bearer_token() === $token, 'Authorization über REDIRECT_HTTP_AUTHORIZATION (Apache-Umleitung) wird gelesen');
$reset(['HTTP_X_DESKHUB_AUTHORIZATION' => "Bearer {$token}"]);
$check(api_bearer_token() === $token, 'Ersatz-Header X-DeskHub-Authorization genügt, wenn Authorization fehlt (Plesk/FPM)');
$reset(['HTTP_AUTHORIZATION' => "Bearer {$token}", 'HTTP_X_DESKHUB_AUTHORIZATION' => "Bearer {$other}"]);
$check(api_bearer_token() === $token, 'ist beides vorhanden, gilt Authorization');
$reset(['HTTP_AUTHORIZATION' => '', 'HTTP_X_DESKHUB_AUTHORIZATION' => "Bearer {$token}"]);
$check(api_bearer_token() === $token, 'leerer Authorization-Header fällt auf den Ersatz-Header zurück');
$reset([]);
$check(api_bearer_token() === null && api_authorization_header() === '', 'ohne Header: kein Token');
$reset(['HTTP_X_DESKHUB_AUTHORIZATION' => 'Basic abc']);
$check(api_bearer_token() === null, 'kein Bearer-Schema: abgelehnt');
$reset(['HTTP_X_DESKHUB_AUTHORIZATION' => 'Bearer kurz']);
$check(api_bearer_token() === null, 'zu kurzes Token: abgelehnt');
$reset(['HTTP_X_DESKHUB_AUTHORIZATION' => "Bearer {$token}\r\nX-Evil: 1"]);
$check(api_bearer_token() === null, 'eingeschleuste Zeilenumbrüche: abgelehnt');
// Klartext aus HTML (Signaturen und Nachrichten in den Apps): Blöcke bleiben getrennt
$check(api_plain_text('<p>Liebe Grüße<br>Lukas</p><div>Inhaber Windelweb</div><div>Zweite Zeile</div>') === "Liebe Grüße\nLukas\n\nInhaber Windelweb\nZweite Zeile", 'Klartext: Absätze und div-Blöcke bleiben getrennt: ' . json_encode(api_plain_text('<p>Liebe Grüße<br>Lukas</p><div>Inhaber Windelweb</div><div>Zweite Zeile</div>')));
$check(api_plain_text('<ul><li>Eins</li><li>Zwei</li></ul>') === "• Eins\n• Zwei", 'Klartext: Listenpunkte');
$check(api_plain_text("<p>A</p><p></p><p></p><p>B</p>") === "A\n\nB", 'Klartext: höchstens eine Leerzeile');
$check(api_plain_text('<p>Text &amp; mehr &lt;b&gt;</p>') === 'Text & mehr <b>', 'Klartext: Entities werden aufgelöst');
$check(api_plain_text(null) === '' && api_plain_text('') === '', 'Klartext: leer');
// Klartext aus HTML (Signaturen und Nachrichten in den Apps): Blöcke bleiben getrennt
$check(api_plain_text('<p>Liebe Grüße<br>Lukas</p><div>Inhaber Windelweb</div><div>Zweite Zeile</div>') === "Liebe Grüße\nLukas\n\nInhaber Windelweb\nZweite Zeile", 'Klartext: Absätze und div-Blöcke bleiben getrennt: ' . json_encode(api_plain_text('<p>Liebe Grüße<br>Lukas</p><div>Inhaber Windelweb</div><div>Zweite Zeile</div>')));
$check(api_plain_text('<ul><li>Eins</li><li>Zwei</li></ul>') === "• Eins\n• Zwei", 'Klartext: Listenpunkte');
$check(api_plain_text("<p>A</p><p></p><p></p><p>B</p>") === "A\n\nB", 'Klartext: höchstens eine Leerzeile');
$check(api_plain_text('<p>Text &amp; mehr &lt;b&gt;</p>') === 'Text & mehr <b>', 'Klartext: Entities werden aufgelöst');
$check(api_plain_text(null) === '' && api_plain_text('') === '', 'Klartext: leer');
// Anwendungs-Code der Apps sendet den Ersatz-Header
$desktop = (string) @file_get_contents(dirname(__DIR__, 2) . '/desktop/src/main.js');
if ($desktop !== '') $check(str_contains($desktop, "X-DeskHub-Authorization"), 'Windows-App sendet den Ersatz-Header');
$android = (string) @file_get_contents(dirname(__DIR__, 2) . '/mobile/src/main/java/de/deskhub/staff/data/remote/DeskHubApi.kt');
if ($android !== '') $check(substr_count($android, 'X-DeskHub-Authorization') >= 2, 'Android-App sendet den Ersatz-Header (Anfragen und Bild-Upload)');
// Apache-Regeln
$htaccess = (string) file_get_contents(dirname(__DIR__) . '/.htaccess');
$check(str_contains($htaccess, 'E=HTTP_AUTHORIZATION:%{HTTP:Authorization}') && str_contains($htaccess, 'CGIPassAuth On'), '.htaccess reicht den Authorization-Header an PHP weiter');
if ($failures) {
fwrite(STDERR, "FEHLER:\n- " . implode("\n- ", $failures) . "\n");
exit(1);
}
echo "API auth header checks passed.\n";