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..77911aff32 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);
+ } elseif ($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..5a90608c9b 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 @@
-