diff --git a/ProcessMaker/Events/SessionStarted.php b/ProcessMaker/Events/SessionStarted.php index c55631656c..e6f6ec69a2 100644 --- a/ProcessMaker/Events/SessionStarted.php +++ b/ProcessMaker/Events/SessionStarted.php @@ -3,6 +3,7 @@ namespace ProcessMaker\Events; use Illuminate\Broadcasting\Channel; +use Illuminate\Support\Facades\Cache; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Broadcasting\PresenceChannel; use Illuminate\Broadcasting\PrivateChannel; @@ -60,6 +61,14 @@ public function broadcastWith() ? 'Number.MAX_SAFE_INTEGER' : config('session.lifetime'); + + // Initialize the session activity control + Cache::put( + 'user_' . $this->user->id . '_active_session_' . RequestDevice::getId(), + ['active' => true, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); + return [ 'lifetime' => $lifetime, 'device_id' => RequestDevice::getId(), diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 190475ec1b..0fd8d21fec 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -249,6 +249,11 @@ public function beforeLogout(Request $request) $userId = Auth::user()->id; Cache::forget("user_{$userId}_permissions"); Cache::forget("user_{$userId}_project_assets"); + Cache::put( + 'user_' . $userId . '_active_session_' . $request->cookie('device_id'), + ['active' => false, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); // Clear the user session $this->forgetUserSession(); @@ -261,12 +266,12 @@ public function beforeLogout(Request $request) // Notify to listeners (package-auth, security logger) $eventResult = event(new Logout(Auth::user())); - // Perform the logout operation - $this->logout($request); - // Remove the Laravel cookie Cookie::queue(Cookie::forget(Passport::cookie())); + // Perform the logout operation + $this->logout($request); + // process any redirects generated by the logout event listeners foreach ($eventResult as $result) { if (is_array($result) && array_key_exists('redirectTo', $result)) { diff --git a/ProcessMaker/Http/Kernel.php b/ProcessMaker/Http/Kernel.php index 5fb8432f48..abbbdf070c 100644 --- a/ProcessMaker/Http/Kernel.php +++ b/ProcessMaker/Http/Kernel.php @@ -83,6 +83,7 @@ class Kernel extends HttpKernel 'session_kill' => Middleware\SessionControlKill::class, 'no-cache' => Middleware\NoCache::class, 'admin' => Middleware\IsAdmin::class, + 'verify_active_session' => Middleware\VerifyActiveSession::class, ]; /** diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php new file mode 100644 index 0000000000..77cce0fb79 --- /dev/null +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -0,0 +1,46 @@ +hasHeader('Authorization') && $request->hasCookie('laravel_token')) { + $user = \Auth::user(); + + $cacheKey = 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'); + $activeSession = Cache::get($cacheKey); + $isActive = $activeSession ? $activeSession['active'] : false; + if (!$isActive) { + return response()->json(['error' => 'Unauthorized'], 401); + } + else { + $lastActivity = $activeSession ? $activeSession['updated_at'] : now(); + // refresh the cache entry's lifetime + if (now()->diffInMinutes($lastActivity) > config('session.lifetime') / 2) { + Cache::put( + $cacheKey, + ['active' => true, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); + } + } + } + + return $next($request); + } +} diff --git a/ProcessMaker/Listeners/LoginListener.php b/ProcessMaker/Listeners/LoginListener.php index 3132fac87b..ba31cc7b2b 100644 --- a/ProcessMaker/Listeners/LoginListener.php +++ b/ProcessMaker/Listeners/LoginListener.php @@ -3,10 +3,10 @@ namespace ProcessMaker\Listeners; use Illuminate\Auth\Events\Login; +use Illuminate\Support\Facades\Cache; use ProcessMaker\Models\InboxRuleLog; use ProcessMaker\Models\User; use ProcessMaker\Notifications\InboxRulesNotification; - class LoginListener { /** diff --git a/routes/api.php b/routes/api.php index 947297d3de..0a5ef80c98 100644 --- a/routes/api.php +++ b/routes/api.php @@ -43,7 +43,7 @@ use ProcessMaker\Http\Controllers\Auth\TwoFactorAuthController; use ProcessMaker\Http\Controllers\TestStatusController; -Route::middleware('auth:api', 'setlocale', 'bindings', 'sanitize')->prefix('api/1.0')->name('api.')->group(function () { +Route::middleware('auth:api', 'setlocale', 'bindings', 'sanitize', 'verify_active_session')->prefix('api/1.0')->name('api.')->group(function () { // Users Route::get('users', [UserController::class, 'index'])->name('users.index'); // Permissions handled in the controller Route::get('users/{user}', [UserController::class, 'show'])->name('users.show'); // Permissions handled in the controller