prepare('SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table'); $stmt->execute([ ':schema' => $schema, ':table' => $table, ]); return (int)$stmt->fetchColumn() > 0; } function getTableColumns(PDO $pdo, string $schema, string $table): array { $stmt = $pdo->prepare('SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema = :schema AND table_name = :table'); $stmt->execute([ ':schema' => $schema, ':table' => $table, ]); $columns = $stmt->fetchAll(PDO::FETCH_COLUMN); return is_array($columns) ? $columns : []; } function resolveUserIdFromBearer(PDO $pdo, string $rawToken): ?int { $token = trim($rawToken); if ($token === '') { return null; } $schema = (string)$pdo->query('SELECT DATABASE()')->fetchColumn(); if ($schema === '') { return null; } $candidates = [ ['table' => 'remember_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => null], ['table' => 'user_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'api_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'access_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ['table' => 'auth_tokens', 'user' => 'user_id', 'token' => 'token', 'expires' => 'expires_at', 'revoked' => 'revoked_at'], ]; $hashes = [$token, hash('sha256', $token)]; foreach ($candidates as $candidate) { if (!tableExists($pdo, $schema, $candidate['table'])) { continue; } $columns = getTableColumns($pdo, $schema, $candidate['table']); if (!in_array($candidate['user'], $columns, true) || !in_array($candidate['token'], $columns, true)) { continue; } $sql = 'SELECT `' . $candidate['user'] . '` AS user_id FROM `' . $candidate['table'] . '` ' . 'WHERE `' . $candidate['token'] . '` IN (:raw, :sha)'; if ($candidate['expires'] !== null && in_array($candidate['expires'], $columns, true)) { $sql .= ' AND (`' . $candidate['expires'] . '` IS NULL OR `' . $candidate['expires'] . '` > NOW())'; } if ($candidate['revoked'] !== null && in_array($candidate['revoked'], $columns, true)) { $sql .= ' AND `' . $candidate['revoked'] . '` IS NULL'; } $sql .= ' ORDER BY user_id DESC LIMIT 1'; $stmt = $pdo->prepare($sql); $stmt->execute([ ':raw' => $hashes[0], ':sha' => $hashes[1], ]); $userId = (int)($stmt->fetchColumn() ?: 0); if ($userId > 0) { return $userId; } } return null; } function resolveAdminUserId(PDO $pdo): ?int { if (!empty($_SESSION['logged_in']) && !empty($_SESSION['role']) && $_SESSION['role'] === 'admin') { $sessionUserId = isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : 0; if ($sessionUserId > 0) { return $sessionUserId; } $sessionUsername = isset($_SESSION['username']) ? trim((string)$_SESSION['username']) : ''; if ($sessionUsername !== '') { $stmt = $pdo->prepare('SELECT id FROM users WHERE username = :u AND role = :role LIMIT 1'); $stmt->execute([ ':u' => $sessionUsername, ':role' => 'admin', ]); $resolvedId = (int)($stmt->fetchColumn() ?: 0); if ($resolvedId > 0) { $_SESSION['user_id'] = $resolvedId; return $resolvedId; } } } $token = getAuthorizationToken(); if ($token === null) { return null; } $tokenUserId = resolveUserIdFromBearer($pdo, $token); if ($tokenUserId === null) { return null; } $stmt = $pdo->prepare('SELECT id FROM users WHERE id = :id AND role = :role LIMIT 1'); $stmt->execute([ ':id' => $tokenUserId, ':role' => 'admin', ]); $adminId = (int)($stmt->fetchColumn() ?: 0); return $adminId > 0 ? $adminId : null; } function ensureBlockedUsernamesTable(PDO $pdo): void { $pdo->exec( "CREATE TABLE IF NOT EXISTS blocked_usernames ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, name VARCHAR(20) NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_by INT NULL, UNIQUE KEY unique_blocked_username (name), KEY idx_blocked_created_by (created_by) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci" ); } $requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET'; if (!in_array($requestMethod, ['POST', 'DELETE'], true)) { blockedNamesRespond(['message' => 'Metoda niedozwolona. Użyj POST lub DELETE.'], 405); } if (!isset($pdo) || !($pdo instanceof PDO)) { blockedNamesRespond(['message' => 'Błąd połączenia z bazą danych.'], 500); } $adminId = resolveAdminUserId($pdo); if ($adminId === null) { blockedNamesRespond(['message' => 'Brak autoryzacji administratora.'], 401); } $rawBody = file_get_contents('php://input'); $body = json_decode((string)$rawBody, true); if (!is_array($body)) { blockedNamesRespond(['message' => 'Nieprawidłowy JSON.'], 400); } try { ensureBlockedUsernamesTable($pdo); if ($requestMethod === 'DELETE') { $blockedId = (int)($body['id'] ?? 0); if ($blockedId <= 0) { blockedNamesRespond(['message' => 'Nieprawidłowy identyfikator blokady.'], 400); } $deleteStmt = $pdo->prepare('DELETE FROM blocked_usernames WHERE id = :id LIMIT 1'); $deleteStmt->execute([':id' => $blockedId]); if ($deleteStmt->rowCount() < 1) { blockedNamesRespond(['message' => 'Nie znaleziono wskazanej zablokowanej nazwy.'], 404); } blockedNamesRespond(['message' => 'Nazwa użytkownika została usunięta z listy zablokowanych.'], 200); } $name = trim((string)($body['name'] ?? '')); if ($name === '') { blockedNamesRespond(['message' => 'Nazwa użytkownika nie może być pusta.'], 400); } if (!preg_match('/^[A-Za-z0-9_&!]{1,20}$/', $name)) { blockedNamesRespond(['message' => 'Niepoprawny format nazwy użytkownika.'], 400); } $existsBlockedStmt = $pdo->prepare('SELECT id FROM blocked_usernames WHERE name = :name LIMIT 1'); $existsBlockedStmt->execute([':name' => $name]); $alreadyBlocked = (bool)$existsBlockedStmt->fetchColumn(); $existsUserStmt = $pdo->prepare('SELECT id FROM users WHERE username = :name AND (disabled IS NULL OR disabled = 0) LIMIT 1'); $existsUserStmt->execute([':name' => $name]); $alreadyUser = (bool)$existsUserStmt->fetchColumn(); if ($alreadyBlocked || $alreadyUser) { blockedNamesRespond(['message' => 'Nazwa jest już zablokowana lub istnieje jako aktywny użytkownik.'], 409); } $insertStmt = $pdo->prepare('INSERT INTO blocked_usernames (name, created_by) VALUES (:name, :created_by)'); $insertStmt->execute([ ':name' => $name, ':created_by' => $adminId, ]); blockedNamesRespond(['message' => 'Nazwa użytkownika została zablokowana pomyślnie.'], 201); } catch (Throwable $e) { blockedNamesRespond(['message' => 'Błąd serwera podczas zapisu blokady.'], 500); }