From cc2d94f3bc7ec5979ee147e728eebe3457233331 Mon Sep 17 00:00:00 2001 From: Jean-Laurent DUZANT <jean-laurent.duzant@xelians.fr> Date: Wed, 27 Sep 2023 12:47:34 +0200 Subject: [PATCH] FEAT #25796 TIME 0:20 rapatriement du code --- bin/notification/process_email_stack.php | 2 +- config/config.json.default | 3 +- config/mc_secret.key | 1 + .../PreProcessActionController.php | 4 +- src/app/action/controllers/ShippingTrait.php | 4 +- .../controllers/ConfigurationController.php | 4 +- .../contact/controllers/ContactController.php | 6 +- src/app/email/controllers/EmailController.php | 4 +- .../controllers/AlfrescoController.php | 16 +- .../SendMessageExchangeController.php | 4 +- .../controllers/MultigestController.php | 10 +- .../outlook/controllers/OutlookController.php | 20 +-- .../ShippingTemplateController.php | 8 +- .../controllers/AuthenticationController.php | 4 +- src/core/controllers/PasswordController.php | 147 ++++++++++++++++++ src/core/models/CoreConfigModel.php | 50 +++++- src/core/models/PasswordModel.php | 30 +++- .../unitTests/core/PasswordControllerTest.php | 144 +++++++++++++++++ 18 files changed, 412 insertions(+), 49 deletions(-) create mode 100644 config/mc_secret.key diff --git a/bin/notification/process_email_stack.php b/bin/notification/process_email_stack.php index af326e8c074..4fae0897cd7 100644 --- a/bin/notification/process_email_stack.php +++ b/bin/notification/process_email_stack.php @@ -98,7 +98,7 @@ foreach ($emails as $key => $email) { if ($configuration['auth']) { $phpmailer->Username = $configuration['user']; if (!empty($configuration['password'])) { - $phpmailer->Password = \SrcCore\models\PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]); + $phpmailer->Password = \SrcCore\controllers\PasswordController::decrypt(['encryptedData' => $configuration['password']]); } } } elseif ($configuration['type'] == 'sendmail') { diff --git a/config/config.json.default b/config/config.json.default index db85718bc82..ed44e2c6f6f 100644 --- a/config/config.json.default +++ b/config/config.json.default @@ -8,7 +8,8 @@ "maarchDirectory" : "/var/www/html/MaarchCourrier/", "customID" : "", "maarchUrl" : "https://demo.maarchcourrier.com/", - "lockAdvancedPrivileges" : true + "lockAdvancedPrivileges" : true, + "privateKeyPath": "/var/www/html/MaarchCourrier/config/mc_secret.key" }, "database": [ { diff --git a/config/mc_secret.key b/config/mc_secret.key new file mode 100644 index 00000000000..5af040ba12d --- /dev/null +++ b/config/mc_secret.key @@ -0,0 +1 @@ +Security Key Maarch Courrier 2008 \ No newline at end of file diff --git a/src/app/action/controllers/PreProcessActionController.php b/src/app/action/controllers/PreProcessActionController.php index 5f5e4985eab..267ed2662b8 100755 --- a/src/app/action/controllers/PreProcessActionController.php +++ b/src/app/action/controllers/PreProcessActionController.php @@ -57,7 +57,7 @@ use User\models\UserEntityModel; use User\models\UserModel; use ExportSeda\controllers\PreProcessActionSEDATrait; use Multigest\controllers\MultigestController; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SignatureBook\controllers\SignatureBookController; class PreProcessActionController @@ -1844,7 +1844,7 @@ class PreProcessActionController $accountCheck = MultigestController::checkAccountWithCredentials([ 'sasId' => $entityInformations['multigest']['sasId'], 'login' => $entityInformations['multigest']['login'], - 'password' => empty($entityInformations['multigest']['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $entityInformations['multigest']['password']]) + 'password' => empty($entityInformations['multigest']['password']) ? '' : PasswordController::decrypt(['encryptedData' => $entityInformations['multigest']['password']]) ]); if (!empty($accountCheck['errors'])) { return $response->withStatus(400)->withJson(['errors' => $accountCheck['errors'], 'lang' => ($accountCheck['lang'] ?? null)]); diff --git a/src/app/action/controllers/ShippingTrait.php b/src/app/action/controllers/ShippingTrait.php index 7baa4ba736f..c5dca8d1a6b 100644 --- a/src/app/action/controllers/ShippingTrait.php +++ b/src/app/action/controllers/ShippingTrait.php @@ -28,7 +28,7 @@ use Shipping\models\ShippingModel; use Shipping\models\ShippingTemplateModel; use SrcCore\models\CoreConfigModel; use SrcCore\models\CurlModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\ValidatorModel; use User\models\UserModel; @@ -197,7 +197,7 @@ trait ShippingTrait 'queryParams' => [ 'grant_type' => 'password', 'username' => $shippingTemplate['account']['id'], - 'password' => PasswordModel::decrypt(['cryptedPassword' => $shippingTemplate['account']['password']]) + 'password' => PasswordController::decrypt(['encryptedData' => $shippingTemplate['account']['password']]) ] ]); if ($curlAuth['code'] != 200) { diff --git a/src/app/configuration/controllers/ConfigurationController.php b/src/app/configuration/controllers/ConfigurationController.php index b41cc006e5b..d233f250723 100755 --- a/src/app/configuration/controllers/ConfigurationController.php +++ b/src/app/configuration/controllers/ConfigurationController.php @@ -28,7 +28,7 @@ use Respect\Validation\Validator; use Slim\Psr7\Request; use SrcCore\http\Response; use SrcCore\models\CoreConfigModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use Status\models\StatusModel; use ContentManagement\controllers\DocumentEditorController; @@ -90,7 +90,7 @@ class ConfigurationController $data['password'] = $configuration['value']['password']; } } elseif ($data['auth'] && !empty($data['password'])) { - $data['password'] = PasswordModel::encrypt(['password' => $data['password']]); + $data['password'] = PasswordController::encrypt(['dataToEncrypt' => $data['password']]); } $check = ConfigurationController::checkMailer($data); if (!empty($check['errors'])) { diff --git a/src/app/contact/controllers/ContactController.php b/src/app/contact/controllers/ContactController.php index 1726d4abd83..f7e1d70dc9e 100755 --- a/src/app/contact/controllers/ContactController.php +++ b/src/app/contact/controllers/ContactController.php @@ -36,7 +36,7 @@ use SrcCore\http\Response; use SrcCore\controllers\AutoCompleteController; use SrcCore\models\CoreConfigModel; use SrcCore\models\DatabaseModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\TextFormatModel; use SrcCore\models\ValidatorModel; use User\models\UserModel; @@ -164,7 +164,7 @@ class ContactController $contactBody['login'] = $body['communicationMeans']['login']; } if (!empty($body['communicationMeans']['password'])) { - $contactBody['password'] = PasswordModel::encrypt(['password' => $body['communicationMeans']['password']]); + $contactBody['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['communicationMeans']['password']]); } } @@ -356,7 +356,7 @@ class ContactController $contactBody['login'] = $body['communicationMeans']['login']; } if (!empty($body['communicationMeans']['password'])) { - $contactBody['password'] = PasswordModel::encrypt(['password' => $body['communicationMeans']['password']]); + $contactBody['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['communicationMeans']['password']]); } } diff --git a/src/app/email/controllers/EmailController.php b/src/app/email/controllers/EmailController.php index 091d2a7ed44..bda7d08bae8 100644 --- a/src/app/email/controllers/EmailController.php +++ b/src/app/email/controllers/EmailController.php @@ -39,7 +39,7 @@ use Respect\Validation\Validator; use Slim\Psr7\Request; use SrcCore\http\Response; use SrcCore\models\CoreConfigModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\TextFormatModel; use SrcCore\models\ValidatorModel; use User\models\UserModel; @@ -669,7 +669,7 @@ class EmailController if ($configuration['auth']) { $phpmailer->Username = $configuration['user']; if (!empty($configuration['password'])) { - $phpmailer->Password = PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]); + $phpmailer->Password = PasswordController::decrypt(['encryptedData' => $configuration['password']]); } } } elseif ($configuration['type'] == 'sendmail') { diff --git a/src/app/external/alfresco/controllers/AlfrescoController.php b/src/app/external/alfresco/controllers/AlfrescoController.php index 513fc188bcf..66290a6e2ee 100644 --- a/src/app/external/alfresco/controllers/AlfrescoController.php +++ b/src/app/external/alfresco/controllers/AlfrescoController.php @@ -33,7 +33,7 @@ use Slim\Psr7\Request; use SrcCore\http\Response; use SrcCore\models\CoreConfigModel; use SrcCore\models\CurlModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\ValidatorModel; use User\models\UserModel; @@ -186,7 +186,7 @@ class AlfrescoController 'id' => $id, 'label' => $body['label'], 'login' => $body['login'], - 'password' => PasswordModel::encrypt(['password' => $body['password']]), + 'password' => PasswordController::encrypt(['dataToEncrypt' => $body['password']]), 'nodeId' => $body['nodeId'] ]; $account = json_encode($account); @@ -238,7 +238,7 @@ class AlfrescoController 'id' => $args['id'], 'label' => $body['label'], 'login' => $body['login'], - 'password' => empty($body['password']) ? $alfresco['alfresco']['password'] : PasswordModel::encrypt(['password' => $body['password']]), + 'password' => empty($body['password']) ? $alfresco['alfresco']['password'] : PasswordController::encrypt(['dataToEncrypt' => $body['password']]), 'nodeId' => $body['nodeId'] ]; $account = json_encode($account); @@ -312,7 +312,7 @@ class AlfrescoController if (empty($alfresco['alfresco']['password'])) { return $response->withStatus(400)->withJson(['errors' => 'Account has no password']); } - $body['password'] = PasswordModel::decrypt(['cryptedPassword' => $alfresco['alfresco']['password']]); + $body['password'] = PasswordController::decrypt(['encryptedData' => $alfresco['alfresco']['password']]); } $configuration = ConfigurationModel::getByPrivilege(['privilege' => 'admin_alfresco']); @@ -390,7 +390,7 @@ class AlfrescoController if (empty($entityInformations['alfresco'])) { return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']); } - $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]); + $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]); $curlResponse = CurlModel::exec([ 'url' => "{$alfrescoUri}/alfresco/versions/1/nodes/{$entityInformations['alfresco']['nodeId']}/children", @@ -440,7 +440,7 @@ class AlfrescoController if (empty($entityInformations['alfresco'])) { return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']); } - $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]); + $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]); $curlResponse = CurlModel::exec([ 'url' => "{$alfrescoUri}/alfresco/versions/1/nodes/{$args['id']}/children", @@ -497,7 +497,7 @@ class AlfrescoController if (empty($entityInformations['alfresco'])) { return $response->withStatus(400)->withJson(['errors' => 'User primary entity has not enough alfresco informations']); } - $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]); + $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]); $search = addslashes($queryParams['search']); $body = [ @@ -559,7 +559,7 @@ class AlfrescoController if (empty($entityInformations['alfresco'])) { return ['errors' => 'User primary entity has not enough alfresco informations']; } - $entityInformations['alfresco']['password'] = PasswordModel::decrypt(['cryptedPassword' => $entityInformations['alfresco']['password']]); + $entityInformations['alfresco']['password'] = PasswordController::decrypt(['encryptedData' => $entityInformations['alfresco']['password']]); $document = ResModel::getById([ 'select' => [ diff --git a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php index 17bf6633c0e..1d662868f9d 100755 --- a/src/app/external/messageExchange/controllers/SendMessageExchangeController.php +++ b/src/app/external/messageExchange/controllers/SendMessageExchangeController.php @@ -28,7 +28,7 @@ use Note\models\NoteModel; use Resource\controllers\ResController; use Resource\models\ResModel; use Respect\Validation\Validator; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\TextFormatModel; use Status\models\StatusModel; use User\models\UserModel; @@ -247,7 +247,7 @@ class SendMessageExchangeController } $url = str_replace($prefix, '', $ArchivalAgencyCommunicationType['value']); $login = $aArchivalAgencyCommunicationType['login'] ?? ''; - $password = !empty($aArchivalAgencyCommunicationType['password']) ? PasswordModel::decrypt(['cryptedPassword' => $aArchivalAgencyCommunicationType['password']]) : ''; + $password = !empty($aArchivalAgencyCommunicationType['password']) ? PasswordController::decrypt(['encryptedData' => $aArchivalAgencyCommunicationType['password']]) : ''; $ArchivalAgencyCommunicationType['value'] = $prefix; if (!empty($login) && !empty($password)) { $ArchivalAgencyCommunicationType['value'] .= $login . ':' . $password . '@'; diff --git a/src/app/external/multigest/controllers/MultigestController.php b/src/app/external/multigest/controllers/MultigestController.php index 263862796ed..385d2a7f8f5 100644 --- a/src/app/external/multigest/controllers/MultigestController.php +++ b/src/app/external/multigest/controllers/MultigestController.php @@ -32,7 +32,7 @@ use Resource\models\ResourceContactModel; use Respect\Validation\Validator; use Slim\Psr7\Request; use SrcCore\http\Response; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\ValidatorModel; use SrcCore\models\CoreConfigModel; use User\models\UserModel; @@ -162,7 +162,7 @@ class MultigestController 'id' => $id, 'label' => $body['label'], 'login' => $body['login'], - 'password' => PasswordModel::encrypt(['password' => $body['password']]), + 'password' => PasswordController::encrypt(['dataToEncrypt' => $body['password']]), 'sasId' => $body['sasId'] ]; $account = json_encode($account); @@ -237,7 +237,7 @@ class MultigestController 'id' => $args['id'], 'label' => $body['label'], 'login' => $body['login'], - 'password' => empty($body['password']) ? $externalId['multigest']['password'] : PasswordModel::encrypt(['password' => $body['password']]), + 'password' => empty($body['password']) ? $externalId['multigest']['password'] : PasswordController::encrypt(['dataToEncrypt' => $body['password']]), 'sasId' => $body['sasId'] ]; $account = json_encode($account); @@ -306,7 +306,7 @@ class MultigestController $result = MultigestController::checkAccountWithCredentials([ 'sasId' => $body['sasId'], 'login' => $body['login'], - 'password' => empty($body['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $body['password']]) + 'password' => empty($body['password']) ? '' : PasswordController::decrypt(['encryptedData' => $body['password']]) ]); if (!empty($result['errors'])) { @@ -531,7 +531,7 @@ class MultigestController try { $soapClient = new \SoapClient($multigestUri, [ 'login' => $configuration['login'], - 'password' => empty($configuration['password']) ? '' : PasswordModel::decrypt(['cryptedPassword' => $configuration['password']]), + 'password' => empty($configuration['password']) ? '' : PasswordController::decrypt(['encryptedData' => $configuration['password']]), 'authentication' => SOAP_AUTHENTICATION_BASIC ]); } catch (\SoapFault $e) { diff --git a/src/app/external/outlook/controllers/OutlookController.php b/src/app/external/outlook/controllers/OutlookController.php index aac921a127a..14d82af6727 100644 --- a/src/app/external/outlook/controllers/OutlookController.php +++ b/src/app/external/outlook/controllers/OutlookController.php @@ -24,7 +24,7 @@ use Slim\Psr7\Request; use SrcCore\http\Response; use SrcCore\controllers\LanguageController; use SrcCore\models\CoreConfigModel; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use Status\models\StatusModel; use User\models\UserModel; @@ -113,9 +113,9 @@ class OutlookController } } - $configuration['value']['tenantId'] = !empty($configuration['value']['tenantId']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['tenantId']]) : ''; - $configuration['value']['clientId'] = !empty($configuration['value']['clientId']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientId']]) : ''; - $configuration['value']['clientSecret'] = !empty($configuration['value']['clientSecret']) ? PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientSecret']]) : ''; + $configuration['value']['tenantId'] = !empty($configuration['value']['tenantId']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['tenantId']]) : ''; + $configuration['value']['clientId'] = !empty($configuration['value']['clientId']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['clientId']]) : ''; + $configuration['value']['clientSecret'] = !empty($configuration['value']['clientSecret']) ? PasswordController::decrypt(['encryptedData' => $configuration['value']['clientSecret']]) : ''; $configuration['value']['outlookConnectionSaved'] = false; if (!empty($configuration['value']['tenantId']) && !empty($configuration['value']['clientId']) && !empty($configuration['value']['clientSecret'])) { @@ -171,9 +171,9 @@ class OutlookController 'statusId' => $body['statusId'], 'attachmentTypeId' => $body['attachmentTypeId'], 'version' => $body['version'], - 'tenantId' => !empty($body['tenantId']) ? PasswordModel::encrypt(['password' => $body['tenantId']]) : '', - 'clientId' => !empty($body['clientId']) ? PasswordModel::encrypt(['password' => $body['clientId']]) : '', - 'clientSecret' => !empty($body['clientSecret']) ? PasswordModel::encrypt(['password' => $body['clientSecret']]) : '' + 'tenantId' => !empty($body['tenantId']) ? PasswordController::encrypt(['password' => $body['tenantId']]) : '', + 'clientId' => !empty($body['clientId']) ? PasswordController::encrypt(['password' => $body['clientId']]) : '', + 'clientSecret' => !empty($body['clientSecret']) ? PasswordController::encrypt(['password' => $body['clientSecret']]) : '' ], JSON_UNESCAPED_SLASHES); if (empty(ConfigurationModel::getByPrivilege(['privilege' => 'admin_addin_outlook', 'select' => [1]]))) { ConfigurationModel::create(['value' => $data, 'privilege' => 'admin_addin_outlook']); @@ -221,9 +221,9 @@ class OutlookController 'ewsHost' => explode('/', $body['ewsUrl'])[0], 'email' => $body['userId'], 'version' => $configuration['value']['version'], - 'tenantId' => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['tenantId']]), - 'clientId' => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientId']]), - 'clientSecret' => PasswordModel::decrypt(['cryptedPassword' => $configuration['value']['clientSecret']]), + 'tenantId' => PasswordController::decrypt(['encryptedData' => $configuration['value']['tenantId']]), + 'clientId' => PasswordController::decrypt(['encryptedData' => $configuration['value']['clientId']]), + 'clientSecret' => PasswordController::decrypt(['encryptedData' => $configuration['value']['clientSecret']]), 'attachmentType' => $attachmentType['type_id'] ]; diff --git a/src/app/shipping/controllers/ShippingTemplateController.php b/src/app/shipping/controllers/ShippingTemplateController.php index 11f65b35e54..1a3e9d36add 100755 --- a/src/app/shipping/controllers/ShippingTemplateController.php +++ b/src/app/shipping/controllers/ShippingTemplateController.php @@ -31,7 +31,7 @@ use SrcCore\controllers\LogsController; use SrcCore\models\CoreConfigModel; use Slim\Psr7\Request; use SrcCore\http\Response; -use SrcCore\models\PasswordModel; +use SrcCore\controllers\PasswordController; use SrcCore\models\ValidatorModel; use Firebase\JWT\JWT; @@ -147,7 +147,7 @@ class ShippingTemplateController } if (!empty($body['account']['password'])) { - $body['account']['password'] = PasswordModel::encrypt(['password' => $body['account']['password']]); + $body['account']['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['account']['password']]); } $body['options'] = !empty($body['options']) ? json_encode($body['options']) : '{}'; @@ -218,7 +218,7 @@ class ShippingTemplateController } if (!empty($body['account']['password'])) { - $body['account']['password'] = PasswordModel::encrypt(['password' => $body['account']['password']]); + $body['account']['password'] = PasswordController::encrypt(['dataToEncrypt' => $body['account']['password']]); } else { $shippingInfo = ShippingTemplateModel::getById(['id' => $args['id'], 'select' => ['account']]); $shippingInfo['account'] = json_decode($shippingInfo['account'], true); @@ -922,7 +922,7 @@ class ShippingTemplateController 'queryParams' => [ 'grant_type' => 'password', 'username' => $shippingTemplateAccount['id'], - 'password' => PasswordModel::decrypt(['cryptedPassword' => $shippingTemplateAccount['password']]) + 'password' => PasswordController::decrypt(['encryptedData' => $shippingTemplateAccount['password']]) ] ]); if ($curlAuth['code'] != 200) { diff --git a/src/core/controllers/AuthenticationController.php b/src/core/controllers/AuthenticationController.php index dc1fa744b24..fa7ae98c4f4 100755 --- a/src/core/controllers/AuthenticationController.php +++ b/src/core/controllers/AuthenticationController.php @@ -56,7 +56,7 @@ class AuthenticationController $parameter = ParameterModel::getById(['id' => 'loginpage_message', 'select' => ['param_value_string']]); - $encryptKey = CoreConfigModel::getEncryptKey(); + $encryptKeyChanged = CoreConfigModel::hasEncryptKeyChanged(); $loggingMethod = CoreConfigModel::getLoggingMethod(); $authUri = null; @@ -112,7 +112,7 @@ class AuthenticationController 'instanceId' => $hashedPath, 'applicationName' => $appName, 'loginMessage' => $parameter['param_value_string'] ?? null, - 'changeKey' => $encryptKey == 'Security Key Maarch Courrier #2008', + 'changeKey' => !$encryptKeyChanged, 'authMode' => $loggingMethod['id'], 'authUri' => $authUri, 'lang' => CoreConfigModel::getLanguage(), diff --git a/src/core/controllers/PasswordController.php b/src/core/controllers/PasswordController.php index 8722c32bfa0..8e55eec387c 100755 --- a/src/core/controllers/PasswordController.php +++ b/src/core/controllers/PasswordController.php @@ -21,6 +21,10 @@ use Slim\Psr7\Request; use SrcCore\http\Response; use SrcCore\models\PasswordModel; use SrcCore\models\ValidatorModel; +use Exception; +use SrcCore\models\CoreConfigModel; +use SrcCore\controllers\LogsController; + class PasswordController { @@ -100,4 +104,147 @@ class PasswordController return true; } + + /** + * Encrypt data with the old vhost or new file encryption key + * + * @return string + */ + public static function encrypt(array $args): string + { + ValidatorModel::notEmpty($args, ['dataToEncrypt']); + ValidatorModel::stringType($args, ['dataToEncrypt']); + + if (CoreConfigModel::useVhostEncryptKey()) { + return PasswordModel::encrypt(['password' => $args['dataToEncrypt']]); + } else { + return PasswordController::newEncrypt(['dataToEncrypt' => $args['dataToEncrypt']]); + } + } + + /** + * Decrypt encrypted data with the old vhost or new file encryption key + * + * @return string + */ + public static function decrypt(array $args): string + { + ValidatorModel::notEmpty($args, ['encryptedData']); + ValidatorModel::stringType($args, ['encryptedData']); + + if (CoreConfigModel::useVhostEncryptKey()) { + return PasswordModel::decrypt(['cryptedPassword' => $args['encryptedData']]); + } else { + return PasswordController::newDecrypt(['encryptedData' => $args['encryptedData']]); + } + } + + /** + * @deprecated This function logic will be moved to PasswordController::encrypt() in future major versions. + * Please use PasswordController::encrypt() instead. + * + * @return string + */ + public static function newEncrypt(array $args): string + { + ValidatorModel::notEmpty($args, ['dataToEncrypt']); + ValidatorModel::stringType($args, ['dataToEncrypt']); + + $encryptedResult = null; + $encryptKey = CoreConfigModel::getEncryptKey(); + $cipherMethod = 'AES-256-CTR'; + + try { + $encryptKeyHash = openssl_digest($encryptKey, 'sha256'); + + $initialisationVector = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipherMethod)); + $encrypted = openssl_encrypt($args['dataToEncrypt'], $cipherMethod, $encryptKeyHash, OPENSSL_RAW_DATA, $initialisationVector); + + if ($encrypted === false) { + LogsController::add([ + 'isTech' => true, + 'moduleId' => 'Encryption/Decryption', + 'level' => 'ERROR', + 'eventType' => 'Decrypt', + 'eventId' => 'Encryption failed: ' . openssl_error_string() + ]); + throw new Exception('Encryption failed: ' . openssl_error_string()); + } + + $encryptedResult = $initialisationVector . $encrypted; + } catch (Exception $e) { + LogsController::add([ + 'isTech' => true, + 'moduleId' => 'Encryption/Decryption', + 'level' => 'ERROR', + 'eventType' => 'Decrypt', + 'eventId' => 'Encryption Exception: ' . $e->getMessage() + ]); + throw new Exception('Encryption Exception: ' . $e->getMessage()); + } + + return base64_encode($encryptedResult); + } + + /** + * @deprecated This function logic will be moved to PasswordController::decrypt() in future major versions. + * Please use PasswordController::decrypt() instead. + * + * @return string + */ + public static function newDecrypt(array $args): string + { + ValidatorModel::notEmpty($args, ['encryptedData']); + ValidatorModel::stringType($args, ['encryptedData']); + + $decryptedResult = null; + $encryptKey = CoreConfigModel::getEncryptKey(); + $cipherMethod = 'AES-256-CTR'; + $encryptedData = base64_decode($args['encryptedData']); + + try { + $initialisationVectorLength = openssl_cipher_iv_length($cipherMethod); + + // encrypted data integrity check on size of data + if (strlen($encryptedData) < $initialisationVectorLength) { + LogsController::add([ + 'isTech' => true, + 'moduleId' => 'Encryption/Decryption', + 'level' => 'ERROR', + 'eventType' => 'Decrypt', + 'eventId' => 'Decryption failed: data length '. strlen($encryptedData). ' is less than iv length ' . $initialisationVectorLength + ]); + throw new Exception('Decryption failed: data length '. strlen($encryptedData). ' is less than iv length ' . $initialisationVectorLength); + } + + // Extract the initialisation vector and encrypted data + $initialisationVector = substr($encryptedData, 0, $initialisationVectorLength); + $encryptedData = substr($encryptedData, $initialisationVectorLength); + $encryptKeyHash = openssl_digest($encryptKey, 'sha256'); + + $decryptedResult = openssl_decrypt($encryptedData, $cipherMethod, $encryptKeyHash, OPENSSL_RAW_DATA, $initialisationVector); + + if ($decryptedResult === false) { + LogsController::add([ + 'isTech' => true, + 'moduleId' => 'Encryption/Decryption', + 'level' => 'ERROR', + 'eventType' => 'Decrypt', + 'eventId' => 'Decryption failed: ' . openssl_error_string() + ]); + throw new Exception('Decryption failed: ' . openssl_error_string()); + } + } catch (Exception $e) { + LogsController::add([ + 'isTech' => true, + 'moduleId' => 'Encryption/Decryption', + 'level' => 'ERROR', + 'eventType' => 'Decrypt', + 'eventId' => 'Decryption Exception: ' . $e->getMessage() + ]); + throw new Exception('Decryption Exception: ' . $e->getMessage()); + } + + return $decryptedResult; + } } diff --git a/src/core/models/CoreConfigModel.php b/src/core/models/CoreConfigModel.php index 0ae565ebd5f..84b5bbfaf3a 100755 --- a/src/core/models/CoreConfigModel.php +++ b/src/core/models/CoreConfigModel.php @@ -178,9 +178,12 @@ class CoreConfigModel /** * Get the Encrypt Key * + * @deprecated This function is deprecated and will be removed in future major versions. + * Please use getEncryptKey() instead. + * * @return string */ - public static function getEncryptKey() + public static function getOldEncryptKey() { if (isset($_SERVER['MAARCH_ENCRYPT_KEY'])) { $encryptKey = $_SERVER['MAARCH_ENCRYPT_KEY']; @@ -193,6 +196,51 @@ class CoreConfigModel return $encryptKey; } + /** + * @deprecated This function will be removed in future major versions. + * Please use getEncryptKey() instead. + * + * @return bool + */ + public static function useVhostEncryptKey(): bool + { + $configPath = CoreConfigModel::getConfigPath(); + $config = json_decode(file_get_contents($configPath), true)['config']; + + return !isset($config['privateKeyPath']) || empty($config['privateKeyPath']); + } + + /** + * Get the Encrypt Key + * + * @return string + */ + public static function getEncryptKey(): string + { + $configPath = CoreConfigModel::getConfigPath(); + $config = json_decode(file_get_contents($configPath), true)['config']; + + $encryptKeyPath = $config['privateKeyPath'] ?? null; + + if (empty($encryptKeyPath)) { + $encryptKey = CoreConfigModel::getOldEncryptKey(); + } elseif (!empty($encryptKeyPath) && is_file($encryptKeyPath) && is_readable($encryptKeyPath)) { + $encryptKey = file_get_contents($encryptKeyPath); + $encryptKey = trim($encryptKey); + } else { + $encryptKey = "Security Key Maarch Courrier 2008"; + } + + return $encryptKey; + } + + public static function hasEncryptKeyChanged(): bool + { + $encryptKey = CoreConfigModel::getEncryptKey(); + + return $encryptKey !== "Security Key Maarch Courrier #2008" && $encryptKey !== "Security Key Maarch Courrier 2008"; + } + public static function getLibrariesDirectory() { if (isset($_SERVER['LIBRARIES_DIR'])) { diff --git a/src/core/models/PasswordModel.php b/src/core/models/PasswordModel.php index 71d6b343947..6c812212630 100755 --- a/src/core/models/PasswordModel.php +++ b/src/core/models/PasswordModel.php @@ -152,13 +152,19 @@ class PasswordModel return true; } + /** + * @deprecated This function is deprecated and will be removed in future major versions. + * Please use PasswordController::encrypt() instead. + * + * @return string + */ public static function encrypt(array $aArgs) { ValidatorModel::notEmpty($aArgs, ['password']); ValidatorModel::stringType($aArgs, ['password']); $enc_key = CoreConfigModel::getEncryptKey(); - + $cipher_method = 'AES-128-CTR'; $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method)); $crypted_token = openssl_encrypt($aArgs['password'], $cipher_method, $enc_key, 0, $enc_iv) . "::" . bin2hex($enc_iv); @@ -166,17 +172,33 @@ class PasswordModel return $crypted_token; } + /** + * @deprecated This function is deprecated and will be removed in future major versions. + * Please use PasswordController::decrypt() instead. + * + * @return string + */ public static function decrypt(array $aArgs) { ValidatorModel::notEmpty($aArgs, ['cryptedPassword']); ValidatorModel::stringType($aArgs, ['cryptedPassword']); $enc_key = CoreConfigModel::getEncryptKey(); - + $cipher_method = 'AES-128-CTR'; - list($crypted_token, $enc_iv) = explode("::", $aArgs['cryptedPassword']); - $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv)); + $token = null; + try { + $cryptedPasswordParts = explode("::", $aArgs['cryptedPassword']); + if (count($cryptedPasswordParts) !== 2) { + throw new \Exception("Invalid format: cryptedPassword should contain two parts separated by '::'"); + } + list($crypted_token, $enc_iv) = $cryptedPasswordParts; + + $token = openssl_decrypt($crypted_token, $cipher_method, $enc_key, 0, hex2bin($enc_iv)); + } catch (\Throwable $th) { + throw new \Exception($th->getMessage()); + } return $token; } diff --git a/test/unitTests/core/PasswordControllerTest.php b/test/unitTests/core/PasswordControllerTest.php index d28be0b166a..1677afd1dbb 100755 --- a/test/unitTests/core/PasswordControllerTest.php +++ b/test/unitTests/core/PasswordControllerTest.php @@ -11,10 +11,30 @@ namespace MaarchCourrier\Tests\core; use SrcCore\controllers\PasswordController; use SrcCore\http\Response; +use SrcCore\models\CoreConfigModel; use MaarchCourrier\Tests\CourrierTestCase; class PasswordControllerTest extends CourrierTestCase { + private static ?string $generalConfigPath = null; + private static $generalConfigOriginal = null; + private static bool $restoreOriginalConfig = false; + + protected function setUp(): void + { + self::$generalConfigPath = (file_exists("config/config.json") ? "config/config.json" : "config/config.json.default"); + + $generalConfig = file_get_contents(self::$generalConfigPath); + $generalConfig = json_decode($generalConfig, true); + $generalConfig['config']['privateKeyPath'] = getcwd() . '/config/mc_secret.key'; + self::$generalConfigOriginal = $generalConfig; + } + + public function testCheckOriginalConfigIsNotEmpty() + { + $this->assertNotEmpty(self::$generalConfigOriginal); + } + public function testGetRules() { $passwordController = new PasswordController(); @@ -153,4 +173,128 @@ class PasswordControllerTest extends CourrierTestCase $isPasswordValid = $passwordController->isPasswordValid(['password' => 'maarch']); $this->assertSame($isPasswordValid, true); } + + public function provideDataToEncrypt() + { + return [ + 'lower case letters' => [ + "dataToEncrypt" => "abcdefghijklmnopqrstuvwxyz" + ], + 'upper case letters' => [ + "dataToEncrypt" => "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ], + 'numbers' => [ + "dataToEncrypt" => "0123456789" + ], + 'special characters' => [ + "dataToEncrypt" => "`~!@#$%^&*()-_=+[{]}\|;:'\",<.>/? " + ], + 'lower and upper case letters' => [ + "dataToEncrypt" => "Maarch Courrier" + ], + 'lower, upper case letters and numbers' => [ + "dataToEncrypt" => "Maarch Courrier 2301" + ], + 'lower, upper case letters, numbers and special characters' => [ + "dataToEncrypt" => "Wêl¢om€ Tô Määrc|-| Cøμrr¡€r 2301" + ], + ]; + } + + /** + * @dataProvider provideDataToEncrypt + */ + public function testEncryptAndDecrypt($dataToEncrypt) + { + $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]); + $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]); + + $this->assertNotEmpty($encryptedData); + $this->assertNotEmpty($decryptedData); + $this->assertSame($decryptedData, $dataToEncrypt); + } + + /** + * @deprecated This test function is deprecated and will be removed in future major versions. + */ + public function testGetEncryptKeyExpectUsingVhostKey() + { + self::removePrivateKeyPath(); + + $useVhostEncryptKey = CoreConfigModel::useVhostEncryptKey(); + + $this->assertNotEmpty($useVhostEncryptKey); + $this->assertSame(true, $useVhostEncryptKey); + } + + /** + * @deprecated This test function is deprecated and will be removed in future major versions. + */ + public function testGetEncryptKeyExpectUsingFileKey() + { + $usePrivateKey = CoreConfigModel::useVhostEncryptKey(); + + $this->assertEmpty($usePrivateKey); + $this->assertSame(false, $usePrivateKey); + } + + /** + * @deprecated This test function is deprecated and will be removed in future major versions. + * @dataProvider provideDataToEncrypt + */ + public function testEncryptAndDecryptUsingOldCipherMethod($dataToEncrypt) + { + self::removePrivateKeyPath(); + + $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]); + $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]); + + $this->assertNotEmpty($encryptedData); + $this->assertNotEmpty($decryptedData); + $this->assertSame($decryptedData, $dataToEncrypt); + } + + /** + * @deprecated This test function is deprecated and will be removed in future major versions. + * @dataProvider provideDataToEncrypt + */ + public function testEncryptUsingNewCipherMethodAndDecryptUsingOldCipherMethod($dataToEncrypt) + { + $encryptedData = PasswordController::encrypt(['dataToEncrypt' => $dataToEncrypt]); + + self::removePrivateKeyPath(); + + $decryptedData = null; + $exceptionError = null; + + try { + $decryptedData = PasswordController::decrypt(['encryptedData' => $encryptedData]); + } catch (\Exception $e) { + $exceptionError = $e->getMessage(); + } + + $this->assertNotEmpty($encryptedData); + $this->assertNotEmpty($exceptionError); + $this->assertEmpty($decryptedData); + } + + /** + * @deprecated This test function is deprecated and will be removed in future major versions. + */ + private function removePrivateKeyPath() + { + $configPath = \SrcCore\models\CoreConfigModel::getConfigPath(); + $coreConfig = json_decode(file_get_contents($configPath), true); + + unset($coreConfig['config']['privateKeyPath']); + file_put_contents($configPath, json_encode($coreConfig)); + self::$restoreOriginalConfig = true; + } + + protected function tearDown(): void + { + if (!empty(self::$restoreOriginalConfig)) { + file_put_contents(self::$generalConfigPath, json_encode(self::$generalConfigOriginal, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); + } + } } -- GitLab