From 98f37c24822d9f0b98199a2604da1801766f94a0 Mon Sep 17 00:00:00 2001 From: Hosteroid Date: Thu, 9 Oct 2025 18:34:09 +0300 Subject: [PATCH] Allow custom admin username and email during install The installer now prompts for and validates a custom admin username and email, updating migrations and SQL placeholders accordingly. Login now accepts either username or email, and the login form and installer views have been updated to reflect these changes. Additional logging and migration handling improvements were made for better installation and authentication workflows. --- app/Controllers/AuthController.php | 17 +++++- app/Controllers/InstallerController.php | 52 ++++++++++++++++--- app/Views/auth/login.php | 4 +- app/Views/installer/complete.php | 2 +- app/Views/installer/welcome.php | 15 ++++++ .../migrations/000_initial_schema_v1.1.0.sql | 22 ++++---- .../migrations/002_create_users_table.sql | 6 +-- 7 files changed, 92 insertions(+), 26 deletions(-) diff --git a/app/Controllers/AuthController.php b/app/Controllers/AuthController.php index 9346994..dbd9b24 100644 --- a/app/Controllers/AuthController.php +++ b/app/Controllers/AuthController.php @@ -51,7 +51,7 @@ class AuthController extends Controller } $username = trim($_POST['username'] ?? ''); - $password = $_POST['password'] ?? ''; + $password = $_POST['password'] ?? ''; // Don't trim - passwords may have intentional spaces $remember = isset($_POST['remember']); // Validate input @@ -61,10 +61,19 @@ class AuthController extends Controller return; } - // Find user + // Find user by username or email $user = $this->userModel->findByUsername($username); + + // If not found by username, try email + if (!$user && filter_var($username, FILTER_VALIDATE_EMAIL)) { + $users = $this->userModel->where('email', $username); + if (!empty($users) && $users[0]['is_active']) { + $user = $users[0]; + } + } if (!$user) { + error_log("Login failed: User '$username' not found or not active"); $_SESSION['error'] = 'Invalid username or password'; $this->redirect('/login'); return; @@ -72,10 +81,14 @@ class AuthController extends Controller // Verify password if (!$this->userModel->verifyPassword($password, $user['password'])) { + error_log("Login failed: Password verification failed for user '$username'"); + error_log("Stored hash: {$user['password']}"); $_SESSION['error'] = 'Invalid username or password'; $this->redirect('/login'); return; } + + error_log("Login successful for user '$username'"); // Check if email verification is required $requireVerification = $this->settingModel->getValue('require_email_verification'); diff --git a/app/Controllers/InstallerController.php b/app/Controllers/InstallerController.php index 7aafcd8..afebf7c 100644 --- a/app/Controllers/InstallerController.php +++ b/app/Controllers/InstallerController.php @@ -175,10 +175,17 @@ class InstallerController extends Controller return; } + $adminUsername = trim($_POST['admin_username'] ?? ''); $adminPassword = trim($_POST['admin_password'] ?? ''); $adminEmail = trim($_POST['admin_email'] ?? ''); // Validate + if (empty($adminUsername) || !preg_match('/^[a-zA-Z0-9_]+$/', $adminUsername)) { + $_SESSION['error'] = 'Username can only contain letters, numbers, and underscores'; + $this->redirect('/install'); + return; + } + if (empty($adminPassword) || strlen($adminPassword) < 8) { $_SESSION['error'] = 'Admin password must be at least 8 characters'; $this->redirect('/install'); @@ -204,10 +211,12 @@ class InstallerController extends Controller $sql = file_get_contents($file); - // Replace password placeholder for user migration - if ($migration === '002_create_users_table.sql') { + // Replace placeholders for user migration or consolidated schema + if ($migration === '002_create_users_table.sql' || $migration === '000_initial_schema_v1.1.0.sql') { $passwordHash = password_hash($adminPassword, PASSWORD_BCRYPT); $sql = str_replace('{{ADMIN_PASSWORD_HASH}}', $passwordHash, $sql); + $sql = str_replace('{{ADMIN_USERNAME}}', $adminUsername, $sql); + $sql = str_replace('{{ADMIN_EMAIL}}', $adminEmail, $sql); } // Execute SQL @@ -233,9 +242,33 @@ class InstallerController extends Controller $results[] = $migration; } - // Update admin email and ensure admin role and verified status - $stmt = $pdo->prepare("UPDATE users SET email = ?, role = 'admin', email_verified = 1 WHERE username = 'admin'"); - $stmt->execute([$adminEmail]); + // If using consolidated schema, mark all individual migrations as executed too + if (in_array('000_initial_schema_v1.1.0.sql', $migrations)) { + $allMigrations = [ + '001_create_tables.sql', + '002_create_users_table.sql', + '003_add_whois_fields.sql', + '004_create_tld_registry_table.sql', + '005_update_tld_import_logs.sql', + '006_add_complete_workflow_import_type.sql', + '007_add_app_and_email_settings.sql', + '008_add_notes_to_domains.sql', + '009_add_authentication_features.sql', + '010_add_app_version_setting.sql', + '011_create_sessions_table.sql', + '012_link_remember_tokens_to_sessions.sql', + '013_create_user_notifications_table.sql' + ]; + + $stmt = $pdo->prepare("INSERT INTO migrations (migration) VALUES (?) ON DUPLICATE KEY UPDATE migration=migration"); + foreach ($allMigrations as $individualMigration) { + $stmt->execute([$individualMigration]); + } + } + + // Update admin user to ensure role and verified status (in case migration already had defaults) + $stmt = $pdo->prepare("UPDATE users SET role = 'admin', email_verified = 1 WHERE username = ?"); + $stmt->execute([$adminUsername]); // Generate encryption key if not exists if (empty($_ENV['APP_ENCRYPTION_KEY'])) { @@ -249,12 +282,13 @@ class InstallerController extends Controller // Create welcome notification for admin try { // Get the admin user ID - $stmt = $pdo->query("SELECT id FROM users WHERE username = 'admin' LIMIT 1"); + $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ? LIMIT 1"); + $stmt->execute([$adminUsername]); $adminUser = $stmt->fetch(\PDO::FETCH_ASSOC); if ($adminUser) { $notificationService = new \App\Services\NotificationService(); - $notificationService->notifyWelcome($adminUser['id'], 'admin'); + $notificationService->notifyWelcome($adminUser['id'], $adminUsername); } } catch (\Exception $e) { // Don't fail install if notification fails @@ -263,6 +297,7 @@ class InstallerController extends Controller // Redirect to complete page $_SESSION['install_complete'] = true; + $_SESSION['admin_username'] = $adminUsername; $_SESSION['admin_password'] = $adminPassword; $this->redirect('/install/complete'); @@ -385,12 +420,15 @@ class InstallerController extends Controller return; } + $adminUsername = $_SESSION['admin_username'] ?? 'admin'; $adminPassword = $_SESSION['admin_password'] ?? null; + unset($_SESSION['admin_username']); unset($_SESSION['admin_password']); unset($_SESSION['install_complete']); $this->view('installer/complete', [ 'title' => 'Installation Complete', + 'adminUsername' => $adminUsername, 'adminPassword' => $adminPassword ]); } diff --git a/app/Views/auth/login.php b/app/Views/auth/login.php index b337498..0e8d013 100644 --- a/app/Views/auth/login.php +++ b/app/Views/auth/login.php @@ -28,7 +28,7 @@ ob_start();
@@ -41,7 +41,7 @@ ob_start(); required autofocus class="w-full pl-10 pr-3 py-2.5 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition-colors text-sm" - placeholder="Enter your username"> + placeholder="Enter your username or email">
diff --git a/app/Views/installer/complete.php b/app/Views/installer/complete.php index 2857072..3e2a698 100644 --- a/app/Views/installer/complete.php +++ b/app/Views/installer/complete.php @@ -45,7 +45,7 @@
Username: - admin +
Password: diff --git a/app/Views/installer/welcome.php b/app/Views/installer/welcome.php index 3814056..0ae5285 100644 --- a/app/Views/installer/welcome.php +++ b/app/Views/installer/welcome.php @@ -78,6 +78,21 @@

Administrator Account

+
+ +
+
+ +
+ +
+

Letters, numbers, and underscores only

+
+