From 4e1b0549682e484b46e6e8a81a9117869ab9b3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Busso?= <90727999+agustinbusso@users.noreply.github.com> Date: Mon, 23 Mar 2026 17:58:13 -0300 Subject: [PATCH] Prevent access for BLOCKED/INACTIVE users Add middleware to block authenticated users whose status is BLOCKED or INACTIVE. Introduces EnsureAccountAllowsAccess middleware (with blockingResponseForRequest and denyAccess helpers) that logs out the user, invalidates the session, and returns a JSON 401 for API requests or redirects to login with appropriate error messages for web requests. Wire the middleware into the HTTP kernel and update ProcessMakerAuthenticate to invoke the same blocking check after successful authentication so auth:api routes (core and packages) are also enforced. --- ProcessMaker/Http/Kernel.php | 1 + .../Middleware/EnsureAccountAllowsAccess.php | 76 +++++++++++++++++++ .../Middleware/ProcessMakerAuthenticate.php | 18 +++++ 3 files changed, 95 insertions(+) create mode 100644 ProcessMaker/Http/Middleware/EnsureAccountAllowsAccess.php diff --git a/ProcessMaker/Http/Kernel.php b/ProcessMaker/Http/Kernel.php index e3f9e8a0f5..86254692f8 100644 --- a/ProcessMaker/Http/Kernel.php +++ b/ProcessMaker/Http/Kernel.php @@ -42,6 +42,7 @@ class Kernel extends HttpKernel \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, Middleware\SessionStarted::class, Middleware\AuthenticateSession::class, + Middleware\EnsureAccountAllowsAccess::class, Middleware\SessionControlKill::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, //\ProcessMaker\Http\Middleware\VerifyCsrfToken::class, diff --git a/ProcessMaker/Http/Middleware/EnsureAccountAllowsAccess.php b/ProcessMaker/Http/Middleware/EnsureAccountAllowsAccess.php new file mode 100644 index 0000000000..5cedec09a4 --- /dev/null +++ b/ProcessMaker/Http/Middleware/EnsureAccountAllowsAccess.php @@ -0,0 +1,76 @@ +check()) { + continue; + } + + /** @var User $user */ + $user = Auth::guard($guard)->user(); + if (!$user instanceof User || !in_array($user->status, self::DENIED_STATUSES, true)) { + continue; + } + + return self::denyAccess($request, $guard, $user); + } + + return null; + } + + public function handle(Request $request, Closure $next): Response + { + $blocked = self::blockingResponseForRequest($request); + if ($blocked !== null) { + return $blocked; + } + + return $next($request); + } + + public static function denyAccess(Request $request, string $guard, User $user): Response + { + Auth::guard($guard)->logout(); + + if ($request->hasSession()) { + $request->session()->invalidate(); + $request->session()->regenerateToken(); + } + + if ($guard === 'api' || $request->expectsJson()) { + $message = $user->status === 'BLOCKED' + ? __('Account locked after too many failed attempts. Contact administrator.') + : __('Unauthorized'); + + return response()->json(['message' => $message], 401); + } + + return redirect() + ->guest(route('login')) + ->withErrors([ + 'username' => $user->status === 'BLOCKED' + ? __('Account locked after too many failed attempts. Contact administrator.') + : __('These credentials do not match our records.'), + ]); + } +} diff --git a/ProcessMaker/Http/Middleware/ProcessMakerAuthenticate.php b/ProcessMaker/Http/Middleware/ProcessMakerAuthenticate.php index 43e882090b..b64c13812e 100644 --- a/ProcessMaker/Http/Middleware/ProcessMakerAuthenticate.php +++ b/ProcessMaker/Http/Middleware/ProcessMakerAuthenticate.php @@ -2,11 +2,29 @@ namespace ProcessMaker\Http\Middleware; +use Closure; use Illuminate\Auth\Middleware\Authenticate; use Illuminate\Support\Str; class ProcessMakerAuthenticate extends Authenticate { + /** + * {@inheritdoc} + * + * After a successful authenticate(), reject BLOCKED/INACTIVE users so every auth:api route + * (core and packages) is covered without listing middleware per route file. + */ + public function handle($request, Closure $next, ...$guards) + { + $this->authenticate($request, $guards); + + if ($blocked = EnsureAccountAllowsAccess::blockingResponseForRequest($request)) { + return $blocked; + } + + return $next($request); + } + protected function authenticate($request, array $guards) { $this->addAcceptJsonHeaderIfApiCall($request, $guards);