Files
app/api/admin/handlers/sessions-handlers.php
Simon 5ef6fc8064 refactor: odstraneni PSR-1 SideEffects warningu
- Handler funkce extrahovany z API souboru do api/admin/handlers/
- config.php rozdeleny na helpers.php (funkce) a constants.php (konstanty)
- require_once odstranen z class souboru (AuditLog, JWTAuth, LeaveNotification)
- vendor/autoload.php presunuto do config.php bootstrap
- totp-handlers.php: pridany use deklarace pro TwoFactorAuth
- phpstan.neon: bootstrapFiles, scanDirectories, dynamicConstantNames
- Opraveny chybejici routing bloky v totp.php a session.php

Vysledek: phpcs 0 errors 0 warnings, PHPStan 0 errors, ESLint 0 errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 14:29:21 +01:00

181 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
/**
* GET - List all active sessions for current user
*/
function handleGetSession(PDO $pdo, int $userId, ?string $currentTokenHash): void
{
// Cleanup: expirované + rotované tokeny po grace period
$stmt = $pdo->prepare(
'DELETE FROM refresh_tokens WHERE user_id = ? AND (expires_at < NOW()'
. ' OR (replaced_at IS NOT NULL AND replaced_at < DATE_SUB(NOW(), INTERVAL '
. JWTAuth::getGracePeriod() . ' SECOND)))'
);
$stmt->execute([$userId]);
// Jen aktivní sessions (nereplacované)
$stmt = $pdo->prepare('
SELECT
id,
ip_address,
user_agent,
created_at,
expires_at,
token_hash
FROM refresh_tokens
WHERE user_id = ? AND replaced_at IS NULL
ORDER BY created_at DESC
');
$stmt->execute([$userId]);
$sessions = $stmt->fetchAll();
// Process sessions to add is_current flag and parse user agent
$processedSessions = array_map(function ($session) use ($currentTokenHash) {
return [
'id' => (int) $session['id'],
'ip_address' => $session['ip_address'],
'user_agent' => $session['user_agent'],
'device_info' => parseUserAgent($session['user_agent']),
'created_at' => $session['created_at'],
'expires_at' => $session['expires_at'],
'is_current' => $currentTokenHash && $session['token_hash'] === $currentTokenHash,
];
}, $sessions);
successResponse([
'sessions' => $processedSessions,
'total' => count($processedSessions),
]);
}
/**
* DELETE - Delete a specific session
*/
function handleDeleteSession(PDO $pdo, int $sessionId, int $userId, ?string $currentTokenHash): void
{
// Verify the session belongs to the current user
$stmt = $pdo->prepare('SELECT token_hash FROM refresh_tokens WHERE id = ? AND user_id = ?');
$stmt->execute([$sessionId, $userId]);
$session = $stmt->fetch();
if (!$session) {
errorResponse('Relace nebyla nalezena', 404);
}
// Check if trying to delete current session
if ($currentTokenHash && $session['token_hash'] === $currentTokenHash) {
// Check if force parameter is set
$input = getJsonInput();
if (!($input['force'] ?? false)) {
errorResponse('Nelze smazat aktuální relaci. Použijte tlačítko odhlášení.', 400);
}
}
// Delete the session
$stmt = $pdo->prepare('DELETE FROM refresh_tokens WHERE id = ? AND user_id = ?');
$stmt->execute([$sessionId, $userId]);
successResponse(null, 'Relace byla úspěšně ukončena');
}
/**
* DELETE - Delete all sessions except current
*/
function handleDeleteAllSessions(PDO $pdo, int $userId, ?string $currentTokenHash): void
{
if (!$currentTokenHash) {
$stmt = $pdo->prepare('DELETE FROM refresh_tokens WHERE user_id = ?');
$stmt->execute([$userId]);
$deleted = $stmt->rowCount();
} else {
// Ponechat aktuální session, smazat ostatní (včetně replaced)
$stmt = $pdo->prepare('DELETE FROM refresh_tokens WHERE user_id = ? AND token_hash != ?');
$stmt->execute([$userId, $currentTokenHash]);
$deleted = $stmt->rowCount();
}
successResponse([
'deleted' => $deleted,
], $deleted > 0 ? 'Ostatní relace byly úspěšně ukončeny' : 'Žádné další relace k ukončení');
}
/**
* Parse user agent string to extract device/browser info
*
* @return array{browser: string, os: string}
*/
function parseUserAgent(?string $userAgent): array
{
if (empty($userAgent)) {
return [
'browser' => 'Neznámý prohlížeč',
'os' => 'Neznámý systém',
'device' => 'Neznámé zařízení',
'icon' => 'device',
];
}
$browser = 'Neznámý prohlížeč';
$os = 'Neznámý systém';
$device = 'desktop';
$icon = 'desktop';
// Detect browser
if (preg_match('/Edg(e|A|iOS)?\/[\d.]+/i', $userAgent)) {
$browser = 'Microsoft Edge';
} elseif (preg_match('/OPR\/[\d.]+|Opera/i', $userAgent)) {
$browser = 'Opera';
} elseif (preg_match('/Chrome\/[\d.]+/i', $userAgent) && !preg_match('/Chromium/i', $userAgent)) {
$browser = 'Google Chrome';
} elseif (preg_match('/Firefox\/[\d.]+/i', $userAgent)) {
$browser = 'Mozilla Firefox';
} elseif (preg_match('/Safari\/[\d.]+/i', $userAgent) && !preg_match('/Chrome/i', $userAgent)) {
$browser = 'Safari';
} elseif (preg_match('/MSIE|Trident/i', $userAgent)) {
$browser = 'Internet Explorer';
}
// Detect OS
if (preg_match('/Windows NT 10/i', $userAgent)) {
$os = 'Windows 10/11';
} elseif (preg_match('/Windows NT 6\.3/i', $userAgent)) {
$os = 'Windows 8.1';
} elseif (preg_match('/Windows NT 6\.2/i', $userAgent)) {
$os = 'Windows 8';
} elseif (preg_match('/Windows NT 6\.1/i', $userAgent)) {
$os = 'Windows 7';
} elseif (preg_match('/Windows/i', $userAgent)) {
$os = 'Windows';
} elseif (preg_match('/Macintosh|Mac OS X/i', $userAgent)) {
$os = 'macOS';
} elseif (preg_match('/Linux/i', $userAgent) && !preg_match('/Android/i', $userAgent)) {
$os = 'Linux';
} elseif (preg_match('/iPhone/i', $userAgent)) {
$os = 'iOS';
$device = 'mobile';
$icon = 'smartphone';
} elseif (preg_match('/iPad/i', $userAgent)) {
$os = 'iPadOS';
$device = 'tablet';
$icon = 'tablet';
} elseif (preg_match('/Android/i', $userAgent)) {
$os = 'Android';
if (preg_match('/Mobile/i', $userAgent)) {
$device = 'mobile';
$icon = 'smartphone';
} else {
$device = 'tablet';
$icon = 'tablet';
}
}
return [
'browser' => $browser,
'os' => $os,
'device' => $device,
'icon' => $icon,
];
}