ysdsd
All checks were successful
Deploy / deploy-staging (push) Successful in 5s
Deploy / deploy-production (push) Has been skipped

This commit is contained in:
2026-04-27 01:50:16 +02:00
parent 44945a31da
commit f94dd83b68
3 changed files with 213 additions and 5 deletions

View File

@@ -25,6 +25,79 @@ $debugPush = static function (string $label, array $payload = []): void {
module_debug_push('pihole', array_merge(['label' => $label], $payload));
};
app()->session()->start();
$sessionFingerprint = static function (array $instance): string {
return sha1(json_encode([
'id' => (string)($instance['id'] ?? ''),
'url' => (string)($instance['url'] ?? ''),
'password' => (string)($instance['password'] ?? ''),
], JSON_UNESCAPED_UNICODE));
};
$getSessionBucket = static function (): array {
$bucket = $_SESSION['pihole_api_sessions'] ?? [];
return is_array($bucket) ? $bucket : [];
};
$readSessionCache = static function (array $instance) use ($getSessionBucket, $sessionFingerprint): ?array {
$bucket = $getSessionBucket();
$key = (string)($instance['id'] ?? '');
if ($key === '' || !isset($bucket[$key]) || !is_array($bucket[$key])) {
return null;
}
$row = $bucket[$key];
if (($row['fingerprint'] ?? '') !== $sessionFingerprint($instance)) {
return null;
}
return $row;
};
$writeSessionCache = static function (array $instance, string $sid, int $validity = 300, ?string $csrf = null) use ($sessionFingerprint): void {
$key = (string)($instance['id'] ?? '');
if ($key === '' || $sid === '') {
return;
}
if (!isset($_SESSION['pihole_api_sessions']) || !is_array($_SESSION['pihole_api_sessions'])) {
$_SESSION['pihole_api_sessions'] = [];
}
$_SESSION['pihole_api_sessions'][$key] = [
'fingerprint' => $sessionFingerprint($instance),
'sid' => $sid,
'csrf' => $csrf ?? '',
'validity' => $validity > 0 ? $validity : 300,
'expires_at' => time() + max(30, $validity - 15),
'updated_at' => time(),
];
};
$touchSessionCache = static function (array $instance, ?int $validity = null) use ($readSessionCache, $writeSessionCache): void {
$cached = $readSessionCache($instance);
if (!$cached || empty($cached['sid'])) {
return;
}
$writeSessionCache(
$instance,
(string)$cached['sid'],
$validity ?? (int)($cached['validity'] ?? 300),
(string)($cached['csrf'] ?? '')
);
};
$clearSessionCache = static function (array $instance): void {
$key = (string)($instance['id'] ?? '');
if ($key === '' || !isset($_SESSION['pihole_api_sessions']) || !is_array($_SESSION['pihole_api_sessions'])) {
return;
}
unset($_SESSION['pihole_api_sessions'][$key]);
};
$normalizeApiPath = function (string $baseUrl, string $apiPath): string {
$base = rtrim($baseUrl, '/');
$path = $apiPath;
@@ -138,7 +211,34 @@ $v5Request = function (array $instance, array $params) use ($normalizeApiPath, $
return $httpRequest('GET', $full, ['Accept: application/json'], null, $verify, $timeout);
};
$v6Auth = function (array $instance) use ($httpRequest): array {
$v6Logout = function (array $instance, string $sid) use ($httpRequest): void {
if ($sid === '') {
return;
}
$base = rtrim((string)$instance['url'], '/');
$url = $base . '/api/auth?sid=' . rawurlencode($sid);
$timeout = (int)($instance['timeout'] ?? 8);
if ($timeout <= 0) {
$timeout = 8;
}
$verify = !empty($instance['verify_tls']);
$httpRequest('DELETE', $url, ['Accept: application/json', 'X-FTL-SID: ' . $sid], null, $verify, $timeout);
};
$v6Auth = function (array $instance) use ($httpRequest, $readSessionCache, $writeSessionCache, $clearSessionCache, $v6Logout): array {
$cached = $readSessionCache($instance);
if ($cached && !empty($cached['sid']) && (int)($cached['expires_at'] ?? 0) > time()) {
return [
'ok' => true,
'http_code' => 200,
'url' => rtrim((string)$instance['url'], '/') . '/api/auth',
'data' => ['session' => ['sid' => (string)$cached['sid'], 'validity' => (int)($cached['validity'] ?? 300)]],
'sid' => (string)$cached['sid'],
'cached' => true,
];
}
$base = rtrim((string)$instance['url'], '/');
$url = $base . '/api/auth';
$timeout = (int)($instance['timeout'] ?? 8);
@@ -150,16 +250,26 @@ $v6Auth = function (array $instance) use ($httpRequest): array {
$body = json_encode($payload, JSON_UNESCAPED_UNICODE);
$res = $httpRequest('POST', $url, ['Accept: application/json', 'Content-Type: application/json'], $body, $verify, $timeout);
if (!$res['ok']) {
$clearSessionCache($instance);
return $res;
}
$data = (array)($res['data'] ?? []);
$session = (array)($data['session'] ?? []);
$sid = (string)($session['sid'] ?? '');
$validity = (int)($session['validity'] ?? 300);
$csrf = (string)($session['csrf'] ?? '');
if ($cached && !empty($cached['sid']) && $cached['sid'] !== $sid) {
$v6Logout($instance, (string)$cached['sid']);
}
if ($sid !== '') {
$writeSessionCache($instance, $sid, $validity, $csrf);
}
$res['sid'] = $sid;
$res['cached'] = false;
return $res;
};
$v6Request = function (array $instance, string $path, string $method, array $payload, string $sid) use ($httpRequest): array {
$v6Request = function (array $instance, string $path, string $method, array $payload, string $sid) use ($httpRequest, $clearSessionCache, $touchSessionCache): array {
$base = rtrim((string)$instance['url'], '/');
$path = ltrim($path, '/');
$url = $base . '/api/' . $path;
@@ -183,7 +293,17 @@ $v6Request = function (array $instance, string $path, string $method, array $pay
$headers[] = 'Content-Type: application/json';
}
return $httpRequest($method, $url, $headers, $body, $verify, $timeout);
$result = $httpRequest($method, $url, $headers, $body, $verify, $timeout);
$httpCode = (int)($result['http_code'] ?? 0);
if ($sid !== '') {
if (($result['ok'] ?? false) === true) {
$touchSessionCache($instance);
} elseif (in_array($httpCode, [401, 403], true)) {
$clearSessionCache($instance);
}
}
return $result;
};
$v6RequestAny = function (array $instance, array $paths, string $method, array $payload, string $sid) use ($v6Request): array {
@@ -202,7 +322,7 @@ $v6RequestAny = function (array $instance, array $paths, string $method, array $
return $last;
};
$detectApi = function (array $instance) use ($v6Auth, $v6RequestAny, $v5Request): array {
$detectApi = function (array $instance) use ($v6Auth, $v6RequestAny, $v5Request, $clearSessionCache): array {
$sid = '';
$authRes = null;
if (!empty($instance['password'])) {
@@ -211,6 +331,14 @@ $detectApi = function (array $instance) use ($v6Auth, $v6RequestAny, $v5Request)
$sid = (string)$authRes['sid'];
$probe = $v6RequestAny($instance, ['dns/blocking', 'stats/summary', 'summary'], 'GET', [], $sid);
if (!(empty($authRes['cached'])) && in_array((int)($probe['http_code'] ?? 0), [401, 403], true)) {
$clearSessionCache($instance);
$authRes = $v6Auth($instance);
if (($authRes['ok'] ?? false) && !empty($authRes['sid'])) {
$sid = (string)$authRes['sid'];
$probe = $v6RequestAny($instance, ['dns/blocking', 'stats/summary', 'summary'], 'GET', [], $sid);
}
}
return ['version' => 6, 'sid' => $sid, 'probe' => $probe, 'auth' => $authRes];
}