share new password page, totp setup focus, logout clear session cookie

This commit is contained in:
Ryan
2025-04-06 01:24:13 -04:00
committed by GitHub
parent 4022ccde84
commit b4445fc4d8
4 changed files with 114 additions and 22 deletions

View File

@@ -5,6 +5,13 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>FileRise</title> <title>FileRise</title>
<script>
const params = new URLSearchParams(window.location.search);
if (params.get('logout') === '1') {
localStorage.removeItem("username");
localStorage.removeItem("userTOTPEnabled");
}
</script>
<link rel="icon" type="image/png" href="/assets/logo.png"> <link rel="icon" type="image/png" href="/assets/logo.png">
<link rel="icon" type="image/svg+xml" href="/assets/logo.svg"> <link rel="icon" type="image/svg+xml" href="/assets/logo.svg">
<meta name="csrf-token" content=""> <meta name="csrf-token" content="">

View File

@@ -1,4 +1,4 @@
import { showToast, toggleVisibility } from './domUtils.js'; import { showToast, toggleVisibility, attachEnterKeyListener } from './domUtils.js';
import { sendRequest } from './networkUtils.js'; import { sendRequest } from './networkUtils.js';
const version = "v1.0.8"; const version = "v1.0.8";
@@ -8,7 +8,7 @@ let lastLoginData = null;
export function setLastLoginData(data) { export function setLastLoginData(data) {
lastLoginData = data; lastLoginData = data;
// expose to auth.js so it can tell form-login vs basic/oidc // expose to auth.js so it can tell form-login vs basic/oidc
window.__lastLoginData = data; //window.__lastLoginData = data;
} }
export function openTOTPLoginModal() { export function openTOTPLoginModal() {
@@ -258,12 +258,34 @@ export function openTOTPModal() {
}) })
.catch(() => { showToast("Error verifying TOTP code."); }); .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 { } else {
totpModal.style.display = "flex"; totpModal.style.display = "flex";
totpModal.style.backgroundColor = overlayBackground; totpModal.style.backgroundColor = overlayBackground;
const modalContent = totpModal.querySelector(".modal-content"); const modalContent = totpModal.querySelector(".modal-content");
modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff"; modalContent.style.background = isDarkMode ? "#2c2c2c" : "#fff";
modalContent.style.color = isDarkMode ? "#e0e0e0" : "#000"; 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");
} }
} }

View File

@@ -5,12 +5,12 @@ require_once 'config.php';
$headers = array_change_key_case(getallheaders(), CASE_LOWER); $headers = array_change_key_case(getallheaders(), CASE_LOWER);
$receivedToken = isset($headers['x-csrf-token']) ? trim($headers['x-csrf-token']) : ''; $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']) { if (isset($_SESSION['csrf_token']) && $receivedToken !== $_SESSION['csrf_token']) {
error_log("CSRF token mismatch on logout. Proceeding with logout."); 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'])) { if (isset($_COOKIE['remember_me_token'])) {
$token = $_COOKIE['remember_me_token']; $token = $_COOKIE['remember_me_token'];
$persistentTokensFile = USERS_DIR . 'persistent_tokens.json'; $persistentTokensFile = USERS_DIR . 'persistent_tokens.json';
@@ -25,13 +25,26 @@ if (isset($_COOKIE['remember_me_token'])) {
} }
} }
// Clear the cookie. // Clear the cookie.
// Ensure $secure is defined; for example:
$secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true); setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true);
} }
// Clear session data and destroy the session. // Clear session data and remove session cookie.
$_SESSION = []; $_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(); session_destroy();
header("Location: index.html"); header("Location: index.html?logout=1");
exit; exit;
?> ?>

View File

@@ -1,10 +1,11 @@
<?php <?php
// share.php // share.php
require_once 'config.php'; require_once 'config.php';
// Get token and password (if provided) // Retrieve and sanitize input
$token = isset($_GET['token']) ? $_GET['token'] : ''; $token = filter_input(INPUT_GET, 'token', FILTER_SANITIZE_STRING);
$providedPass = isset($_GET['pass']) ? $_GET['pass'] : ''; $providedPass = filter_input(INPUT_GET, 'pass', FILTER_SANITIZE_STRING);
if (empty($token)) { if (empty($token)) {
http_response_code(400); http_response_code(400);
@@ -12,7 +13,7 @@ if (empty($token)) {
exit; exit;
} }
// Load share links. // Load share links from file
$shareFile = META_DIR . "share_links.json"; $shareFile = META_DIR . "share_links.json";
if (!file_exists($shareFile)) { if (!file_exists($shareFile)) {
http_response_code(404); http_response_code(404);
@@ -36,18 +37,54 @@ if (time() > $record['expires']) {
exit; 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)) { if (!empty($record['password']) && empty($providedPass)) {
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Enter Password</title> <title>Enter Password</title>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background-color: #f4f4f4;
color: #333;
}
form {
max-width: 400px;
margin: 40px auto;
background: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
input[type="password"] {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
padding: 10px 20px;
background: #007BFF;
border: none;
border-radius: 4px;
color: #fff;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
</style>
</head> </head>
<body style="font-family: Arial, sans-serif; padding: 20px;"> <body>
<h2>This file is protected by a password.</h2> <h2>This file is protected by a password.</h2>
<form method="get" action="share.php"> <form method="get" action="share.php">
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token); ?>"> <input type="hidden" name="token" value="<?php echo htmlspecialchars($token, ENT_QUOTES, 'UTF-8'); ?>">
<label for="pass">Password:</label> <label for="pass">Password:</label>
<input type="password" name="pass" id="pass" required> <input type="password" name="pass" id="pass" required>
<button type="submit">Submit</button> <button type="submit">Submit</button>
@@ -58,7 +95,7 @@ if (!empty($record['password']) && empty($providedPass)) {
exit; exit;
} }
// If a password was set, validate it. // Validate provided password if set.
if (!empty($record['password'])) { if (!empty($record['password'])) {
if (!password_verify($providedPass, $record['password'])) { if (!password_verify($providedPass, $record['password'])) {
http_response_code(403); http_response_code(403);
@@ -67,7 +104,7 @@ if (!empty($record['password'])) {
} }
} }
// Build file path. // Build file path securely.
$folder = trim($record['folder'], "/\\ "); $folder = trim($record['folder'], "/\\ ");
$file = $record['file']; $file = $record['file'];
$filePath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR; $filePath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR;
@@ -76,24 +113,37 @@ if (!empty($folder) && strtolower($folder) !== 'root') {
} }
$filePath .= $file; $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); http_response_code(404);
echo json_encode(["error" => "File not found."]); echo json_encode(["error" => "File not found."]);
exit; exit;
} }
// Serve the file. // Serve the file.
$mimeType = mime_content_type($filePath); $mimeType = mime_content_type($realFilePath);
header("Content-Type: " . $mimeType); header("Content-Type: " . $mimeType);
// Determine extension and set disposition accordingly. // Set Content-Disposition based on file type.
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION)); $ext = strtolower(pathinfo($realFilePath, PATHINFO_EXTENSION));
if (in_array($ext, ['jpg','jpeg','png','gif','bmp','webp','svg','ico'])) { 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 { } 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; exit;
?> ?>