Compare commits

..

11 Commits

Author SHA1 Message Date
Ryan
a2d678ee19 1.0.7 2025-04-04 18:18:19 -04:00
Ryan
da62e70c02 mitigate path traversal vulnerability by validating folder and file inputs 2025-04-04 18:02:21 -04:00
Ryan
f19d30f58a demo.filerise.net 2025-04-04 16:16:21 -04:00
Ryan
a8202adbec demo.filerise.net 2025-04-04 16:16:01 -04:00
Ryan
5dc58ffa42 loadCsrfTokenWithRetry 2025-04-04 02:29:27 -04:00
Ryan
f4f700ecda Chain Initialization After CSRF Token Is Loaded 2025-04-04 02:13:00 -04:00
Ryan
94178775d5 loadUserPermissions cleanup 2025-04-04 01:58:36 -04:00
Ryan
1d3f731483 fix UserPermission missing function. 2025-04-03 23:24:43 -04:00
Ryan
6926d5b065 userPermissions issue fixed 2025-04-03 22:06:49 -04:00
Ryan
46e9761cae Add click event listener to the “oidcLoginBtn” 2025-04-03 21:43:32 -04:00
Ryan
fa828f5dea new image 2025-04-03 20:13:41 -04:00
16 changed files with 186 additions and 135 deletions

View File

@@ -1,5 +1,10 @@
# FileRise - Elevate your File Management # FileRise - Elevate your File Management
**Demo link:** https://demo.filerise.net
**UserName:** demo
**Password:** demo
Read only permissions but can view the interface.
**4/3/2025 Video demo:** **4/3/2025 Video demo:**
https://github.com/user-attachments/assets/221f6a53-85f5-48d4-9abe-89445e0af90e https://github.com/user-attachments/assets/221f6a53-85f5-48d4-9abe-89445e0af90e

View File

@@ -400,6 +400,13 @@ document.addEventListener("DOMContentLoaded", function () {
disableBasicAuth: localStorage.getItem("disableBasicAuth") === "true", disableBasicAuth: localStorage.getItem("disableBasicAuth") === "true",
disableOIDCLogin: localStorage.getItem("disableOIDCLogin") === "true" disableOIDCLogin: localStorage.getItem("disableOIDCLogin") === "true"
}); });
const oidcLoginBtn = document.getElementById("oidcLoginBtn");
if (oidcLoginBtn) {
oidcLoginBtn.addEventListener("click", () => {
// Redirect to the OIDC auth endpoint. The endpoint can be adjusted if needed.
window.location.href = "auth.php?oidc=initiate";
});
}
}); });
export { initAuth, checkAuthentication }; export { initAuth, checkAuthentication };

View File

@@ -1,7 +1,7 @@
import { showToast, toggleVisibility } from './domUtils.js'; import { showToast, toggleVisibility } from './domUtils.js';
import { sendRequest } from './networkUtils.js'; import { sendRequest } from './networkUtils.js';
const version = "v1.0.6"; const version = "v1.0.7";
const adminTitle = `Admin Panel <small style="font-size: 12px; color: gray;">${version}</small>`; const adminTitle = `Admin Panel <small style="font-size: 12px; color: gray;">${version}</small>`;
let lastLoginData = null; let lastLoginData = null;

View File

@@ -55,7 +55,7 @@ if (!$encryptionKey) {
function loadUserPermissions($username) function loadUserPermissions($username)
{ {
global $encryptionKey; // Ensure $encryptionKey is available global $encryptionKey;
$permissionsFile = USERS_DIR . 'userPermissions.json'; $permissionsFile = USERS_DIR . 'userPermissions.json';
if (file_exists($permissionsFile)) { if (file_exists($permissionsFile)) {
@@ -69,21 +69,12 @@ function loadUserPermissions($username)
$permissions = json_decode($content, true); $permissions = json_decode($content, true);
} }
if (!is_array($permissions)) {
} else {
}
if (is_array($permissions) && array_key_exists($username, $permissions)) { if (is_array($permissions) && array_key_exists($username, $permissions)) {
$result = $permissions[$username]; $result = $permissions[$username];
if (empty($result)) { return !empty($result) ? $result : false;
return false;
}
return $result;
} else {
} }
} else {
error_log("loadUserPermissions: Permissions file not found: $permissionsFile");
} }
// Removed error_log() to prevent flooding logs when file is not found.
return false; // Return false if no permissions found. return false; // Return false if no permissions found.
} }
@@ -132,7 +123,7 @@ if (!isset($_SESSION["authenticated"]) && isset($_COOKIE['remember_me_token']))
$_SESSION["authenticated"] = true; $_SESSION["authenticated"] = true;
$_SESSION["username"] = $tokenData["username"]; $_SESSION["username"] = $tokenData["username"];
// IMPORTANT: Set the folderOnly flag here for auto-login. // IMPORTANT: Set the folderOnly flag here for auto-login.
$_SESSION["folderOnly"] = loadFolderPermission($tokenData["username"]); $_SESSION["folderOnly"] = loadUserPermissions($tokenData["username"]);
} else { } else {
unset($persistentTokens[$_COOKIE['remember_me_token']]); unset($persistentTokens[$_COOKIE['remember_me_token']]);
$newEncryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey); $newEncryptedContent = encryptData(json_encode($persistentTokens, JSON_PRETTY_PRINT), $encryptionKey);

