176 lines
8.1 KiB
PHP
176 lines
8.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Config; // Import Config facade
|
|
use Illuminate\Support\Facades\Session;
|
|
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
|
|
|
class CheckProfileInactivity
|
|
{
|
|
/**
|
|
* Handle an incoming request.
|
|
*
|
|
* @param \Illuminate\Http\Request $request
|
|
* @param \Closure(\Illuminate\Http\Request): (\Illuminate\Http\Response|\Illuminate\Http\RedirectResponse) $next
|
|
* @return \Illuminate\Http\Response|\Illuminate\Http\RedirectResponse
|
|
*/
|
|
public function handle(Request $request, Closure $next)
|
|
{
|
|
// Only proceed if a user is authenticated
|
|
if (!Auth::check()) {
|
|
Session::forget('last_activity'); // Clear timestamp if user logs out
|
|
return $next($request); // Pass the request to the next middleware - route-level auth middleware will handle redirect
|
|
}
|
|
|
|
// When user is authenticated
|
|
$activeProfileType = Session::get('activeProfileType', \App\Models\User::class); // Default to User class if not set
|
|
|
|
// Initialize active profile if not set (happens after login)
|
|
if (!Session::has('activeProfileId')) {
|
|
$user = Auth::guard('web')->user();
|
|
if ($user) {
|
|
Session::put([
|
|
'activeProfileType' => \App\Models\User::class,
|
|
'activeProfileId' => $user->id,
|
|
'activeProfileName' => $user->name,
|
|
'activeProfilePhoto' => $user->profile_photo_path,
|
|
]);
|
|
}
|
|
}
|
|
|
|
$lastActivity = Session::get('last_activity');
|
|
|
|
// If last_activity is not set, initialize it and continue (first request after login)
|
|
if (!$lastActivity) {
|
|
Session::put('last_activity', now());
|
|
return $next($request);
|
|
}
|
|
|
|
// Check base session timeout first (this should never exceed SESSION_LIFETIME)
|
|
// This prevents profile-specific timeouts from keeping sessions alive beyond the base session lifetime
|
|
$baseSessionLifetime = Config::get('session.lifetime', 120); // minutes
|
|
$minutesSinceActivity = now()->diffInMinutes($lastActivity);
|
|
|
|
// If base session has expired, force logout regardless of profile type
|
|
if ($minutesSinceActivity >= $baseSessionLifetime) {
|
|
$user = Auth::guard('web')->user();
|
|
Auth::guard('web')->logout();
|
|
Session::invalidate();
|
|
Session::regenerateToken();
|
|
|
|
// For AJAX requests, return a JSON response indicating logout
|
|
if ($request->expectsJson() || $request->ajax()) {
|
|
return response()->json([
|
|
'message' => __('Your session timed out due to inactivity. Please log in again.'),
|
|
'action' => 'logout',
|
|
'redirect_url' => LaravelLocalization::localizeUrl('/login')
|
|
], 419); // 419 Authentication Timeout
|
|
} else {
|
|
// For standard browser requests, redirect to login
|
|
$loginUrl = LaravelLocalization::localizeUrl('/login');
|
|
return redirect()->to($loginUrl)
|
|
->with('warning', __('Your session timed out due to inactivity. Please log in again.'));
|
|
}
|
|
}
|
|
|
|
// Get the specific timeout for the current profile type, or use the default of 120 min.
|
|
$configuredTimeout = timebank_config('profile_timeouts.' . $activeProfileType, timebank_config('profile_timeout_default', 120));
|
|
|
|
// Profile-specific timeout cannot exceed base session lifetime
|
|
// This ensures elevated profiles timeout at or before the base session expiration
|
|
$timeoutMinutes = min($configuredTimeout, $baseSessionLifetime);
|
|
|
|
// Log when timeout is being capped for debugging purposes
|
|
if ($configuredTimeout > $baseSessionLifetime) {
|
|
\Log::info('Profile timeout capped by SESSION_LIFETIME', [
|
|
'profile_type' => class_basename($activeProfileType),
|
|
'configured_timeout' => $configuredTimeout,
|
|
'session_lifetime' => $baseSessionLifetime,
|
|
'effective_timeout' => $timeoutMinutes,
|
|
]);
|
|
}
|
|
|
|
// Check if the timestamp exists and if the inactivity period has passed
|
|
if ($minutesSinceActivity >= $timeoutMinutes) {
|
|
$user = Auth::guard('web')->user();
|
|
|
|
// Check if the current active profile is already the User profile
|
|
if ($activeProfileType === \App\Models\User::class) {
|
|
// If the active profile is User and their session expires, log them out
|
|
Auth::guard('web')->logout();
|
|
Session::invalidate();
|
|
Session::regenerateToken();
|
|
|
|
// For AJAX requests, return a JSON response indicating logout
|
|
if ($request->expectsJson() || $request->ajax()) {
|
|
return response()->json([
|
|
'message' => __('Your session timed out due to inactivity. Please log in again.'),
|
|
'action' => 'logout', // Indicate a full logout
|
|
'redirect_url' => LaravelLocalization::localizeUrl('/login') // Provide login URL
|
|
], 419); // 419 Authentication Timeout
|
|
} else {
|
|
// For standard browser requests, redirect to login
|
|
$loginUrl = LaravelLocalization::localizeUrl('/login');
|
|
return redirect()->to($loginUrl)
|
|
->with('warning', __('Your session timed out due to inactivity. Please log in again.'));
|
|
}
|
|
} else {
|
|
// If the active profile is an elevated profile (Bank, Admin, etc.), switch back to User profile
|
|
Session::put([
|
|
'activeProfileType' => \App\Models\User::class,
|
|
'activeProfileId' => $user->id,
|
|
'activeProfileName' => $user->name,
|
|
'activeProfilePhoto' => $user->profile_photo_path,
|
|
'profile-switched-notification' => true,
|
|
]);
|
|
|
|
session(['notification.alert' => 'Your previous profile session timed out due to inactivity.']); // Will become a translation key in notification component
|
|
|
|
Session::forget('last_activity'); // Clear the timestamp
|
|
|
|
event(new \App\Events\ProfileSwitchEvent($user));
|
|
|
|
// Check if the request expects JSON (common for AJAX) or is an AJAX request
|
|
if ($request->expectsJson() || $request->ajax()) {
|
|
session(['notification.alert' => 'Your previous profile session timed out due to inactivity.']); // Will become a translation key in notification component
|
|
|
|
// Get the user's locale from the session (set by localization middleware)
|
|
$userLocale = Session::get('locale', config('app.fallback_locale'));
|
|
// Use LaravelLocalization to get the full, localized URL
|
|
$redirectUrl = LaravelLocalization::getURLFromRouteNameTranslated($userLocale, 'routes.main');
|
|
Session::save();
|
|
|
|
// Return a JSON response indicating timeout, use 419 status code
|
|
return response()->json([
|
|
'message' => __('Your previous profile session timed out due to inactivity.'),
|
|
'action' => 'redirect', // Change action name to 'redirect'
|
|
'redirect_url' => $redirectUrl // <-- Add URL to payload
|
|
], 419); // 419 Authentication Timeout
|
|
} else {
|
|
// For standard browser requests, perform the redirect with flash message
|
|
$mainUrl = LaravelLocalization::localizeUrl('/main-page');
|
|
return redirect()->to($mainUrl)
|
|
->with('warning', __('Your previous profile session timed out due to inactivity.'));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Update the activity timestamp on relevant requests
|
|
// Exclude background heartbeats but allow user-initiated Livewire interactions
|
|
$isLivewireUpdate = $request->is(['*/livewire/update', 'livewire/update']);
|
|
$isUserInteraction = $isLivewireUpdate && $request->has('components');
|
|
|
|
if (!$request->is(['api/messenger/heartbeat']) && (!$isLivewireUpdate || $isUserInteraction)) {
|
|
Session::put('last_activity', now());
|
|
}
|
|
|
|
return $next($request); // Ensure the request is passed to the next middleware
|
|
}
|
|
|
|
}
|
|
|