From af4af876b51b051f5dfb77eb3bb06f80275b1ee6 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 29 Aug 2023 15:32:28 +0200 Subject: [PATCH] Added endpoints to load language limitations for content --- phpstan-baseline-7.4.neon | 5 - phpstan-baseline-8.0.neon | 5 - phpstan-baseline.neon | 10 +- .../LanguageLimitationController.php | 120 ++++++++ src/bundle/Resources/config/routing.yaml | 30 ++ .../config/services/controllers.yaml | 6 + .../config/services/permissions.yaml | 5 + .../Permission/PermissionCheckerInterface.php | 12 +- src/lib/Permission/LimitationResolver.php | 173 +++++++++++ .../LimitationResolverInterface.php | 50 ++++ src/lib/Permission/PermissionChecker.php | 111 +------ .../lib/Permission/LimitationResolverTest.php | 283 ++++++++++++++++++ .../lib/Permission/PermissionCheckerTest.php | 32 +- 13 files changed, 703 insertions(+), 139 deletions(-) create mode 100644 src/bundle/Controller/Permission/LanguageLimitationController.php create mode 100644 src/lib/Permission/LimitationResolver.php create mode 100644 src/lib/Permission/LimitationResolverInterface.php create mode 100644 tests/lib/Permission/LimitationResolverTest.php diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon index 8df75b2147..3aabfa93f4 100644 --- a/phpstan-baseline-7.4.neon +++ b/phpstan-baseline-7.4.neon @@ -210,11 +210,6 @@ parameters: count: 1 path: src/lib/Pagination/Mapper/AbstractPagerContentToDataMapper.php - - - message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Permission/PermissionChecker.php - - message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 diff --git a/phpstan-baseline-8.0.neon b/phpstan-baseline-8.0.neon index 5d7532674a..31d6260782 100644 --- a/phpstan-baseline-8.0.neon +++ b/phpstan-baseline-8.0.neon @@ -150,11 +150,6 @@ parameters: count: 1 path: src/lib/Pagination/Mapper/AbstractPagerContentToDataMapper.php - - - message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\ given\\.$#" - count: 1 - path: src/lib/Permission/PermissionChecker.php - - message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\ given\\.$#" count: 1 diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d823af36bf..12ff611c53 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -8480,6 +8480,11 @@ parameters: count: 1 path: src/lib/Pagination/Pagerfanta/URLWildcardAdapter.php + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupLimitationResult\\:\\:\\$hasAccess\\.$#" + count: 1 + path: src/lib/Permission/LimitationResolver.php + - message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupLimitationResult\\:\\:\\$lookupPolicyLimitations\\.$#" count: 2 @@ -8535,11 +8540,6 @@ parameters: count: 1 path: src/lib/Permission/PermissionChecker.php - - - message: "#^Parameter \\#1 \\$contentTypeIds of method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\\\Builder\\\\VersionBuilder\\:\\:createFromAnyContentTypeOf\\(\\) expects array\\, array\\ given\\.$#" - count: 2 - path: src/lib/Permission/PermissionChecker.php - - message: "#^Method Ibexa\\\\AdminUi\\\\QueryType\\\\LocationPathQueryType\\:\\:doGetQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" count: 1 diff --git a/src/bundle/Controller/Permission/LanguageLimitationController.php b/src/bundle/Controller/Permission/LanguageLimitationController.php new file mode 100644 index 0000000000..ace7f50b5e --- /dev/null +++ b/src/bundle/Controller/Permission/LanguageLimitationController.php @@ -0,0 +1,120 @@ +contentService = $contentService; + $this->limitationResolver = $limitationResolver; + $this->locationService = $locationService; + } + + public function loadLanguageLimitationsForContentCreateAction(Location $location): Response + { + $contentInfo = $location->getContentInfo(); + $contentType = $contentInfo->getContentType(); + $contentCreateStruct = $this->contentService->newContentCreateStruct( + $contentType, + $contentInfo->getMainLanguageCode() + ); + $contentCreateStruct->sectionId = $contentInfo->getSection(); + $locationCreateStruct = $this->locationService->newLocationCreateStruct($location->id); + + return new JsonResponse( + $this->limitationResolver->getLanguageLimitations( + 'create', + $contentCreateStruct, + [], + [ + $locationCreateStruct, + ] + ) + ); + } + + public function loadLanguageLimitationsForContentEditAction( + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?Location $location = null + ): Response { + return new JsonResponse( + $this->getLanguageLimitationsByFunction( + 'edit', + $contentInfo, + $versionInfo, + $location + ) + ); + } + + public function loadLanguageLimitationsForContentReadAction( + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?Location $location = null + ): Response { + return new JsonResponse( + $this->getLanguageLimitationsByFunction( + 'read', + $contentInfo, + $versionInfo, + $location + ) + ); + } + + /** + * @return array + */ + private function getLanguageLimitationsByFunction( + string $function, + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?Location $location = null + ): array { + $versionInfo ??= $this->contentService->loadVersionInfo($contentInfo); + $location ??= $contentInfo->getMainLocation(); + $targets = []; + + if (null !== $location) { + $targets[] = $location; + } + + return $this->limitationResolver->getLanguageLimitations( + $function, + $contentInfo, + $versionInfo->getLanguages(), + $targets + ); + } +} diff --git a/src/bundle/Resources/config/routing.yaml b/src/bundle/Resources/config/routing.yaml index eaee6cf806..c6f677abc4 100644 --- a/src/bundle/Resources/config/routing.yaml +++ b/src/bundle/Resources/config/routing.yaml @@ -958,3 +958,33 @@ ibexa.asset.upload_image: defaults: _controller: 'Ibexa\Bundle\AdminUi\Controller\AssetController::uploadImageAction' methods: [POST] + +# +# Permissions +# +ibexa.permission.limitation.language.content_create: + path: /permission/limitation/language/content-create/{locationId} + options: + expose: true + controller: 'Ibexa\Bundle\AdminUi\Controller\Permission\LanguageLimitationController::loadLanguageLimitationsForContentCreateAction' + methods: [GET] + requirements: + locationId: \d+ + +ibexa.permission.limitation.language.content_edit: + path: /permission/limitation/language/content-edit/{contentInfoId} + options: + expose: true + controller: 'Ibexa\Bundle\AdminUi\Controller\Permission\LanguageLimitationController::loadLanguageLimitationsForContentEditAction' + methods: [GET] + requirements: + contentInfoId: \d+ + +ibexa.permission.limitation.language.content_read: + path: /permission/limitation/language/content-read/{contentInfoId} + options: + expose: true + controller: 'Ibexa\Bundle\AdminUi\Controller\Permission\LanguageLimitationController::loadLanguageLimitationsForContentReadAction' + methods: [GET] + requirements: + contentInfoId: \d+ diff --git a/src/bundle/Resources/config/services/controllers.yaml b/src/bundle/Resources/config/services/controllers.yaml index 8dcc72755a..5f269e81d7 100644 --- a/src/bundle/Resources/config/services/controllers.yaml +++ b/src/bundle/Resources/config/services/controllers.yaml @@ -212,3 +212,9 @@ services: autowire: true Ibexa\Bundle\AdminUi\Controller\User\InvitationController: ~ + + Ibexa\Bundle\AdminUi\Controller\Permission\LanguageLimitationController: + parent: Ibexa\Contracts\AdminUi\Controller\Controller + autowire: true + tags: + - controller.service_arguments diff --git a/src/bundle/Resources/config/services/permissions.yaml b/src/bundle/Resources/config/services/permissions.yaml index 203a4748cb..c152f57d37 100644 --- a/src/bundle/Resources/config/services/permissions.yaml +++ b/src/bundle/Resources/config/services/permissions.yaml @@ -10,3 +10,8 @@ services: alias: Ibexa\AdminUi\Permission\PermissionChecker Ibexa\AdminUi\Permission\LookupLimitationsTransformer: ~ + + Ibexa\AdminUi\Permission\LimitationResolver: ~ + + Ibexa\AdminUi\Permission\LimitationResolverInterface: + alias: Ibexa\AdminUi\Permission\LimitationResolver diff --git a/src/contracts/Permission/PermissionCheckerInterface.php b/src/contracts/Permission/PermissionCheckerInterface.php index 23befd3c65..9dc9ea6e84 100644 --- a/src/contracts/Permission/PermissionCheckerInterface.php +++ b/src/contracts/Permission/PermissionCheckerInterface.php @@ -19,20 +19,24 @@ public function getRestrictions(array $hasAccess, string $class): array; public function canCreateInLocation(Location $location, $hasAccess): bool; /** - * @internal - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @internal + * @deprecated 4.6.0 The "\Ibexa\Contracts\AdminUi\Permission\PermissionCheckerInterface::getContentCreateLimitations()" method is deprecated, will be removed in 5.0. + * Use { @see \Ibexa\AdminUi\Permission\LimitationResolverInterface::getContentCreateLimitations() } instead. */ public function getContentCreateLimitations(Location $parentLocation): LookupLimitationResult; /** - * @internal - * * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @internal + * @deprecated 4.6.0 The "\Ibexa\Contracts\AdminUi\Permission\PermissionCheckerInterface::getContentUpdateLimitations()" method is deprecated, will be removed in 5.0. + * Use { @see \Ibexa\AdminUi\Permission\LimitationResolverInterface::getContentUpdateLimitations } instead. */ public function getContentUpdateLimitations(Location $parentLocation): LookupLimitationResult; } diff --git a/src/lib/Permission/LimitationResolver.php b/src/lib/Permission/LimitationResolver.php new file mode 100644 index 0000000000..dd1ceadae6 --- /dev/null +++ b/src/lib/Permission/LimitationResolver.php @@ -0,0 +1,173 @@ +contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->languageService = $languageService; + $this->locationService = $locationService; + $this->lookupLimitationsTransformer = $lookupLimitationsTransformer; + $this->permissionResolver = $permissionResolver; + } + + public function getContentCreateLimitations(Location $parentLocation): LookupLimitationResult + { + $contentInfo = $parentLocation->getContentInfo(); + $contentType = $contentInfo->getContentType(); + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, $contentInfo->getMainLanguageCode()); + $contentCreateStruct->sectionId = $contentInfo->getSection(); + $locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocation->id); + + $versionBuilder = new VersionBuilder(); + $versionBuilder->translateToAnyLanguageOf($this->getActiveLanguageCodes()); + $versionBuilder->createFromAnyContentTypeOf($this->getContentTypeIds()); + + return $this->permissionResolver->lookupLimitations( + 'content', + 'create', + $contentCreateStruct, + [$versionBuilder->build(), $locationCreateStruct], + [Limitation::CONTENTTYPE, Limitation::LANGUAGE] + ); + } + + public function getContentUpdateLimitations(Location $parentLocation): LookupLimitationResult + { + $versionBuilder = new VersionBuilder(); + $versionBuilder->translateToAnyLanguageOf($this->getActiveLanguageCodes()); + $versionBuilder->createFromAnyContentTypeOf($this->getContentTypeIds()); + + return $this->permissionResolver->lookupLimitations( + 'content', + 'edit', + $parentLocation->getContentInfo(), + [$versionBuilder->build(), $parentLocation], + [Limitation::CONTENTTYPE, Limitation::LANGUAGE] + ); + } + + public function getLanguageLimitations( + string $function, + ValueObject $valueObject, + iterable $languages = [], + array $targets = [] + ): array { + $languages = !empty($languages) ? $languages : $this->languageService->loadLanguages(); + $versionBuilder = new VersionBuilder(); + $versionBuilder->translateToAnyLanguageOf($this->getActiveLanguageCodes($languages)); + + $lookupLimitations = $this->permissionResolver->lookupLimitations( + 'content', + $function, + $valueObject, + array_merge( + $targets, + [$versionBuilder->build()] + ), + [Limitation::LANGUAGE] + ); + + $limitationLanguageCodes = $this->lookupLimitationsTransformer->getFlattenedLimitationsValues($lookupLimitations); + $languageLimitations = []; + foreach ($languages as $language) { + $languageLimitations[] = [ + 'languageCode' => $language->getLanguageCode(), + 'name' => $language->getName(), + 'hasAccess' => $lookupLimitations->hasAccess && $this->hasAccessToLanguage($language, $limitationLanguageCodes), + ]; + } + + return $languageLimitations; + } + + /** + * @param array $limitationLanguageCodes + */ + private function hasAccessToLanguage(Language $language, array $limitationLanguageCodes): bool + { + return $language->isEnabled() + && ( + empty($limitationLanguageCodes) + || in_array($language->getLanguageCode(), $limitationLanguageCodes, true) + ); + } + + /** + * @return array + */ + private function getContentTypeIds(): array + { + $contentTypeIds = []; + + $contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); + foreach ($contentTypeGroups as $contentTypeGroup) { + $contentTypes = $this->contentTypeService->loadContentTypes($contentTypeGroup); + foreach ($contentTypes as $contentType) { + $contentTypeIds[] = $contentType->id; + } + } + + return $contentTypeIds; + } + + /** + * @param iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Language>|null $languages + * + * @return array + */ + private function getActiveLanguageCodes(?iterable $languages = null): array + { + $languages ??= $this->languageService->loadLanguages(); + $languageCodes = []; + foreach ($languages as $language) { + if ($language->isEnabled()) { + $languageCodes[] = $language->getLanguageCode(); + } + } + + return $languageCodes; + } +} diff --git a/src/lib/Permission/LimitationResolverInterface.php b/src/lib/Permission/LimitationResolverInterface.php new file mode 100644 index 0000000000..7ba4692e86 --- /dev/null +++ b/src/lib/Permission/LimitationResolverInterface.php @@ -0,0 +1,50 @@ + $languages + * @param array<\Ibexa\Contracts\Core\Repository\Values\ValueObject> $targets + * + * @return array + */ + public function getLanguageLimitations( + string $function, + ValueObject $valueObject, + iterable $languages = [], + array $targets = [] + ): array; +} diff --git a/src/lib/Permission/PermissionChecker.php b/src/lib/Permission/PermissionChecker.php index 1c7b2f4e99..6728b7d682 100644 --- a/src/lib/Permission/PermissionChecker.php +++ b/src/lib/Permission/PermissionChecker.php @@ -9,16 +9,9 @@ namespace Ibexa\AdminUi\Permission; use Ibexa\Contracts\AdminUi\Permission\PermissionCheckerInterface; -use Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder; -use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\ContentTypeService; -use Ibexa\Contracts\Core\Repository\LanguageService; -use Ibexa\Contracts\Core\Repository\LocationService; use Ibexa\Contracts\Core\Repository\PermissionResolver; use Ibexa\Contracts\Core\Repository\UserService; -use Ibexa\Contracts\Core\Repository\Values\Content\Language; use Ibexa\Contracts\Core\Repository\Values\Content\Location; -use Ibexa\Contracts\Core\Repository\Values\User\Limitation; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation; @@ -39,40 +32,16 @@ class PermissionChecker implements PermissionCheckerInterface /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; + private LimitationResolverInterface $limitationResolver; - /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; - - /** @var \Ibexa\Contracts\Core\Repository\LanguageService */ - private $languageService; - - /** - * @param \Ibexa\Contracts\Core\Repository\PermissionResolver $permissionResolver - * @param \Ibexa\Contracts\Core\Repository\UserService $userService - * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService - * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService - * @param \Ibexa\Contracts\Core\Repository\ContentTypeService $contentTypeService - * @param \Ibexa\Contracts\Core\Repository\LanguageService $languageService - */ public function __construct( PermissionResolver $permissionResolver, - UserService $userService, - LocationService $locationService, - ContentService $contentService, - ContentTypeService $contentTypeService, - LanguageService $languageService + LimitationResolverInterface $limitationResolver, + UserService $userService ) { $this->permissionResolver = $permissionResolver; + $this->limitationResolver = $limitationResolver; $this->userService = $userService; - $this->locationService = $locationService; - $this->contentTypeService = $contentTypeService; - $this->contentService = $contentService; - $this->languageService = $languageService; } /** @@ -190,39 +159,24 @@ public function canCreateInLocation(Location $location, $hasAccess): bool public function getContentCreateLimitations(Location $parentLocation): LookupLimitationResult { - $contentType = $this->contentTypeService->loadContentType($parentLocation->contentInfo->contentTypeId); - $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, $parentLocation->contentInfo->mainLanguageCode); - $contentCreateStruct->sectionId = $parentLocation->contentInfo->sectionId; - $locationCreateStruct = $this->locationService->newLocationCreateStruct($parentLocation->id); - - $versionBuilder = new VersionBuilder(); - $versionBuilder->translateToAnyLanguageOf($this->getActiveLanguageCodes()); - $versionBuilder->createFromAnyContentTypeOf($this->getContentTypeIds()); - - return $this->permissionResolver->lookupLimitations( - 'content', - 'create', - $contentCreateStruct, - [$versionBuilder->build(), $locationCreateStruct], - [Limitation::CONTENTTYPE, Limitation::LANGUAGE] + trigger_deprecation( + 'ibexa/admin-ui', + '4.6', + sprintf('The %s() method is deprecated, will be removed in 5.0.', __METHOD__) ); + + return $this->limitationResolver->getContentCreateLimitations($parentLocation); } public function getContentUpdateLimitations(Location $location): LookupLimitationResult { - $contentInfo = $location->getContentInfo(); - - $versionBuilder = new VersionBuilder(); - $versionBuilder->translateToAnyLanguageOf($this->getActiveLanguageCodes()); - $versionBuilder->createFromAnyContentTypeOf($this->getContentTypeIds()); - - return $this->permissionResolver->lookupLimitations( - 'content', - 'edit', - $contentInfo, - [$versionBuilder->build(), $location], - [Limitation::CONTENTTYPE, Limitation::LANGUAGE] + trigger_deprecation( + 'ibexa/admin-ui', + '4.6', + sprintf('The %s() method is deprecated, will be removed in 5.0.', __METHOD__) ); + + return $this->limitationResolver->getContentUpdateLimitations($location); } /** @@ -299,39 +253,6 @@ private function loadAllUserGroupsIdsOfUser(User $user): array return $allUserGroups; } - - /** - * @return string[] - */ - private function getActiveLanguageCodes(): array - { - $filter = array_filter( - $this->languageService->loadLanguages(), - static function (Language $language) { - return $language->enabled; - } - ); - - return array_column($filter, 'languageCode'); - } - - /** - * @return string[] - */ - private function getContentTypeIds(): array - { - $contentTypeIds = []; - - $contentTypeGroups = $this->contentTypeService->loadContentTypeGroups(); - foreach ($contentTypeGroups as $contentTypeGroup) { - $contentTypes = $this->contentTypeService->loadContentTypes($contentTypeGroup); - foreach ($contentTypes as $contentType) { - $contentTypeIds[] = $contentType->id; - } - } - - return $contentTypeIds; - } } class_alias(PermissionChecker::class, 'EzSystems\EzPlatformAdminUi\Permission\PermissionChecker'); diff --git a/tests/lib/Permission/LimitationResolverTest.php b/tests/lib/Permission/LimitationResolverTest.php new file mode 100644 index 0000000000..084046f6d1 --- /dev/null +++ b/tests/lib/Permission/LimitationResolverTest.php @@ -0,0 +1,283 @@ +permissionResolver = $this->createMock(PermissionResolver::class); + + $this->limitationResolver = new LimitationResolver( + $this->createMock(ContentService::class), + $this->createMock(ContentTypeService::class), + $this->createMock(LanguageService::class), + $this->createMock(LocationService::class), + new LookupLimitationsTransformer(), + $this->permissionResolver + ); + } + + /** + * @dataProvider provideDataForTestGetLanguageLimitations + * + * @param array $expected + * @param iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Language> $languages + */ + public function testGetLanguageLimitations( + array $expected, + ContentInfo $contentInfo, + Location $location, + LookupLimitationResult $lookupLimitationResult, + iterable $languages + ): void { + $this->mockPermissionResolverLookupLimitations( + $contentInfo, + $location, + $lookupLimitationResult + ); + self::assertEquals( + $expected, + $this->limitationResolver->getLanguageLimitations( + 'edit', + $contentInfo, + $languages, + [$location] + ) + ); + } + + /** + * @return iterable, + * \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo, + * \Ibexa\Contracts\Core\Repository\Values\Content\Location, + * \Ibexa\Contracts\Core\Repository\Values\User\LookupLimitationResult, + * iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Language> + * }> + */ + public function provideDataForTestGetLanguageLimitations(): iterable + { + $english = $this->createLanguage(1, true, 'eng-GB', 'English'); + $german = $this->createLanguage(2, true, 'ger-DE', 'German'); + $french = $this->createLanguage(3, false, 'fra-FR', 'French'); + $contentInfo = $this->createContentInfo(); + $location = $this->createLocation(); + $languages = [ + $english, + $german, + $french, + ]; + + yield 'No access to all languages' => [ + [ + $this->getLanguageAccessData(false, $english), + $this->getLanguageAccessData(false, $german), + $this->getLanguageAccessData(false, $french), + ], + $contentInfo, + $location, + new LookupLimitationResult(false), + $languages, + ]; + + yield 'Access to all enabled languages' => [ + [ + $this->getLanguageAccessData(true, $english), + $this->getLanguageAccessData(true, $german), + $this->getLanguageAccessData(false, $french), + ], + $contentInfo, + $location, + new LookupLimitationResult(true), + $languages, + ]; + + yield 'Limited access to English language by policy limitation' => [ + [ + $this->getLanguageAccessData(true, $english), + $this->getLanguageAccessData(false, $german), + $this->getLanguageAccessData(false, $french), + ], + $contentInfo, + $location, + new LookupLimitationResult( + true, + [], + [ + new LookupPolicyLimitations( + $this->createMock(Policy::class), + [ + $this->createLanguageLimitation(['eng-GB']), + ] + ), + ] + ), + $languages, + ]; + + yield 'Limited access to German language by role limitation' => [ + [ + $this->getLanguageAccessData(false, $english), + $this->getLanguageAccessData(true, $german), + $this->getLanguageAccessData(false, $french), + ], + $contentInfo, + $location, + new LookupLimitationResult( + true, + [ + $this->createLanguageLimitation(['ger-DE']), + ], + ), + $languages, + ]; + + yield 'Limited access to English and German languages by role and policy limitations' => [ + [ + $this->getLanguageAccessData(true, $english), + $this->getLanguageAccessData(true, $german), + $this->getLanguageAccessData(false, $french), + ], + $contentInfo, + $location, + new LookupLimitationResult( + true, + [ + $this->createLanguageLimitation(['eng-GB', 'fra-FR']), + ], + [ + new LookupPolicyLimitations( + $this->createMock(Policy::class), + [ + $this->createLanguageLimitation(['ger-DE', 'fra-FR']), + ] + ), + ] + ), + $languages, + ]; + } + + private function createContentInfo(): ContentInfo + { + return $this->createMock(ContentInfo::class); + } + + private function createLocation(): Location + { + return $this->createMock(Location::class); + } + + private function createLanguage( + int $id, + bool $enabled, + string $languageCode, + string $name + ): Language { + return new Language( + [ + 'id' => $id, + 'enabled' => $enabled, + 'languageCode' => $languageCode, + 'name' => $name, + ] + ); + } + + /** + * @return array{ + * languageCode: string, + * name: string, + * hasAccess: bool, + * } + */ + private function getLanguageAccessData( + bool $hasAccess, + Language $language + ): array { + return [ + 'languageCode' => $language->getLanguageCode(), + 'name' => $language->getName(), + 'hasAccess' => $hasAccess, + ]; + } + + /** + * @param array $limitationValues + */ + private function createLanguageLimitation(array $limitationValues): Limitation\LanguageLimitation + { + return new Limitation\LanguageLimitation( + [ + 'limitationValues' => $limitationValues, + ] + ); + } + + private function mockPermissionResolverLookupLimitations( + ContentInfo $contentInfo, + Location $location, + LookupLimitationResult $lookupLimitationResult + ): void { + $languageCodes = [ + 'eng-GB', + 'ger-DE', + ]; + $targets = [ + $location, + (new VersionBuilder())->translateToAnyLanguageOf($languageCodes)->build(), + ]; + + $this->permissionResolver + ->method('lookupLimitations') + ->with( + 'content', + 'edit', + $contentInfo, + $targets, + [Limitation::LANGUAGE], + ) + ->willReturn($lookupLimitationResult); + } +} diff --git a/tests/lib/Permission/PermissionCheckerTest.php b/tests/lib/Permission/PermissionCheckerTest.php index caba5cec03..57e6328be2 100644 --- a/tests/lib/Permission/PermissionCheckerTest.php +++ b/tests/lib/Permission/PermissionCheckerTest.php @@ -8,11 +8,8 @@ namespace Ibexa\Tests\AdminUi\Permission; +use Ibexa\AdminUi\Permission\LimitationResolverInterface; use Ibexa\AdminUi\Permission\PermissionChecker; -use Ibexa\Contracts\Core\Repository\ContentService; -use Ibexa\Contracts\Core\Repository\ContentTypeService; -use Ibexa\Contracts\Core\Repository\LanguageService; -use Ibexa\Contracts\Core\Repository\LocationService; use Ibexa\Contracts\Core\Repository\PermissionResolver; use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Contracts\Core\Repository\Values\Content; @@ -27,23 +24,14 @@ class PermissionCheckerTest extends TestCase { private const USER_ID = 14; - /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver&\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; - /** @var \Ibexa\Contracts\Core\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject */ private $userService; - /** @var \Ibexa\Contracts\Core\Repository\LocationService */ - private $locationService; - - /** @var \Ibexa\Contracts\Core\Repository\ContentService */ - private $contentService; - - /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \Ibexa\Contracts\Core\Repository\LanguageService */ - private $languageService; + /** @var \Ibexa\AdminUi\Permission\LimitationResolverInterface&\PHPUnit\Framework\MockObject\MockObject */ + private LimitationResolverInterface $permissionLimitationResolver; /** @var \Ibexa\AdminUi\Permission\PermissionChecker */ private $permissionChecker; @@ -55,19 +43,13 @@ public function setUp(): void ->method('getCurrentUserReference') ->willReturn($this->generateUser(self::USER_ID)); + $this->permissionLimitationResolver = $this->createMock(LimitationResolverInterface::class); $this->userService = $this->createMock(UserService::class); - $this->locationService = $this->createMock(LocationService::class); - $this->contentService = $this->createMock(ContentService::class); - $this->contentTypeService = $this->createMock(ContentTypeService::class); - $this->languageService = $this->createMock(LanguageService::class); $this->permissionChecker = new PermissionChecker( $this->permissionResolver, + $this->permissionLimitationResolver, $this->userService, - $this->locationService, - $this->contentService, - $this->contentTypeService, - $this->languageService ); }