From b4445fc4d81d939fb42c9a4ada5a80228e346956 Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 6 Apr 2025 01:24:13 -0400 Subject: [PATCH] share new password page, totp setup focus, logout clear session cookie --- index.html | 7 +++++ js/authModals.js | 26 +++++++++++++-- logout.php | 21 ++++++++++--- share.php | 82 ++++++++++++++++++++++++++++++++++++++---------- 4 files changed, 114 insertions(+), 22 deletions(-) diff --git a/index.html b/index.html index d541d4e..4baef0e 100644 --- a/index.html +++ b/index.html @@ -5,6 +5,13 @@ FileRise + diff --git a/js/authModals.js b/js/authModals.js index 1e80360..8cd7ca0 100644 --- a/js/authModals.js +++ b/js/authModals.js @@ -1,4 +1,4 @@ -import { showToast, toggleVisibility } from './domUtils.js'; +import { showToast, toggleVisibility, attachEnterKeyListener } from './domUtils.js'; import { sendRequest } from './networkUtils.js'; const version = "v1.0.8"; @@ -8,7 +8,7 @@ let lastLoginData = null; export function setLastLoginData(data) { lastLoginData = data; // expose to auth.js so it can tell form-login vs basic/oidc - window.__lastLoginData = data; + //window.__lastLoginData = data; } export function openTOTPLoginModal() { @@ -258,12 +258,34 @@ export function openTOTPModal() { }) .catch(() => { showToast("Error verifying TOTP code."); }); }); + + // Focus the input and attach enter key listener + const totpConfirmInput = document.getElementById("totpConfirmInput"); + if (totpConfirmInput) { + setTimeout(() => { + const totpConfirmInput = document.getElementById("totpConfirmInput"); + if (totpConfirmInput) totpConfirmInput.focus(); + }, 100); + } + attachEnterKeyListener("totpModal", "confirmTOTPBtn"); + } else { totpModal.style.display = "flex"; totpModal.style.backgroundColor = overlayBackground; const modalContent = totpModal.querySelector(".modal-content"); modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; + + // Focus the input and attach enter key listener + const totpConfirmInput = document.getElementById("totpConfirmInput"); + if (totpConfirmInput) { + totpConfirmInput.value = ""; + setTimeout(() => { + const totpConfirmInput = document.getElementById("totpConfirmInput"); + if (totpConfirmInput) totpConfirmInput.focus(); + }, 100); + } + attachEnterKeyListener("totpModal", "confirmTOTPBtn"); } } diff --git a/logout.php b/logout.php index 1572888..a8c2d3e 100644 --- a/logout.php +++ b/logout.php @@ -5,12 +5,12 @@ require_once 'config.php'; $headers = array_change_key_case(getallheaders(), CASE_LOWER); $receivedToken = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; -// If there's a mismatch, log it but continue with logout. +// Log CSRF mismatch but proceed with logout. if (isset($_SESSION['csrf_token']) && $receivedToken !== $_SESSION['csrf_token']) { error_log("CSRF token mismatch on logout. Proceeding with logout."); } -// If the remember me token is set, remove it from the persistent tokens file. +// Remove the remember_me token. if (isset($_COOKIE['remember_me_token'])) { $token = $_COOKIE['remember_me_token']; $persistentTokensFile = USERS_DIR . 'persistent_tokens.json'; @@ -25,13 +25,26 @@ if (isset($_COOKIE['remember_me_token'])) { } } // Clear the cookie. + // Ensure $secure is defined; for example: + $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'); setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true); } -// Clear session data and destroy the session. +// Clear session data and remove session cookie. $_SESSION = []; + +// Clear the session cookie. +if (ini_get("session.use_cookies")) { + $params = session_get_cookie_params(); + setcookie(session_name(), '', time() - 42000, + $params["path"], $params["domain"], + $params["secure"], $params["httponly"] + ); +} + +// Destroy the session. session_destroy(); -header("Location: index.html"); +header("Location: index.html?logout=1"); exit; ?> \ No newline at end of file diff --git a/share.php b/share.php index 430301d..948cda6 100644 --- a/share.php +++ b/share.php @@ -1,10 +1,11 @@ $record['expires']) { exit; } -// If a password is required and none is provided, show a simple form. +// If a password is required and none is provided, show a password form. if (!empty($record['password']) && empty($providedPass)) { ?> + + Enter Password + - +

This file is protected by a password.

- + @@ -58,7 +95,7 @@ if (!empty($record['password']) && empty($providedPass)) { exit; } -// If a password was set, validate it. +// Validate provided password if set. if (!empty($record['password'])) { if (!password_verify($providedPass, $record['password'])) { http_response_code(403); @@ -67,7 +104,7 @@ if (!empty($record['password'])) { } } -// Build file path. +// Build file path securely. $folder = trim($record['folder'], "/\\ "); $file = $record['file']; $filePath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR; @@ -76,24 +113,37 @@ if (!empty($folder) && strtolower($folder) !== 'root') { } $filePath .= $file; -if (!file_exists($filePath)) { +// Resolve the real path and ensure it's within the allowed directory. +$realFilePath = realpath($filePath); +$uploadDirReal = realpath(UPLOAD_DIR); +if ($realFilePath === false || strpos($realFilePath, $uploadDirReal) !== 0) { + http_response_code(404); + echo json_encode(["error" => "File not found."]); + exit; +} + +if (!file_exists($realFilePath)) { http_response_code(404); echo json_encode(["error" => "File not found."]); exit; } // Serve the file. -$mimeType = mime_content_type($filePath); +$mimeType = mime_content_type($realFilePath); header("Content-Type: " . $mimeType); -// Determine extension and set disposition accordingly. -$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); +// Set Content-Disposition based on file type. +$ext = strtolower(pathinfo($realFilePath, PATHINFO_EXTENSION)); if (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg','ico'])) { - header('Content-Disposition: inline; filename="' . basename($filePath) . '"'); + header('Content-Disposition: inline; filename="' . basename($realFilePath) . '"'); } else { - header('Content-Disposition: attachment; filename="' . basename($filePath) . '"'); + header('Content-Disposition: attachment; filename="' . basename($realFilePath) . '"'); } -readfile($filePath); +// Optionally disable caching for sensitive files. +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Pragma: no-cache"); + +readfile($realFilePath); exit; ?> \ No newline at end of file