Skip to content

Commit 49ec36e

Browse files
authored
[Workspaces] Adapt logic to align more with classic UI permission system (#315)
* adapt logic to align more with classic ui permission system * Apply php-cs-fixer changes * fis STAN --------- Co-authored-by: lukmzig <lukmzig@users.noreply.github.com>
1 parent 124d760 commit 49ec36e

File tree

10 files changed

+438
-94
lines changed

10 files changed

+438
-94
lines changed

doc/01_Installation/02_Upgrade.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Following steps are necessary during updating to newer versions.
2121
- Added `getSpecialPermissions` method to `Pimcore\Bundle\GenericDataIndexBundle\Service\Permission\ElementPermissionServiceInterface` to get special permissions workspace language permissions for elements
2222
- Removed layout permission from `Pimcore\Bundle\GenericDataIndexBundle\Permission\DataObjectPermissions` as they are not index relevant
2323
- Removed property `isLocked` from Index for elements as it needs to be dynamically calculated
24+
- Changed workspace permissions evaluation in order to align more with the Pimcore Classic bundle permission system
2425

2526
#### Interface changes
2627
- Added `PermissionTypes $permissionType` parameter with default type `PermissionTypes::LIST` to

src/SearchIndexAdapter/DefaultSearch/Workspace/QueryService.php

+159-88
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ final class QueryService implements QueryServiceInterface
4242
{
4343
use LoggerAwareTrait;
4444

45+
private const ALLOWED_PATHS_KEY = 'allowedPaths';
46+
47+
private const DECLINED_PATHS_KEY = 'declinedPaths';
48+
4549
public function __construct(
4650
private readonly PermissionServiceInterface $permissionService,
4751
private readonly WorkspaceServiceInterface $workspaceService,
@@ -72,27 +76,16 @@ public function getWorkspaceQuery(string $workspaceType, ?User $user, string $pe
7276

7377
private function getWorkspaceGroupsQuery(string $workspaceType, ?User $user, string $permission): BoolQuery
7478
{
75-
$workspaceGroups = $this->getGroupedWorkspaces(
76-
$workspaceType,
77-
$user
78-
);
79+
$group = $this->getGroupedWorkspaces($workspaceType, $user);
7980

80-
if (empty($workspaceGroups)) {
81+
if (empty($group)) {
8182
return $this->createNoWorkspaceAllowedQuery();
8283
}
8384

84-
$workspacesQuery = new BoolQuery();
85-
86-
foreach ($workspaceGroups as $group) {
87-
$workspacesQuery->addCondition(
88-
ConditionType::SHOULD->value,
89-
[
90-
'bool' => $this->createWorkspacesGroupQuery($workspaceType, $group, $permission)->toArray(),
91-
]
92-
);
93-
}
94-
95-
return $workspacesQuery;
85+
return $this->createWorkspacesGroupQuery(
86+
$workspaceType,
87+
$this->getCategorizedWorkspacePaths($group, $permission)
88+
);
9689
}
9790

9891
private function getGroupedWorkspaces(string $workspaceType, ?User $user): array
@@ -107,27 +100,26 @@ private function getGroupedWorkspaces(string $workspaceType, ?User $user): array
107100
$user
108101
);
109102

110-
if (!empty($userWorkspaces)) {
111-
$groupedWorkspaces[] = $userWorkspaces;
103+
/** @var WorkspaceInterface $userWorkspace */
104+
foreach ($userWorkspaces as $userWorkspace) {
105+
$groupedWorkspaces[$userWorkspace->getPath()] = $userWorkspace;
112106
}
113107

114108
foreach ($user->getRoles() as $roleId) {
115-
$roleWorkspaces = $this->workspaceService->getRoleWorkspaces(
116-
$workspaceType,
117-
$roleId
118-
);
119-
120-
if (!empty($roleWorkspaces)) {
121-
$groupedWorkspaces[] = $roleWorkspaces;
109+
$roleWorkspaces = $this->workspaceService->getRoleWorkspaces($workspaceType, $roleId);
110+
/** @var WorkspaceInterface $roleWorkspace */
111+
foreach ($roleWorkspaces as $roleWorkspace) {
112+
if (!isset($groupedWorkspaces[$roleWorkspace->getPath()])) {
113+
$groupedWorkspaces[$roleWorkspace->getPath()] = $roleWorkspace;
114+
}
122115
}
123116
}
124117

125118
return $groupedWorkspaces;
126119
}
127120

128-
private function createWorkspacesGroupQuery(string $workspaceType, array $group, string $permission): BoolQuery
121+
private function getCategorizedWorkspacePaths(array $group, string $permission): array
129122
{
130-
131123
$allowedPaths = [];
132124
$declinedPaths = [];
133125

@@ -143,71 +135,39 @@ private function createWorkspacesGroupQuery(string $workspaceType, array $group,
143135

144136
$allowedPaths = array_unique($allowedPaths);
145137
$declinedPaths = array_unique($declinedPaths);
146-
$declinedPaths = $this->evaluateDeclinedPaths($workspaceType, $allowedPaths, $declinedPaths);
138+
$declinedPaths = $this->keepDeclinedPathsWithinAllowed($declinedPaths, $allowedPaths);
139+
140+
return [
141+
self::ALLOWED_PATHS_KEY => $allowedPaths,
142+
self::DECLINED_PATHS_KEY => $declinedPaths,
143+
];
144+
}
145+
146+
private function createWorkspacesGroupQuery(string $workspaceType, array $categorizedPaths): BoolQuery
147+
{
148+
$allowedPaths = $categorizedPaths[self::ALLOWED_PATHS_KEY];
149+
$originalDeclinedPaths = $categorizedPaths[self::DECLINED_PATHS_KEY];
150+
$declinedPaths = $this->evaluateDeclinedPaths($workspaceType, $allowedPaths, $originalDeclinedPaths);
147151

148152
if (empty($allowedPaths)) {
149153
return $this->createNoWorkspaceAllowedQuery();
150154
}
151155

152156
$excludedPaths = $this->evaluateExcludedPaths($allowedPaths, $declinedPaths);
153157
$excludedFullPaths = $this->evaluateExcludedFullPaths($allowedPaths, $declinedPaths);
154-
$additionalIncludedPaths = [];
155158

156159
$query = new BoolQuery();
157160

161+
$this->addQueryByMainPath($query, $allowedPaths);
158162
$allowedMainPaths = $this->pathService->removeSubPaths($allowedPaths);
159163

160-
if (count($allowedMainPaths) === 1 && $allowedMainPaths[0] === '/') {
161-
$query->addCondition(
162-
ConditionType::SHOULD->value,
163-
[
164-
'exists' => [
165-
'field' => SystemField::FULL_PATH->getPath(),
166-
],
167-
]
168-
);
169-
} else {
170-
$query->addCondition(
171-
ConditionType::SHOULD->value,
172-
[
173-
'terms' => [
174-
SystemField::FULL_PATH->getPath() => $allowedMainPaths,
175-
],
176-
]
177-
);
178-
}
179-
180164
if (count($excludedFullPaths) > 0) {
181-
182-
$query->addCondition(
183-
ConditionType::MUST_NOT->value,
184-
[
185-
'terms' => [
186-
SystemField::FULL_PATH->getPath() => $this->pathService->removeSubPaths($excludedFullPaths),
187-
],
188-
]
189-
);
165+
$this->addQueryToExcludeFullPaths($query, $excludedFullPaths);
190166
}
191167

168+
$additionalIncludedPaths = [];
192169
if (count($excludedPaths) > 0) {
193-
$query->addCondition(
194-
ConditionType::MUST_NOT->value,
195-
[
196-
'terms' => [
197-
SystemField::PATH->getPath('keyword')
198-
=> $this->pathService->appendSlashes($excludedPaths),
199-
],
200-
]
201-
);
202-
203-
$query->addCondition(
204-
ConditionType::MUST_NOT->value,
205-
[
206-
'terms' => [
207-
SystemField::FULL_PATH->getPath('keyword') => $excludedPaths,
208-
],
209-
]
210-
);
170+
$this->addQueryForExcludedPaths($query, $excludedPaths);
211171

212172
/* we need to explicitly include all allowed sub paths
213173
as all direct children are excluded by the condition above */
@@ -221,24 +181,108 @@ private function createWorkspacesGroupQuery(string $workspaceType, array $group,
221181
as otherwise it will not be possible to navigate to the allowed paths in the tree */
222182
$additionalIncludedPaths = array_merge(
223183
$additionalIncludedPaths,
224-
$this->pathService->getAllParentPaths($allowedMainPaths)
184+
$this->pathService->getAllParentPaths($allowedMainPaths),
185+
$this->getAllDeclinedParentPaths($originalDeclinedPaths, $allowedPaths)
225186
);
187+
$additionalIncludedPaths = array_unique($additionalIncludedPaths);
226188

227189
if (count($additionalIncludedPaths)) {
190+
return $this->addQueryForAdditionalIncludedPaths($query, $additionalIncludedPaths);
191+
}
228192

229-
return new BoolQuery([
230-
ConditionType::SHOULD->value => [
231-
$query,
232-
[
233-
'terms' => [
234-
SystemField::FULL_PATH->getPath('keyword') => $additionalIncludedPaths,
235-
],
193+
return $query;
194+
}
195+
196+
private function keepDeclinedPathsWithinAllowed(array $declinedPaths, array $allowedPaths): array
197+
{
198+
foreach ($declinedPaths as $index => $declinedPath) {
199+
$isIncluded = false;
200+
foreach ($allowedPaths as $allowedPath) {
201+
if ($declinedPath === $allowedPath || $this->pathService->isSubPath($declinedPath, $allowedPath)) {
202+
$isIncluded = true;
203+
}
204+
}
205+
if (!$isIncluded) {
206+
unset($declinedPaths[$index]);
207+
}
208+
}
209+
210+
return $declinedPaths;
211+
}
212+
213+
private function addQueryByMainPath(BoolQuery $query, array $allowedPaths): void
214+
{
215+
$allowedMainPaths = $this->pathService->removeSubPaths($allowedPaths);
216+
217+
if (count($allowedMainPaths) === 1 && $allowedMainPaths[0] === '/') {
218+
$query->addCondition(
219+
ConditionType::SHOULD->value,
220+
[
221+
'exists' => [
222+
'field' => SystemField::FULL_PATH->getPath(),
236223
],
237-
],
238-
]);
224+
]
225+
);
226+
227+
return;
239228
}
240229

241-
return $query;
230+
$query->addCondition(
231+
ConditionType::SHOULD->value,
232+
[
233+
'terms' => [
234+
SystemField::FULL_PATH->getPath() => $allowedMainPaths,
235+
],
236+
]
237+
);
238+
}
239+
240+
private function addQueryToExcludeFullPaths(BoolQuery $query, array $excludedFullPaths): void
241+
{
242+
$query->addCondition(
243+
ConditionType::MUST_NOT->value,
244+
[
245+
'terms' => [
246+
SystemField::FULL_PATH->getPath() => $this->pathService->removeSubPaths($excludedFullPaths),
247+
],
248+
]
249+
);
250+
}
251+
252+
private function addQueryForExcludedPaths(BoolQuery $query, array $excludedPaths): void
253+
{
254+
$query->addCondition(
255+
ConditionType::MUST_NOT->value,
256+
[
257+
'terms' => [
258+
SystemField::PATH->getPath('keyword')
259+
=> $this->pathService->appendSlashes($excludedPaths),
260+
],
261+
]
262+
);
263+
264+
$query->addCondition(
265+
ConditionType::MUST_NOT->value,
266+
[
267+
'terms' => [
268+
SystemField::FULL_PATH->getPath('keyword') => $excludedPaths,
269+
],
270+
]
271+
);
272+
}
273+
274+
private function addQueryForAdditionalIncludedPaths(BoolQuery $query, array $additionalIncludedPaths): BoolQuery
275+
{
276+
return new BoolQuery([
277+
ConditionType::SHOULD->value => [
278+
$query,
279+
[
280+
'terms' => [
281+
SystemField::FULL_PATH->getPath('keyword') => $additionalIncludedPaths,
282+
],
283+
],
284+
],
285+
]);
242286
}
243287

244288
/**
@@ -296,6 +340,9 @@ private function evaluateDeclinedPaths(string $workspaceType, array $allowedPath
296340
$result = $this->searchIndexService->search($search, $indexName);
297341
$buckets = $result->getAggregation('paths')?->getBuckets() ?? [];
298342
foreach ($buckets as $bucket) {
343+
if ($bucket->getKey() === '/') {
344+
continue;
345+
}
299346
$declinedPaths[] = rtrim($bucket->getKey(), '/');
300347
}
301348
}
@@ -358,4 +405,28 @@ private function createNoWorkspaceAllowedQuery(): BoolQuery
358405
],
359406
]);
360407
}
408+
409+
private function getAllDeclinedParentPaths(array $declinedPaths, array $allowedPaths): array
410+
{
411+
if (empty($declinedPaths)) {
412+
return [];
413+
}
414+
415+
$allowedParentPaths = $this->pathService->getAllParentPaths($allowedPaths, false);
416+
$declinedParentPaths = [];
417+
foreach ($allowedParentPaths as $allowedParentPath) {
418+
foreach ($declinedPaths as $declinedPath) {
419+
if ($allowedParentPath === $declinedPath ||
420+
$this->pathService->isSubPath($allowedParentPath, $declinedPath)
421+
) {
422+
$declinedParentPaths[] = $allowedParentPath;
423+
}
424+
}
425+
}
426+
427+
$declinedParentPaths = array_unique($declinedParentPaths);
428+
sort($declinedParentPaths);
429+
430+
return $declinedParentPaths;
431+
}
361432
}

src/Service/PathService.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,11 @@ public function appendSlashes(array $paths): array
9696
return array_map(static fn (string $path) => rtrim($path, '/') . '/', $paths);
9797
}
9898

99-
public function getAllParentPaths(array $paths): array
99+
public function getAllParentPaths(array $paths, bool $removeSubPaths = true): array
100100
{
101-
$paths = $this->removeSubPaths($paths);
101+
if ($removeSubPaths) {
102+
$paths = $this->removeSubPaths($paths);
103+
}
102104
if (count($paths) === 1 && $paths[0] === '/') {
103105
return [];
104106
}
@@ -112,6 +114,7 @@ public function getAllParentPaths(array $paths): array
112114
}
113115
}
114116
}
117+
$result = array_unique($result);
115118
sort($result);
116119

117120
return $result;

src/Service/PathServiceInterface.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,5 @@ public function calculateLongestPathLevel(array $paths): int;
3333

3434
public function appendSlashes(array $paths): array;
3535

36-
public function getAllParentPaths(array $paths): array;
36+
public function getAllParentPaths(array $paths, bool $removeSubPaths = true): array;
3737
}

tests/Functional.suite.yml

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ modules:
33
enabled:
44
- \Pimcore\Bundle\GenericDataIndexBundle\Tests\Helper\Index
55
- \Pimcore\Bundle\GenericDataIndexBundle\Tests\Helper\GenericDataIndex
6+
- \Pimcore\Bundle\GenericDataIndexBundle\Tests\Helper\Service
67
- \Pimcore\Tests\Support\Helper\DataType\TestDataHelper
78
- \Pimcore\Tests\Support\Helper\ClassManager
89
- \Pimcore\Tests\Support\Helper\Pimcore:

0 commit comments

Comments
 (0)