share new password page, totp setup focus, logout clear session cookie
This commit is contained in:
@@ -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="">
|
||||||
|
|||||||
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
21
logout.php
21
logout.php
@@ -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;
|
||||||
?>
|
?>
|
||||||
82
share.php
82
share.php
@@ -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;
|
||||||
?>
|
?>
|
||||||
Reference in New Issue
Block a user