From 8fce75c52deffd54d5c59418b9f2a7326c2b6c76 Mon Sep 17 00:00:00 2001
From: Damien <damien.burel@maarch.org>
Date: Fri, 10 Apr 2020 18:01:42 +0200
Subject: [PATCH] FEAT #13667 TIME 1:35 Authentication locked and suspended
 informations

---
 .../controllers/AuthenticationController.php  | 66 +++++++++++--------
 src/core/models/AuthenticationModel.php       | 53 ---------------
 src/core/models/CoreConfigModel.php           | 14 +++-
 .../core/AuthenticationControllerTest.php     | 28 +++++---
 4 files changed, 70 insertions(+), 91 deletions(-)

diff --git a/src/core/controllers/AuthenticationController.php b/src/core/controllers/AuthenticationController.php
index d74d1cf7b7f..b601c560edd 100755
--- a/src/core/controllers/AuthenticationController.php
+++ b/src/core/controllers/AuthenticationController.php
@@ -38,13 +38,13 @@ class AuthenticationController
 
     public function getInformations(Request $request, Response $response)
     {
-//        $path = CoreConfigModel::getConfigPath();
-//        $hashedPath = md5($path);
+        $path = CoreConfigModel::getConfigPath();
+        $hashedPath = md5($path);
 
         $appName = CoreConfigModel::getApplicationName();
         $parameter = ParameterModel::getById(['id' => 'loginpage_message', 'select' => ['param_value_string']]);
 
-        return $response->withJson(['instanceId' => null, 'applicationName' => $appName, 'loginMessage' => $parameter['param_value_string'] ?? null]);
+        return $response->withJson(['instanceId' => $hashedPath, 'applicationName' => $appName, 'loginMessage' => $parameter['param_value_string'] ?? null]);
     }
 
     public static function authentication($authorizationHeaders = [])
@@ -129,39 +129,37 @@ class AuthenticationController
         return ['isRouteAvailable' => true];
     }
 
