$cfEmail, 'auth_method' => 'cloudflare_access', 'role' => getUserRole($cfEmail) ]; } // Fall back to session-based auth if (session_status() === PHP_SESSION_NONE) { session_start(); } if (!empty($_SESSION['authenticated']) && !empty($_SESSION['user'])) { return [ 'email' => $_SESSION['user'], 'auth_method' => 'session', 'role' => getUserRole($_SESSION['user']) ]; } return null; } /** * Ensure admin_users table exists and seed from ADMIN_EMAILS if empty */ function ensureAdminUsersTable() { if (!function_exists('getDB')) { return false; } try { $db = getDB(); // Create table if not exists $db->exec(" CREATE TABLE IF NOT EXISTS admin_users ( id INT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL, role ENUM('staff', 'admin') NOT NULL DEFAULT 'staff', display_name VARCHAR(255) DEFAULT NULL, active TINYINT(1) DEFAULT 1, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, created_by VARCHAR(255) DEFAULT NULL, UNIQUE KEY unique_email (email), INDEX idx_role (role), INDEX idx_active (active) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci "); // Check if table is empty $count = $db->query("SELECT COUNT(*) FROM admin_users")->fetchColumn(); if ($count == 0) { // Seed from ADMIN_EMAILS environment variable or constant $adminEmails = getenv('ADMIN_EMAILS'); if (empty($adminEmails) && defined('ADMIN_EMAILS')) { $adminEmails = ADMIN_EMAILS; } if (!empty($adminEmails)) { $emails = array_map('trim', explode(',', $adminEmails)); $stmt = $db->prepare("INSERT INTO admin_users (email, role, created_by) VALUES (?, 'admin', 'system_seed')"); foreach ($emails as $email) { if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) { try { $stmt->execute([strtolower($email)]); } catch (Exception $e) { // Ignore duplicates } } } } } return true; } catch (Exception $e) { return false; } } /** * Get user role from database * * @param string $email User email * @return string Role (admin or staff) */ function getUserRole($email) { if (empty($email)) { return ROLE_STAFF; } // Normalize email for comparison (lowercase, trimmed) $email = strtolower(trim($email)); // Ensure table exists and is seeded ensureAdminUsersTable(); // Try database lookup if (function_exists('getDB')) { try { $db = getDB(); $stmt = $db->prepare("SELECT role FROM admin_users WHERE LOWER(email) = ? AND active = 1"); $stmt->execute([$email]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result && !empty($result['role'])) { return $result['role']; } } catch (Exception $e) { // Table might not exist yet, fall through to default } } // Default to staff if not found in database return ROLE_STAFF; } /** * Check if current user has a specific role * * @param string $requiredRole Role to check for * @return bool True if user has the role */ function hasRole($requiredRole) { $user = getCurrentUser(); if (!$user) { return false; } // Admin has access to everything if ($user['role'] === ROLE_ADMIN) { return true; } return $user['role'] === $requiredRole; } /** * Check if current user is an admin * * @return bool True if user is admin */ function isAdmin() { return hasRole(ROLE_ADMIN); } /** * Check if current user is staff (or admin) * * @return bool True if user is staff or admin */ function isStaff() { return hasRole(ROLE_STAFF) || hasRole(ROLE_ADMIN); } /** * Require a specific role to access a page * Redirects to appropriate page if not authorized * * @param string $requiredRole Role required * @param string $redirectUrl URL to redirect to if unauthorized */ function requireRole($requiredRole, $redirectUrl = '/') { if (!hasRole($requiredRole)) { header('HTTP/1.1 403 Forbidden'); header('Location: ' . $redirectUrl . '?error=unauthorized'); exit; } } /** * Require admin role to access a page */ function requireAdmin() { requireRole(ROLE_ADMIN, '/'); } /** * Get user identifier for audit logging * Returns email or 'anonymous' if not authenticated * * @return string User identifier */ function getAuditUser() { $user = getCurrentUser(); if ($user) { return $user['email']; } // Check legacy session if (session_status() === PHP_SESSION_NONE) { session_start(); } return $_SESSION['user'] ?? 'anonymous'; } /** * Get user display info for header * * @return array User display info */ function getUserDisplayInfo() { $user = getCurrentUser(); if (!$user) { return [ 'name' => 'Guest', 'email' => '', 'role' => '', 'initials' => 'G', 'auth_method' => 'none', 'is_admin' => false ]; } $email = $user['email']; $name = explode('@', $email)[0]; $initials = strtoupper(substr($name, 0, 2)); // Try to get display name from database $displayName = null; if (function_exists('getDB')) { try { $db = getDB(); $stmt = $db->prepare("SELECT display_name FROM admin_users WHERE LOWER(email) = ?"); $stmt->execute([strtolower($email)]); $result = $stmt->fetch(PDO::FETCH_ASSOC); if ($result && !empty($result['display_name'])) { $displayName = $result['display_name']; $nameParts = explode(' ', $displayName); $initials = strtoupper(substr($nameParts[0], 0, 1) . (isset($nameParts[1]) ? substr($nameParts[1], 0, 1) : substr($nameParts[0], 1, 1))); } } catch (Exception $e) { // Ignore } } return [ 'name' => $displayName ?: ucfirst($name), 'email' => $email, 'role' => $user['role'], 'initials' => $initials, 'auth_method' => $user['auth_method'], 'is_admin' => $user['role'] === ROLE_ADMIN ]; } /** * Get all admin users from database * * @return array List of admin users */ function getAdminUsers() { if (!function_exists('getDB')) { return []; } try { $db = getDB(); $stmt = $db->query("SELECT id, email, role, display_name, active, created_at, created_by FROM admin_users ORDER BY role DESC, email ASC"); return $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (Exception $e) { return []; } } /** * Add or update an admin user * * @param string $email User email * @param string $role User role (staff or admin) * @param string|null $displayName Display name * @param string $createdBy Who created this user * @return bool Success */ function saveAdminUser($email, $role, $displayName = null, $createdBy = null) { if (!function_exists('getDB')) { return false; } $email = strtolower(trim($email)); if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { return false; } if (!in_array($role, [ROLE_STAFF, ROLE_ADMIN])) { $role = ROLE_STAFF; } try { $db = getDB(); $stmt = $db->prepare(" INSERT INTO admin_users (email, role, display_name, created_by) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE role = VALUES(role), display_name = VALUES(display_name), updated_at = CURRENT_TIMESTAMP "); $stmt->execute([$email, $role, $displayName, $createdBy]); return true; } catch (Exception $e) { return false; } } /** * Delete an admin user * * @param int $id User ID * @return bool Success */ function deleteAdminUser($id) { if (!function_exists('getDB')) { return false; } try { $db = getDB(); $stmt = $db->prepare("DELETE FROM admin_users WHERE id = ?"); $stmt->execute([$id]); return $stmt->rowCount() > 0; } catch (Exception $e) { return false; } } /** * Toggle admin user active status * * @param int $id User ID * @return bool Success */ function toggleAdminUser($id) { if (!function_exists('getDB')) { return false; } try { $db = getDB(); $stmt = $db->prepare("UPDATE admin_users SET active = NOT active, updated_at = CURRENT_TIMESTAMP WHERE id = ?"); $stmt->execute([$id]); return $stmt->rowCount() > 0; } catch (Exception $e) { return false; } }