diff --git a/application/Espo/Core/Acl.php b/application/Espo/Core/Acl.php index f7da34df43..d2979825e4 100644 --- a/application/Espo/Core/Acl.php +++ b/application/Espo/Core/Acl.php @@ -113,5 +113,10 @@ class Acl { return $this->getAclManager()->getScopeForbiddenAttributeList($this->getUser(), $scope, $action, $thresholdLevel); } + + public function getScopeForbiddenFieldList($scope, $action = 'read', $thresholdLevel = 'no') + { + return $this->getAclManager()->getScopeForbiddenFieldList($this->getUser(), $scope, $action, $thresholdLevel); + } } diff --git a/application/Espo/Core/Acl/Table.php b/application/Espo/Core/Acl/Table.php index 4a2eba2394..77a9df01db 100644 --- a/application/Espo/Core/Acl/Table.php +++ b/application/Espo/Core/Acl/Table.php @@ -61,6 +61,8 @@ class Table protected $forbiddenAttributesCache = array(); + protected $forbiddenFieldsCache = array(); + public function __construct(User $user, Config $config = null, FileManager $fileManager = null, Metadata $metadata = null, FieldManager $fieldManager = null) { $this->data = (object) [ @@ -269,6 +271,42 @@ class Table return $attributeList; } + public function getScopeForbiddenFieldList($scope, $action = 'read', $thresholdLevel = 'no') + { + $key = $scope . '_'. $action . '_' . $thresholdLevel; + if (isset($this->forbiddenFieldsCache[$key])) { + return $this->forbiddenFieldsCache[$key]; + } + + $fieldTableQuickAccess = $this->data->fieldTableQuickAccess; + + if (!isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->fields) || !isset($fieldTableQuickAccess->$scope->fields->$action)) { + $this->forbiddenFieldsCache[$key] = []; + return []; + } + + $levelList = []; + foreach ($this->fieldLevelList as $level) { + if (array_search($level, $this->fieldLevelList) >= array_search($thresholdLevel, $this->fieldLevelList)) { + $levelList[] = $level; + } + } + + $fieldList = []; + + foreach ($levelList as $level) { + if (!isset($fieldTableQuickAccess->$scope->fields->$action->$level)) continue; + foreach ($fieldTableQuickAccess->$scope->fields->$action->$level as $field) { + if (in_array($field, $fieldList)) continue; + $fieldList[] = $field; + } + } + + $this->forbiddenFieldsCache[$key] = $fieldList; + + return $fieldList; + } + protected function fillFieldTableQuickAccess() { $fieldTable = $this->data->fieldTable; diff --git a/application/Espo/Core/AclManager.php b/application/Espo/Core/AclManager.php index c25ca41346..d36381ac7b 100644 --- a/application/Espo/Core/AclManager.php +++ b/application/Espo/Core/AclManager.php @@ -225,5 +225,11 @@ class AclManager if ($user->isAdmin()) return []; return $this->getTable($user)->getScopeForbiddenAttributeList($scope, $action, $thresholdLevel); } + + public function getScopeForbiddenFieldList(User $user, $scope, $action = 'read', $thresholdLevel = 'no') + { + if ($user->isAdmin()) return []; + return $this->getTable($user)->getScopeForbiddenFieldList($scope, $action, $thresholdLevel); + } } diff --git a/application/Espo/Core/SelectManagers/Base.php b/application/Espo/Core/SelectManagers/Base.php index 735c0b99bd..bf9451dfbb 100644 --- a/application/Espo/Core/SelectManagers/Base.php +++ b/application/Espo/Core/SelectManagers/Base.php @@ -30,6 +30,7 @@ namespace Espo\Core\SelectManagers; use \Espo\Core\Exceptions\Error; +use \Espo\Core\Exceptions\Forbidden; use \Espo\Core\Acl; @@ -428,7 +429,7 @@ class Base return $result; } - public function getSelectParams(array $params, $withAcl = false) + public function getSelectParams(array $params, $withAcl = false, $checkWherePermission = false) { $result = array(); $this->prepareResult($result); @@ -459,6 +460,9 @@ class Base } if (!empty($params['where']) && is_array($params['where'])) { + if ($checkWherePermission) { + $this->checkWhere($params['where']); + } $this->where($params['where'], $result); } @@ -475,6 +479,26 @@ class Base return $result; } + protected function checkWhere($where) + { + foreach ($where as $w) { + if (isset($w['field'])) { + if (isset($w['type']) && $w['type'] === 'linkedWith') { + if (in_array($w['field'], $this->getAcl()->getScopeForbiddenFieldList($this->getEntityType()))) { + throw new Forbidden(); + } + } else { + if (in_array($w['field'], $this->getAcl()->getScopeForbiddenAttributeList($this->getEntityType()))) { + throw new Forbidden(); + } + } + } + if (!empty($w['value']) && is_array($w['value'])) { + $this->checkWhere($w['value']); + } + } + } + protected function getUserTimeZone() { if (empty($this->userTimeZone)) { diff --git a/application/Espo/Services/Record.php b/application/Espo/Services/Record.php index 579b803512..57fe07fdb9 100644 --- a/application/Espo/Services/Record.php +++ b/application/Espo/Services/Record.php @@ -617,7 +617,7 @@ class Record extends \Espo\Core\Services\Base protected function getSelectParams($params) { - $selectParams = $this->getSelectManager($this->entityType)->getSelectParams($params, true); + $selectParams = $this->getSelectManager($this->entityType)->getSelectParams($params, true, true); return $selectParams; } @@ -803,7 +803,6 @@ class Record extends \Espo\Core\Services\Base } $params['where'] = $where; - $selectParams = $this->getRecordService($foreignEntityType)->getSelectParams($params); return $this->getRepository()->massRelate($entity, $link, $selectParams); @@ -839,7 +838,7 @@ class Record extends \Espo\Core\Services\Base $where = $params['where']; $p = array(); $p['where'] = $where; - $selectParams = $this->getSelectParams($p, true); + $selectParams = $this->getSelectParams($p); $collection = $repository->find($selectParams); @@ -890,7 +889,7 @@ class Record extends \Espo\Core\Services\Base $where = $params['where']; $p = array(); $p['where'] = $where; - $selectParams = $this->getSelectParams($p, true); + $selectParams = $this->getSelectParams($p); $collection = $repository->find($selectParams); foreach ($collection as $entity) { @@ -978,13 +977,13 @@ class Record extends \Espo\Core\Services\Base 'value' => $ids ) ); - $selectParams = $this->getSelectManager($this->entityType)->getSelectParams(array('where' => $where), true); + $selectParams = $this->getSelectManager($this->entityType)->getSelectParams(array('where' => $where), true, true); } else if (array_key_exists('where', $params)) { $where = $params['where']; $p = array(); $p['where'] = $where; - $selectParams = $this->getSelectParams($p, true); + $selectParams = $this->getSelectParams($p); } else { throw new BadRequest(); } diff --git a/frontend/client/src/views/record/search.js b/frontend/client/src/views/record/search.js index 07ee6fc4f8..c3478bc56e 100644 --- a/frontend/client/src/views/record/search.js +++ b/frontend/client/src/views/record/search.js @@ -36,7 +36,7 @@ Espo.define('views/record/search', 'view', function (Dep) { searchManager: null, - fields: ['name'], + fieldList: ['name'], textFilter: '', @@ -73,14 +73,19 @@ Espo.define('views/record/search', 'view', function (Dep) { } this.addReadyCondition(function () { - return this.fields != null && this.moreFields != null; + return this.fieldList != null && this.moreFieldList != null; }.bind(this)); this.boolFilterList = Espo.Utils.clone(this.getMetadata().get('clientDefs.' + this.scope + '.boolFilterList') || []); + var forbiddenFieldList = this.getAcl().getScopeForbiddenFieldList(this.scope) || []; this._helper.layoutManager.get(this.scope, 'filters', function (list) { - this.moreFields = list; + this.moreFieldList = []; + (list || []).forEach(function (field) { + if (~forbiddenFieldList.indexOf(field)) return; + this.moreFieldList.push(field); + }, this); this.tryReady(); }.bind(this)); @@ -632,8 +637,8 @@ Espo.define('views/record/search', 'view', function (Dep) { getAdvancedDefs: function () { var defs = []; - for (var i in this.moreFields) { - var field = this.moreFields[i]; + for (var i in this.moreFieldList) { + var field = this.moreFieldList[i]; var o = { name: field, checked: (field in this.advanced), @@ -641,7 +646,8 @@ Espo.define('views/record/search', 'view', function (Dep) { defs.push(o); } return defs; - }, + } + }); });