diff --git a/module/Core/src/Tag/Repository/TagRepository.php b/module/Core/src/Tag/Repository/TagRepository.php index adfb6480..278dbe8b 100644 --- a/module/Core/src/Tag/Repository/TagRepository.php +++ b/module/Core/src/Tag/Repository/TagRepository.php @@ -59,8 +59,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito default => $qb, }); - // For admins and when no API key is present, we'll return tags which are not linked to any short URL - $joiningMethod = ApiKey::isAdmin($apiKey) ? 'leftJoin' : 'join'; + // For non-restricted API keys, we'll return tags which are not linked to any short URL + $joiningMethod = ! ApiKey::isShortUrlRestricted($apiKey) ? 'leftJoin' : 'join'; $tagsSubQb = $conn->createQueryBuilder(); $tagsSubQb ->select('t.id AS tag_id', 't.name AS tag', 'COUNT(DISTINCT s.id) AS short_urls_count') diff --git a/module/Core/src/Tag/TagService.php b/module/Core/src/Tag/TagService.php index ea9a4e8b..36fd0514 100644 --- a/module/Core/src/Tag/TagService.php +++ b/module/Core/src/Tag/TagService.php @@ -59,7 +59,7 @@ class TagService implements TagServiceInterface */ public function deleteTags(array $tagNames, ?ApiKey $apiKey = null): void { - if (! ApiKey::isAdmin($apiKey)) { + if (ApiKey::isShortUrlRestricted($apiKey)) { throw ForbiddenTagOperationException::forDeletion(); } @@ -75,7 +75,7 @@ class TagService implements TagServiceInterface */ public function renameTag(TagRenaming $renaming, ?ApiKey $apiKey = null): Tag { - if (! ApiKey::isAdmin($apiKey)) { + if (ApiKey::isShortUrlRestricted($apiKey)) { throw ForbiddenTagOperationException::forRenaming(); } diff --git a/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php b/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php index 9a8f8056..122829ed 100644 --- a/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php +++ b/module/Rest/src/ApiKey/Spec/WithApiKeySpecsEnsuringJoin.php @@ -18,7 +18,7 @@ class WithApiKeySpecsEnsuringJoin extends BaseSpecification protected function getSpec(): Specification { - return $this->apiKey === null || ApiKey::isAdmin($this->apiKey) ? Spec::andX() : Spec::andX( + return $this->apiKey === null || ! ApiKey::isShortUrlRestricted($this->apiKey) ? Spec::andX() : Spec::andX( Spec::join($this->fieldToJoin, 's'), $this->apiKey->spec($this->fieldToJoin), ); diff --git a/module/Rest/src/Entity/ApiKey.php b/module/Rest/src/Entity/ApiKey.php index 88cfa27e..72977c86 100644 --- a/module/Rest/src/Entity/ApiKey.php +++ b/module/Rest/src/Entity/ApiKey.php @@ -122,6 +122,21 @@ class ApiKey extends AbstractEntity return $apiKey === null || $apiKey->roles->isEmpty(); } + /** + * Tells if provided API key has any of the roles restricting at the short URL level + */ + public static function isShortUrlRestricted(?ApiKey $apiKey): bool + { + if ($apiKey === null) { + return false; + } + + return ( + $apiKey->roles->containsKey(Role::AUTHORED_SHORT_URLS->value) + || $apiKey->roles->containsKey(Role::DOMAIN_SPECIFIC->value) + ); + } + public function hasRole(Role $role): bool { return $this->roles->containsKey($role->value);