Skip to content

Commit

Permalink
Implement permission checks
Browse files Browse the repository at this point in the history
  • Loading branch information
markus-moser committed May 27, 2024
1 parent d477d5f commit 22ca7e9
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 60 deletions.
3 changes: 3 additions & 0 deletions config/services/search-index-adapter/open-search.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ services:
Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\QueryServiceInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\OpenSearch\Workspace\QueryService

Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\ElementWorkspacesQueryServiceInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\OpenSearch\Workspace\ElementWorkspacesQueryService

Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\QueryLanguage\PqlAdapterInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\OpenSearch\QueryLanguage\PqlAdapter

Expand Down
7 changes: 4 additions & 3 deletions config/services/search/search-services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ services:
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\DataObject\DataObjectSearchService

Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Document\DocumentSearchServiceInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Document\DocumentSearchService
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Document\DocumentSearchService

Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\ElementSearchServiceInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\ElementSearchService
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\ElementSearchService

Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\DataObject\SearchHelper: ~
Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Asset\SearchHelper: ~
Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Document\SearchHelper: ~
Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\SearchHelper: ~
Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\ElementSearchHelperInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element\SearchHelper

Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\SearchProviderInterface:
class: Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\SearchProvider
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);

/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Commercial License (PCL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PCL
*/

namespace Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\Filter\Workspaces;

use Pimcore\Bundle\GenericDataIndexBundle\Enum\Permission\PermissionTypes;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\SearchModifierInterface;
use Pimcore\Model\User;

final readonly class ElementWorkspacesQuery implements SearchModifierInterface
{
public function __construct(
private ?User $user = null,
private ?string $permission = null
) {
}

public function getUser(): ?User
{
return $this->user;
}

public function getPermission(): ?string
{
return $this->permission ?? PermissionTypes::LIST->value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

use Pimcore\Bundle\GenericDataIndexBundle\Attribute\OpenSearch\AsSearchModifierHandler;
use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Modifier\SearchModifierContextInterface;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\Filter\Workspaces\ElementWorkspacesQuery;
use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Modifier\Filter\Workspaces\WorkspaceQuery;
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\ElementWorkspacesQueryServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\QueryServiceInterface;

/**
Expand All @@ -27,7 +29,8 @@
final readonly class WorkspaceQueryHandler
{
public function __construct(
private QueryServiceInterface $workspaceQueryService
private QueryServiceInterface $workspaceQueryService,
private ElementWorkspacesQueryServiceInterface $elementWorkspacesQueryService
) {
}

Expand All @@ -48,4 +51,21 @@ public function handleWorkspaceQuery(
)
);
}

#[AsSearchModifierHandler]
public function handleElementWorkspacesQuery(
ElementWorkspacesQuery $elementWorkspacesQuery,
SearchModifierContextInterface $context
): void {
if (!$elementWorkspacesQuery->getUser()) {
return;
}

$context->getSearch()->addQuery(
$this->elementWorkspacesQueryService->getWorkspaceQuery(
user: $elementWorkspacesQuery->getUser(),
permission: $elementWorkspacesQuery->getPermission()
)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php
declare(strict_types=1);

namespace Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\OpenSearch\Workspace;

use Pimcore\Bundle\GenericDataIndexBundle\Enum\Permission\UserPermissionTypes;
use Pimcore\Bundle\GenericDataIndexBundle\Enum\SearchIndex\ElementType;
use Pimcore\Bundle\GenericDataIndexBundle\Enum\SearchIndex\FieldCategory\SystemField;
use Pimcore\Bundle\GenericDataIndexBundle\Enum\SearchIndex\OpenSearch\ConditionType;
use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Query\BoolQuery;
use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Query\TermFilter;
use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Query\TermsFilter;
use Pimcore\Bundle\GenericDataIndexBundle\Permission\Workspace\AssetWorkspace;
use Pimcore\Bundle\GenericDataIndexBundle\Permission\Workspace\DataObjectWorkspace;
use Pimcore\Bundle\GenericDataIndexBundle\Permission\Workspace\DocumentWorkspace;
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\ElementWorkspacesQueryServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace\QueryServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\Service\Permission\UserPermissionServiceInterface;
use Pimcore\Model\User;

/**
* @internal
*/
final readonly class ElementWorkspacesQueryService implements ElementWorkspacesQueryServiceInterface
{
private const WORKSPACE_PERMISSION_MAPPING = [
AssetWorkspace::WORKSPACE_TYPE => UserPermissionTypes::ASSETS->value,
DataObjectWorkspace::WORKSPACE_TYPE => UserPermissionTypes::OBJECTS->value,
DocumentWorkspace::WORKSPACE_TYPE => UserPermissionTypes::DOCUMENTS->value,
];

public function __construct(
private QueryServiceInterface $workspaceQueryService,
private UserPermissionServiceInterface $userPermissionService
) {
}

public function getWorkspaceQuery(?User $user, string $permission): BoolQuery
{
$boolQuery = new BoolQuery();
if ($user?->isAdmin()) {
return $boolQuery;
}

$boolQuery->addCondition(
ConditionType::FILTER->value,
$this->getAllowedElementTypesFilter($user)->toArrayAsSubQuery()
);

$workspacesQuery = new BoolQuery();
foreach (array_keys(self::WORKSPACE_PERMISSION_MAPPING) as $workspaceType) {
$workspaceQuery = new BoolQuery([
ConditionType::FILTER->value => [
new TermFilter(
SystemField::ELEMENT_TYPE->getPath(), ElementType::fromShortValue($workspaceType)->value
),
$this->workspaceQueryService->getWorkspaceQuery($workspaceType, $user, $permission)
]
]);

$workspacesQuery->addCondition(ConditionType::SHOULD->value, $workspaceQuery->toArray(true));
}

if (!$workspacesQuery->isEmpty()) {
$boolQuery->addCondition(ConditionType::MUST->value, $workspacesQuery->toArray(true));
}

return $boolQuery;
}

private function getAllowedElementTypesFilter(User $user): TermsFilter
{
$allowedElementTypes = [];
foreach (self::WORKSPACE_PERMISSION_MAPPING as $workspaceType => $permission) {
if ($this->userPermissionService->hasPermission($user, $permission)) {
$allowedElementTypes[] = ElementType::fromShortValue($workspaceType)->value;
}
}

return new TermsFilter(SystemField::ELEMENT_TYPE->getPath(), $allowedElementTypes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);

namespace Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Workspace;

use Pimcore\Bundle\GenericDataIndexBundle\Model\OpenSearch\Query\BoolQuery;
use Pimcore\Model\User;

/**
* @internal
*/
interface ElementWorkspacesQueryServiceInterface
{
/**
* Returns a query which respects the workspace permissions for all element types.
*/
public function getWorkspaceQuery(?User $user, string $permission): BoolQuery;
}
2 changes: 1 addition & 1 deletion src/Service/Permission/UserPermissionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function canSearch(
string $userPermission
): void {
if (!$this->hasPermission($user, $userPermission)) {
throw new UserPermissionException('User does not have permission to view assets');
throw new UserPermissionException('User does not have permission to view ' . $userPermission);
}
}

Expand Down
33 changes: 3 additions & 30 deletions src/Service/Search/SearchService/AbstractSearchHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\Search\Modifier\SearchModifierServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\SearchIndexAdapter\SearchIndexServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\Service\Permission\UserPermissionServiceInterface;
use Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Traits\SearchHelperTrait;
use Pimcore\Model\User;

/**
* @internal
*/
abstract class AbstractSearchHelper implements SearchHelperInterface
{
use SearchHelperTrait;

public function __construct(
private readonly SearchIndexServiceInterface $searchIndexService,
private readonly SearchModifierServiceInterface $searchModifierService,
Expand Down Expand Up @@ -63,22 +66,6 @@ public function addSearchRestrictions(
return $search;
}

public function performSearch(SearchInterface $search, string $indexName): SearchResult
{
$adapterSearch = $this->searchIndexService->createPaginatedSearch(
$search->getPage(),
$search->getPageSize(),
$search->isAggregationsOnly()
);

$search->addModifier(new OrderByPageNumber($indexName, $search));
$this->searchModifierService->applyModifiersFromSearch($search, $adapterSearch);

return $this
->searchIndexService
->search($adapterSearch, $indexName);
}

/**
* @return int[]
*/
Expand Down Expand Up @@ -113,20 +100,6 @@ public function getChildrenCounts(
return $childrenCounts;
}

public function hydrateSearchResultHits(
SearchResult $searchResult,
array $childrenCounts,
?User $user = null
): array {
$results = [];

foreach ($searchResult->getHits() as $hit) {
$results[] = $this->hydrateSearchResultHit($hit, $childrenCounts, $user);
}

return $results;
}

abstract public function hydrateSearchResultHit(
SearchResultHit $searchResultHit,
array $childrenCounts,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Pimcore\Bundle\GenericDataIndexBundle\Service\Search\SearchService\Element;


use Pimcore\Bundle\GenericDataIndexBundle\Model\Search\Interfaces\SearchInterface;
use Pimcore\Bundle\GenericDataIndexBundle\Model\SearchIndexAdapter\SearchResult;
use Pimcore\Model\User;

/**
* @internal
*/
interface ElementSearchHelperInterface
{
public function addSearchRestrictions(SearchInterface $search): SearchInterface;

public function performSearch(SearchInterface $search, string $indexName): SearchResult;

public function hydrateSearchResultHits(SearchResult $searchResult, array $childrenCounts, ?User $user = null): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
public function __construct(
private GlobalIndexAliasServiceInterface $globalIndexAliasService,
private PaginationInfoServiceInterface $paginationInfoService,
private SearchHelper $searchHelper,
private ElementSearchHelperInterface $searchHelper,
private DataObjectSearchServiceInterface $dataObjectSearchService,
private AssetSearchServiceInterface $assetSearchService,
private DocumentSearchServiceInterface $documentSearchService,
Expand All @@ -46,15 +46,11 @@ public function __construct(

public function search(SearchInterface $elementSearch): ElementSearchResult
{
/* $documentSearch = $this->searchHelper->addSearchRestrictions(
search: $elementSearch,
userPermission: UserPermissionTypes::DOCUMENTS->value,
workspaceType: DocumentWorkspace::WORKSPACE_TYPE
);*/
$elementSearch = $this->searchHelper->addSearchRestrictions($elementSearch);

$searchResult = $this->searchHelper->performSearch(
search: $elementSearch,
indexName: $this->globalIndexAliasService->getElementSearchAliasName()
$elementSearch,
$this->globalIndexAliasService->getElementSearchAliasName()
);

try {
Expand Down Expand Up @@ -84,7 +80,7 @@ public function byId(ElementType $elementType, int $id, ?User $user = null): ?El
ElementType::ASSET => $this->assetSearchService->byId($id, $user),
ElementType::DATA_OBJECT => $this->dataObjectSearchService->byId($id, $user),
};
} catch(Exception $e) {
} catch (Exception $e) {
throw new ElementSearchException($e->getMessage(), 0, $e);
}

Expand Down
Loading

0 comments on commit 22ca7e9

Please sign in to comment.