. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Core\Acl\Map; use Espo\Core\Acl\Table; use Espo\Core\Utils\Cache\DataCacheAccess; use Espo\Core\Utils\ObjectUtil; use stdClass; use RuntimeException; /** * Provides quick access to ACL data. */ class Map { /** @var array */ private $forbiddenFieldsCache = []; /** @var array */ private $forbiddenAttributesCache; /** @var string[] */ private $fieldLevelList = [ Table::LEVEL_YES, Table::LEVEL_NO, ]; /** * @param DataCacheAccess $dataCacheAccess */ public function __construct( Table $table, private DataBuilder $dataBuilder, CacheKeyProvider $cacheKeyProvider, private DataCacheAccess $dataCacheAccess, ) { $this->dataCacheAccess->init( key: $cacheKeyProvider->get(), loader: fn () => $this->dataBuilder->build($table), ); } /** * Get raw data (for front-end). */ public function getData(): stdClass { return ObjectUtil::clone($this->dataCacheAccess->get()); } /** * Get a list of forbidden attributes for a scope and action. * * @param string $scope A scope. * @param string $action An action. * @param string $thresholdLevel An attribute will be treated as forbidden if the level is * equal to or lower than the threshold. * @return string[] */ public function getScopeForbiddenAttributeList( string $scope, string $action = Table::ACTION_READ, string $thresholdLevel = Table::LEVEL_NO ): array { if ( !in_array($thresholdLevel, $this->fieldLevelList) || $thresholdLevel === Table::LEVEL_YES ) { throw new RuntimeException("Bad threshold level."); } $key = $scope . '_'. $action . '_' . $thresholdLevel; if (isset($this->forbiddenAttributesCache[$key])) { return $this->forbiddenAttributesCache[$key]; } $data = $this->dataCacheAccess->get(); $fieldTableQuickAccess = $data->fieldTableQuickAccess; if ( !isset($fieldTableQuickAccess->$scope) || !isset($fieldTableQuickAccess->$scope->attributes) || !isset($fieldTableQuickAccess->$scope->attributes->$action) ) { $this->forbiddenAttributesCache[$key] = []; return []; } $levelList = []; foreach ($this->fieldLevelList as $level) { if ( array_search($level, $this->fieldLevelList) >= array_search($thresholdLevel, $this->fieldLevelList) ) { $levelList[] = $level; } } $attributeList = []; foreach ($levelList as $level) { if (!isset($fieldTableQuickAccess->$scope->attributes->$action->$level)) { continue; } foreach ($fieldTableQuickAccess->$scope->attributes->$action->$level as $attribute) { if (in_array($attribute, $attributeList)) { continue; } $attributeList[] = $attribute; } } $this->forbiddenAttributesCache[$key] = $attributeList; return $attributeList; } /** * Get a list of forbidden fields for a scope and action. * * @param string $scope A scope. * @param string $action An action. * @param string $thresholdLevel An attribute will be treated as forbidden if the level is * equal to or lower than the threshold. * @return string[] */ public function getScopeForbiddenFieldList( string $scope, string $action = Table::ACTION_READ, string $thresholdLevel = Table::LEVEL_NO ): array { if ( !in_array($thresholdLevel, $this->fieldLevelList) || $thresholdLevel === Table::LEVEL_YES ) { throw new RuntimeException("Bad threshold level."); } $key = $scope . '_'. $action . '_' . $thresholdLevel; if (isset($this->forbiddenFieldsCache[$key])) { return $this->forbiddenFieldsCache[$key]; } $data = $this->dataCacheAccess->get(); $fieldTableQuickAccess = $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; } }