release(v2.0.4): harden sessions and align Pro paths with USERS_DIR
This commit is contained in:
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Changes 11/26/2025 (v2.0.4)
|
||||||
|
|
||||||
|
release(v2.0.4): harden sessions and align Pro paths with USERS_DIR
|
||||||
|
|
||||||
|
- Enable strict_types in config.php and AdminController
|
||||||
|
- Decouple PHP session lifetime from "remember me" window
|
||||||
|
- Regenerate session ID on persistent token auto-login
|
||||||
|
- Point Pro license / bundle paths at USERS_DIR instead of hardcoded /users
|
||||||
|
- Tweak folder management card drag offset for better alignment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Changes 11/26/2025 (v2.0.3)
|
## Changes 11/26/2025 (v2.0.3)
|
||||||
|
|
||||||
release(v2.0.3): polish uploads, header dock, and panel fly animations
|
release(v2.0.3): polish uploads, header dock, and panel fly animations
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
// config.php
|
// config.php
|
||||||
|
|
||||||
// Define constants
|
// Define constants
|
||||||
@@ -101,10 +102,15 @@ $secure = ($envSecure !== false)
|
|||||||
? filter_var($envSecure, FILTER_VALIDATE_BOOLEAN)
|
? filter_var($envSecure, FILTER_VALIDATE_BOOLEAN)
|
||||||
: (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
: (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off');
|
||||||
|
|
||||||
// Choose session lifetime based on "remember me" cookie
|
|
||||||
|
// PHP session lifetime (independent of "remember me")
|
||||||
|
// Keep this reasonably short; "remember me" uses its own token.
|
||||||
$defaultSession = 7200; // 2 hours
|
$defaultSession = 7200; // 2 hours
|
||||||
|
$sessionLifetime = $defaultSession;
|
||||||
|
|
||||||
|
// "Remember me" window (how long the persistent token itself is valid)
|
||||||
|
// This is used in persistent_tokens.json, *not* for PHP session lifetime.
|
||||||
$persistentDays = 30 * 24 * 60 * 60; // 30 days
|
$persistentDays = 30 * 24 * 60 * 60; // 30 days
|
||||||
$sessionLifetime = isset($_COOKIE['remember_me_token']) ? $persistentDays : $defaultSession;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start session idempotently:
|
* Start session idempotently:
|
||||||
@@ -155,6 +161,11 @@ if (empty($_SESSION["authenticated"]) && !empty($_COOKIE['remember_me_token']))
|
|||||||
if (!empty($tokens[$token])) {
|
if (!empty($tokens[$token])) {
|
||||||
$data = $tokens[$token];
|
$data = $tokens[$token];
|
||||||
if ($data['expiry'] >= time()) {
|
if ($data['expiry'] >= time()) {
|
||||||
|
// NEW: mitigate session fixation
|
||||||
|
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
}
|
||||||
|
|
||||||
$_SESSION["authenticated"] = true;
|
$_SESSION["authenticated"] = true;
|
||||||
$_SESSION["username"] = $data["username"];
|
$_SESSION["username"] = $data["username"];
|
||||||
$_SESSION["folderOnly"] = loadUserPermissions($data["username"]);
|
$_SESSION["folderOnly"] = loadUserPermissions($data["username"]);
|
||||||
@@ -162,7 +173,11 @@ if (empty($_SESSION["authenticated"]) && !empty($_COOKIE['remember_me_token']))
|
|||||||
} else {
|
} else {
|
||||||
// expired — clean up
|
// expired — clean up
|
||||||
unset($tokens[$token]);
|
unset($tokens[$token]);
|
||||||
file_put_contents($tokFile, encryptData(json_encode($tokens, JSON_PRETTY_PRINT), $encryptionKey), LOCK_EX);
|
file_put_contents(
|
||||||
|
$tokFile,
|
||||||
|
encryptData(json_encode($tokens, JSON_PRETTY_PRINT), $encryptionKey),
|
||||||
|
LOCK_EX
|
||||||
|
);
|
||||||
setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true);
|
setcookie('remember_me_token', '', time() - 3600, '/', '', $secure, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,14 +268,14 @@ if (!defined('FR_PRO_LICENSE')) {
|
|||||||
|
|
||||||
// JSON license file used by AdminController::setLicense()
|
// JSON license file used by AdminController::setLicense()
|
||||||
if (!defined('PRO_LICENSE_FILE')) {
|
if (!defined('PRO_LICENSE_FILE')) {
|
||||||
define('PRO_LICENSE_FILE', PROJECT_ROOT . '/users/proLicense.json');
|
define('PRO_LICENSE_FILE', rtrim(USERS_DIR, "/\\") . '/proLicense.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional plain-text license file (used as fallback in bootstrap)
|
// Optional plain-text license file (used as fallback in bootstrap)
|
||||||
if (!defined('FR_PRO_LICENSE_FILE')) {
|
if (!defined('FR_PRO_LICENSE_FILE')) {
|
||||||
$lf = getenv('FR_PRO_LICENSE_FILE');
|
$lf = getenv('FR_PRO_LICENSE_FILE');
|
||||||
if ($lf === false || $lf === '') {
|
if ($lf === false || $lf === '') {
|
||||||
$lf = PROJECT_ROOT . '/users/proLicense.txt';
|
$lf = rtrim(USERS_DIR, "/\\") . '/proLicense.txt';
|
||||||
}
|
}
|
||||||
define('FR_PRO_LICENSE_FILE', $lf);
|
define('FR_PRO_LICENSE_FILE', $lf);
|
||||||
}
|
}
|
||||||
@@ -268,7 +283,7 @@ if (!defined('FR_PRO_LICENSE_FILE')) {
|
|||||||
// Where Pro code lives by default → inside users volume
|
// Where Pro code lives by default → inside users volume
|
||||||
$proDir = getenv('FR_PRO_BUNDLE_DIR');
|
$proDir = getenv('FR_PRO_BUNDLE_DIR');
|
||||||
if ($proDir === false || $proDir === '') {
|
if ($proDir === false || $proDir === '') {
|
||||||
$proDir = PROJECT_ROOT . '/users/pro';
|
$proDir = rtrim(USERS_DIR, "/\\") . '/pro';
|
||||||
}
|
}
|
||||||
$proDir = rtrim($proDir, "/\\");
|
$proDir = rtrim($proDir, "/\\");
|
||||||
if (!defined('FR_PRO_BUNDLE_DIR')) {
|
if (!defined('FR_PRO_BUNDLE_DIR')) {
|
||||||
|
|||||||
@@ -524,7 +524,7 @@ function animateCardsOutOfHeaderThen(done) {
|
|||||||
if (card.id === 'uploadCard') {
|
if (card.id === 'uploadCard') {
|
||||||
toCy -= 48; // a bit higher
|
toCy -= 48; // a bit higher
|
||||||
} else if (card.id === 'folderManagementCard') {
|
} else if (card.id === 'folderManagementCard') {
|
||||||
toCy += 60; // a bit lower
|
toCy += 48; // a bit lower
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
// src/controllers/AdminController.php
|
// src/controllers/AdminController.php
|
||||||
|
|
||||||
require_once __DIR__ . '/../../config/config.php';
|
require_once __DIR__ . '/../../config/config.php';
|
||||||
@@ -241,7 +242,7 @@ public function setLicense(): void
|
|||||||
// Store license + updatedAt in JSON file
|
// Store license + updatedAt in JSON file
|
||||||
if (!defined('PRO_LICENSE_FILE')) {
|
if (!defined('PRO_LICENSE_FILE')) {
|
||||||
// Fallback if constant not defined for some reason
|
// Fallback if constant not defined for some reason
|
||||||
define('PRO_LICENSE_FILE', PROJECT_ROOT . '/users/proLicense.json');
|
define('PRO_LICENSE_FILE', rtrim(USERS_DIR, "/\\") . '/proLicense.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
$payload = [
|
$payload = [
|
||||||
@@ -566,10 +567,11 @@ public function installProBundle(): void
|
|||||||
|
|
||||||
$projectRoot = rtrim(PROJECT_ROOT, DIRECTORY_SEPARATOR);
|
$projectRoot = rtrim(PROJECT_ROOT, DIRECTORY_SEPARATOR);
|
||||||
|
|
||||||
// Where Pro bundle code lives (defaults to PROJECT_ROOT . '/users/pro')
|
// Where Pro bundle code lives (defaults to USERS_DIR . '/pro')
|
||||||
|
$projectRoot = rtrim(PROJECT_ROOT, DIRECTORY_SEPARATOR);
|
||||||
$bundleRoot = defined('FR_PRO_BUNDLE_DIR')
|
$bundleRoot = defined('FR_PRO_BUNDLE_DIR')
|
||||||
? rtrim(FR_PRO_BUNDLE_DIR, DIRECTORY_SEPARATOR)
|
? rtrim(FR_PRO_BUNDLE_DIR, DIRECTORY_SEPARATOR)
|
||||||
: ($projectRoot . DIRECTORY_SEPARATOR . 'users' . DIRECTORY_SEPARATOR . 'pro');
|
: (rtrim(USERS_DIR, "/\\") . DIRECTORY_SEPARATOR . 'pro');
|
||||||
|
|
||||||
// Put README-Pro.txt / LICENSE-Pro.txt inside the bundle dir as well
|
// Put README-Pro.txt / LICENSE-Pro.txt inside the bundle dir as well
|
||||||
$proDocsDir = $bundleRoot;
|
$proDocsDir = $bundleRoot;
|
||||||
|
|||||||
Reference in New Issue
Block a user