PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]); } if (in_array($driver, ['pgsql','postgres','postgresql'], true)) { $dsn = 'pgsql:host=' . DB_HOST; if (defined('DB_PORT') && DB_PORT !== '') $dsn .= ';port=' . DB_PORT; $dsn .= ';dbname=' . DB_NAME; $pdo = new PDO($dsn, DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]); try { $pdo->exec("SET client_encoding TO 'UTF8'"); } catch (Throwable $e) { /* optional */ } return $pdo; } $dsn = 'mysql:host=' . DB_HOST; if (defined('DB_PORT') && DB_PORT !== '') $dsn .= ';port=' . DB_PORT; $dsn .= ';dbname=' . DB_NAME . ';charset=utf8mb4'; return new PDO($dsn, DB_USER, DB_PASS, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]); } function bg_users_schema(PDO $pdo) { $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); if ($driver === 'sqlite') { $pdo->exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT NOT NULL DEFAULT '', password_hash TEXT NOT NULL, role TEXT NOT NULL DEFAULT 'admin', created_at TEXT DEFAULT CURRENT_TIMESTAMP)"); $cols = []; foreach ($pdo->query('PRAGMA table_info(users)') as $row) $cols[$row['name']] = true; if (empty($cols['email'])) $pdo->exec("ALTER TABLE users ADD COLUMN email TEXT NOT NULL DEFAULT ''"); if (empty($cols['role'])) $pdo->exec("ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT 'admin'"); } elseif ($driver === 'pgsql') { $pdo->exec("CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, username VARCHAR(100) UNIQUE NOT NULL, email VARCHAR(190) NOT NULL DEFAULT '', password_hash VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT 'admin', created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP)"); try { $pdo->exec("ALTER TABLE users ADD COLUMN IF NOT EXISTS email VARCHAR(190) NOT NULL DEFAULT ''"); } catch (Throwable $e) { /* already present */ } try { $pdo->exec("ALTER TABLE users ADD COLUMN IF NOT EXISTS role VARCHAR(20) NOT NULL DEFAULT 'admin'"); } catch (Throwable $e) { /* already present */ } } else { $pdo->exec("CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(100) UNIQUE NOT NULL, email VARCHAR(190) NOT NULL DEFAULT '', password_hash VARCHAR(255) NOT NULL, role VARCHAR(20) NOT NULL DEFAULT 'admin', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"); try { $pdo->exec("ALTER TABLE users ADD COLUMN email VARCHAR(190) NOT NULL DEFAULT ''"); } catch (Throwable $e) { /* already present */ } try { $pdo->exec("ALTER TABLE users ADD COLUMN role VARCHAR(20) NOT NULL DEFAULT 'admin'"); } catch (Throwable $e) { /* already present */ } } } function bg_normalize_role($role) { $role = strtolower(trim((string)$role)); return in_array($role, ['admin','user'], true) ? $role : 'user'; } function bg_create_user($username, $password, $role = 'user', $email = '') { $username = trim((string)$username); if ($username === '') throw new InvalidArgumentException('Username is required.'); if (strlen((string)$password) < 8) throw new InvalidArgumentException('Password must be at least 8 characters.'); $role = bg_normalize_role($role); $email = trim((string)$email); if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) throw new InvalidArgumentException('Invalid email address.'); $pdo = bg_user_pdo(); bg_users_schema($pdo); $stmt = $pdo->prepare('INSERT INTO users (username, email, password_hash, role) VALUES (?, ?, ?, ?)'); return $stmt->execute([$username, $email, password_hash($password, PASSWORD_DEFAULT), $role]); } function bg_verify_user($username, $password) { $pdo = bg_user_pdo(); bg_users_schema($pdo); $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?'); $stmt->execute([trim((string)$username)]); $user = $stmt->fetch(); if (!$user) return false; $ok = false; if (!empty($user['password_hash']) && password_verify($password, $user['password_hash'])) $ok = true; if (!$ok && !empty($user['password_sha256']) && !empty($user['salt'])) { $hash = hash('sha256', $user['salt'] . $password); $ok = hash_equals($user['password_sha256'], $hash); } if (!$ok) return false; if (empty($user['role'])) $user['role'] = 'admin'; // compatibility with pre-role databases $user['role'] = bg_normalize_role($user['role']); return $user; } function bg_current_user() { return isset($_SESSION['user']) ? (string)$_SESSION['user'] : ''; } function bg_current_user_role() { $role = isset($_SESSION['role']) ? (string)$_SESSION['role'] : ''; return bg_normalize_role($role ?: 'user'); } function bg_current_user_is_admin() { return bg_current_user() !== '' && bg_current_user_role() === 'admin'; } function bg_require_admin() { if (bg_current_user_is_admin()) return; if (!headers_sent()) http_response_code(403); include __DIR__ . '/header.php'; include __DIR__ . '/sidebar.php'; echo '
' . bg_e(bg_t('admin_required_desc')) . '