View File

@@ -18,9 +18,8 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -23,9 +23,9 @@ if ($receivedToken !== $_SESSION['csrf_token']) {
http_response_code(403); http_response_code(403);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -23,9 +23,9 @@ if ($receivedToken !== $_SESSION['csrf_token']) {
http_response_code(403); http_response_code(403);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -1,8 +1,6 @@
<?php <?php
require_once 'config.php'; require_once 'config.php';
// For GET requests (which download.php will use), we assume session authentication is enough.
// Check if the user is authenticated. // Check if the user is authenticated.
if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) { if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
@@ -22,38 +20,70 @@ if (!preg_match('/^[A-Za-z0-9_\-\.\(\) ]+$/', $file)) {
exit; exit;
} }
// Determine the directory. // Get the realpath of the upload directory.
if ($folder !== 'root') { $uploadDirReal = realpath(UPLOAD_DIR);
$directory = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $folder . DIRECTORY_SEPARATOR; if ($uploadDirReal === false) {
} else { http_response_code(500);
$directory = UPLOAD_DIR; echo json_encode(["error" => "Server misconfiguration."]);
exit;
} }
$filePath = $directory . $file; // Determine the directory.
if ($folder === 'root') {
$directory = $uploadDirReal;
} else {
// Prevent path traversal in folder parameter.
if (strpos($folder, '..') !== false) {
http_response_code(400);
echo json_encode(["error" => "Invalid folder name."]);
exit;
}
if (!file_exists($filePath)) { $directoryPath = rtrim(UPLOAD_DIR, '/\\') . DIRECTORY_SEPARATOR . $folder;
$directory = realpath($directoryPath);
// Ensure that the resolved directory exists and is within the allowed UPLOAD_DIR.
if ($directory === false || strpos($directory, $uploadDirReal) !== 0) {
http_response_code(400);
echo json_encode(["error" => "Invalid folder path."]);
exit;
}
}
// Build the file path.
$filePath = $directory . DIRECTORY_SEPARATOR . $file;
$realFilePath = realpath($filePath);
// Validate that the real file path exists and is within the allowed directory.
if ($realFilePath === false || strpos($realFilePath, $uploadDirReal) !== 0) {
http_response_code(403);
echo json_encode(["error" => "Access forbidden."]);
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);
// For images, serve inline; for other types, force download. // For images, serve inline; for other types, force download.
$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) . '"');
} }
header('Content-Length: ' . filesize($filePath)); header('Content-Length: ' . filesize($realFilePath));
// Disable caching. // Disable caching.
header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache'); header('Pragma: no-cache');
readfile($filePath); readfile($realFilePath);
exit; exit;
?> ?>

View File

@@ -17,9 +17,9 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
echo json_encode(["error" => "Unauthorized"]); echo json_encode(["error" => "Unauthorized"]);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

199
main.js
View File

