diff --git a/rest/index.php b/rest/index.php index dd05a3cc3f148eae26a0c2b788ae4b7424f33f69..aa2f89eef8b2fa1a8892742da612b002a643d6a2 100755 --- a/rest/index.php +++ b/rest/index.php @@ -190,7 +190,7 @@ $app->delete('/parameters/{id}', \Parameter\controllers\ParameterController::cla //PasswordRules $app->get('/passwordRules', \SrcCore\controllers\PasswordController::class . ':getRules'); -$app->put('/passwordRules/{id}', \SrcCore\controllers\PasswordController::class . ':updateRule'); +$app->put('/passwordRules', \SrcCore\controllers\PasswordController::class . ':updateRules'); //Priorities $app->get('/priorities', \Priority\controllers\PriorityController::class . ':get'); diff --git a/sql/data_fr.sql b/sql/data_fr.sql index 0eebfe067473b643015d7bad488a1e7fc2b98a08..8610e21c344f919a0a58bf70960fd50ad1f5dce1 100755 --- a/sql/data_fr.sql +++ b/sql/data_fr.sql @@ -1750,6 +1750,13 @@ UPDATE doctypes SET retention_rule = 'compta_3_03'; UPDATE doctypes SET duration_current_use = 12; /* Password Management */ +INSERT INTO password_rules (label, "value") VALUES ('minLength', 6); +INSERT INTO password_rules (label, "value") VALUES ('complexityUpper', 0); +INSERT INTO password_rules (label, "value") VALUES ('complexityNumber', 0); +INSERT INTO password_rules (label, "value") VALUES ('complexitySpecial', 0); +INSERT INTO password_rules (label, "value") VALUES ('lockAttempts', 3); +INSERT INTO password_rules (label, "value") VALUES ('lockTime', 5); +INSERT INTO password_rules (label, "value") VALUES ('UseNumber', 2); INSERT INTO password_rules (label, "value") VALUES ('renewal', 90); diff --git a/sql/develop.sql b/sql/develop.sql index 628422fd1664ce2acb8e2f078879f361dc4ed3c1..164f9721a8433543d10fb9193ba289a52a430581 100644 --- a/sql/develop.sql +++ b/sql/develop.sql @@ -32,7 +32,8 @@ UPDATE actions_groupbaskets SET used_in_action_page = 'Y' WHERE used_in_basketli DELETE FROM usergroups_services WHERE service_id = 'view_baskets'; ALTER TABLE groupbasket_status DROP COLUMN IF EXISTS "order"; ALTER TABLE groupbasket_status ADD COLUMN "order" integer; -UPDATE groupbasket_status SET "order" = 1; +UPDATE groupbasket_status SET "order" = 0 WHERE status_id = 'NEW'; +UPDATE groupbasket_status SET "order" = 1 WHERE status_id != 'NEW'; ALTER TABLE groupbasket_status ALTER COLUMN "order" SET NOT NULL; @@ -82,6 +83,13 @@ CREATE TABLE password_rules CONSTRAINT password_rules_label_key UNIQUE (label) ) WITH (OIDS=FALSE); +INSERT INTO password_rules (label, "value") VALUES ('minLength', 6); +INSERT INTO password_rules (label, "value") VALUES ('complexityUpper', 0); +INSERT INTO password_rules (label, "value") VALUES ('complexityNumber', 0); +INSERT INTO password_rules (label, "value") VALUES ('complexitySpecial', 0); +INSERT INTO password_rules (label, "value") VALUES ('lockAttempts', 3); +INSERT INTO password_rules (label, "value") VALUES ('lockTime', 5); +INSERT INTO password_rules (label, "value") VALUES ('UseNumber', 2); INSERT INTO password_rules (label, "value") VALUES ('renewal', 90); ALTER TABLE users DROP COLUMN IF EXISTS password_modification_date; ALTER TABLE users ADD COLUMN password_modification_date timestamp without time zone; diff --git a/src/app/user/controllers/UserController.php b/src/app/user/controllers/UserController.php index 06b8b93b3eafaf620aadf61c6fba44e92844c092..3d15a761505402f488d502af56f9c5eeda2e9075 100644 --- a/src/app/user/controllers/UserController.php +++ b/src/app/user/controllers/UserController.php @@ -32,6 +32,7 @@ use Respect\Validation\Validator; use Slim\Http\Request; use Slim\Http\Response; use SrcCore\models\CoreConfigModel; +use SrcCore\models\PasswordModel; use SrcCore\models\SecurityModel; use User\models\UserBasketPreferenceModel; use User\models\UserEntityModel; @@ -251,6 +252,7 @@ class UserController $user['baskets'] = BasketModel::getBasketsByUserId(['userId' => $user['user_id'], 'unneededBasketId' => ['IndexingBasket']]); $user['redirectedBaskets'] = BasketModel::getRedirectedBasketsByUserId(['userId' => $user['user_id']]); $user['regroupedBaskets'] = BasketModel::getRegroupedBasketsByUserId(['userId' => $user['user_id']]); + $user['passwordRules'] = PasswordModel::getEnabledRules(); $user['canModifyPassword'] = true; $loggingMethod = CoreConfigModel::getLoggingMethod(); @@ -305,6 +307,8 @@ class UserController return $response->withStatus(400)->withJson(['errors' => 'Bad Request']); } elseif (!SecurityModel::authentication(['userId' => $GLOBALS['userId'], 'password' => $data['currentPassword']])) { return $response->withStatus(401)->withJson(['errors' => _WRONG_PSW]); + } elseif (PasswordModel::isPasswordValid(['password' => $data['newPassword']])) { + return $response->withStatus(400)->withJson(['errors' => 'Password does not match security criteria']); } $user = UserModel::getByUserId(['userId' => $GLOBALS['userId'], 'select' => ['id']]); diff --git a/src/core/controllers/PasswordController.php b/src/core/controllers/PasswordController.php index 49290642aac1252165c6f9b3a73b0ba87d3e8510..dbe9cc5b8ff1853b7b66e5cc67580d1f119a807b 100644 --- a/src/core/controllers/PasswordController.php +++ b/src/core/controllers/PasswordController.php @@ -32,34 +32,40 @@ class PasswordController return $response->withJson(['rules' => PasswordModel::getRules()]); } - public function updateRule(Request $request, Response $response, array $aArgs) + public function updateRules(Request $request, Response $response) { if (!ServiceModel::hasService(['id' => 'admin_password_rules', 'userId' => $GLOBALS['userId'], 'location' => 'apps', 'type' => 'admin'])) { return $response->withStatus(403)->withJson(['errors' => 'Service forbidden']); } - $rule = PasswordModel::getRuleById(['id' => $aArgs['id'], 'select' => [1]]); - if (empty($rule)) { - return $response->withStatus(400)->withJson(['errors' => 'Rule does not exist']); - } - $data = $request->getParams(); - $check = Validator::intVal()->validate($data['value']); - $check = $check && Validator::boolType()->validate($data['enabled']); + $check = Validator::arrayType()->notEmpty()->validate($data['rules']); if (!$check) { return $response->withStatus(400)->withJson(['errors' => 'Bad Request']); } - $data['id'] = $aArgs['id']; - PasswordModel::updateRule($data); + foreach ($data['rules'] as $rule) { + $existingRule = PasswordModel::getRuleById(['id' => $rule['id'], 'select' => [1]]); + if (empty($existingRule)) { + continue; + } + + $check = Validator::intVal()->validate($rule['value']); + $check = $check && Validator::boolType()->validate($rule['enabled']); + if (!$check) { + continue; + } + + PasswordModel::updateRule($rule); + } HistoryController::add([ 'tableName' => 'password_rules', - 'recordId' => $aArgs['id'], + 'recordId' => 'rules', 'eventType' => 'UP', - 'info' => _PASSWORD_RULE_UPDATED . " : {$data['label']}", + 'info' => _PASSWORD_RULES_UPDATED, 'moduleId' => 'core', - 'eventId' => 'passwordRuleModification', + 'eventId' => 'passwordRulesModification', ]); return $response->withJson(['success' => 'success']); diff --git a/src/core/lang/lang-en.php b/src/core/lang/lang-en.php index 24c25ce98ead257393442f115ad1aededf093017..a6a23c065644e29290a9b040ea7fbf059a617143 100644 --- a/src/core/lang/lang-en.php +++ b/src/core/lang/lang-en.php @@ -68,7 +68,7 @@ define('_PARAMETER_CREATION', 'Parameter creation'); define('_PARAMETER_MODIFICATION', 'Parameter modification'); define('_PARAMETER_SUPPRESSION', 'Parameter suppression'); define('_PARAMETER_ID_ALREADY_EXISTS', 'Parameter already exists'); -define('_PASSWORD_RULE_UPDATED', 'Password rule updated'); +define('_PASSWORD_RULES_UPDATED', 'Password rules updated'); define('_PRIORITY_CREATION', 'Priority creation'); define('_PRIORITY_MODIFICATION', 'Priority modification'); define('_PRIORITY_SUPPRESSION', 'Priority suppression'); diff --git a/src/core/lang/lang-fr.php b/src/core/lang/lang-fr.php index 66b1ee44762e0a48cfad4312ff4e4195e96e9ef9..e2126dde1749ef7169f64c437fc69114b0f967e4 100644 --- a/src/core/lang/lang-fr.php +++ b/src/core/lang/lang-fr.php @@ -68,7 +68,7 @@ define('_PARAMETER_CREATION', 'Création paramètre'); define('_PARAMETER_MODIFICATION', 'Modification paramètre'); define('_PARAMETER_SUPPRESSION', 'Suppression paramètre'); define('_PARAMETER_ID_ALREADY_EXISTS', 'Ce paramètre existe déjà '); -define('_PASSWORD_RULE_UPDATED', 'Règle de mot de passe modifiée'); +define('_PASSWORD_RULES_UPDATED', 'Règles de mot de passe modifiées'); define('_PRIORITY_CREATION', 'Création priorité'); define('_PRIORITY_MODIFICATION', 'Modification priorité'); define('_PRIORITY_SUPPRESSION', 'Suppression priorité'); diff --git a/src/core/models/PasswordModel.php b/src/core/models/PasswordModel.php index 410e6bcf1fd94d5d30240b6ccb4bc2f67ab67a64..15141a3889ab5ded73ec72abae76d14384704e4f 100644 --- a/src/core/models/PasswordModel.php +++ b/src/core/models/PasswordModel.php @@ -30,6 +30,27 @@ class PasswordModel return $aRules; } + public static function getEnabledRules() + { + $aRules = DatabaseModel::select([ + 'select' => ['label', 'value'], + 'table' => ['password_rules'], + 'where' => ['enabled = ?'], + 'data' => [true], + ]); + + $formattedRules = []; + foreach ($aRules as $rule) { + if (strpos($rule['label'], 'complexity') === false) { + $formattedRules[$rule['label']] = $rule['value']; + } else { + $formattedRules[$rule['label']] = true; + } + } + + return $formattedRules; + } + public static function getRuleById(array $aArgs) { ValidatorModel::notEmpty($aArgs, ['id']); @@ -64,4 +85,35 @@ class PasswordModel return true; } + + public static function isPasswordValid(array $aArgs) + { + ValidatorModel::notEmpty($aArgs, ['password']); + ValidatorModel::stringType($aArgs, ['password']); + + $passwordRules = PasswordModel::getEnabledRules(); + + if (!empty($passwordRules['minLength'])) { + if (strlen($aArgs['password']) < $passwordRules['minLength']) { + return false; + } + } + if (!empty($passwordRules['complexityUpper'])) { + if (!preg_match('/[A-Z]/', $aArgs['password'])) { + return false; + } + } + if (!empty($passwordRules['complexityNumber'])) { + if (!preg_match('/[0-9]/', $aArgs['password'])) { + return false; + } + } + if (!empty($passwordRules['complexitySpecial'])) { + if (!preg_match('/[^a-zA-Z0-9]/', $aArgs['password'])) { + return false; + } + } + + return true; + } }