From a7de9979ff836402d51bc4266afc75f4313ab912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Cesar=20Laura=20Avenda=C3=B1o?= Date: Tue, 5 Dec 2023 17:52:44 +0000 Subject: [PATCH 1/2] FOUR-11419 Block the user when has X incorrect attempts --- .../Http/Controllers/Admin/UserController.php | 1 + .../Http/Controllers/Auth/LoginController.php | 61 ++++++++++++++++++- ProcessMaker/Models/User.php | 4 +- .../admin/users/components/UsersListing.vue | 5 +- resources/lang/de.json | 4 +- resources/lang/en.json | 2 + resources/lang/es.json | 4 +- resources/lang/fr.json | 4 +- storage/api-docs/api-docs.json | 3 +- 9 files changed, 79 insertions(+), 9 deletions(-) diff --git a/ProcessMaker/Http/Controllers/Admin/UserController.php b/ProcessMaker/Http/Controllers/Admin/UserController.php index e2ae6619dc..f1e0d6b00d 100644 --- a/ProcessMaker/Http/Controllers/Admin/UserController.php +++ b/ProcessMaker/Http/Controllers/Admin/UserController.php @@ -56,6 +56,7 @@ public function edit(User $user) $status = [ ['value' => 'ACTIVE', 'text' => __('Active')], ['value' => 'INACTIVE', 'text' => __('Inactive')], + ['value' => 'BLOCKED', 'text' => __('Blocked')], ]; $timezones = array_reduce( diff --git a/ProcessMaker/Http/Controllers/Auth/LoginController.php b/ProcessMaker/Http/Controllers/Auth/LoginController.php index e87517c8d1..530b150e3f 100644 --- a/ProcessMaker/Http/Controllers/Auth/LoginController.php +++ b/ProcessMaker/Http/Controllers/Auth/LoginController.php @@ -7,6 +7,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cookie; +use Illuminate\Validation\ValidationException; use ProcessMaker\Events\Logout; use ProcessMaker\Http\Controllers\Controller; use ProcessMaker\Managers\LoginManager; @@ -177,6 +178,8 @@ public function loginWithIntendedCheck(Request $request) $user = User::where('username', $request->input('username'))->first(); if (!$user || $user->status === 'INACTIVE') { $this->sendFailedLoginResponse($request); + } else if ($user->status === 'BLOCKED') { + $this->throwLockedLoginResponse(); } $addons = $this->getPluginAddons('command', []); @@ -194,7 +197,7 @@ public function loginWithIntendedCheck(Request $request) } } - return $this->login($request); + return $this->login($request, $user); } /** @@ -237,4 +240,60 @@ public function loggedOut(Request $request) return $response; } + + /** + * Handle a login request to the application. + * Overrides the original login action. + * + * @param \Illuminate\Http\Request $request + * @param \ProcessMaker\Models\User $user + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse + * + * @throws \Illuminate\Validation\ValidationException + */ + public function login(Request $request, User $user) + { + $this->validateLogin($request); + + // If the class is using the ThrottlesLogins trait, we can automatically throttle + // the login attempts for this application. We'll key this by the username and + // the IP address of the client making these requests into this application. + if (method_exists($this, 'hasTooManyLoginAttempts') && + $this->hasTooManyLoginAttempts($request)) { + + // Block the user + $user->status = 'BLOCKED'; + $user->save(); + + // Throw locked error message + $this->throwLockedLoginResponse(); + } + + if ($this->attemptLogin($request)) { + if ($request->hasSession()) { + $request->session()->put('auth.password_confirmed_at', time()); + } + + return $this->sendLoginResponse($request); + } + + // If the login attempt was unsuccessful we will increment the number of attempts + // to login and redirect the user back to the login form. Of course, when this + // user surpasses their maximum number of attempts they will get locked out. + $this->incrementLoginAttempts($request); + + return $this->sendFailedLoginResponse($request); + } + + /** + * Throws locked error message + * + * @throws \Illuminate\Validation\ValidationException + */ + protected function throwLockedLoginResponse() + { + throw ValidationException::withMessages([ + $this->username() => [_('Account locked after too many failed attempts. Contact administrator.')], + ]); + } } diff --git a/ProcessMaker/Models/User.php b/ProcessMaker/Models/User.php index e7da2d9334..17a57b13d4 100644 --- a/ProcessMaker/Models/User.php +++ b/ProcessMaker/Models/User.php @@ -73,7 +73,7 @@ class User extends Authenticatable implements HasMedia * @OA\Property(property="expires_at", type="string"), * @OA\Property(property="loggedin_at", type="string"), * @OA\Property(property="remember_token", type="string"), - * @OA\Property(property="status", type="string", enum={"ACTIVE", "INACTIVE", "SCHEDULED", "OUT_OF_OFFICE"}), + * @OA\Property(property="status", type="string", enum={"ACTIVE", "INACTIVE", "SCHEDULED", "OUT_OF_OFFICE", "BLOCKED"}), * @OA\Property(property="fullname", type="string"), * @OA\Property(property="avatar", type="string"), * @OA\Property(property="media", type="array", @OA\Items(ref="#/components/schemas/media")), @@ -170,7 +170,7 @@ public static function rules(self $existing = null) 'phone' /*******/ => ['nullable', 'regex:/^[+\.0-9x\)\(\-\s\/]*$/'], 'fax' /*********/ => ['nullable', 'regex:/^[+\.0-9x\)\(\-\s\/]*$/'], 'cell' /********/ => ['nullable', 'regex:/^[+\.0-9x\)\(\-\s\/]*$/'], - 'status' /******/ => ['required', 'in:ACTIVE,INACTIVE,OUT_OF_OFFICE,SCHEDULED'], + 'status' /******/ => ['required', 'in:ACTIVE,INACTIVE,OUT_OF_OFFICE,SCHEDULED,BLOCKED'], 'password' /****/ => static::passwordRules($existing), ]; } diff --git a/resources/js/admin/users/components/UsersListing.vue b/resources/js/admin/users/components/UsersListing.vue index 73c36ce6d8..313966ada7 100644 --- a/resources/js/admin/users/components/UsersListing.vue +++ b/resources/js/admin/users/components/UsersListing.vue @@ -27,7 +27,7 @@