@@ -17,12 +17,17 @@ import { loadFolderTree } from './folderManager.js';
import { initUpload } from './upload.js'; import { initUpload } from './upload.js';
import { initAuth, checkAuthentication } from './auth.js'; import { initAuth, checkAuthentication } from './auth.js';
import { setupTrashRestoreDelete } from './trashRestoreDelete.js'; import { setupTrashRestoreDelete } from './trashRestoreDelete.js';
import { initDragAndDrop, loadSidebarOrder, loadHeaderOrder } from './dragAndDrop.js' import { initDragAndDrop, loadSidebarOrder, loadHeaderOrder } from './dragAndDrop.js';
import { initTagSearch, openTagModal, filterFilesByTag } from './fileTags.js'; import { initTagSearch, openTagModal, filterFilesByTag } from './fileTags.js';
function loadCsrfToken() { function loadCsrfTokenWithRetry(retries = 3, delay = 1000) {
fetch('token.php', { credentials: 'include' }) return fetch('token.php', { credentials: 'include' })
.then(response => response.json()) .then(response => {
if (!response.ok) {
throw new Error("Token fetch failed with status: " + response.status);
}
return response.json();
})
.then(data => { .then(data => {
// Set global variables. // Set global variables.
window.csrfToken = data.csrf_token; window.csrfToken = data.csrf_token;
@@ -45,11 +50,19 @@ function loadCsrfToken() {
document.head.appendChild(metaShare); document.head.appendChild(metaShare);
} }
metaShare.setAttribute('content', data.share_url); metaShare.setAttribute('content', data.share_url);
})
.catch(error => console.error("Error loading CSRF token and share URL:", error));
}
document.addEventListener("DOMContentLoaded", loadCsrfToken); return data;
})
.catch(error => {
if (retries > 0) {
console.warn(`CSRF token load failed. Retrying in ${delay}ms... (${retries} retries left)`, error);
return new Promise(resolve => setTimeout(resolve, delay))
.then(() => loadCsrfTokenWithRetry(retries - 1, delay * 2));
}
console.error("Failed to load CSRF token after retries.", error);
throw error;
});
}
// Expose functions for inline handlers. // Expose functions for inline handlers.
window.sendRequest = sendRequest; window.sendRequest = sendRequest;
@@ -63,96 +76,104 @@ window.renameFile = renameFile;
window.currentFolder = "root"; window.currentFolder = "root";
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
// Call initAuth synchronously. // First, load the CSRF token (with retry).
initAuth(); loadCsrfTokenWithRetry().then(() => {
// Once CSRF token is loaded, initialize authentication.
initAuth();
const newPasswordInput = document.getElementById("newPassword"); // Continue with initializations that rely on a valid CSRF token:
if (newPasswordInput) { checkAuthentication().then(authenticated => {
newPasswordInput.addEventListener("input", function() { if (authenticated) {
console.log("newPassword input event:", this.value); window.currentFolder = "root";
}); initTagSearch();
} else { loadFileList(window.currentFolder);
console.error("newPassword input not found!"); initDragAndDrop();
} loadSidebarOrder();
// --- Dark Mode Persistence --- loadHeaderOrder();
const darkModeToggle = document.getElementById("darkModeToggle"); initFileActions();
const storedDarkMode = localStorage.getItem("darkMode"); initUpload();
loadFolderTree();
setupTrashRestoreDelete();
if (storedDarkMode === "true") { const helpBtn = document.getElementById("folderHelpBtn");
document.body.classList.add("dark-mode"); const helpTooltip = document.getElementById("folderHelpTooltip");
} else if (storedDarkMode === "false") { helpBtn.addEventListener("click", function () {
document.body.classList.remove("dark-mode"); // Toggle display of the tooltip.
} else { if (helpTooltip.style.display === "none" || helpTooltip.style.display === "") {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) { helpTooltip.style.display = "block";
document.body.classList.add("dark-mode"); } else {
} else { helpTooltip.style.display = "none";
document.body.classList.remove("dark-mode"); }
} });
}
if (darkModeToggle) {
darkModeToggle.textContent = document.body.classList.contains("dark-mode")
? "Light Mode"
: "Dark Mode";
darkModeToggle.addEventListener("click", function () {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
localStorage.setItem("darkMode", "false");
darkModeToggle.textContent = "Dark Mode";
} else { } else {
document.body.classList.add("dark-mode"); console.warn("User not authenticated. Data loading deferred.");
localStorage.setItem("darkMode", "true");
darkModeToggle.textContent = "Light Mode";
} }
}); });
}
if (localStorage.getItem("darkMode") === null && window.matchMedia) { // Other DOM initialization that can happen after CSRF is ready.
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => { const newPasswordInput = document.getElementById("newPassword");
if (event.matches) { if (newPasswordInput) {
document.body.classList.add("dark-mode"); newPasswordInput.addEventListener("input", function() {
if (darkModeToggle) darkModeToggle.textContent = "Light Mode"; console.log("newPassword input event:", this.value);
} else {
document.body.classList.remove("dark-mode");
if (darkModeToggle) darkModeToggle.textContent = "Dark Mode";
}
});
}
// --- End Dark Mode Persistence ---
const message = sessionStorage.getItem("welcomeMessage");
if (message) {
showToast(message);
sessionStorage.removeItem("welcomeMessage");
}
checkAuthentication().then(authenticated => {
if (authenticated) {
window.currentFolder = "root";
initTagSearch();
loadFileList(window.currentFolder);
initDragAndDrop();
loadSidebarOrder();
loadHeaderOrder();
initFileActions();
initUpload();
loadFolderTree();
setupTrashRestoreDelete();
const helpBtn = document.getElementById("folderHelpBtn");
const helpTooltip = document.getElementById("folderHelpTooltip");
helpBtn.addEventListener("click", function () {
// Toggle display of the tooltip.
if (helpTooltip.style.display === "none" || helpTooltip.style.display === "") {
helpTooltip.style.display = "block";
} else {
helpTooltip.style.display = "none";
}
}); });
} else { } else {
console.warn("User not authenticated. Data loading deferred."); console.error("newPassword input not found!");
} }
// --- Dark Mode Persistence ---
const darkModeToggle = document.getElementById("darkModeToggle");
const storedDarkMode = localStorage.getItem("darkMode");
if (storedDarkMode === "true") {
document.body.classList.add("dark-mode");
} else if (storedDarkMode === "false") {
document.body.classList.remove("dark-mode");
} else {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.body.classList.add("dark-mode");
} else {
document.body.classList.remove("dark-mode");
}
}
if (darkModeToggle) {
darkModeToggle.textContent = document.body.classList.contains("dark-mode")
? "Light Mode"
: "Dark Mode";
darkModeToggle.addEventListener("click", function () {
if (document.body.classList.contains("dark-mode")) {
document.body.classList.remove("dark-mode");
localStorage.setItem("darkMode", "false");
darkModeToggle.textContent = "Dark Mode";
} else {
document.body.classList.add("dark-mode");
localStorage.setItem("darkMode", "true");
darkModeToggle.textContent = "Light Mode";
}
});
}
if (localStorage.getItem("darkMode") === null && window.matchMedia) {
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (event) => {
if (event.matches) {
document.body.classList.add("dark-mode");
if (darkModeToggle) darkModeToggle.textContent = "Light Mode";
} else {
document.body.classList.remove("dark-mode");
if (darkModeToggle) darkModeToggle.textContent = "Dark Mode";
}
});
}
// --- End Dark Mode Persistence ---
const message = sessionStorage.getItem("welcomeMessage");
if (message) {
showToast(message);
sessionStorage.removeItem("welcomeMessage");
}
}).catch(error => {
console.error("Initialization halted due to CSRF token load failure.", error);
}); });
// --- Auto-scroll During Drag --- // --- Auto-scroll During Drag ---

View File

@@ -20,9 +20,8 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -21,9 +21,9 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -27,9 +27,8 @@ if ($receivedToken !== $_SESSION['csrf_token']) {
http_response_code(403); http_response_code(403);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 459 KiB

After

Width:  |  Height:  |  Size: 499 KiB

View File

@@ -18,9 +18,8 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
// Check if the user is read-only. (Assuming that if readOnly is true, deletion is disallowed.)
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) { if (isset($userPermissions['readOnly']) && $userPermissions['readOnly'] === true) {

View File

@@ -18,8 +18,9 @@ if (!isset($_SESSION['authenticated']) || $_SESSION['authenticated'] !== true) {
http_response_code(401); http_response_code(401);
exit; exit;
} }
$userPermissions = loadUserPermissions($username);
$username = $_SESSION['username'] ?? ''; $username = $_SESSION['username'] ?? '';
$userPermissions = loadUserPermissions($username);
if ($username) { if ($username) {
$userPermissions = loadUserPermissions($username); $userPermissions = loadUserPermissions($username);
if (isset($userPermissions['disableUpload']) && $userPermissions['disableUpload'] === true) { if (isset($userPermissions['disableUpload']) && $userPermissions['disableUpload'] === true) {