From d263c5a7ca6c1c4750a5744b34046240b9245393 Mon Sep 17 00:00:00 2001 From: Roly Rudy Gutierrez Pinto Date: Tue, 28 Aug 2018 09:34:11 -0400 Subject: [PATCH] HOR-4492 --- composer.json | 3 +- framework/src/Maveriks/WebApplication.php | 23 ++- gulliver/system/class.g.php | 21 ++- .../translations/english/processmaker.en.po | 18 ++ workflow/engine/controllers/adminProxy.php | 10 +- workflow/engine/controllers/pmTablesProxy.php | 10 ++ workflow/engine/data/mysql/insert.sql | 3 + .../methods/appFolder/appFolderAjax.php | 22 ++- .../methods/enterprise/addonsStoreAction.php | 14 ++ .../methods/enterprise/pluginsImportFile.php | 4 + .../processes/processes_Import_Ajax.php | 10 ++ .../methods/processes/processes_doUpload.php | 25 ++- .../engine/methods/setup/languages_Import.php | 5 + .../methods/setup/pluginsImportFile.php | 4 + workflow/engine/methods/setup/skin_Ajax.php | 4 + .../BusinessModel/Cases/InputDocument.php | 55 +++--- .../BusinessModel/FilesManager.php | 5 - .../src/ProcessMaker/BusinessModel/Light.php | 10 -- .../engine/src/ProcessMaker/Core/System.php | 4 +- .../src/ProcessMaker/Services/Api/Light.php | 9 +- .../Services/Api/Project/FilesManager.php | 19 +- .../ProcessMaker/Validation/Exception429.php | 23 +++ .../src/ProcessMaker/Validation/Rule.php | 137 ++++++++++++++ .../Validation/ValidationUploadedFiles.php | 167 ++++++++++++++++++ .../src/ProcessMaker/Validation/Validator.php | 138 +++++++++++++++ workflow/engine/templates/admin/pmLogo.js | 5 +- workflow/public_html/sysGeneric.php | 7 +- 27 files changed, 658 insertions(+), 97 deletions(-) create mode 100644 workflow/engine/src/ProcessMaker/Validation/Exception429.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/Rule.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php create mode 100644 workflow/engine/src/ProcessMaker/Validation/Validator.php diff --git a/composer.json b/composer.json index 6c2931319..b13829995 100644 --- a/composer.json +++ b/composer.json @@ -87,7 +87,8 @@ "thirdparty/pake/pakeFunction.php", "thirdparty/HTMLPurifier/HTMLPurifier.auto.php", "workflow/engine/classes/class.pmFunctions.php", - "workflow/engine/src/ProcessMaker/Util/helpers.php" + "workflow/engine/src/ProcessMaker/Util/helpers.php", + "framework/src/Maveriks/Extension/Restler/UploadFormat.php" ] }, "autoload-dev": { diff --git a/framework/src/Maveriks/WebApplication.php b/framework/src/Maveriks/WebApplication.php index fe9632e83..9344be9dd 100644 --- a/framework/src/Maveriks/WebApplication.php +++ b/framework/src/Maveriks/WebApplication.php @@ -3,14 +3,16 @@ namespace Maveriks; use Bootstrap; +use G; +use Illuminate\Foundation\Http\Kernel; +use Luracast\Restler\Format\UploadFormat; +use Luracast\Restler\RestException; use Maveriks\Util; use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; use ProcessMaker\Services; use ProcessMaker\Services\Api; -use Luracast\Restler\RestException; -use Illuminate\Foundation\Http\Kernel; -use G; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * Web application bootstrap @@ -252,8 +254,6 @@ class WebApplication */ protected function initRest($uri, $version, $multipart = false) { - require_once $this->rootDir . "/framework/src/Maveriks/Extension/Restler/UploadFormat.php"; - // $servicesDir contains directory where Services Classes are allocated $servicesDir = $this->workflowDir . 'engine' . DS . 'src' . DS . 'ProcessMaker' . DS . 'Services' . DS; // $apiDir - contains directory to scan classes and add them to Restler @@ -331,6 +331,19 @@ class WebApplication $this->rest->setOverridingFormats('JsonFormat', 'UploadFormat'); + UploadFormat::$customValidationFunction = function($target) { + $validator = ValidationUploadedFiles::getValidationUploadedFiles()->runRules([ + 'filename' => $target['name'], + 'path' => $target['tmp_name'] + ]); + if ($validator->fails()) { + if ($validator->getStatus() === 429) { + throw new RestException($validator->getStatus()); + } + } + return true; + }; + // scan all api directory to find api classes $classesList = Util\Common::rglob($apiDir . "/*"); diff --git a/gulliver/system/class.g.php b/gulliver/system/class.g.php index f7bf48ac6..6392a4696 100644 --- a/gulliver/system/class.g.php +++ b/gulliver/system/class.g.php @@ -4,6 +4,7 @@ use ProcessMaker\Core\System; use ProcessMaker\AuditLog\AuditLog; use ProcessMaker\Plugins\PluginRegistry; use ProcessMaker\Services\OAuth2\Server; +use ProcessMaker\Validation\ValidationUploadedFiles; class G { @@ -1183,7 +1184,7 @@ class G \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 200, 'Php Execution', $filename); require_once($filename); } else { - $message = G::LoadTranslation('THE_PHP_FILES_EXECUTION_WAS_DISABLED'); + $message = G::LoadTranslation('ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED'); \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 550, $message, $filename); echo $message; } @@ -5488,6 +5489,16 @@ class G */ public static function verifyInputDocExtension($InpDocAllowedFiles, $fileName, $filesTmpName) { + $error = null; + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) use(&$error) { + $error = new stdclass(); + $error->status = false; + $error->message = $validator->getMessage(); + }); + if (!is_null($error)) { + return $error; + } + // Initialize variables $res = new stdclass(); $res->status = false; @@ -5497,14 +5508,6 @@ class G $aux = pathinfo($fileName); $fileExtension = isset($aux['extension']) ? strtolower($aux['extension']) : ''; - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $fileExtension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileName); - $res->status = false; - $res->message = $message; - return $res; - } - // If required extension is *.* don't validate if (in_array('*', $allowedTypes)) { $res->status = true; diff --git a/workflow/engine/content/translations/english/processmaker.en.po b/workflow/engine/content/translations/english/processmaker.en.po index 0114a1a82..68431bc17 100644 --- a/workflow/engine/content/translations/english/processmaker.en.po +++ b/workflow/engine/content/translations/english/processmaker.en.po @@ -24851,12 +24851,24 @@ msgstr "The default configuration was not defined" msgid "The change might cause data loss in the PM table. Do you want to continue?" msgstr "The change might cause data loss in the PM table. Do you want to continue?" +# TRANSLATION +# LABEL/ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED +#: LABEL/ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED +msgid "The PHP files execution was disabled please contact the system administrator." +msgstr "The PHP files execution was disabled please contact the system administrator." + # TRANSLATION # LABEL/ID_THE_REASON_REASSIGN_USER_EMPTY #: LABEL/ID_THE_REASON_REASSIGN_USER_EMPTY msgid "Please complete the reassign reason." msgstr "Please complete the reassign reason." +# TRANSLATION +# LABEL/ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED +#: LABEL/ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED +msgid "The upload of PHP files was disabled please contact the system administrator." +msgstr "The upload of PHP files was disabled please contact the system administrator." + # TRANSLATION # LABEL/ID_THE_USERNAME_EMAIL_IS_INCORRECT #: LABEL/ID_THE_USERNAME_EMAIL_IS_INCORRECT @@ -25181,6 +25193,12 @@ msgstr "Today" msgid "Tools" msgstr "Tools" +# TRANSLATION +# LABEL/ID_TOO_MANY_REQUESTS +#: LABEL/ID_TOO_MANY_REQUESTS +msgid "Too many requests" +msgstr "Too many requests" + # TRANSLATION # LABEL/ID_TOP_MARGIN #: LABEL/ID_TOP_MARGIN diff --git a/workflow/engine/controllers/adminProxy.php b/workflow/engine/controllers/adminProxy.php index 6c86f2c06..4809054bc 100644 --- a/workflow/engine/controllers/adminProxy.php +++ b/workflow/engine/controllers/adminProxy.php @@ -2,6 +2,7 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * adminProxy.php @@ -1025,7 +1026,14 @@ class adminProxy extends HttpProxyController */ public function uploadImage() { - //!dataSystem + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + echo G::json_encode([ + 'success' => true, + 'failed' => true, + 'message' => $validator->getMessage() + ]); + exit(); + }); $filter = new InputFilter(); $_SERVER["REQUEST_URI"] = $filter->xssFilterHard($_SERVER["REQUEST_URI"]); diff --git a/workflow/engine/controllers/pmTablesProxy.php b/workflow/engine/controllers/pmTablesProxy.php index ed62a64a1..4507ea2ff 100644 --- a/workflow/engine/controllers/pmTablesProxy.php +++ b/workflow/engine/controllers/pmTablesProxy.php @@ -8,6 +8,8 @@ */ use ProcessMaker\Core\System; +use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ValidationUploadedFiles; header("Content-type: text/html;charset=utf-8"); require_once 'classes/model/AdditionalTables.php'; @@ -723,6 +725,9 @@ class pmTablesProxy extends HttpProxyController } try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception429($validator->getMessage()); + }); $result = new stdClass(); $errors = ''; $fromConfirm = false; @@ -891,6 +896,11 @@ class pmTablesProxy extends HttpProxyController } $result->message = $msg; + } catch (Exception429 $e) { + $result = new stdClass(); + $result->success = false; + $result->errorType = 'notice'; + $result->message = $e->getMessage(); } catch (Exception $e) { $result = new stdClass(); $result->fromAdmin = $fromAdmin; diff --git a/workflow/engine/data/mysql/insert.sql b/workflow/engine/data/mysql/insert.sql index b531f3398..346bffb5d 100644 --- a/workflow/engine/data/mysql/insert.sql +++ b/workflow/engine/data/mysql/insert.sql @@ -61035,7 +61035,9 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_THE_APPLICATION_IS_NOT_CANCELED','en','Error: The application {0} is not canceled.','2016-06-15') , ( 'LABEL','ID_THE_DEFAULT_CONFIGURATION','en','The default configuration was not defined','2016-11-16') , ( 'LABEL','ID_THE_NAME_CHANGE_MAY_CAUSE_DATA_LOSS','en','The change might cause data loss in the PM table. Do you want to continue?','2017-03-30') , +( 'LABEL','ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED','en','The PHP files execution was disabled please contact the system administrator.','2018-04-20') , ( 'LABEL','ID_THE_REASON_REASSIGN_USER_EMPTY','en','Please complete the reassign reason.','2016-10-20') , +( 'LABEL','ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED','en','The upload of PHP files was disabled please contact the system administrator.','2018-04-20') , ( 'LABEL','ID_THE_USERNAME_EMAIL_IS_INCORRECT','en','The username or email is incorrect','2018-01-18') , ( 'LABEL','ID_THIS_MONTH','en','This Month','2014-01-15') , ( 'LABEL','ID_THIS_QUARTER','en','This quarter','2014-01-15') , @@ -61092,6 +61094,7 @@ INSERT INTO TRANSLATION (TRN_CATEGORY,TRN_ID,TRN_LANG,TRN_VALUE,TRN_UPDATE_DATE ( 'LABEL','ID_TO','en','To','2014-01-15') , ( 'LABEL','ID_TODAY','en','Today','2014-01-15') , ( 'LABEL','ID_TOOLS','en','Tools','2014-01-15') , +( 'LABEL','ID_TOO_MANY_REQUESTS','en','Too many requests','2018-04-25') , ( 'LABEL','ID_TOP_MARGIN','en','Top Margin','2014-01-15') , ( 'LABEL','ID_TOTAL_CASES','en','Total Cases','2014-01-15') , ( 'LABEL','ID_TOTAL_CASES_REASSIGNED','en','Total Cases Reassigned','2014-01-15') , diff --git a/workflow/engine/methods/appFolder/appFolderAjax.php b/workflow/engine/methods/appFolder/appFolderAjax.php index b75f038b5..33072d250 100644 --- a/workflow/engine/methods/appFolder/appFolderAjax.php +++ b/workflow/engine/methods/appFolder/appFolderAjax.php @@ -1,6 +1,7 @@ xssFilterHard($_POST); @@ -1445,6 +1446,15 @@ function checkTree($uidOriginFolder, $uidNewFolder) */ function uploadExternalDocument() { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + $response = [ + 'error' => $validator->getMessage(), + 'message' => $validator->getMessage(), + 'success' => false + ]; + print_r(G::json_encode($response)); + die(); + }); $response = []; $response['action'] = $_POST['action'] . " - " . $_POST['option']; $response['error'] = "error"; @@ -1531,18 +1541,6 @@ function uploadExternalDocument() //Read. Instance Document classes if (!empty($quequeUpload)) { - foreach ($quequeUpload as $key => $fileObj) { - $extension = pathinfo($fileObj['fileName'], PATHINFO_EXTENSION); - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileObj['fileName']); - $response['error'] = $message; - $response['message'] = $message; - $response['success'] = false; - print_r(G::json_encode($response)); - exit(); - } - } $docUid = $_POST['docUid']; $appDocUid = isset($_POST['APP_DOC_UID']) ? $_POST['APP_DOC_UID'] : ""; $docVersion = isset($_POST['docVersion']) ? $_POST['docVersion'] : ""; diff --git a/workflow/engine/methods/enterprise/addonsStoreAction.php b/workflow/engine/methods/enterprise/addonsStoreAction.php index 9dc5133af..7197606cb 100644 --- a/workflow/engine/methods/enterprise/addonsStoreAction.php +++ b/workflow/engine/methods/enterprise/addonsStoreAction.php @@ -2,6 +2,8 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\Exception429; +use ProcessMaker\Validation\ValidationUploadedFiles; function runBgProcessmaker($task, $log) { @@ -16,6 +18,9 @@ function runBgProcessmaker($task, $log) } try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception429($validator->getMessage()); + }); if (isset($_REQUEST["action"])) { $action = $_REQUEST["action"]; } else { @@ -312,6 +317,15 @@ try { $result["addons"] = array(); } G::outRes(G::json_encode($result)); +} catch (Exception429 $e) { + $token = strtotime("now"); + PMException::registerErrorLog($e, $token); + G::outRes( + G::json_encode(array( + "success" => false, + "errors" => $e->getMessage() + )) + ); } catch (Exception $e) { $token = strtotime("now"); PMException::registerErrorLog($e, $token); diff --git a/workflow/engine/methods/enterprise/pluginsImportFile.php b/workflow/engine/methods/enterprise/pluginsImportFile.php index 490d3f204..64939795f 100644 --- a/workflow/engine/methods/enterprise/pluginsImportFile.php +++ b/workflow/engine/methods/enterprise/pluginsImportFile.php @@ -27,6 +27,7 @@ global $RBAC; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; $RBAC->requirePermissions("PM_SETUP_ADVANCE"); require_once PATH_CORE . 'methods' . PATH_SEP . 'enterprise' . PATH_SEP . 'enterprise.php'; @@ -35,6 +36,9 @@ $response = array(); $status = 1; try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); if (!isset($_FILES["form"]["error"]["PLUGIN_FILENAME"]) || $_FILES["form"]["error"]["PLUGIN_FILENAME"] == 1) { $str = "There was an error uploading the file, probably the file size if greater than upload_max_filesize parameter in php.ini, please check this parameter and try again."; diff --git a/workflow/engine/methods/processes/processes_Import_Ajax.php b/workflow/engine/methods/processes/processes_Import_Ajax.php index 870db04eb..0f724bc58 100644 --- a/workflow/engine/methods/processes/processes_Import_Ajax.php +++ b/workflow/engine/methods/processes/processes_Import_Ajax.php @@ -23,6 +23,16 @@ */ use \ProcessMaker\Importer\XmlImporter; +use ProcessMaker\Validation\ValidationUploadedFiles; + +ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + echo G::json_encode([ + 'status' => 'ERROR', + 'success' => true, + 'catchMessage' => $validator->getMessage() + ]); + exit(); +}); ini_set("max_execution_time", 0); $affectedGroups = array(); diff --git a/workflow/engine/methods/processes/processes_doUpload.php b/workflow/engine/methods/processes/processes_doUpload.php index 07cdf06e4..db0077ef9 100644 --- a/workflow/engine/methods/processes/processes_doUpload.php +++ b/workflow/engine/methods/processes/processes_doUpload.php @@ -1,5 +1,7 @@ userCanAccess('PM_FACTORY') == 1) { @@ -25,26 +27,23 @@ if ($RBAC->userCanAccess('PM_FACTORY') == 1) { } } - $fileName = $_FILES['form']['name']; - $canUploadPhpFile = true; - $extension = pathinfo($fileName, PATHINFO_EXTENSION); - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php') { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $fileName); - $canUploadPhpFile = false; - } + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + $response = [ + 'result' => 0, + 'msg' => $validator->getMessage() + ]; + print_r(G::json_encode($response)); + die(); + }); - if ($_FILES['form']['error'] == "0" && $canUploadPhpFile) { + $fileName = $_FILES['form']['name']; + if ($_FILES['form']['error'] == "0") { G::uploadFile($_FILES['form']['tmp_name'], $sDirectory, $fileName); $msg = "Uploaded (" . (round((filesize($sDirectory . $fileName) / 1024) * 10) / 10) . " kb)"; $result = 1; } else { $msg = "Failed"; - if ($canUploadPhpFile === false) { - $msg = $message; - } $result = 0; } - echo "{'result': $result, 'msg':'$msg'}"; } diff --git a/workflow/engine/methods/setup/languages_Import.php b/workflow/engine/methods/setup/languages_Import.php index ce808315b..1a1f3fa55 100644 --- a/workflow/engine/methods/setup/languages_Import.php +++ b/workflow/engine/methods/setup/languages_Import.php @@ -2,6 +2,8 @@ require_once "classes/model/Language.php"; +use ProcessMaker\Validation\ValidationUploadedFiles; + global $RBAC; $access = $RBAC->userCanAccess('PM_SETUP_ADVANCE'); @@ -24,6 +26,9 @@ if ($access != 1) { $result = new stdClass(); try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); //if the xmlform path is writeable if (!is_writable(PATH_XMLFORM)) { throw new Exception(G::LoadTranslation('IMPORT_LANGUAGE_ERR_NO_WRITABLE')); diff --git a/workflow/engine/methods/setup/pluginsImportFile.php b/workflow/engine/methods/setup/pluginsImportFile.php index 3cb70b2d0..8ddce45fa 100644 --- a/workflow/engine/methods/setup/pluginsImportFile.php +++ b/workflow/engine/methods/setup/pluginsImportFile.php @@ -26,11 +26,15 @@ use ProcessMaker\Core\System; use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; global $RBAC; $RBAC->requirePermissions('PM_SETUP_ADVANCE'); try { + ValidationUploadedFiles::getValidationUploadedFiles()->dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); //load the variables if (!isset($_FILES['form']['error']['PLUGIN_FILENAME']) || $_FILES['form']['error']['PLUGIN_FILENAME'] == 1) { throw (new Exception(G::loadTranslation('ID_ERROR_UPLOADING_PLUGIN_FILENAME'))); diff --git a/workflow/engine/methods/setup/skin_Ajax.php b/workflow/engine/methods/setup/skin_Ajax.php index d939fac45..ad47b31bb 100644 --- a/workflow/engine/methods/setup/skin_Ajax.php +++ b/workflow/engine/methods/setup/skin_Ajax.php @@ -1,6 +1,7 @@ dispach(function($validator) { + throw new Exception($validator->getMessage()); + }); if (! isset( $_FILES['uploadedFile'] )) { throw (new Exception( G::LoadTranslation( 'ID_SKIN_FILE_REQUIRED' ) )); } diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php index f8e177c1e..1b73cb143 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Cases/InputDocument.php @@ -1,28 +1,29 @@ dispach(function($validator) { + G::SendMessageText($validator->getMessage(), "ERROR"); + $url = explode("sys" . config("system.workspace"), $_SERVER['HTTP_REFERER']); + G::header("location: " . "/sys" . config("system.workspace") . $url[1]); + die(); + }); $arrayField = array(); $arrayFileName = array(); $arrayFileTmpName = array(); @@ -1034,16 +1041,6 @@ class InputDocument $aFields = array("APP_UID" => $appUid, "DEL_INDEX" => $delIndex, "USR_UID" => $userUid, "DOC_UID" => -1, "APP_DOC_TYPE" => "ATTACHED", "APP_DOC_CREATE_DATE" => date("Y-m-d H:i:s"), "APP_DOC_COMMENT" => "", "APP_DOC_TITLE" => "", "APP_DOC_FILENAME" => $arrayFileName[$i], "APP_DOC_FIELDNAME" => $fieldName); } - $sExtension = pathinfo($aFields["APP_DOC_FILENAME"]); - if (Bootstrap::getDisablePhpUploadExecution() === 1 && $sExtension["extension"] === 'php') { - $message = G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, 'processmaker.log'); - G::SendMessageText($message, "ERROR"); - $backUrlObj = explode("sys" . config("system.workspace"), $_SERVER['HTTP_REFERER']); - G::header("location: " . "/sys" . config("system.workspace") . $backUrlObj[1]); - die(); - } - $oAppDocument = new AppDocument(); $oAppDocument->create($aFields); diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php b/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php index 2a91bcde0..c1260a5dd 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/FilesManager.php @@ -196,11 +196,6 @@ class FilesManager if ($extention == '.exe') { throw new \Exception(\G::LoadTranslation('ID_FILE_UPLOAD_INCORRECT_EXTENSION')); } - if (\Bootstrap::getDisablePhpUploadExecution() === 1 && $extention === '.php' && !$isImport) { - $message = \G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - \Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $aData['prf_filename']); - throw new \Exception($message); - } break; default: $sDirectory = PATH_DATA_MAILTEMPLATES . $sProcessUID . PATH_SEP . $sSubDirectory . $aData['prf_filename']; diff --git a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php index eaeba18ec..fda4a306e 100644 --- a/workflow/engine/src/ProcessMaker/BusinessModel/Light.php +++ b/workflow/engine/src/ProcessMaker/BusinessModel/Light.php @@ -995,16 +995,6 @@ class Light $confEnvSetting = $config->getFormats(); $user = new Users(); foreach ($requestData as $k => $file) { - $ext = pathinfo($file['name'], PATHINFO_EXTENSION); - if (Bootstrap::getDisablePhpUploadExecution() === 1 && $ext === 'php') { - $message = G::LoadTranslation('THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED'); - Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $message, $file['name']); - $response[$k]['error'] = array( - "code" => "400", - "message" => $message - ); - continue; - } $cases = new Cases(); $delIndex = $cases->getCurrentDelegation($appUid, $userUid); $docUid = !empty($file['docUid']) ? $file['docUid'] : -1; diff --git a/workflow/engine/src/ProcessMaker/Core/System.php b/workflow/engine/src/ProcessMaker/Core/System.php index 1a5f7aea3..5e5475ace 100644 --- a/workflow/engine/src/ProcessMaker/Core/System.php +++ b/workflow/engine/src/ProcessMaker/Core/System.php @@ -66,7 +66,9 @@ class System 'logging_level' => 'INFO', 'smtp_timeout' => 20, 'google_map_api_key' => '', - 'google_map_signature' => '' + 'google_map_signature' => '', + 'logging_level' => 'INFO', + 'upload_attempts_limit_per_user' => '60,1' ); /** diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Light.php b/workflow/engine/src/ProcessMaker/Services/Api/Light.php index 11f126b25..7dec46cf7 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Light.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Light.php @@ -23,6 +23,7 @@ use ProcessMaker\Project\Adapter; use ProcessMaker\Services\Api; use ProcessMaker\Services\Api\Project\Activity\Step; use ProcessMaker\Util\DateTime; +use ProcessMaker\Validation\Exception429; use RBAC; use stdclass; use StepPeer; @@ -1466,10 +1467,11 @@ class Light extends Api $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); $filesUids = $oMobile->postUidUploadFiles($userUid, $app_uid, $request_data); + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); } catch (Exception $e) { - throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); + throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } - return $filesUids; } @@ -1501,10 +1503,11 @@ class Light extends Api $userUid = $this->getUserId(); $oMobile = new BusinessModelLight(); $response = $oMobile->documentUploadFiles($userUid, $app_uid, $app_doc_uid, $request_data); + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); } catch (Exception $e) { throw (new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage())); } - return $response; } diff --git a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php index 043ec9326..876cd15cd 100644 --- a/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php +++ b/workflow/engine/src/ProcessMaker/Services/Api/Project/FilesManager.php @@ -1,8 +1,11 @@ getUserId(); $request_data = (array)($request_data); $request_data = array_merge(array('prf_content' => $prf_content ), $request_data); - $filesManager = new \ProcessMaker\BusinessModel\FilesManager(); + $filesManager = new FilesManagerBusinessModel(); $arrayData = $filesManager->addProcessFilesManager($prj_uid, $userUid, $request_data); //Response $response = $arrayData; - } catch (\Exception $e) { + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); + } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } @@ -85,11 +90,13 @@ class FilesManager extends Api public function doPostProcessFilesManagerUpload($prj_uid, $prf_uid) { try { - $filesManager = new \ProcessMaker\BusinessModel\FilesManager(); + $filesManager = new FilesManagerBusinessModel(); $sData = $filesManager->uploadProcessFilesManager($prj_uid, $prf_uid); //Response $response = $sData; - } catch (\Exception $e) { + } catch (Exception429 $e) { + throw new RestException($e->getStatus()); + } catch (Exception $e) { //response throw new RestException(Api::STAT_APP_EXCEPTION, $e->getMessage()); } diff --git a/workflow/engine/src/ProcessMaker/Validation/Exception429.php b/workflow/engine/src/ProcessMaker/Validation/Exception429.php new file mode 100644 index 000000000..200844d39 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Exception429.php @@ -0,0 +1,23 @@ +status; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/Rule.php b/workflow/engine/src/ProcessMaker/Validation/Rule.php new file mode 100644 index 000000000..7d2610b14 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Rule.php @@ -0,0 +1,137 @@ +data; + } + + /** + * Get the Closure function. + * @return Closure + */ + function getCallback() + { + return $this->callback; + } + + /** + * Gets the Closure function that applies the validation rule. + * @return Closure + */ + function getCallbackLog() + { + return $this->callbackLog; + } + + /** + * Get the message to be saved in the log if the rule is not fulfilled. + * @return string + */ + function getMessage() + { + return $this->message; + } + + /** + * Get status code. + * @return int + */ + function getStatus() + { + return $this->status; + } + + /** + * Registers the data and the Closure function that contains the validation + * rule. + * @param array $data + * @param Closure $callback + * @return Rule + */ + public function validate($data, $callback = null) + { + $this->data = (object) $data; + if (is_callable($callback)) { + $this->callback = $callback; + } + return $this; + } + + /** + * Registers the customized message in case the validation rule is not met. + * @param string $message + * @return Rule + */ + public function message($message = "") + { + $this->message = $message; + return $this; + } + + /** + * Set status code. + * @param int $status + * @return $this + */ + function status($status = 0) + { + $this->status = $status; + return $this; + } + + /** + * Registers the Closure function in case the validation rule is not met. + * @param Closure $callback + * @return Rule + */ + public function log($callback = null) + { + if (is_callable($callback)) { + $this->callbackLog = $callback; + } + return $this; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php new file mode 100644 index 000000000..05146777b --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/ValidationUploadedFiles.php @@ -0,0 +1,167 @@ +addRule() + ->validate($file, function($file) { + $filesystem = new Filesystem(); + $extension = $filesystem->extension($file->filename); + + return Bootstrap::getDisablePhpUploadExecution() === 1 && $extension === 'php'; + }) + ->status(550) + ->message(G::LoadTranslation('ID_THE_UPLOAD_OF_PHP_FILES_WAS_DISABLED')) + ->log(function($rule) { + /** + * Levels supported by MonologProvider is: + * 100 "DEBUG" + * 200 "INFO" + * 250 "NOTICE" + * 300 "WARNING" + * 400 "ERROR" + * 500 "CRITICAL" + * 550 "ALERT" + * 600 "EMERGENCY" + */ + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 550, $rule->getMessage(), $rule->getData()->filename); + }); + + //rule: upload_attempts_limit_per_user + $validator->addRule() + ->validate($file, function($file) { + $systemConfiguration = System::getSystemConfiguration('', '', config("system.workspace")); + $filesWhiteList = explode(',', $systemConfiguration['upload_attempts_limit_per_user']); + $userId = Server::getUserId(); + $key = config("system.workspace") . '/' . $userId; + $attemps = (int) trim($filesWhiteList[0]); + $minutes = (int) trim($filesWhiteList[1]); + $pastAttemps = Cache::remember($key, $minutes, function() { + return 1; + }); + //We only increase when the file path exists, useful when pre-validation is done. + if (isset($file->path)) { + Cache::increment($key, 1); + } + if ($pastAttemps <= $attemps) { + return false; + } + return true; + }) + ->status(429) + ->message(G::LoadTranslation('ID_TOO_MANY_REQUESTS')) + ->log(function($rule) { + /** + * Levels supported by MonologProvider is: + * 100 "DEBUG" + * 200 "INFO" + * 250 "NOTICE" + * 300 "WARNING" + * 400 "ERROR" + * 500 "CRITICAL" + * 550 "ALERT" + * 600 "EMERGENCY" + */ + Bootstrap::registerMonologPhpUploadExecution('phpUpload', 250, $rule->getMessage(), $rule->getData()->filename); + }); + + return $validator->validate(); + } + + /** + * File upload validation. + * + * @return $this + */ + public function runRulesToAllUploadedFiles() + { + $files = $_FILES; + if (!is_array($files)) { + return; + } + $this->fails = []; + foreach ($files as $file) { + $data = (object) $file; + if (!is_array($data->name) || !is_array($data->tmp_name)) { + $data->name = [$data->name]; + $data->tmp_name = [$data->tmp_name]; + } + foreach ($data->name as $key => $value) { + if (empty($value)) { + continue; + } + $validator = $this->runRules(['filename' => $value, 'path' => $data->tmp_name[$key]]); + if ($validator->fails()) { + $this->fails[] = $validator; + } + } + } + return $this; + } + + /** + * Get the first error and call the argument function. + * + * @param function $callback + * @return $this + */ + public function dispach($callback) + { + if (!empty($this->fails[0])) { + if (!empty($callback) && is_callable($callback)) { + $callback($this->fails[0], $this->fails); + } + } + return $this; + } + + /** + * It obtains a single object to be used as a record of the whole environment. + * + * @return object + */ + public static function getValidationUploadedFiles() + { + if (self::$validationUploadedFiles === null) { + self::$validationUploadedFiles = new ValidationUploadedFiles(); + } + return self::$validationUploadedFiles; + } +} diff --git a/workflow/engine/src/ProcessMaker/Validation/Validator.php b/workflow/engine/src/ProcessMaker/Validation/Validator.php new file mode 100644 index 000000000..0fc1fe428 --- /dev/null +++ b/workflow/engine/src/ProcessMaker/Validation/Validator.php @@ -0,0 +1,138 @@ +message; + } + + /** + * Get status code. + * @return int + */ + function getStatus() + { + return $this->status; + } + + /** + * Get the Closure function. + * @return Closure + */ + function getCallback() + { + return $this->callback; + } + + /** + * Add a validation rule. + * The rules are verified in the order in which they have been added. + * + * @param Rule $rule + * @return Rule + */ + public function addRule($rule = null) + { + if (!$rule instanceof Rule) { + $rule = new Rule(); + } + $this->rules[] = $rule; + return $rule; + } + + /** + * Process all added validation rules. + * @return Validator + */ + public function validate() + { + $this->message = ""; + $this->status = 0; + $this->fails = false; + foreach ($this->rules as $rule) { + $callback = $rule->getCallback(); + if (is_callable($callback)) { + if ($callback($rule->getData())) { + $this->message = $rule->getMessage(); + $this->status = $rule->getStatus(); + $this->fails = true; + $getCallbackLog = $rule->getCallbackLog(); + if (is_callable($getCallbackLog)) { + $getCallbackLog($rule); + } + break; + } + } + } + $callbackAfter = $this->getCallback(); + if (is_callable($callbackAfter)) { + $callbackAfter($this); + } + return $this; + } + + /** + * Get the current status of the validation, the value is true if there was a + * failure and false if all the validation rules have been passed. + * @return boolean + */ + public function fails() + { + return $this->fails; + } + + /** + * The Closure function is called when the validation process is finished. + * @param Closure $callback + * @return Validator + */ + public function after($callback) + { + if (is_callable($callback)) { + $this->callback = $callback; + } + return $this; + } +} diff --git a/workflow/engine/templates/admin/pmLogo.js b/workflow/engine/templates/admin/pmLogo.js index 1993cdf44..c4327afd1 100644 --- a/workflow/engine/templates/admin/pmLogo.js +++ b/workflow/engine/templates/admin/pmLogo.js @@ -283,7 +283,10 @@ Ext.onReady(function () { } else { var messageError = ""; - if (obj.failed == "1") { + if (obj.failed === true) { + messageError = obj.message; + } + else if (obj.failed == "1") { //| 1-> Fail in the type of the image messageError = _('ID_ERROR_UPLOADING_IMAGE_TYPE'); } diff --git a/workflow/public_html/sysGeneric.php b/workflow/public_html/sysGeneric.php index 773910f0e..ae4928652 100644 --- a/workflow/public_html/sysGeneric.php +++ b/workflow/public_html/sysGeneric.php @@ -6,6 +6,7 @@ use ProcessMaker\Core\AppEvent; use ProcessMaker\ChangeLog\ChangeLog; /*----------------------------------********---------------------------------*/ use ProcessMaker\Plugins\PluginRegistry; +use ProcessMaker\Validation\ValidationUploadedFiles; /** * bootstrap - ProcessMaker Bootstrap @@ -818,7 +819,7 @@ if (substr(SYS_COLLECTION, 0, 8) === 'gulliver') { $isWebEntry = \ProcessMaker\BusinessModel\WebEntry::isWebEntry(SYS_COLLECTION, $phpFile); if (\Bootstrap::getDisablePhpUploadExecution() === 1 && !$isWebEntry) { - $message = \G::LoadTranslation('THE_PHP_FILES_EXECUTION_WAS_DISABLED'); + $message = \G::LoadTranslation('ID_THE_PHP_FILES_EXECUTION_WAS_DISABLED'); \Bootstrap::registerMonologPhpUploadExecution('phpExecution', 550, $message, $phpFile); echo $message; die(); @@ -1085,6 +1086,8 @@ if (!defined('EXECUTE_BY_CRON')) { $oPluginRegistry->init(); if ($isControllerCall) { //Instance the Controller object and call the request method + ValidationUploadedFiles::getValidationUploadedFiles() + ->runRulesToAllUploadedFiles(); $controller = new $controllerClass(); $controller->setHttpRequestData($_REQUEST);//NewRelic Snippet - By JHL transactionLog($controllerAction); @@ -1105,6 +1108,8 @@ if (!defined('EXECUTE_BY_CRON')) { ->setLanguage(SYS_LANG) ->getUsrIdByUsrUid(empty($_SESSION['USER_LOGGED']) ? '' : $_SESSION['USER_LOGGED']); /*----------------------------------********---------------------------------*/ + ValidationUploadedFiles::getValidationUploadedFiles() + ->runRulesToAllUploadedFiles(); require_once $phpFile; }