diff --git a/composer.json b/composer.json index 125c633d81a3f5a18d3e1cfdaf1b1656048bf6a1..85a4e173e45d773f2c7a647fa9a1a809edb518c5 100755 --- a/composer.json +++ b/composer.json @@ -4,6 +4,7 @@ "SrcCore\\" : "src/core/", "Attachment\\" : "src/app/attachment/", "Action\\" : "src/app/action/", + "Configuration\\" : "src/app/configuration/", "Convert\\" : "src/app/convert/", "Docserver\\" : "src/app/docserver/", "Document\\" : "src/app/document/", diff --git a/rest/index.php b/rest/index.php index 64560305f8919bcea088b0ed86e7056f2f6a2aab..b2839008e15a76a785e64a36b2e476472711dd65 100755 --- a/rest/index.php +++ b/rest/index.php @@ -53,6 +53,9 @@ $app->get('/actions', \Action\controllers\ActionController::class . ':get'); //Attachments $app->get('/attachments/{id}', \Attachment\controllers\AttachmentController::class . ':getById'); +//Configurations +$app->put('/configurations/{identifier}', \Configuration\controllers\ConfigurationController::class . ':update'); + //Documents $app->post('/documents', \Document\controllers\DocumentController::class . ':create'); $app->get('/documents', \Document\controllers\DocumentController::class . ':get'); diff --git a/sql/data_fr.sql b/sql/data_fr.sql index e7f0f4ca7f819b9763bf1cfd14499854f8b9b09d..1be6f36a2b10df858ff44225b7e4e8a07e2f0090 100755 --- a/sql/data_fr.sql +++ b/sql/data_fr.sql @@ -25,6 +25,7 @@ ALTER SEQUENCE groups_privileges_id_seq RESTART WITH 1; INSERT INTO groups_privileges (group_id, privilege) VALUES (1, 'manage_rest_users'); INSERT INTO groups_privileges (group_id, privilege) VALUES (2, 'manage_users'); INSERT INTO groups_privileges (group_id, privilege) VALUES (2, 'manage_documents'); +INSERT INTO groups_privileges (group_id, privilege) VALUES (2, 'manage_configuration'); TRUNCATE TABLE users_groups; ALTER SEQUENCE users_groups_id_seq RESTART WITH 1; @@ -79,3 +80,8 @@ INSERT INTO password_rules (label, "value") VALUES ('lockTime', 5); INSERT INTO password_rules (label, "value") VALUES ('historyLastUse', 2); INSERT INTO password_rules (label, "value") VALUES ('renewal', 90); +----- +-- CONFIGURATIONS +----- +INSERT INTO configurations (identifier, value) VALUES ('emailServer', '{"type" : "smtp", "host" : "smtp.gmail.com", "port" : 465, "user" : "", "password" : "", "auth" : true, "secure" : "ssl", "from" : "notifications@maarch.org", "charset" : "utf-8"}'); +ALTER SEQUENCE configurations_id_seq RESTART WITH 2; diff --git a/sql/structure.sql b/sql/structure.sql index 626cd1889d6de4158a87f406ffd26f919160b7f5..a940838c096a1048765c7a5d0ef59671a43b1fac 100755 --- a/sql/structure.sql +++ b/sql/structure.sql @@ -216,3 +216,14 @@ CREATE TABLE password_history CONSTRAINT password_history_pkey PRIMARY KEY (id) ) WITH (OIDS=FALSE); + +DROP TABLE IF EXISTS configurations; +CREATE TABLE configurations +( +id serial NOT NULL, +identifier CHARACTER VARYING (64) NOT NULL, +value json DEFAULT '{}' NOT NULL, +CONSTRAINT configuration_pkey PRIMARY KEY (id), +CONSTRAINT configuration_unique_key UNIQUE (identifier) +) +WITH (OIDS=FALSE); diff --git a/src/app/configuration/controllers/ConfigurationController.php b/src/app/configuration/controllers/ConfigurationController.php new file mode 100755 index 0000000000000000000000000000000000000000..89a49bcbf576850231db9a9d5209066b3de3d74e --- /dev/null +++ b/src/app/configuration/controllers/ConfigurationController.php @@ -0,0 +1,101 @@ +<?php + +/** + * Copyright Maarch since 2008 under licence GPLv3. + * See LICENCE.txt file at the root folder for more details. + * This file is part of Maarch software. + * + */ + +/** + * @brief Configuration Controller + * @author dev@maarch.org + */ + +namespace Configuration\controllers; + +use Configuration\models\ConfigurationModel; +use Respect\Validation\Validator; +use Slim\Http\Request; +use Slim\Http\Response; +use SrcCore\models\AuthenticationModel; +use User\controllers\UserController; + +class ConfigurationController +{ + public function update(Request $request, Response $response, array $args) + { + if (!UserController::hasPrivilege(['userId' => $GLOBALS['id'], 'privilege' => 'manage_configuration'])) { + return $response->withStatus(403)->withJson(['errors' => 'Privilege forbidden']); + } + + $body = $request->getParsedBody(); + + $configuration = ConfigurationModel::getByIdentifier(['identifier' => $args['identifier']]); + + if ($args['identifier'] == 'emailServer') { + $check = ConfigurationController::checkMailer($body); + if (!empty($check['errors'])) { + return $response->withStatus(400)->withJson(['errors' => $check['errors']]); + } + + if ($body['auth'] && empty($body['password']) && !empty($configuration)) { + $configuration['value'] = json_decode($configuration['value'], true); + if (!empty($configuration['value']['password'])) { + $body['password'] = $configuration['value']['password']; + } + } elseif ($body['auth'] && !empty($body['password'])) { + $body['password'] = AuthenticationModel::encrypt(['password' => $body['password']]); + } + $data = json_encode([ + 'type' => $body['type'], + 'host' => $body['host'], + 'port' => $body['port'], + 'user' => empty($body['user']) ? null : $body['user'], + 'password' => empty($body['password']) ? null : $body['password'], + 'auth' => $body['auth'], + 'secure' => $body['secure'], + 'from' => $body['from'], + 'charset' => empty($body['charset']) ? 'utf-8' : $body['charset'] + ]); + } + + if (!empty($data)) { + if (empty($configuration)) { + ConfigurationModel::create(['identifier' => $args['identifier'], 'value' => $data]); + } else { + ConfigurationModel::update(['set' => ['value' => $data], 'where' => ['identifier = ?'], 'data' => [$args['identifier']]]); + } + } + + return $response->withStatus(204); + } + + private static function checkMailer(array $args) + { + if (!Validator::stringType()->notEmpty()->validate($args['type'])) { + return ['errors' => 'Body type is empty or not a string']; + } + + if ($args['type'] == 'smtp') { + if (!Validator::stringType()->notEmpty()->validate($args['host'])) { + return ['errors' => 'Body host is empty or not a string']; + } elseif (!Validator::intVal()->notEmpty()->validate($args['port'])) { + return ['errors' => 'Body port is empty or not an integer']; + } elseif (!Validator::boolType()->validate($args['auth'])) { + return ['errors' => 'Body auth is empty or not a boolean']; + } elseif (!Validator::stringType()->notEmpty()->validate($args['secure'])) { + return ['errors' => 'Body secure is empty or not a string']; + } elseif (!Validator::stringType()->notEmpty()->validate($args['from'])) { + return ['errors' => 'Body from is empty or not a string']; + } + if ($args['auth']) { + if (!Validator::stringType()->notEmpty()->validate($args['user'])) { + return ['errors' => 'Body user is empty or not a string']; + } + } + } + + return ['success' => 'success']; + } +} diff --git a/src/app/configuration/models/ConfigurationModel.php b/src/app/configuration/models/ConfigurationModel.php new file mode 100755 index 0000000000000000000000000000000000000000..56c09e524f9d6867a491c635b714b5f314d50f9a --- /dev/null +++ b/src/app/configuration/models/ConfigurationModel.php @@ -0,0 +1,72 @@ +<?php + +/** +* Copyright Maarch since 2008 under licence GPLv3. +* See LICENCE.txt file at the root folder for more details. +* This file is part of Maarch software. +* +*/ + +/** +* @brief Configuration Model +* @author dev@maarch.org +*/ + +namespace Configuration\models; + +use SrcCore\models\DatabaseModel; +use SrcCore\models\ValidatorModel; + +class ConfigurationModel +{ + public static function getByIdentifier(array $aArgs) + { + ValidatorModel::notEmpty($aArgs, ['identifier']); + ValidatorModel::stringType($aArgs, ['identifier']); + ValidatorModel::arrayType($aArgs, ['select']); + + $configuration = DatabaseModel::select([ + 'select' => empty($aArgs['select']) ? ['*'] : $aArgs['select'], + 'table' => ['configurations'], + 'where' => ['identifier = ?'], + 'data' => [$aArgs['identifier']], + ]); + + if (empty($configuration[0])) { + return []; + } + + return $configuration[0]; + } + + public static function create(array $args) + { + ValidatorModel::notEmpty($args, ['identifier', 'value']); + ValidatorModel::stringType($args, ['identifier', 'value']); + + DatabaseModel::insert([ + 'table' => 'configurations', + 'columnsValues' => [ + 'identifier' => $args['identifier'], + 'value' => $args['value'] + ] + ]); + + return true; + } + + public static function update(array $aArgs) + { + ValidatorModel::notEmpty($aArgs, ['set', 'where', 'data']); + ValidatorModel::arrayType($aArgs, ['set', 'where', 'data']); + + DatabaseModel::update([ + 'table' => 'configurations', + 'set' => $aArgs['set'], + 'where' => $aArgs['where'], + 'data' => $aArgs['data'] + ]); + + return true; + } +} diff --git a/src/core/models/AuthenticationModel.php b/src/core/models/AuthenticationModel.php index 20c27c2978566f1f134bb0c6a824921429717c66..97e77c660765703a7adefab9aff61353a1a93998 100755 --- a/src/core/models/AuthenticationModel.php +++ b/src/core/models/AuthenticationModel.php @@ -153,4 +153,33 @@ class AuthenticationModel return true; } + + public static function encrypt(array $aArgs) + { + ValidatorModel::notEmpty($aArgs, ['password']); + ValidatorModel::stringType($aArgs, ['password']); + + $encryptKey = CoreConfigModel::getEncryptKey(); + + $cipher_method = 'AES-128-CTR'; + $enc_iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($cipher_method)); + $cryptedPassword = openssl_encrypt($aArgs['password'], $cipher_method, $encryptKey, 0, $enc_iv) . "::" . bin2hex($enc_iv); + + return $cryptedPassword; + } + + public static function decrypt(array $aArgs) + { + ValidatorModel::notEmpty($aArgs, ['cryptedPassword']); + ValidatorModel::stringType($aArgs, ['cryptedPassword']); + + $encryptKey = CoreConfigModel::getEncryptKey(); + + $cipher_method = 'AES-128-CTR'; + + list($crypted_token, $enc_iv) = explode("::", $aArgs['cryptedPassword']); + $password = openssl_decrypt($crypted_token, $cipher_method, $encryptKey, 0, hex2bin($enc_iv)); + + return $password; + } } diff --git a/src/core/models/CoreConfigModel.php b/src/core/models/CoreConfigModel.php index 41338c6708131056fcf0e39aa005144f2db11ff9..943bfde606dfa2436ef454e77435d71eb3e0e2a5 100755 --- a/src/core/models/CoreConfigModel.php +++ b/src/core/models/CoreConfigModel.php @@ -94,4 +94,17 @@ class CoreConfigModel return $xmlfile; } + + public static function getEncryptKey() + { + if (!empty($_SERVER['MAARCH_ENCRYPT_KEY'])) { + $encriptKey = $_SERVER['MAARCH_ENCRYPT_KEY']; + } elseif (!empty($_SERVER['REDIRECT_MAARCH_ENCRYPT_KEY'])) { + $encriptKey = $_SERVER['REDIRECT_MAARCH_ENCRYPT_KEY']; + } else { + $encriptKey = "Security Key Maarch Courrier #2008"; + } + + return $encriptKey; + } } diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index 2c72175e7723ad0c73fd3154eb0fecc420810448..fce8549f0781bafdc7da2301b84d048286757445 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -279,7 +279,7 @@ class ClassLoader */ public function setApcuPrefix($apcuPrefix) { - $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; + $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; } /** @@ -377,11 +377,11 @@ class ClassLoader $subPath = $class; while (false !== $lastPos = strrpos($subPath, '\\')) { $subPath = substr($subPath, 0, $lastPos); - $search = $subPath.'\\'; + $search = $subPath . '\\'; if (isset($this->prefixDirsPsr4[$search])) { + $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); foreach ($this->prefixDirsPsr4[$search] as $dir) { - $length = $this->prefixLengthsPsr4[$first][$search]; - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { + if (file_exists($file = $dir . $pathEnd)) { return $file; } } diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 37371201998a4eb294eec8b32712936b7cd2cb7a..51b5b26cdad5be38c95544bfc0ec08be94b34fa9 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -21,6 +21,7 @@ return array( 'Document\\' => array($baseDir . '/src/app/document'), 'Docserver\\' => array($baseDir . '/src/app/docserver'), 'Convert\\' => array($baseDir . '/src/app/convert'), + 'Configuration\\' => array($baseDir . '/src/app/configuration'), 'Attachment\\' => array($baseDir . '/src/app/attachment'), 'Action\\' => array($baseDir . '/src/app/action'), ); diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 48ba9097022211b845ad0740df2298bfe5964979..26394d415203a488daeb9ea47114b31ae4e8bff4 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -56,6 +56,7 @@ class ComposerStaticInit637514d10f1ed5d4c55a005a428a3656 'C' => array ( 'Convert\\' => 8, + 'Configuration\\' => 14, ), 'A' => array ( @@ -125,6 +126,10 @@ class ComposerStaticInit637514d10f1ed5d4c55a005a428a3656 array ( 0 => __DIR__ . '/../..' . '/src/app/convert', ), + 'Configuration\\' => + array ( + 0 => __DIR__ . '/../..' . '/src/app/configuration', + ), 'Attachment\\' => array ( 0 => __DIR__ . '/../..' . '/src/app/attachment',