diff --git a/application/Espo/Core/Utils/File/Manager.php b/application/Espo/Core/Utils/File/Manager.php index 8296acc189..5d2c623628 100644 --- a/application/Espo/Core/Utils/File/Manager.php +++ b/application/Espo/Core/Utils/File/Manager.php @@ -44,7 +44,6 @@ class Manager if (isset($config)) { $params = array( 'defaultPermissions' => $config->get('defaultPermissions'), - 'permissionMap' => $config->get('permissionMap'), ); } @@ -381,11 +380,11 @@ class Manager { $fullPath = $this->concatPaths($path); - if (file_exists($fullPath) && is_dir($path)) { + if (file_exists($fullPath) && is_dir($fullPath)) { return true; } - $defaultPermissions = $this->getPermissionUtils()->getDefaultPermissions(); + $defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($fullPath); if (!isset($permission)) { $permission = (string) $defaultPermissions['dir']; @@ -393,7 +392,11 @@ class Manager } try { + $umask = @umask(0); $result = mkdir($fullPath, $permission, true); + if ($umask) { + @umask($umask); + } if (!empty($defaultPermissions['user'])) { $this->getPermissionUtils()->chown($fullPath); @@ -484,11 +487,11 @@ class Manager */ public function checkCreateFile($filePath) { - $defaultPermissions = $this->getPermissionUtils()->getDefaultPermissions(); + $defaultPermissions = $this->getPermissionUtils()->getRequiredPermissions($filePath); if (file_exists($filePath)) { if (!is_writable($filePath) && !in_array($this->getPermissionUtils()->getCurrentPermission($filePath), array($defaultPermissions['file'], $defaultPermissions['dir']))) { - return $this->getPermissionUtils()->setDefaultPermissions($filePath, true); + return $this->getPermissionUtils()->setDefaultPermissions($filePath); } return true; } @@ -504,7 +507,7 @@ class Manager } if (touch($filePath)) { - return $this->getPermissionUtils()->setDefaultPermissions($filePath, true); + return $this->getPermissionUtils()->setDefaultPermissions($filePath); } return false; diff --git a/application/Espo/Core/Utils/File/Permission.php b/application/Espo/Core/Utils/File/Permission.php index 41ee2a1109..1c260c1a57 100644 --- a/application/Espo/Core/Utils/File/Permission.php +++ b/application/Espo/Core/Utils/File/Permission.php @@ -28,8 +28,9 @@ ************************************************************************/ namespace Espo\Core\Utils\File; -use Espo\Core\Utils, - Espo\Core\Exceptions\Error; + +use Espo\Core\Utils; +use Espo\Core\Exceptions\Error; class Permission { @@ -44,47 +45,45 @@ class Permission protected $permissionErrorRules = null; - protected $params = array( - 'defaultPermissions' => array ( - 'dir' => '0775', - 'file' => '0664', - 'user' => '', - 'group' => '', - ), - 'permissionMap' => array( + protected $writableMap = [ + 'data' => [ + 'recursive' => true, + ], + 'application/Espo/Modules' => [ + 'recursive' => false, + ], + 'client/modules' => [ + 'recursive' => false, + ], + 'custom/Espo/Custom' => [ + 'recursive' => true, + ], + ]; - /** array('0664', '0775') */ - 'writable' => array( - 'data', - 'custom', - ), - - /** array('0644', '0755') */ - 'readable' => array( - 'api', - 'application', - 'client', - 'vendor', - 'index.php', - 'cron.php', - 'rebuild.php', - 'main.html', - 'reset.html', - ), - ), - ); - - protected $permissionRules = array( - 'writable' => array('0664', '0775'), - 'readable' => array('0644', '0755'), - ); + protected $defaultPermissions = [ + 'dir' => '0755', + 'file' => '0644', + 'user' => null, + 'group' => null, + ]; + protected $writablePermissions = [ + 'file' => '0664', + 'dir' => '0775', + ]; public function __construct(Manager $fileManager, array $params = null) { $this->fileManager = $fileManager; - if (isset($params)) { - $this->params = $params; + + if ($params) { + foreach ($params as $paramName => $paramValue) { + switch ($paramName) { + case 'defaultPermissions': + $this->defaultPermissions = array_merge($this->defaultPermissions, $paramValue); + break; + } + } } } @@ -93,11 +92,6 @@ class Permission return $this->fileManager; } - protected function getParams() - { - return $this->params; - } - /** * Get default settings * @@ -105,14 +99,34 @@ class Permission */ public function getDefaultPermissions() { - $params = $this->getParams(); - return $params['defaultPermissions']; + return $this->defaultPermissions; } - - public function getPermissionRules() + public function getWritableMap() { - return $this->permissionRules; + return $this->writableMap; + } + + public function getWritableList() + { + return array_keys($this->writableMap); + } + + public function getRequiredPermissions($path) + { + $permission = $this->getDefaultPermissions(); + + foreach ($this->getWritableMap() as $writablePath => $writableOptions) { + if (!$writableOptions['recursive'] && $path == $writablePath) { + return array_merge($permission, $this->writablePermissions); + } + + if ($writableOptions['recursive'] && substr($path, 0, strlen($writablePath)) == $writablePath) { + return array_merge($permission, $this->writablePermissions); + } + } + + return $permission; } /** @@ -129,7 +143,7 @@ class Permission return false; } - $permission = $this->getDefaultPermissions(); + $permission = $this->getRequiredPermissions($path); $result = $this->chmod($path, array($permission['file'], $permission['dir']), $recurse); if (!empty($permission['user'])) { @@ -342,8 +356,8 @@ class Permission * * @return bool */ - protected function chgrpRecurse($path, $group) { - + protected function chgrpRecurse($path, $group) + { if (!file_exists($path)) { return false; } @@ -461,60 +475,38 @@ class Permission * * @return bool */ - public function setMapPermission($mode = null) + public function setMapPermission() { $this->permissionError = array(); $this->permissionErrorRules = array(); - $params = $this->getParams(); - - $permissionRules = $this->permissionRules; - if (isset($mode)) { - foreach ($permissionRules as &$value) { - $value = $mode; - } - } - $result = true; - foreach ($params['permissionMap'] as $type => $items) { - $permission = $permissionRules[$type]; + foreach ($this->getWritableMap() as $path => $options) { + if (!file_exists($path)) continue; - foreach ($items as $item) { + try { + $this->chmod($path, $this->writablePermissions, $options['recursive']); + } catch (\Throwable $e) {} - if (file_exists($item)) { + /** check is writable */ + $res = is_writable($path); - try { - $this->chmod($item, $permission, true); - } catch (\Exception $e) { - } - - $res = is_readable($item); - - /** check is wtitable */ - if ($type == 'writable') { - - $res &= is_writable($item); - - if (is_dir($item)) { - $name = uniqid(); - - try { - $res &= $this->getFileManager()->putContents(array($item, $name), 'test'); - $res &= $this->getFileManager()->removeFile($name, $item); - } catch (\Exception $e) { - $res = false; - } - } - } - - if (!$res) { - $result = false; - $this->permissionError[] = $item; - $this->permissionErrorRules[$item] = $permission; - } + if (is_dir($path)) { + try { + $name = uniqid(); + $res &= $this->getFileManager()->putContents([$path, $name], 'test'); + $res &= $this->getFileManager()->removeFile($name, $path); + } catch (\Throwable $e) { + $res = false; } } + + if (!$res) { + $result = false; + $this->permissionError[] = $path; + $this->permissionErrorRules[$path] = $this->writablePermissions; + } } return $result; @@ -607,4 +599,3 @@ class Permission } } - diff --git a/application/Espo/Core/Utils/SystemRequirements.php b/application/Espo/Core/Utils/SystemRequirements.php index 943fe4e8ec..30d2347fea 100644 --- a/application/Espo/Core/Utils/SystemRequirements.php +++ b/application/Espo/Core/Utils/SystemRequirements.php @@ -159,11 +159,12 @@ class SystemRequirements { return $this->getRequiredList('permissionRequirements', [ 'permissionMap.writable', - 'permissionMap.readable', - ], $additionalData); + ], $additionalData, [ + 'permissionMap.writable' => $this->getFileManager()->getPermissionUtils()->getWritableList(), + ]); } - protected function getRequiredList($type, $checkList, array $additionalData = null) + protected function getRequiredList($type, $checkList, array $additionalData = null, array $predefinedData = []) { $config = $this->getConfig(); @@ -172,7 +173,8 @@ class SystemRequirements foreach ($checkList as $itemName) { $methodName = 'check' . ucfirst($type); if (method_exists($this, $methodName)) { - $result = $this->$methodName($itemName, $config->get($itemName), $additionalData); + $itemValue = isset($predefinedData[$itemName]) ? $predefinedData[$itemName] : $config->get($itemName); + $result = $this->$methodName($itemName, $itemValue, $additionalData); $list = array_merge($list, $result); } } diff --git a/application/Espo/Core/defaults/systemConfig.php b/application/Espo/Core/defaults/systemConfig.php index 2aae5c290e..49c8133bdc 100644 --- a/application/Espo/Core/defaults/systemConfig.php +++ b/application/Espo/Core/defaults/systemConfig.php @@ -29,31 +29,9 @@ return [ 'defaultPermissions' => [ - 'dir' => '0775', - 'file' => '0664', 'user' => '', 'group' => '' ], - 'permissionMap' => [ - /** array('0664', '0775') */ - 'writable' => [ - 'data', - 'custom', - 'application/Espo/Modules', - 'client/modules' - ], - /** array('0644', '0755') */ - 'readable' => [ - 'api', - 'application', - 'client', - 'vendor', - 'index.php', - 'cron.php', - 'rebuild.php', - 'clear_cache.php' - ], - ], 'jobMaxPortion' => 15, /** Max number of jobs per one execution. */ 'jobPeriod' => 7800, /** Max execution time (in seconds) allocated for a sinle job. If exceeded then set to Failed.*/ 'jobPeriodForActiveProcess' => 36000, /** Max execution time (in seconds) allocated for a sinle job with active process. If exceeded then set to Failed.*/ @@ -89,10 +67,8 @@ return [ 'crud', 'logger', 'isInstalled', - 'defaultPermissions', 'systemUser', - 'permissionMap', - 'permissionRules', + 'defaultPermissions', 'passwordSalt', 'cryptKey', 'apiSecretKeys', diff --git a/install/core/Installer.php b/install/core/Installer.php index 550441ed9f..6c5ef0bb4d 100644 --- a/install/core/Installer.php +++ b/install/core/Installer.php @@ -45,8 +45,6 @@ class Installer protected $isAuth = false; - protected $permissionMap; - protected $permissionError; private $passwordHash; @@ -88,10 +86,6 @@ class Installer require_once('install/core/SystemHelper.php'); $this->systemHelper = new SystemHelper(); - $configPath = $this->getConfig()->getConfigPath(); - $this->permissionMap = $this->getConfig()->get('permissionMap'); - $this->permissionMap['writable'][] = $configPath; - $this->databaseHelper = new \Espo\Core\Utils\Database\Helper($this->getConfig()); } diff --git a/install/core/SystemHelper.php b/install/core/SystemHelper.php index 44577fa78e..682cbf3d04 100644 --- a/install/core/SystemHelper.php +++ b/install/core/SystemHelper.php @@ -41,6 +41,8 @@ class SystemHelper extends \Espo\Core\Utils\System protected $combineOperator = '&&'; + protected $writableMap; + public function __construct() { $this->config = include('config.php'); @@ -50,6 +52,9 @@ class SystemHelper extends \Espo\Core\Utils\System } $this->apiPath = $this->config['apiPath']; + + $permission = new \Espo\Core\Utils\File\Permission(new \Espo\Core\Utils\File\Manager()); + $this->writableMap = $permission->getWritableMap(); } protected function getMainConfig($optionName, $returns = null) @@ -124,7 +129,7 @@ class SystemHelper extends \Espo\Core\Utils\System return $cd.$sudoStr.'chown -R '.$owner.':'.$group.' '.$path; } - public function getChmodCommand($path, $permissions = array('755'), $isSudo = false, $isFile = null, $isCd = true) + public function getChmodCommand($path, $permissions = ['755'], $isRecursive = true, $isSudo = false, $isFile = null) { $path = empty($path) ? '.' : $path; if (is_array($path)) { @@ -133,27 +138,32 @@ class SystemHelper extends \Espo\Core\Utils\System $sudoStr = $isSudo ? 'sudo ' : ''; - $cd = $isCd ? $this->getCd(true) : ''; - if (is_string($permissions)) { $permissions = (array) $permissions; } if (!isset($isFile) && count($permissions) == 1) { - return $cd. $sudoStr . 'find '.$path.' -type d -exec ' . $sudoStr . 'chmod '.$permissions[0].' {} +'; + if ($isRecursive) { + return $sudoStr . 'find '. $path .' -type d -exec ' . $sudoStr . 'chmod '. $permissions[0] .' {} +'; + } + return $sudoStr . 'chmod '. $permissions[0] .' '. $path; } $bufPerm = (count($permissions) == 1) ? array_fill(0, 2, $permissions[0]) : $permissions; $commands = array(); - if ($isCd) { - $commands[] = $this->getCd(); + if ($isRecursive) { + $commands[] = $sudoStr. 'find '.$path.' -type f -exec ' .$sudoStr.'chmod '.$bufPerm[0].' {} +'; + $commands[] = $sudoStr . 'find '.$path.' -type d -exec ' .$sudoStr. 'chmod '.$bufPerm[1].' {} +'; + } else { + if (file_exists($path) && is_file($path)) { + $commands[] = $sudoStr. 'chmod '. $bufPerm[0] .' '. $path; + } else { + $commands[] = $sudoStr. 'chmod '. $bufPerm[1] .' '. $path; + } } - $commands[] = $sudoStr. 'find '.$path.' -type f -exec ' .$sudoStr.'chmod '.$bufPerm[0].' {} +';//.'chmod '.$bufPerm[0].' $(find '.$path.' -type f)'; - $commands[] = $sudoStr . 'find '.$path.' -type d -exec ' .$sudoStr. 'chmod '.$bufPerm[1].' {} +';//.'chmod '.$bufPerm[1].' $(find '.$path.' -type d)'; - if (count($permissions) >= 2) { return implode(' ' . $this->combineOperator . ' ', $commands); } @@ -187,7 +197,7 @@ class SystemHelper extends \Espo\Core\Utils\System * @param bool $isFile * @return string */ - public function getPermissionCommands($path, $permissions = array('644', '755'), $isSudo = false, $isFile = null, $changeOwner = true) + public function getPermissionCommands($path, $permissions = ['644', '755'], $isSudo = false, $isFile = null, $changeOwner = true, $isCd = true) { if (is_string($path)) { $path = array_fill(0, 2, $path); @@ -195,7 +205,30 @@ class SystemHelper extends \Espo\Core\Utils\System list($chmodPath, $chownPath) = $path; $commands = array(); - $commands[] = $this->getChmodCommand($chmodPath, $permissions, $isSudo, $isFile); + + if ($isCd) { + $commands[] = $this->getCd(); + } + + $chmodPath = (array) $chmodPath; + + $pathList = []; + $recursivePathList = []; + foreach ($chmodPath as $pathItem) { + if (isset($this->writableMap[$pathItem]) && !$this->writableMap[$pathItem]['recursive']) { + $pathList[] = $pathItem; + continue; + } + $recursivePathList[] = $pathItem; + } + + if (!empty($pathList)) { + $commands[] = $this->getChmodCommand($pathList, $permissions, false, $isSudo, $isFile); + } + + if (!empty($recursivePathList)) { + $commands[] = $this->getChmodCommand($recursivePathList, $permissions, true, $isSudo, $isFile); + } if ($changeOwner) { $chown = $this->getChownCommand($chownPath, $isSudo, false); diff --git a/install/core/actions/checkPermission.php b/install/core/actions/checkPermission.php index d544791449..90f0ff6c58 100644 --- a/install/core/actions/checkPermission.php +++ b/install/core/actions/checkPermission.php @@ -50,7 +50,7 @@ if (!$installer->checkPermission()) { $changeOwner = false; } } - $result['errorMsg'] = $langs['messages']['Permission denied to'] . ':
/'.implode('
/', $urls).'
'; + $result['errorMsg'] = $langs['messages']['Permission denied to'] . ':
'.implode('
', $urls).'
'; $result['errorFixInstruction'] = str_replace( '"{C}"' , $instruction, $langs['messages']['permissionInstruction']) . "
" . str_replace( '{CSU}' , $instructionSU, $langs['messages']['operationNotPermitted']); }