-    public static function handleFailedAuthentication(array $aArgs)
+    public static function handleFailedAuthentication(array $args)
     {
-        ValidatorModel::notEmpty($aArgs, ['userId']);
-        ValidatorModel::stringType($aArgs, ['userId']);
+        ValidatorModel::notEmpty($args, ['userId']);
+        ValidatorModel::intVal($args, ['userId']);
 
         $passwordRules = PasswordModel::getEnabledRules();
 
         if (!empty($passwordRules['lockAttempts'])) {
-            $user = UserModel::getByLowerLogin(['select' => ['failed_authentication', 'locked_until'], 'login' => $aArgs['userId']]);
-
-            if (!empty($user)) {
-                if (!empty($user['locked_until'])) {
-                    $lockedDate = new \DateTime($user['locked_until']);
-                    $currentDate = new \DateTime();
-                    if ($currentDate > $lockedDate) {
-                        AuthenticationModel::resetFailedAuthentication(['userId' => $aArgs['userId']]);
-                        $user['failed_authentication'] = 0;
-                    } else {
-                        return _ACCOUNT_LOCKED_UNTIL . " {$lockedDate->format('d/m/Y H:i')}";
-                    }
-                }
+            $user = UserModel::getById(['select' => ['failed_authentication', 'locked_until'], 'id' => $args['userId']]);
+            if (!empty($user['locked_until'])) {
+                return ['accountLocked' => true, 'lockedDate' => $user['locked_until']];
+            }
 
-                AuthenticationModel::increaseFailedAuthentication(['userId' => $aArgs['userId'], 'tentatives' => $user['failed_authentication'] + 1]);
+            UserModel::update([
+                'set'       => ['failed_authentication' => $user['failed_authentication'] + 1],
+                'where'     => ['id = ?'],
+                'data'      => [$args['userId']]
+            ]);
 
-                if (!empty($user['failed_authentication']) && ($user['failed_authentication'] + 1) >= $passwordRules['lockAttempts'] && !empty($passwordRules['lockTime'])) {
-                    $lockedUntil = time() + 60 * $passwordRules['lockTime'];
-                    AuthenticationModel::lockUser(['userId' => $aArgs['userId'], 'lockedUntil' => $lockedUntil]);
-                    return _ACCOUNT_LOCKED_FOR . " {$passwordRules['lockTime']} mn";
-                }
+            if (!empty($user['failed_authentication']) && ($user['failed_authentication'] + 1) >= $passwordRules['lockAttempts'] && !empty($passwordRules['lockTime'])) {
+                $lockedUntil = time() + 60 * $passwordRules['lockTime'];
+                UserModel::update([
+                    'set'   => ['locked_until'  => date('Y-m-d H:i:s', $lockedUntil)],
+                    'where'     => ['id = ?'],
+                    'data'      => [$args['userId']]
+                ]);
+                return ['accountLocked' => true];
             }
         }
 
-        return _BAD_LOGIN_OR_PSW;
+        return true;
     }
 
     public function authenticate(Request $request, Response $response)
@@ -177,7 +175,18 @@ class AuthenticationController
         $login = strtolower($body['login']);
         $authenticated = AuthenticationModel::authentication(['login' => $login, 'password' => $body['password']]);
         if (empty($authenticated)) {
-            return $response->withStatus(401)->withJson(['errors' => 'Authentication Failed']);
+            $user = UserModel::getByLogin(['login' => $login, 'select' => ['id', 'status']]);
+            if (empty($user)) {
+                return $response->withStatus(401)->withJson(['errors' => 'Authentication Failed']);
+            } elseif ($user['status'] == 'SPD') {
+                return $response->withStatus(401)->withJson(['errors' => 'Account Suspended']);
+            } else {
+                $handle = AuthenticationController::handleFailedAuthentication(['userId' => $user['id']]);
+                if (!empty($handle['accountLocked'])) {
+                    return $response->withStatus(401)->withJson(['errors' => 'Account Locked', 'date' => $handle['lockedDate'] ?? null]);
+                }
+                return $response->withStatus(401)->withJson(['errors' => 'Authentication Failed']);
+            }
         }
 
         $user = UserModel::getByLogin(['login' => $login, 'select' => ['id', 'loginmode', 'refresh_token', 'user_id']]);
@@ -204,10 +213,11 @@ class AuthenticationController
         $refreshToken = AuthenticationController::getRefreshJWT();
         $user['refresh_token'][] = $refreshToken;
         UserModel::update([
-            'set'   => ['reset_token' => null, 'refresh_token' => json_encode($user['refresh_token'])],
+            'set'   => ['reset_token' => null, 'refresh_token' => json_encode($user['refresh_token']), 'failed_authentication' => 0, 'locked_until' => null],
             'where' => ['id = ?'],
             'data'  => [$user['id']]
         ]);
+
         $response = $response->withHeader('Token', AuthenticationController::getJWT());
         $response = $response->withHeader('Refresh-Token', $refreshToken);
 
diff --git a/src/core/models/AuthenticationModel.php b/src/core/models/AuthenticationModel.php
index 9939b840192..ffde303a700 100755
--- a/src/core/models/AuthenticationModel.php
+++ b/src/core/models/AuthenticationModel.php
@@ -39,59 +39,6 @@ class AuthenticationModel
 
         return password_verify($args['password'], $aReturn[0]['password']);
     }
-    
-    public static function resetFailedAuthentication(array $aArgs)
-    {
-        ValidatorModel::notEmpty($aArgs, ['userId']);
-        ValidatorModel::stringType($aArgs, ['userId']);
-
-        DatabaseModel::update([
-            'table'     => 'users',
-            'set'       => [
-                'failed_authentication' => 0,
-                'locked_until'          => null,
-            ],
-            'where'     => ['lower(user_id) = lower(?)'],
-            'data'      => [$aArgs['userId']]
-        ]);
-
-        return true;
-    }
-
-    public static function increaseFailedAuthentication(array $aArgs)
-    {
-        ValidatorModel::notEmpty($aArgs, ['userId', 'tentatives']);
-        ValidatorModel::stringType($aArgs, ['userId']);
-        ValidatorModel::intVal($aArgs, ['tentatives']);
-
-        DatabaseModel::update([
-            'table'     => 'users',
-            'set'       => [
-                'failed_authentication' => $aArgs['tentatives']
-            ],
-            'where'     => ['lower(user_id) = lower(?)'],
-            'data'      => [$aArgs['userId']]
-        ]);
-
-        return true;
-    }
-
-    public static function lockUser(array $aArgs)
-    {
-        ValidatorModel::notEmpty($aArgs, ['userId', 'lockedUntil']);
-        ValidatorModel::stringType($aArgs, ['userId']);
-
-        DatabaseModel::update([
-            'table' => 'users',
-            'set'   => [
-                'locked_until'  => date('Y-m-d H:i:s', $aArgs['lockedUntil'])
-            ],
-            'where' => ['lower(user_id) = lower(?)'],
-            'data'  => [$aArgs['userId']]
-        ]);
-
-        return true;
-    }
 
     public static function generatePassword()
     {
diff --git a/src/core/models/CoreConfigModel.php b/src/core/models/CoreConfigModel.php
index a484520157f..bb915e99e04 100755
--- a/src/core/models/CoreConfigModel.php
+++ b/src/core/models/CoreConfigModel.php
@@ -58,6 +58,18 @@ class CoreConfigModel
         return $customId;
     }
 
