diff --git a/addUser.php b/addUser.php index e42ee65..482ba59 100644 --- a/addUser.php +++ b/addUser.php @@ -6,9 +6,9 @@ $usersFile = USERS_DIR . USERS_FILE; // Determine if we are in setup mode: // - Query parameter setup=1 is passed -// - And users.txt is either missing or empty +// - And users.txt is either missing or empty (zero bytes or trimmed content is empty) $isSetup = (isset($_GET['setup']) && $_GET['setup'] === '1'); -if ($isSetup && (!file_exists($usersFile) || trim(file_get_contents($usersFile)) === '')) { +if ($isSetup && (!file_exists($usersFile) || filesize($usersFile) == 0 || trim(file_get_contents($usersFile)) === '')) { // Allow initial admin creation without session checks. $setupMode = true; } else { diff --git a/auth.js b/auth.js index 5e1b75b..ee10a68 100644 --- a/auth.js +++ b/auth.js @@ -78,9 +78,14 @@ function initAuth() { if (authForm) { authForm.addEventListener("submit", function (event) { event.preventDefault(); + // Get the "Remember me" checkbox value. + const rememberMe = document.getElementById("rememberMeCheckbox") + ? document.getElementById("rememberMeCheckbox").checked + : false; const formData = { username: document.getElementById("loginUsername").value.trim(), - password: document.getElementById("loginPassword").value.trim() + password: document.getElementById("loginPassword").value.trim(), + remember_me: rememberMe }; sendRequest("auth.php", "POST", formData, { "X-CSRF-Token": window.csrfToken }) .then(data => { diff --git a/auth.php b/auth.php index cd2199b..28c6d60 100644 --- a/auth.php +++ b/auth.php @@ -10,6 +10,9 @@ $lockoutTime = 30 * 60; // 30 minutes in seconds $attemptsFile = USERS_DIR . 'failed_logins.json'; // JSON file for tracking failed login attempts $failedLogFile = USERS_DIR . 'failed_login.log'; // Plain text log for fail2ban +// Persistent tokens file for "Remember me" +$persistentTokensFile = USERS_DIR . 'persistent_tokens.json'; + // Load failed attempts data from file. function loadFailedAttempts($file) { if (file_exists($file)) { @@ -46,11 +49,9 @@ if (isset($failedAttempts[$ip])) { function authenticate($username, $password) { global $usersFile; - if (!file_exists($usersFile)) { return false; } - $lines = file($usersFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { list($storedUser, $storedPass, $storedRole) = explode(':', trim($line), 3); @@ -65,6 +66,7 @@ function authenticate($username, $password) $data = json_decode(file_get_contents("php://input"), true); $username = trim($data["username"] ?? ""); $password = trim($data["password"] ?? ""); +$rememberMe = isset($data["remember_me"]) && $data["remember_me"] === true; // Validate input: ensure both fields are provided. if (!$username || !$password) { @@ -72,7 +74,7 @@ if (!$username || !$password) { exit; } -// Validate username format: allow only letters, numbers, underscores, dashes, and spaces. +// Validate username format. if (!preg_match('/^[A-Za-z0-9_\- ]+$/', $username)) { echo json_encode(["error" => "Invalid username format. Only letters, numbers, underscores, dashes, and spaces are allowed."]); exit; @@ -92,6 +94,29 @@ if ($userRole !== false) { $_SESSION["username"] = $username; $_SESSION["isAdmin"] = ($userRole === "1"); // "1" indicates admin + // If "Remember me" is checked, generate a persistent login token. + if ($rememberMe) { + // Generate a secure random token. + $token = bin2hex(random_bytes(32)); + $expiry = time() + (30 * 24 * 60 * 60); // 30 days + // Load existing persistent tokens. + $persistentTokens = []; + if (file_exists($persistentTokensFile)) { + $persistentTokens = json_decode(file_get_contents($persistentTokensFile), true); + if (!is_array($persistentTokens)) { + $persistentTokens = []; + } + } + // Save token along with username and expiry. + $persistentTokens[$token] = [ + "username" => $username, + "expiry" => $expiry + ]; + file_put_contents($persistentTokensFile, json_encode($persistentTokens, JSON_PRETTY_PRINT)); + // Set the cookie. (Assuming $secure is defined in config.php.) + setcookie('remember_me_token', $token, $expiry, '/', '', $secure, true); + } + echo json_encode(["success" => "Login successful", "isAdmin" => $_SESSION["isAdmin"]]); } else { // On failed login, update failed attempts. diff --git a/config.php b/config.php index 1cc3889..c9af650 100644 --- a/config.php +++ b/config.php @@ -27,6 +27,29 @@ if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } +// Auto-login via persistent token if session is not active. +if (!isset($_SESSION["authenticated"]) && isset($_COOKIE['remember_me_token'])) { + $persistentTokensFile = USERS_DIR . 'persistent_tokens.json'; + if (file_exists($persistentTokensFile)) { + $persistentTokens = json_decode(file_get_contents($persistentTokensFile), true); + if (is_array($persistentTokens) && isset($persistentTokens[$_COOKIE['remember_me_token']])) { + $tokenData = $persistentTokens[$_COOKIE['remember_me_token']]; + if ($tokenData['expiry'] >= time()) { + // Token is valid; auto-authenticate the user. + $_SESSION["authenticated"] = true; + $_SESSION["username"] = $tokenData["username"]; + // Optionally, set admin status if stored in token data: + // $_SESSION["isAdmin"] = $tokenData["isAdmin"]; + } else { + // Token expired; remove it and clear the cookie. + unset($persistentTokens[$_COOKIE['remember_me_token']]); + file_put_contents($persistentTokensFile, json_encode($persistentTokens, JSON_PRETTY_PRINT)); + setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true); + } + } + } +} + // Define BASE_URL (this should point to where index.html is, e.g. your uploads directory) define('BASE_URL', 'http://yourwebsite/uploads/'); diff --git a/index.html b/index.html index 2869804..cb410c4 100644 --- a/index.html +++ b/index.html @@ -143,6 +143,10 @@ +