Compare commits
9 Commits
b6420391e1
...
16854f0f77
Author | SHA1 | Date |
---|---|---|
|
16854f0f77 | |
|
582b5492fe | |
|
101f4c539a | |
|
522cded113 | |
|
f77e15bf44 | |
|
dbdbe1bf49 | |
|
d3f0c90272 | |
|
566b16190e | |
|
5281102e36 |
|
@ -35,6 +35,10 @@ class Feedback {
|
|||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'SESSION_TIMEOUT' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => true
|
||||
],
|
||||
'IP_BLACKLISTED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Session Class
|
||||
*
|
||||
* Core session management functionality for the application
|
||||
*/
|
||||
class Session {
|
||||
private static $sessionOptions = [
|
||||
'cookie_httponly' => 1,
|
||||
'cookie_secure' => 1,
|
||||
'cookie_samesite' => 'Strict',
|
||||
'gc_maxlifetime' => 7200 // 2 hours
|
||||
];
|
||||
|
||||
/**
|
||||
* Start or resume a session with secure options
|
||||
*/
|
||||
public static function startSession() {
|
||||
session_name('jilo');
|
||||
if (session_status() !== PHP_SESSION_ACTIVE && !headers_sent()) {
|
||||
session_start(self::$sessionOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy current session and clean up
|
||||
*/
|
||||
public static function destroySession() {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current username if set
|
||||
*/
|
||||
public static function getUsername() {
|
||||
return isset($_SESSION['username']) ? htmlspecialchars($_SESSION['username']) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current user ID if set
|
||||
*/
|
||||
public static function getUserId() {
|
||||
return isset($_SESSION['user_id']) ? (int)$_SESSION['user_id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current session is valid
|
||||
*/
|
||||
public static function isValidSession() {
|
||||
// Check required session variables
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check session timeout
|
||||
$session_timeout = isset($_SESSION['REMEMBER_ME']) ? (30 * 24 * 60 * 60) : 7200; // 30 days or 2 hours
|
||||
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $session_timeout)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update last activity time
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
|
||||
// Regenerate session ID periodically (every 30 minutes)
|
||||
if (!isset($_SESSION['CREATED'])) {
|
||||
$_SESSION['CREATED'] = time();
|
||||
} else if (time() - $_SESSION['CREATED'] > 1800) {
|
||||
// Regenerate session ID and update creation time
|
||||
if (!headers_sent() && session_status() === PHP_SESSION_ACTIVE) {
|
||||
$oldData = $_SESSION;
|
||||
session_regenerate_id(true);
|
||||
$_SESSION = $oldData;
|
||||
$_SESSION['CREATED'] = time();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set remember me option for extended session
|
||||
*/
|
||||
public static function setRememberMe($value = true) {
|
||||
$_SESSION['REMEMBER_ME'] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear session data and cookies
|
||||
*/
|
||||
public static function cleanup($config) {
|
||||
self::destroySession();
|
||||
|
||||
// Clear cookies if headers not sent
|
||||
if (!headers_sent()) {
|
||||
setcookie('username', '', [
|
||||
'expires' => time() - 3600,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
}
|
||||
|
||||
// Start fresh session
|
||||
self::startSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new authenticated session for a user
|
||||
*/
|
||||
public static function createAuthSession($userId, $username, $rememberMe, $config) {
|
||||
// Set cookie lifetime based on remember me
|
||||
$cookieLifetime = $rememberMe ? time() + (30 * 24 * 60 * 60) : 0;
|
||||
|
||||
// Regenerate session ID to prevent session fixation
|
||||
session_regenerate_id(true);
|
||||
|
||||
// Set cookie with secure options
|
||||
setcookie('username', $username, [
|
||||
'expires' => $cookieLifetime,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
|
||||
// Set session variables
|
||||
$_SESSION['user_id'] = $userId;
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
if ($rememberMe) {
|
||||
self::setRememberMe(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store 2FA pending information in session
|
||||
*/
|
||||
public static function store2FAPending($userId, $username, $rememberMe = false) {
|
||||
$_SESSION['2fa_pending_user_id'] = $userId;
|
||||
$_SESSION['2fa_pending_username'] = $username;
|
||||
if ($rememberMe) {
|
||||
$_SESSION['2fa_pending_remember'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear 2FA pending information from session
|
||||
*/
|
||||
public static function clear2FAPending() {
|
||||
unset($_SESSION['2fa_pending_user_id']);
|
||||
unset($_SESSION['2fa_pending_username']);
|
||||
unset($_SESSION['2fa_pending_remember']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get 2FA pending information
|
||||
*/
|
||||
public static function get2FAPending() {
|
||||
if (!isset($_SESSION['2fa_pending_user_id']) || !isset($_SESSION['2fa_pending_username'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'user_id' => $_SESSION['2fa_pending_user_id'],
|
||||
'username' => $_SESSION['2fa_pending_username'],
|
||||
'remember_me' => isset($_SESSION['2fa_pending_remember'])
|
||||
];
|
||||
}
|
||||
}
|
|
@ -4,94 +4,29 @@
|
|||
* Session Middleware
|
||||
*
|
||||
* Validates session status and handles session timeout.
|
||||
* This middleware should be included in all protected pages.
|
||||
* If session is invalid, redirects to login page.
|
||||
*/
|
||||
|
||||
function applySessionMiddleware($config, $app_root) {
|
||||
$isTest = defined('PHPUNIT_RUNNING');
|
||||
|
||||
// Access $_SESSION directly in test mode
|
||||
if (!$isTest) {
|
||||
function applySessionMiddleware($config, $app_root, $isTest = false) {
|
||||
// Start session if not already started
|
||||
if (session_status() !== PHP_SESSION_ACTIVE && !headers_sent()) {
|
||||
session_start([
|
||||
'cookie_httponly' => 1,
|
||||
'cookie_secure' => 1,
|
||||
'cookie_samesite' => 'Strict',
|
||||
'gc_maxlifetime' => 7200 // 2 hours
|
||||
]);
|
||||
}
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
Session::startSession();
|
||||
}
|
||||
|
||||
// Check if user is logged in with all required session variables
|
||||
if (!isset($_SESSION['user_id']) || !isset($_SESSION['username'])) {
|
||||
cleanupSession($config, $app_root, $isTest);
|
||||
// Check session validity
|
||||
if (!Session::isValidSession()) {
|
||||
// Session invalid, clean up and redirect
|
||||
Session::cleanup($config);
|
||||
|
||||
// Flash session timeout message
|
||||
Feedback::flash('LOGIN', 'SESSION_TIMEOUT');
|
||||
|
||||
if (!$isTest) {
|
||||
header('Location: ' . $app_root . '?page=login');
|
||||
exit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check session timeout
|
||||
$session_timeout = isset($_SESSION['REMEMBER_ME']) ? (30 * 24 * 60 * 60) : 7200; // 30 days or 2 hours
|
||||
if (isset($_SESSION['LAST_ACTIVITY']) && (time() - $_SESSION['LAST_ACTIVITY'] > $session_timeout)) {
|
||||
// Session has expired
|
||||
cleanupSession($config, $app_root, $isTest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update last activity time
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
|
||||
// Regenerate session ID periodically (every 30 minutes)
|
||||
if (!isset($_SESSION['CREATED'])) {
|
||||
$_SESSION['CREATED'] = time();
|
||||
} else if (time() - $_SESSION['CREATED'] > 1800) {
|
||||
// Regenerate session ID and update creation time
|
||||
if (!$isTest && !headers_sent() && session_status() === PHP_SESSION_ACTIVE) {
|
||||
$oldData = $_SESSION;
|
||||
session_regenerate_id(true);
|
||||
$_SESSION = $oldData;
|
||||
$_SESSION['CREATED'] = time();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to clean up session data and redirect
|
||||
*/
|
||||
function cleanupSession($config, $app_root, $isTest) {
|
||||
// Always clear session data
|
||||
$_SESSION = array();
|
||||
|
||||
if (!$isTest) {
|
||||
if (session_status() === PHP_SESSION_ACTIVE) {
|
||||
session_unset();
|
||||
session_destroy();
|
||||
|
||||
// Start a new session to prevent errors
|
||||
if (!headers_sent()) {
|
||||
session_start([
|
||||
'cookie_httponly' => 1,
|
||||
'cookie_secure' => 1,
|
||||
'cookie_samesite' => 'Strict',
|
||||
'gc_maxlifetime' => 7200
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear cookies
|
||||
if (!headers_sent()) {
|
||||
setcookie('username', '', [
|
||||
'expires' => time() - 3600,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
}
|
||||
|
||||
header('Location: ' . $app_root . '?page=login&timeout=1');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ return [
|
|||
'LOGIN_SUCCESS' => 'Login successful.',
|
||||
'LOGIN_FAILED' => 'Login failed. Please check your credentials.',
|
||||
'LOGOUT_SUCCESS' => 'Logout successful. You can log in again.',
|
||||
'SESSION_TIMEOUT' => 'Your session has expired. Please log in again.',
|
||||
'IP_BLACKLISTED' => 'Access denied. Your IP address is blacklisted.',
|
||||
'IP_NOT_WHITELISTED' => 'Access denied. Your IP address is not whitelisted.',
|
||||
'TOO_MANY_ATTEMPTS' => 'Too many login attempts. Please try again later.',
|
||||
|
|
|
@ -14,12 +14,6 @@
|
|||
* - `password`: Change password
|
||||
*/
|
||||
|
||||
// Check if user is logged in
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
header("Location: $app_root?page=login");
|
||||
exit();
|
||||
}
|
||||
|
||||
$user_id = $_SESSION['user_id'];
|
||||
|
||||
// Initialize user object
|
||||
|
|
|
@ -33,21 +33,23 @@ try {
|
|||
if ($action === 'verify' && isset($_SESSION['2fa_pending_user_id'])) {
|
||||
// Handle 2FA verification
|
||||
$code = $_POST['code'] ?? '';
|
||||
$userId = $_SESSION['2fa_pending_user_id'];
|
||||
$username = $_SESSION['2fa_pending_username'];
|
||||
$rememberMe = isset($_SESSION['2fa_pending_remember']);
|
||||
$pending2FA = Session::get2FAPending();
|
||||
|
||||
if (!$pending2FA) {
|
||||
header('Location: ' . htmlspecialchars($app_root) . '?page=login');
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once '../app/classes/twoFactorAuth.php';
|
||||
$twoFactorAuth = new TwoFactorAuthentication($db);
|
||||
|
||||
if ($twoFactorAuth->verify($userId, $code)) {
|
||||
if ($twoFactorAuth->verify($pending2FA['user_id'], $code)) {
|
||||
// Complete login
|
||||
handleSuccessfulLogin($userId, $username, $rememberMe, $config, $logObject, $user_IP);
|
||||
handleSuccessfulLogin($pending2FA['user_id'], $pending2FA['username'],
|
||||
$pending2FA['remember_me'], $config, $logObject, $user_IP);
|
||||
|
||||
// Clean up 2FA session data
|
||||
unset($_SESSION['2fa_pending_user_id']);
|
||||
unset($_SESSION['2fa_pending_username']);
|
||||
unset($_SESSION['2fa_pending_remember']);
|
||||
Session::clear2FAPending();
|
||||
|
||||
exit();
|
||||
}
|
||||
|
@ -232,11 +234,8 @@ try {
|
|||
switch ($loginResult['status']) {
|
||||
case 'requires_2fa':
|
||||
// Store pending 2FA info
|
||||
$_SESSION['2fa_pending_user_id'] = $loginResult['user_id'];
|
||||
$_SESSION['2fa_pending_username'] = $loginResult['username'];
|
||||
if (isset($formData['remember_me'])) {
|
||||
$_SESSION['2fa_pending_remember'] = true;
|
||||
}
|
||||
Session::store2FAPending($loginResult['user_id'], $loginResult['username'],
|
||||
isset($formData['remember_me']));
|
||||
|
||||
// Redirect to 2FA verification
|
||||
header('Location: ?page=login&action=verify');
|
||||
|
@ -282,36 +281,8 @@ include '../app/templates/form-login.php';
|
|||
* Handle successful login by setting up session and cookies
|
||||
*/
|
||||
function handleSuccessfulLogin($userId, $username, $rememberMe, $config, $logObject, $userIP) {
|
||||
if ($rememberMe) {
|
||||
// 30*24*60*60 = 30 days
|
||||
$cookie_lifetime = 30 * 24 * 60 * 60;
|
||||
$setcookie_lifetime = time() + 30 * 24 * 60 * 60;
|
||||
} else {
|
||||
// 0 - session end on browser close
|
||||
$cookie_lifetime = 0;
|
||||
$setcookie_lifetime = 0;
|
||||
}
|
||||
|
||||
// Regenerate session ID to prevent session fixation
|
||||
session_regenerate_id(true);
|
||||
|
||||
// set session lifetime and cookies
|
||||
setcookie('username', $username, [
|
||||
'expires' => $setcookie_lifetime,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
|
||||
// Set session variables
|
||||
$_SESSION['user_id'] = $userId;
|
||||
$_SESSION['USERNAME'] = $username;
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
if ($rememberMe) {
|
||||
$_SESSION['REMEMBER_ME'] = true;
|
||||
}
|
||||
// Create authenticated session
|
||||
Session::createAuthSession($userId, $username, $rememberMe, $config);
|
||||
|
||||
// Log successful login
|
||||
$logObject->insertLog($userId, "Login: User \"$username\" logged in. IP: $userIP", 'user');
|
||||
|
|
|
@ -9,11 +9,6 @@ if (!($userObject->hasRight($user_id, 'superuser') ||
|
|||
exit;
|
||||
}
|
||||
|
||||
if (!isset($currentUser)) {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get current section
|
||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
<?php if (isset($currentUser) && $page !== 'logout') { ?>
|
||||
<?php if (Session::getUsername() && $page !== 'logout') { ?>
|
||||
<script src="static/js/sidebar.js"></script>
|
||||
<?php } ?>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/main.css">
|
||||
<link rel="stylesheet" type="text/css" href="<?= htmlspecialchars($app_root) ?>static/css/messages.css">
|
||||
<script src="<?= htmlspecialchars($app_root) ?>static/js/messages.js"></script>
|
||||
<?php if (isset($currentUser)) { ?>
|
||||
<?php if (Session::getUsername()) { ?>
|
||||
<script>
|
||||
// restore sidebar state before the page is rendered
|
||||
(function () {
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
version <?= htmlspecialchars($config['version']) ?>
|
||||
</li>
|
||||
|
||||
<?php if (isset($_SESSION['username']) && isset($_SESSION['user_id'])) { ?>
|
||||
<?php if (Session::isValidSession()) { ?>
|
||||
|
||||
<?php foreach ($platformsAll as $platform) {
|
||||
$platform_switch_url = switchPlatform($platform['id']);
|
||||
|
@ -40,7 +40,7 @@
|
|||
</ul>
|
||||
|
||||
<ul class="menu-right">
|
||||
<?php if (isset($_SESSION['username']) && isset($_SESSION['user_id'])) { ?>
|
||||
<?php if (Session::isValidSession()) { ?>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
|
||||
<i class="fas fa-user"></i>
|
||||
|
|
|
@ -6,11 +6,14 @@
|
|||
* $totalPages - Total number of pages
|
||||
*/
|
||||
|
||||
// Ensure required variables are set
|
||||
// Validate required pagination variables
|
||||
if (!isset($currentPage) || !isset($totalPages)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure valid values
|
||||
$currentPage = max(1, min($currentPage, $totalPages));
|
||||
|
||||
// Number of page links to show before and after current page
|
||||
$range = 2;
|
||||
?>
|
||||
|
|
|
@ -15,14 +15,18 @@
|
|||
// flush it later only when there is no redirect
|
||||
ob_start();
|
||||
|
||||
// Start session before any session-dependent code
|
||||
require_once '../app/classes/session.php';
|
||||
Session::startSession();
|
||||
|
||||
// Apply security headers
|
||||
require_once '../app/includes/security_headers_middleware.php';
|
||||
|
||||
// sanitize all input vars that may end up in URLs or forms
|
||||
require '../app/includes/sanitize.php';
|
||||
|
||||
session_name('jilo');
|
||||
session_start();
|
||||
// Check session validity
|
||||
$validSession = Session::isValidSession();
|
||||
|
||||
// Initialize feedback message system
|
||||
require_once '../app/classes/feedback.php';
|
||||
|
@ -64,6 +68,8 @@ $allowed_urls = [
|
|||
'login',
|
||||
'logout',
|
||||
'register',
|
||||
|
||||
'about',
|
||||
];
|
||||
|
||||
// cnfig file
|
||||
|
@ -92,17 +98,26 @@ if ($config_file) {
|
|||
|
||||
$app_root = $config['folder'];
|
||||
|
||||
// check if logged in
|
||||
unset($currentUser);
|
||||
if (isset($_COOKIE['username'])) {
|
||||
if ( !isset($_SESSION['username']) ) {
|
||||
$_SESSION['username'] = $_COOKIE['username'];
|
||||
}
|
||||
$currentUser = htmlspecialchars($_SESSION['username']);
|
||||
// List of pages that don't require authentication
|
||||
$public_pages = ['login', 'register', 'help', 'about'];
|
||||
|
||||
// Check if the requested page requires authentication
|
||||
if (!isset($_COOKIE['username']) && !$validSession && !in_array($page, $public_pages)) {
|
||||
require_once '../app/includes/session_middleware.php';
|
||||
applySessionMiddleware($config, $app_root);
|
||||
}
|
||||
|
||||
// redirect to login
|
||||
if ( !isset($_COOKIE['username']) && ($page !== 'login' && $page !== 'register') ) {
|
||||
// Check session and redirect if needed
|
||||
$currentUser = null;
|
||||
if ($validSession) {
|
||||
$currentUser = Session::getUsername();
|
||||
} else if (isset($_COOKIE['username']) && !in_array($page, $public_pages)) {
|
||||
// Cookie exists but session is invalid - redirect to login
|
||||
Feedback::flash('LOGIN', 'SESSION_TIMEOUT');
|
||||
header('Location: ' . htmlspecialchars($app_root) . '?page=login&timeout=1');
|
||||
exit();
|
||||
} else if (!in_array($page, $public_pages)) {
|
||||
// No valid session or cookie, and not a public page
|
||||
header('Location: ' . htmlspecialchars($app_root) . '?page=login');
|
||||
exit();
|
||||
}
|
||||
|
@ -164,11 +179,10 @@ if ($page == 'logout') {
|
|||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||
|
||||
// clean up session
|
||||
session_unset();
|
||||
session_destroy();
|
||||
Session::destroySession();
|
||||
|
||||
// start new session for the login page
|
||||
session_start();
|
||||
Session::startSession();
|
||||
|
||||
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
|
||||
|
||||
|
@ -186,8 +200,7 @@ if ($page == 'logout') {
|
|||
} else {
|
||||
|
||||
// if user is logged in, we need user details and rights
|
||||
if (isset($currentUser)) {
|
||||
|
||||
if ($validSession) {
|
||||
// If by error a logged in user requests the login page
|
||||
if ($page === 'login') {
|
||||
header('Location: ' . htmlspecialchars($app_root));
|
||||
|
@ -211,18 +224,10 @@ if ($page == 'logout') {
|
|||
}
|
||||
}
|
||||
|
||||
// List of pages that don't require authentication
|
||||
$public_pages = ['login', 'register'];
|
||||
|
||||
// Check if the requested page requires authentication
|
||||
if (!in_array($page, $public_pages)) {
|
||||
require_once '../app/includes/session_middleware.php';
|
||||
}
|
||||
|
||||
// page building
|
||||
include '../app/templates/page-header.php';
|
||||
include '../app/templates/page-menu.php';
|
||||
if (isset($currentUser)) {
|
||||
if ($validSession) {
|
||||
include '../app/templates/page-sidebar.php';
|
||||
}
|
||||
if (in_array($page, $allowed_urls)) {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Middleware\Mock;
|
||||
|
||||
class Feedback {
|
||||
public static function flash($type, $message) {}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Middleware\Mock;
|
||||
|
||||
class Session {
|
||||
public static function startSession() {}
|
||||
|
||||
public static function isValidSession() {
|
||||
return isset($_SESSION["user_id"]) &&
|
||||
isset($_SESSION["username"]) &&
|
||||
(!isset($_SESSION["LAST_ACTIVITY"]) ||
|
||||
$_SESSION["LAST_ACTIVITY"] > time() - 7200 ||
|
||||
isset($_SESSION["REMEMBER_ME"]));
|
||||
}
|
||||
|
||||
public static function cleanup($config) {
|
||||
$_SESSION = [];
|
||||
}
|
||||
}
|
|
@ -1,8 +1,11 @@
|
|||
<?php
|
||||
|
||||
require_once dirname(__DIR__, 3) . '/app/includes/session_middleware.php';
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Tests\Feature\Middleware\Mock\Session;
|
||||
use Tests\Feature\Middleware\Mock\Feedback;
|
||||
|
||||
require_once __DIR__ . '/MockSession.php';
|
||||
require_once __DIR__ . '/MockFeedback.php';
|
||||
|
||||
class SessionMiddlewareTest extends TestCase
|
||||
{
|
||||
|
@ -38,11 +41,24 @@ class SessionMiddlewareTest extends TestCase
|
|||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
public function testSessionStart()
|
||||
protected function applyMiddleware()
|
||||
{
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
// Check session validity
|
||||
if (!Session::isValidSession()) {
|
||||
// Session invalid, clean up
|
||||
Session::cleanup($this->config);
|
||||
Feedback::flash("LOGIN", "SESSION_TIMEOUT");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function testValidSession()
|
||||
{
|
||||
$result = $this->applyMiddleware();
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->assertArrayHasKey('LAST_ACTIVITY', $_SESSION);
|
||||
|
@ -54,24 +70,10 @@ class SessionMiddlewareTest extends TestCase
|
|||
public function testSessionTimeout()
|
||||
{
|
||||
$_SESSION['LAST_ACTIVITY'] = time() - (self::SESSION_TIMEOUT + 60); // 2 hours + 1 minute ago
|
||||
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
$result = $this->applyMiddleware();
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertArrayNotHasKey('user_id', $_SESSION, 'Session should be cleared after timeout');
|
||||
}
|
||||
|
||||
public function testSessionRegeneration()
|
||||
{
|
||||
$now = time();
|
||||
$_SESSION['CREATED'] = $now - 1900; // 31+ minutes ago
|
||||
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->assertEquals(1, $_SESSION['user_id']);
|
||||
$this->assertGreaterThanOrEqual($now - 1900, $_SESSION['CREATED']);
|
||||
$this->assertLessThanOrEqual($now + 10, $_SESSION['CREATED']);
|
||||
$this->assertEmpty($_SESSION);
|
||||
}
|
||||
|
||||
public function testRememberMe()
|
||||
|
@ -79,7 +81,7 @@ class SessionMiddlewareTest extends TestCase
|
|||
$_SESSION['REMEMBER_ME'] = true;
|
||||
$_SESSION['LAST_ACTIVITY'] = time() - (self::SESSION_TIMEOUT + 60); // More than 2 hours ago
|
||||
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
$result = $this->applyMiddleware();
|
||||
|
||||
$this->assertTrue($result);
|
||||
$this->assertArrayHasKey('user_id', $_SESSION);
|
||||
|
@ -88,19 +90,19 @@ class SessionMiddlewareTest extends TestCase
|
|||
public function testNoUserSession()
|
||||
{
|
||||
unset($_SESSION['user_id']);
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
$result = $this->applyMiddleware();
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertArrayNotHasKey('user_id', $_SESSION);
|
||||
$this->assertEmpty($_SESSION);
|
||||
}
|
||||
|
||||
public function testSessionHeaders()
|
||||
public function testInvalidSession()
|
||||
{
|
||||
$_SESSION['LAST_ACTIVITY'] = time() - (self::SESSION_TIMEOUT + 60); // 2 hours + 1 minute ago
|
||||
|
||||
$result = applySessionMiddleware($this->config, $this->app_root);
|
||||
unset($_SESSION['REMEMBER_ME']);
|
||||
$result = $this->applyMiddleware();
|
||||
|
||||
$this->assertFalse($result);
|
||||
$this->assertArrayNotHasKey('user_id', $_SESSION, 'Session should be cleared after timeout');
|
||||
$this->assertEmpty($_SESSION);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Classes;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SessionTest extends TestCase
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
require_once __DIR__ . '/../../../app/classes/session.php';
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::tearDown();
|
||||
$_SESSION = [];
|
||||
}
|
||||
|
||||
public function testGetUsername()
|
||||
{
|
||||
$_SESSION['username'] = 'testuser';
|
||||
$this->assertEquals('testuser', \Session::getUsername());
|
||||
unset($_SESSION['username']);
|
||||
$this->assertNull(\Session::getUsername());
|
||||
}
|
||||
|
||||
public function testGetUserId()
|
||||
{
|
||||
$_SESSION['user_id'] = 123;
|
||||
$this->assertEquals(123, \Session::getUserId());
|
||||
unset($_SESSION['user_id']);
|
||||
$this->assertNull(\Session::getUserId());
|
||||
}
|
||||
|
||||
public function testIsValidSession()
|
||||
{
|
||||
// Invalid without required variables
|
||||
$this->assertFalse(\Session::isValidSession());
|
||||
|
||||
// Valid with required variables
|
||||
$_SESSION['user_id'] = 123;
|
||||
$_SESSION['username'] = 'testuser';
|
||||
$_SESSION['LAST_ACTIVITY'] = time();
|
||||
$this->assertTrue(\Session::isValidSession());
|
||||
|
||||
// Invalid after timeout
|
||||
$_SESSION['LAST_ACTIVITY'] = time() - 8000; // More than 2 hours
|
||||
$this->assertFalse(\Session::isValidSession());
|
||||
|
||||
// Valid with remember me
|
||||
$_SESSION = [
|
||||
'user_id' => 123,
|
||||
'username' => 'testuser',
|
||||
'REMEMBER_ME' => true,
|
||||
'LAST_ACTIVITY' => time() - 8000
|
||||
];
|
||||
$this->assertTrue(\Session::isValidSession());
|
||||
}
|
||||
|
||||
public function testSetRememberMe()
|
||||
{
|
||||
\Session::setRememberMe(true);
|
||||
$this->assertTrue($_SESSION['REMEMBER_ME']);
|
||||
\Session::setRememberMe(false);
|
||||
$this->assertFalse($_SESSION['REMEMBER_ME']);
|
||||
}
|
||||
|
||||
public function test2FASession()
|
||||
{
|
||||
// Test storing 2FA pending info
|
||||
\Session::store2FAPending(123, 'testuser', true);
|
||||
$this->assertEquals(123, $_SESSION['2fa_pending_user_id']);
|
||||
$this->assertEquals('testuser', $_SESSION['2fa_pending_username']);
|
||||
$this->assertTrue(isset($_SESSION['2fa_pending_remember']));
|
||||
|
||||
// Test getting 2FA pending info
|
||||
$pendingInfo = \Session::get2FAPending();
|
||||
$this->assertEquals([
|
||||
'user_id' => 123,
|
||||
'username' => 'testuser',
|
||||
'remember_me' => true
|
||||
], $pendingInfo);
|
||||
|
||||
// Test clearing 2FA pending info
|
||||
\Session::clear2FAPending();
|
||||
$this->assertNull(\Session::get2FAPending());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue