From fdc357cbc236fc1570857196b80f6e016e20a0de Mon Sep 17 00:00:00 2001 From: danloa Date: Fri, 6 Dec 2024 16:16:22 -0400 Subject: [PATCH 1/6] =?UTF-8?q?Store=20and=20verify=20if=20session=20is=20?= =?UTF-8?q?active=20in=20Cach=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Http/Controllers/Auth/LoginController.php | 9 +++-- ProcessMaker/Http/Kernel.php | 1 + .../Http/Middleware/VerifyActiveSession.php | 33 +++++++++++++++++++ routes/api.php | 2 +- 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 ProcessMaker/Http/Middleware/VerifyActiveSession.php diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 190475ec1b..93ffa79261 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -214,6 +214,8 @@ public function loginWithIntendedCheck(Request $request) } } + Cache::put('user_'.$user->id.'_active_session', true); + return $this->login($request, $user); } @@ -249,6 +251,7 @@ 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', false, now()->addHour()); // Clear the user session $this->forgetUserSession(); @@ -261,12 +264,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..b052554c9b --- /dev/null +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -0,0 +1,33 @@ +cookie('laravel_token'); + + if ($cookie) { + $user = \Auth::user(); + $isActive = Cache::get('user_'.$user->id.'_active_session', true); + if (!$isActive) { + return response()->json(['error' => 'Unauthorized'], 401); + } + } + + return $next($request); + } +} 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 From d381d18e6ffac464127fd56139597227ff4bdaed Mon Sep 17 00:00:00 2001 From: danloa Date: Fri, 6 Dec 2024 17:32:08 -0400 Subject: [PATCH 2/6] Re-arrange the code --- .../Http/Controllers/Auth/LoginController.php | 7 +++++-- .../Http/Middleware/VerifyActiveSession.php | 18 ++++++++++++++---- ProcessMaker/Listeners/LoginListener.php | 8 +++++++- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 93ffa79261..55a201fc21 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -214,7 +214,6 @@ public function loginWithIntendedCheck(Request $request) } } - Cache::put('user_'.$user->id.'_active_session', true); return $this->login($request, $user); } @@ -251,7 +250,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', false, now()->addHour()); + Cache::put( + 'user_' . $userId . '_active_session', + ['active' => false, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); // Clear the user session $this->forgetUserSession(); diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php index b052554c9b..3cfa15960f 100644 --- a/ProcessMaker/Http/Middleware/VerifyActiveSession.php +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -18,14 +18,24 @@ class VerifyActiveSession */ public function handle(Request $request, Closure $next): Response { - $cookie = $request->cookie('laravel_token'); - - if ($cookie) { + if (!$request->hasHeader('Authorization')) { $user = \Auth::user(); - $isActive = Cache::get('user_'.$user->id.'_active_session', true); + $activeSession = Cache::get('user_' . $user->id . '_active_session'); + $isActive = $activeSession ? $activeSession['active'] : true; if (!$isActive) { return response()->json(['error' => 'Unauthorized'], 401); } + else { + $lastActivity = $activeSession['updated_at']; + // refresh the cache key lifetime + if (now()->diffInMinutes($lastActivity) > config('session.lifetime') / 2) { + Cache::put( + 'user_' . $user->id . '_active_session', + ['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..22f60364ff 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 { /** @@ -32,5 +32,11 @@ public function handle(Login $event): void $user->setAttribute('loggedin_at', now()); $user->save(); + + Cache::put( + 'user_' . $user->id . '_active_session', + ['active' => true, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); } } From 516c89e4fd59055684349c5b6b683cd59c884109 Mon Sep 17 00:00:00 2001 From: danloa Date: Fri, 6 Dec 2024 19:52:55 -0400 Subject: [PATCH 3/6] Handle the case when the active session cache does not exist --- ProcessMaker/Http/Middleware/VerifyActiveSession.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php index 3cfa15960f..9045b1613c 100644 --- a/ProcessMaker/Http/Middleware/VerifyActiveSession.php +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -26,7 +26,7 @@ public function handle(Request $request, Closure $next): Response return response()->json(['error' => 'Unauthorized'], 401); } else { - $lastActivity = $activeSession['updated_at']; + $lastActivity = $activeSession ? $activeSession['updated_at'] : now(); // refresh the cache key lifetime if (now()->diffInMinutes($lastActivity) > config('session.lifetime') / 2) { Cache::put( From c38814e4bef2b6e190c2c13fb988775916343084 Mon Sep 17 00:00:00 2001 From: danloa Date: Mon, 9 Dec 2024 10:52:28 -0400 Subject: [PATCH 4/6] Include the user device when tracking user sessions --- ProcessMaker/Http/Controllers/Auth/LoginController.php | 7 ++++++- ProcessMaker/Http/Middleware/VerifyActiveSession.php | 6 +++--- ProcessMaker/Listeners/LoginListener.php | 6 ------ 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index 55a201fc21..ea36aba6b1 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -214,6 +214,11 @@ public function loginWithIntendedCheck(Request $request) } } + Cache::put( + 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'), + ['active' => true, 'updated_at' => now()], + now()->addMinutes(config('session.lifetime')) + ); return $this->login($request, $user); } @@ -251,7 +256,7 @@ public function beforeLogout(Request $request) Cache::forget("user_{$userId}_permissions"); Cache::forget("user_{$userId}_project_assets"); Cache::put( - 'user_' . $userId . '_active_session', + 'user_' . $userId . '_active_session_' . $request->cookie('device_id'), ['active' => false, 'updated_at' => now()], now()->addMinutes(config('session.lifetime')) ); diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php index 9045b1613c..a8e76077ef 100644 --- a/ProcessMaker/Http/Middleware/VerifyActiveSession.php +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -20,17 +20,17 @@ public function handle(Request $request, Closure $next): Response { if (!$request->hasHeader('Authorization')) { $user = \Auth::user(); - $activeSession = Cache::get('user_' . $user->id . '_active_session'); + $activeSession = Cache::get('user_' . $user->id . '_active_session_' . $request->cookie('device_id')); $isActive = $activeSession ? $activeSession['active'] : true; if (!$isActive) { return response()->json(['error' => 'Unauthorized'], 401); } else { $lastActivity = $activeSession ? $activeSession['updated_at'] : now(); - // refresh the cache key lifetime + // refresh the cache entry's lifetime if (now()->diffInMinutes($lastActivity) > config('session.lifetime') / 2) { Cache::put( - 'user_' . $user->id . '_active_session', + 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'), ['active' => true, 'updated_at' => now()], now()->addMinutes(config('session.lifetime')) ); diff --git a/ProcessMaker/Listeners/LoginListener.php b/ProcessMaker/Listeners/LoginListener.php index 22f60364ff..ba31cc7b2b 100644 --- a/ProcessMaker/Listeners/LoginListener.php +++ b/ProcessMaker/Listeners/LoginListener.php @@ -32,11 +32,5 @@ public function handle(Login $event): void $user->setAttribute('loggedin_at', now()); $user->save(); - - Cache::put( - 'user_' . $user->id . '_active_session', - ['active' => true, 'updated_at' => now()], - now()->addMinutes(config('session.lifetime')) - ); } } From d652eea29481bc5416bff920d393f10027f875c2 Mon Sep 17 00:00:00 2001 From: danloa Date: Mon, 9 Dec 2024 11:12:28 -0400 Subject: [PATCH 5/6] Initialize session activity control when the session is started --- ProcessMaker/Events/SessionStarted.php | 9 +++++++++ ProcessMaker/Http/Controllers/Auth/LoginController.php | 6 ------ ProcessMaker/Http/Middleware/VerifyActiveSession.php | 8 +++++--- 3 files changed, 14 insertions(+), 9 deletions(-) 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 ea36aba6b1..0fd8d21fec 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -214,12 +214,6 @@ public function loginWithIntendedCheck(Request $request) } } - Cache::put( - 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'), - ['active' => true, 'updated_at' => now()], - now()->addMinutes(config('session.lifetime')) - ); - return $this->login($request, $user); } diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php index a8e76077ef..8e40af9125 100644 --- a/ProcessMaker/Http/Middleware/VerifyActiveSession.php +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -20,8 +20,10 @@ public function handle(Request $request, Closure $next): Response { if (!$request->hasHeader('Authorization')) { $user = \Auth::user(); - $activeSession = Cache::get('user_' . $user->id . '_active_session_' . $request->cookie('device_id')); - $isActive = $activeSession ? $activeSession['active'] : true; + $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); } @@ -30,7 +32,7 @@ public function handle(Request $request, Closure $next): Response // refresh the cache entry's lifetime if (now()->diffInMinutes($lastActivity) > config('session.lifetime') / 2) { Cache::put( - 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'), + $cacheKey, ['active' => true, 'updated_at' => now()], now()->addMinutes(config('session.lifetime')) ); From ddacf0ec02ca1f9b9df850fe37f9e69bdc1e4fc8 Mon Sep 17 00:00:00 2001 From: danloa Date: Mon, 9 Dec 2024 13:48:00 -0400 Subject: [PATCH 6/6] Verify session liftime if using auth. token in cookie --- ProcessMaker/Http/Middleware/VerifyActiveSession.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ProcessMaker/Http/Middleware/VerifyActiveSession.php b/ProcessMaker/Http/Middleware/VerifyActiveSession.php index 8e40af9125..77cce0fb79 100644 --- a/ProcessMaker/Http/Middleware/VerifyActiveSession.php +++ b/ProcessMaker/Http/Middleware/VerifyActiveSession.php @@ -18,10 +18,11 @@ class VerifyActiveSession */ public function handle(Request $request, Closure $next): Response { - if (!$request->hasHeader('Authorization')) { + // if the authorization is via token in cookie + if (!$request->hasHeader('Authorization') && $request->hasCookie('laravel_token')) { $user = \Auth::user(); - $cacheKey = 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'); + $cacheKey = 'user_' . $user->id . '_active_session_' . $request->cookie('device_id'); $activeSession = Cache::get($cacheKey); $isActive = $activeSession ? $activeSession['active'] : false; if (!$isActive) {