+    public static function getConfigPath()
+    {
+        $customId = CoreConfigModel::getCustomId();
+        if (!empty($customId) && is_file("custom/{$customId}/apps/maarch_entreprise/xml/config.xml")) {
+            $path = "custom/{$customId}/apps/maarch_entreprise/xml/config.xml";
+        } else {
+            $path = 'apps/maarch_entreprise/xml/config.xml';
+        }
+
+        return $path;
+    }
+
     public static function getApplicationName()
     {
         static $applicationName;
@@ -225,7 +237,7 @@ class CoreConfigModel
 
         $customId = CoreConfigModel::getCustomId();
 
-        if (file_exists("custom/{$customId}/{$args['path']}")) {
+        if (is_file("custom/{$customId}/{$args['path']}")) {
             $path = "custom/{$customId}/{$args['path']}";
         } else {
             $path = $args['path'];
diff --git a/test/unitTests/core/AuthenticationControllerTest.php b/test/unitTests/core/AuthenticationControllerTest.php
index 4ffc4095159..5ccee361f75 100755
--- a/test/unitTests/core/AuthenticationControllerTest.php
+++ b/test/unitTests/core/AuthenticationControllerTest.php
@@ -113,17 +113,27 @@ class AuthenticationControllerTest extends TestCase
             $fullRequest = \httpRequestCustom::addContentInBody(['rules' => $rules], $request);
             $passwordController->updateRules($fullRequest, new \Slim\Http\Response());
     
-            $response = \SrcCore\models\AuthenticationModel::resetFailedAuthentication(['userId' => 'superadmin']);
-            $this->assertSame(true, $response);
-    
+            \User\models\UserModel::update([
+                'set'   => ['failed_authentication' => 0, 'locked_until' => null],
+                'where' => ['user_id = ?'],
+                'data'  => ['superadmin']
+            ]);
+
             for ($i = 1; $i < $lockAttempts; $i++) {
-                $response = \SrcCore\controllers\AuthenticationController::handleFailedAuthentication(['userId' => 'superadmin']);
-                $this->assertSame(_BAD_LOGIN_OR_PSW, $response);
+                $response = \SrcCore\controllers\AuthenticationController::handleFailedAuthentication(['userId' => $GLOBALS['id']]);
+                $this->assertSame(true, $response);
             }
-            $response = \SrcCore\controllers\AuthenticationController::handleFailedAuthentication(['userId' => 'superadmin']);
-            $this->assertSame(_ACCOUNT_LOCKED_FOR . " " . $lockTime . " mn", $response);
-
-            $response = \SrcCore\models\AuthenticationModel::resetFailedAuthentication(['userId' => 'superadmin']);
+            $response = \SrcCore\controllers\AuthenticationController::handleFailedAuthentication(['userId' => $GLOBALS['id']]);
+            $this->assertSame(true, $response['accountLocked']);
+            $response = \SrcCore\controllers\AuthenticationController::handleFailedAuthentication(['userId' => $GLOBALS['id']]);
+            $this->assertSame(true, $response['accountLocked']);
+            $this->assertNotNull($response['lockedDate']);
+
+            \User\models\UserModel::update([
+                'set'   => ['failed_authentication' => 0, 'locked_until' => null],
+                'where' => ['user_id = ?'],
+                'data'  => ['superadmin']
+            ]);
         }
     }
 }
-- 
GitLab