metadata aclDependency anyScopeList

This commit is contained in:
Yuri Kuznetsov
2025-10-25 12:47:21 +03:00
parent 4e0d5a2267
commit 599646c397
5 changed files with 68 additions and 23 deletions
@@ -31,10 +31,14 @@ namespace Espo\Tools\App\Metadata;
class AclDependencyItem class AclDependencyItem
{ {
/**
* @param ?string[] $anyScopeList
*/
public function __construct( public function __construct(
private string $target, private string $target,
private string $scope, private ?string $scope,
private ?string $field private ?string $field,
private ?array $anyScopeList = null,
) {} ) {}
/** /**
@@ -45,7 +49,7 @@ class AclDependencyItem
return $this->target; return $this->target;
} }
public function getScope(): string public function getScope(): ?string
{ {
return $this->scope; return $this->scope;
} }
@@ -54,4 +58,13 @@ class AclDependencyItem
{ {
return $this->field; return $this->field;
} }
/**
* @return ?string[]
* @since 9.2.5
*/
public function getAnyScopeList(): ?array
{
return $this->anyScopeList;
}
} }
@@ -95,15 +95,13 @@ class AclDependencyProvider
$data = []; $data = [];
foreach (($this->metadata->get(['app', 'metadata', 'aclDependencies']) ?? []) as $target => $item) { foreach (($this->metadata->get(['app', 'metadata', 'aclDependencies']) ?? []) as $target => $item) {
$anyScopeList = $item['anyScopeList'] ?? null;
$scope = $item['scope'] ?? null; $scope = $item['scope'] ?? null;
$field = $item['field'] ?? null; $field = $item['field'] ?? null;
if (!$scope) {
continue;
}
$data[] = [ $data[] = [
'target' => $target, 'target' => $target,
'anyScopeList' => $anyScopeList,
'scope' => $scope, 'scope' => $scope,
'field' => $field, 'field' => $field,
]; ];
@@ -196,8 +194,14 @@ class AclDependencyProvider
$target = $rawItem['target'] ?? null; $target = $rawItem['target'] ?? null;
$scope = $rawItem['scope'] ?? null; $scope = $rawItem['scope'] ?? null;
$field = $rawItem['field'] ?? null; $field = $rawItem['field'] ?? null;
$anyScopeList = $rawItem['anyScopeList'] ?? null;
$list[] = new AclDependencyItem($target, $scope, $field); $list[] = new AclDependencyItem(
target: $target,
scope: $scope,
field: $field,
anyScopeList: $anyScopeList,
);
} }
return $list; return $list;
+22 -10
View File
@@ -178,20 +178,32 @@ class MetadataService
foreach ($this->aclDependencyProvider->get() as $dependencyItem) { foreach ($this->aclDependencyProvider->get() as $dependencyItem) {
$aclScope = $dependencyItem->getScope(); $aclScope = $dependencyItem->getScope();
$aclField = $dependencyItem->getField(); $aclField = $dependencyItem->getField();
$anyScopeList = $dependencyItem->getAnyScopeList();
if (!$aclScope) { if ($anyScopeList) {
continue; $skip = true;
foreach ($anyScopeList as $itemScope) {
if ($this->acl->tryCheck($itemScope)) {
$skip = false;
break;
}
}
if ($skip) {
continue;
}
} }
if (!$this->acl->tryCheck($aclScope)) { if ($aclScope) {
continue; if (!$this->acl->tryCheck($aclScope)) {
} continue;
}
if ( if ($aclField && in_array($aclField, $this->acl->getScopeForbiddenFieldList($aclScope))) {
$aclField && continue;
in_array($aclField, $this->acl->getScopeForbiddenFieldList($aclScope)) }
) {
continue;
} }
$targetArr = explode('.', $dependencyItem->getTarget()); $targetArr = explode('.', $dependencyItem->getTarget());
+4 -4
View File
@@ -44,21 +44,21 @@
}, },
"aclDependencies": { "aclDependencies": {
"type": "object", "type": "object",
"description": "Rules making a metadata sections available for a user when they don't have access to a scope.", "description": "Rules making a metadata sections available for the user when they don't have access to the scope.",
"additionalProperties": { "additionalProperties": {
"description": "A metadata path, items are separated by dots.", "description": "A metadata path, items are separated by dots.",
"properties": { "properties": {
"scope": { "scope": {
"type": "string", "type": "string",
"description": "If a user have access to the scope, they will have access to a metadata section defined by a key." "description": "If the user has access to the scope, they will have access to the metadata section defined by the key."
}, },
"field": { "field": {
"type": "string", "type": "string",
"description": "If a user have access to the field (of a scope), they will have access to a metadata section defined by a key." "description": "If the user has access to the field (of the scope), they will have access to the metadata section defined by the key."
}, },
"anyScopeList": { "anyScopeList": {
"type": "array", "type": "array",
"description": "Not supported. TBD.", "description": "If the user has access to any of the list scopes, they will have access to the metadata section defined by the key. As of v9.2.5.",
"items": { "items": {
"type": "string" "type": "string"
} }
@@ -29,6 +29,7 @@
namespace tests\integration\Espo\Tools\App; namespace tests\integration\Espo\Tools\App;
use Espo\Core\Utils\Metadata;
use Espo\Tools\App\MetadataService; use Espo\Tools\App\MetadataService;
use tests\integration\Core\BaseTestCase; use tests\integration\Core\BaseTestCase;
@@ -36,6 +37,20 @@ class MetadataTest extends BaseTestCase
{ {
public function testAclDependency(): void public function testAclDependency(): void
{ {
$metadata = $this->getContainer()->getByClass(Metadata::class);
$metadata->set('app', 'metadata', [
'aclDependencies' => [
'entityDefs.Campaign' => [
'anyScopeList' => ['Opportunity'],
],
],
]);
$metadata->save();
$this->reCreateApplication();
$this->createUser('tester', [ $this->createUser('tester', [
'data' => [ 'data' => [
'Lead' => false, 'Lead' => false,
@@ -55,6 +70,7 @@ class MetadataTest extends BaseTestCase
$data = $this->getInjectableFactory()->create(MetadataService::class)->getDataForFrontend(); $data = $this->getInjectableFactory()->create(MetadataService::class)->getDataForFrontend();
$this->assertIsArray($data?->entityDefs?->Lead?->fields?->source?->options); $this->assertIsArray($data?->entityDefs?->Lead?->fields?->source?->options);
$this->assertNull($data->entityDefs->Lead->fields?->name ?? null); $this->assertNull($data->entityDefs->Lead->fields->name ?? null);
$this->assertNotNull($data?->entityDefs->Campaign ?? null);
} }
} }