From cc09ff54bdcfff63cc5d0ec4a05b6cc3404ee151 Mon Sep 17 00:00:00 2001 From: mattamon Date: Fri, 1 Mar 2024 10:17:50 +0100 Subject: [PATCH 01/14] Initial draft --- .../api_platform/resources/translation.yaml | 9 +++ config/serialization/translation.yaml | 6 ++ config/services.yaml | 1 + src/Dto/Translation.php | 37 +++++++++ src/State/TranslationProcessor.php | 78 +++++++++++++++++++ src/Util/Constants/PublicTranslations.php | 21 +++++ 6 files changed, 152 insertions(+) create mode 100644 config/api_platform/resources/translation.yaml create mode 100644 config/serialization/translation.yaml create mode 100644 src/Dto/Translation.php create mode 100644 src/State/TranslationProcessor.php create mode 100644 src/Util/Constants/PublicTranslations.php diff --git a/config/api_platform/resources/translation.yaml b/config/api_platform/resources/translation.yaml new file mode 100644 index 000000000..cd4a3c53e --- /dev/null +++ b/config/api_platform/resources/translation.yaml @@ -0,0 +1,9 @@ +resources: + Pimcore\Bundle\StudioApiBundle\Dto\Translation: + operations: + ApiPlatform\Metadata\Post: + processor: Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor + openapiContext: + summary: 'Getting translations based on context' + normalizationContext: + groups: [ 'translation:read' ] diff --git a/config/serialization/translation.yaml b/config/serialization/translation.yaml new file mode 100644 index 000000000..10f072e54 --- /dev/null +++ b/config/serialization/translation.yaml @@ -0,0 +1,6 @@ +Pimcore\Bundle\StudioApiBundle\Dto\Translation: + attributes: + locale: + groups: ['translation:read'] + translationKeys: + groups: ['translation:read'] \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index de85f81de..4ea0ff116 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -27,6 +27,7 @@ services: # Processors Pimcore\Bundle\StudioApiBundle\State\ResetPasswordProcessor: ~ + Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor: ~ # Filters Pimcore\Bundle\StudioApiBundle\Filter\AssetParentIdFilter: diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php new file mode 100644 index 000000000..e572a9205 --- /dev/null +++ b/src/Dto/Translation.php @@ -0,0 +1,37 @@ +locale; + } + + public function getTranslationKeys(): array + { + return $this->translationKeys; + } +} \ No newline at end of file diff --git a/src/State/TranslationProcessor.php b/src/State/TranslationProcessor.php new file mode 100644 index 000000000..61703961c --- /dev/null +++ b/src/State/TranslationProcessor.php @@ -0,0 +1,78 @@ +translator = $translator; + } + + public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Translation + { + + // if ( + // !$operation instanceof Post || + // !$data instanceof Translation || + // $operation->getUriTemplate() !== '/translations' + // ) { + // // wrong operation + // throw new OperationNotFoundException(); + // } + + $translationKeys = $data->getTranslationKeys(); + + if(empty($translationKeys)) { + return $this->getAllTranslations($data->getLocale()); + } + + return $this->getTranslations($data->getLocale(), $translationKeys); + } + + private function getAllTranslations(string $locale): Translation + { + return new Translation( + $locale, + $this->translator->getCatalogue($locale)->all(self::DOMAIN) + ); + } + + private function getTranslations(string $locale, array $translationKeys): Translation + { + $translations = []; + foreach ($translationKeys as $translationKey) { + $translations[$translationKey] = $this->translator->getCatalogue($locale)->get($translationKey, self::DOMAIN); + } + return new Translation($locale, $translations); + } +} \ No newline at end of file diff --git a/src/Util/Constants/PublicTranslations.php b/src/Util/Constants/PublicTranslations.php new file mode 100644 index 000000000..15947fae4 --- /dev/null +++ b/src/Util/Constants/PublicTranslations.php @@ -0,0 +1,21 @@ + Date: Fri, 1 Mar 2024 13:00:26 +0100 Subject: [PATCH 02/14] Add checks and add default language --- config/api_platform/resources/translation.yaml | 6 ++++-- src/Dto/Translation.php | 2 +- src/State/TranslationProcessor.php | 18 +++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/config/api_platform/resources/translation.yaml b/config/api_platform/resources/translation.yaml index cd4a3c53e..c10adbbea 100644 --- a/config/api_platform/resources/translation.yaml +++ b/config/api_platform/resources/translation.yaml @@ -3,7 +3,9 @@ resources: operations: ApiPlatform\Metadata\Post: processor: Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor + uriTemplate: '/translations' openapiContext: - summary: 'Getting translations based on context' + summary: 'Getting translations based on context' + normalizationContext: - groups: [ 'translation:read' ] + groups: [ 'translation:read' ] diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php index e572a9205..f0e82dff6 100644 --- a/src/Dto/Translation.php +++ b/src/Dto/Translation.php @@ -19,7 +19,7 @@ final readonly class Translation { public function __construct( - private string $locale, + private string $locale = 'en', private array $translationKeys = [] ) { diff --git a/src/State/TranslationProcessor.php b/src/State/TranslationProcessor.php index 61703961c..a4e65ba59 100644 --- a/src/State/TranslationProcessor.php +++ b/src/State/TranslationProcessor.php @@ -14,6 +14,7 @@ namespace Pimcore\Bundle\StudioApiBundle\State; use ApiPlatform\Metadata\Operation; +use ApiPlatform\Metadata\Post; use ApiPlatform\State\ProcessorInterface; use InvalidArgumentException; use Pimcore\Bundle\StudioApiBundle\Dto\Translation; @@ -40,15 +41,14 @@ public function __construct( public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Translation { - - // if ( - // !$operation instanceof Post || - // !$data instanceof Translation || - // $operation->getUriTemplate() !== '/translations' - // ) { - // // wrong operation - // throw new OperationNotFoundException(); - // } + if ( + !$operation instanceof Post || + !$data instanceof Translation || + $operation->getUriTemplate() !== '/translations' + ) { + // wrong operation + throw new OperationNotFoundException(); + } $translationKeys = $data->getTranslationKeys(); From 34966e46007bb07591eb3af42d373ba4cd93d509 Mon Sep 17 00:00:00 2001 From: mattamon Date: Fri, 1 Mar 2024 13:15:38 +0100 Subject: [PATCH 03/14] Introduce translatorservice --- config/serialization/translation.yaml | 2 +- config/services.yaml | 5 +- src/Dto/Translation.php | 6 +-- src/Service/TranslatorService.php | 56 ++++++++++++++++++++++ src/Service/TranslatorServiceInterface.php | 26 ++++++++++ src/State/TranslationProcessor.php | 41 ++++------------ 6 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 src/Service/TranslatorService.php create mode 100644 src/Service/TranslatorServiceInterface.php diff --git a/config/serialization/translation.yaml b/config/serialization/translation.yaml index 10f072e54..992a739b6 100644 --- a/config/serialization/translation.yaml +++ b/config/serialization/translation.yaml @@ -2,5 +2,5 @@ Pimcore\Bundle\StudioApiBundle\Dto\Translation: attributes: locale: groups: ['translation:read'] - translationKeys: + keys: groups: ['translation:read'] \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 4ea0ff116..e0896774a 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -73,4 +73,7 @@ services: class: Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\AssetSearchAdapter Pimcore\Bundle\StudioApiBundle\Service\IconServiceInterface: - class: Pimcore\Bundle\StudioApiBundle\Service\IconService \ No newline at end of file + class: Pimcore\Bundle\StudioApiBundle\Service\IconService + + Pimcore\Bundle\StudioApiBundle\Service\TranslatorServiceInterface: + class: Pimcore\Bundle\StudioApiBundle\Service\TranslatorService \ No newline at end of file diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php index f0e82dff6..723116a43 100644 --- a/src/Dto/Translation.php +++ b/src/Dto/Translation.php @@ -20,7 +20,7 @@ { public function __construct( private string $locale = 'en', - private array $translationKeys = [] + private array $keys = [] ) { } @@ -30,8 +30,8 @@ public function getLocale(): string return $this->locale; } - public function getTranslationKeys(): array + public function getKeys(): array { - return $this->translationKeys; + return $this->keys; } } \ No newline at end of file diff --git a/src/Service/TranslatorService.php b/src/Service/TranslatorService.php new file mode 100644 index 000000000..ffb59b778 --- /dev/null +++ b/src/Service/TranslatorService.php @@ -0,0 +1,56 @@ +translator = $translator; + } + + public function getAllTranslations(string $locale): Translation + { + return new Translation( + $locale, + $this->translator->getCatalogue($locale)->all(self::DOMAIN) + ); + } + + public function getTranslationsForKeys(string $locale, array $keys): Translation + { + $translations = []; + foreach ($keys as $key) { + $translations[$key] = $this->translator->getCatalogue($locale)->get($key, self::DOMAIN); + } + return new Translation($locale, $translations); + } +} \ No newline at end of file diff --git a/src/Service/TranslatorServiceInterface.php b/src/Service/TranslatorServiceInterface.php new file mode 100644 index 000000000..d498c3810 --- /dev/null +++ b/src/Service/TranslatorServiceInterface.php @@ -0,0 +1,26 @@ +translator = $translator; } public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Translation @@ -44,35 +37,17 @@ public function process(mixed $data, Operation $operation, array $uriVariables = if ( !$operation instanceof Post || !$data instanceof Translation || - $operation->getUriTemplate() !== '/translations' + $operation->getUriTemplate() !== self::OPERATION_URI_TEMPLATE ) { // wrong operation throw new OperationNotFoundException(); } - $translationKeys = $data->getTranslationKeys(); - if(empty($translationKeys)) { - return $this->getAllTranslations($data->getLocale()); + if(empty($data->getKeys())) { + return $this->translatorService->getAllTranslations($data->getLocale()); } - return $this->getTranslations($data->getLocale(), $translationKeys); - } - - private function getAllTranslations(string $locale): Translation - { - return new Translation( - $locale, - $this->translator->getCatalogue($locale)->all(self::DOMAIN) - ); - } - - private function getTranslations(string $locale, array $translationKeys): Translation - { - $translations = []; - foreach ($translationKeys as $translationKey) { - $translations[$translationKey] = $this->translator->getCatalogue($locale)->get($translationKey, self::DOMAIN); - } - return new Translation($locale, $translations); + return $this->translatorService->getTranslationsForKeys($data->getLocale(), $data->getKeys()); } } \ No newline at end of file From 6ca6c500f82ee117b5f4e79ad3934e7121ca61f4 Mon Sep 17 00:00:00 2001 From: mattamon Date: Mon, 4 Mar 2024 08:23:05 +0100 Subject: [PATCH 04/14] Follow conventions --- src/State/TranslationProcessor.php | 7 ++++++- src/Util/Constants/PublicTranslations.php | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/State/TranslationProcessor.php b/src/State/TranslationProcessor.php index fce3faf2f..528cd1824 100644 --- a/src/State/TranslationProcessor.php +++ b/src/State/TranslationProcessor.php @@ -32,7 +32,12 @@ public function __construct( ) { } - public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): Translation + public function process( + mixed $data, + Operation $operation, + array $uriVariables = [], + array $context = [] + ): Translation { if ( !$operation instanceof Post || diff --git a/src/Util/Constants/PublicTranslations.php b/src/Util/Constants/PublicTranslations.php index 15947fae4..70724de7b 100644 --- a/src/Util/Constants/PublicTranslations.php +++ b/src/Util/Constants/PublicTranslations.php @@ -11,8 +11,10 @@ * @license http://www.pimcore.org/license PCL */ +namespace Pimcore\Bundle\StudioApiBundle\Util\Constants; + final readonly class PublicTranslations { - public const KEYS = [ + public const PUBLIC_KEYS = [ 'username', 'password', 'login', From d6f6f4d52ca4064e6ef25d84aead575214ba560f Mon Sep 17 00:00:00 2001 From: mattamon Date: Mon, 4 Mar 2024 10:32:19 +0100 Subject: [PATCH 05/14] Add translator service test --- .../Translator/TranslatorServiceTest.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 tests/Unit/Service/Translator/TranslatorServiceTest.php diff --git a/tests/Unit/Service/Translator/TranslatorServiceTest.php b/tests/Unit/Service/Translator/TranslatorServiceTest.php new file mode 100644 index 000000000..b3cb0be39 --- /dev/null +++ b/tests/Unit/Service/Translator/TranslatorServiceTest.php @@ -0,0 +1,64 @@ +mockTranslatorService(); + $locale = 'en'; + + $translations = $translatorService->getAllTranslations($locale); + + $this->assertEquals($locale, $translations->getLocale()); + $this->assertEmpty($translations->getKeys()); + } + + /** + * @throws Exception + */ + public function testGetTranslationsForKeys(): void + { + $translatorService = $this->mockTranslatorService(); + $locale = 'fr'; + $keys = PublicTranslations::PUBLIC_KEYS; + + $translations = $translatorService->getTranslationsForKeys($locale, $keys); + + $this->assertEquals($locale, $translations->getLocale()); + foreach ($keys as $key) { + $this->assertArrayHasKey($key, $translations->getKeys()); + } + } + + /** + * @throws Exception + */ + private function mockTranslatorService(): TranslatorServiceInterface + { + $translator = $this->makeEmpty(Translator::class); + return new TranslatorService($translator); + } +} \ No newline at end of file From e21c1bebf540946dac20d5dfac8ae5e48c93d728 Mon Sep 17 00:00:00 2001 From: mattamon Date: Mon, 4 Mar 2024 11:03:49 +0100 Subject: [PATCH 06/14] Add processor test --- src/Service/TranslatorServiceInterface.php | 2 +- tests/Unit/State/TranslationProcessorTest.php | 97 +++++++++++++++++++ 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 tests/Unit/State/TranslationProcessorTest.php diff --git a/src/Service/TranslatorServiceInterface.php b/src/Service/TranslatorServiceInterface.php index d498c3810..5c70f987b 100644 --- a/src/Service/TranslatorServiceInterface.php +++ b/src/Service/TranslatorServiceInterface.php @@ -13,7 +13,7 @@ namespace Pimcore\Bundle\StudioApiBundle\Service; -use Pimcore\Bundle\StudioApiBundle\Dto\Translation; +use Pimcore\Bundle\StudioApiBundle\Dto\Translation;; /** * @internal diff --git a/tests/Unit/State/TranslationProcessorTest.php b/tests/Unit/State/TranslationProcessorTest.php new file mode 100644 index 000000000..675b2d86b --- /dev/null +++ b/tests/Unit/State/TranslationProcessorTest.php @@ -0,0 +1,97 @@ +mockTranslationProcessor(); + + $this->expectException(OperationNotFoundException::class); + + $translationProcessor->process( + $this->getTranslation(), + $this->getPostOperation('/wrong-uri-template') + ); + } + + /** + * @throws Exception + */ + + public function testWrongOperation(): void + { + $translationProcessor = $this->mockTranslationProcessor(); + + $this->expectException(OperationNotFoundException::class); + + $translationProcessor->process( + $this->getTranslation(), + $this->getGetOperation() + ); + } + + /** + * @throws Exception + */ + public function testWrongData(): void + { + $translationProcessor = $this->mockTranslationProcessor(); + + $this->expectException(OperationNotFoundException::class); + + $translationProcessor->process( + new stdClass(), + $this->getPostOperation('/translations') + ); + } + + /** + * @throws Exception + */ + private function mockTranslationProcessor(): TranslationProcessor + { + $translatorInterface = $this->makeEmpty(TranslatorServiceInterface::class); + return new TranslationProcessor($translatorInterface); + } + + private function getTranslation(): Translation + { + return new Translation('en', []); + } + + private function getPostOperation(string $uriTemplate): Post + { + return new Post(uriTemplate: $uriTemplate); + } + + private function getGetOperation(): Get + { + return new Get(uriTemplate: '/translations'); + } +} \ No newline at end of file From 9ec0d4d684817e690fed298e184c389a876c2d54 Mon Sep 17 00:00:00 2001 From: mattamon Date: Mon, 4 Mar 2024 11:10:58 +0100 Subject: [PATCH 07/14] Remove semi-colon --- src/Service/TranslatorServiceInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Service/TranslatorServiceInterface.php b/src/Service/TranslatorServiceInterface.php index 5c70f987b..d498c3810 100644 --- a/src/Service/TranslatorServiceInterface.php +++ b/src/Service/TranslatorServiceInterface.php @@ -13,7 +13,7 @@ namespace Pimcore\Bundle\StudioApiBundle\Service; -use Pimcore\Bundle\StudioApiBundle\Dto\Translation;; +use Pimcore\Bundle\StudioApiBundle\Dto\Translation; /** * @internal From 42e64be3e68ad44567f70a3f5143aa5b56bc83fa Mon Sep 17 00:00:00 2001 From: mattamon Date: Mon, 4 Mar 2024 12:21:36 +0100 Subject: [PATCH 08/14] Add dto translation test --- tests/Unit/Dto/TranslationTest.php | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/Unit/Dto/TranslationTest.php diff --git a/tests/Unit/Dto/TranslationTest.php b/tests/Unit/Dto/TranslationTest.php new file mode 100644 index 000000000..b48e49445 --- /dev/null +++ b/tests/Unit/Dto/TranslationTest.php @@ -0,0 +1,29 @@ +assertSame('en', $translation->getLocale()); + $this->assertIsArray($translation->getKeys()); + $this->assertCount(1, $translation->getKeys()); + $this->assertContains('login', $translation->getKeys()); + } +} \ No newline at end of file From 1b90aa14a1f83e50407b663d89c4e39f67fa073f Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 08:39:39 +0100 Subject: [PATCH 09/14] Add public voter --- .../api_platform/resources/translation.yaml | 1 + config/services.yaml | 8 ++- src/Exception/NoRequestException.php | 25 +++++++ src/Security/Trait/PublicTranslationTrait.php | 41 +++++++++++ src/Security/Voter/PublicTokenVoter.php | 70 +++++++++++++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/Exception/NoRequestException.php create mode 100644 src/Security/Trait/PublicTranslationTrait.php create mode 100644 src/Security/Voter/PublicTokenVoter.php diff --git a/config/api_platform/resources/translation.yaml b/config/api_platform/resources/translation.yaml index c10adbbea..4f2d2d77c 100644 --- a/config/api_platform/resources/translation.yaml +++ b/config/api_platform/resources/translation.yaml @@ -1,5 +1,6 @@ resources: Pimcore\Bundle\StudioApiBundle\Dto\Translation: + security: 'is_granted("PUBLIC_API_PLATFORM", "translation")' operations: ApiPlatform\Metadata\Post: processor: Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor diff --git a/config/services.yaml b/config/services.yaml index e128801a5..5eb9c4be8 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -85,4 +85,10 @@ services: class: Pimcore\Bundle\StudioApiBundle\Service\GenericData\V1\AssetQueryProvider Pimcore\Bundle\StudioApiBundle\Service\TranslatorServiceInterface: - class: Pimcore\Bundle\StudioApiBundle\Service\TranslatorService \ No newline at end of file + class: Pimcore\Bundle\StudioApiBundle\Service\TranslatorService + + #Voters + Pimcore\Bundle\StudioApiBundle\Security\Voter\PublicTokenVoter: + arguments: [ '@request_stack' ] + tags: + - { name: security.voter } \ No newline at end of file diff --git a/src/Exception/NoRequestException.php b/src/Exception/NoRequestException.php new file mode 100644 index 000000000..d86875cd4 --- /dev/null +++ b/src/Exception/NoRequestException.php @@ -0,0 +1,25 @@ +all(); + if(!array_key_exists(self::ARRAY_KEYS_INDEX, $parameters)) { + return false; + } + + foreach($parameters[self::ARRAY_KEYS_INDEX] as $key) { + // Allow only public keys + if(!in_array($key, PublicTranslations::PUBLIC_KEYS, true)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/Security/Voter/PublicTokenVoter.php b/src/Security/Voter/PublicTokenVoter.php new file mode 100644 index 000000000..c76e78517 --- /dev/null +++ b/src/Security/Voter/PublicTokenVoter.php @@ -0,0 +1,70 @@ +getCurrentRequest(); + + // TODO Add security service once merged with PR#5 + return $this->voteOnRequest($request, $subject); + } + + private function getCurrentRequest(): Request + { + $request = $this->requestStack->getCurrentRequest(); + if(!$request) { + throw new NoRequestException('No request found'); + } + + return $request; + } + + private function voteOnRequest(Request $request, string $subject): bool + { + return match ($subject) { + 'translation' => $this->voteOnTranslation($request->getPayload()), + default => false, + }; + } +} \ No newline at end of file From 6309abf64b95d6982439cdb16a497ad71fb49203 Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 07:40:14 +0000 Subject: [PATCH 10/14] Apply php-cs-fixer changes --- src/Dto/Translation.php | 5 ++--- src/Exception/NoRequestException.php | 11 +++------- src/Security/Trait/PublicTranslationTrait.php | 10 ++++----- src/Security/Voter/PublicTokenVoter.php | 9 +++----- src/Service/TranslatorService.php | 5 +++-- src/Service/TranslatorServiceInterface.php | 4 ++-- src/State/TranslationProcessor.php | 22 +++++++++---------- src/Util/Constants/PublicTranslations.php | 7 +++--- tests/Unit/Dto/TranslationTest.php | 2 +- .../Translator/TranslatorServiceTest.php | 3 ++- tests/Unit/State/TranslationProcessorTest.php | 4 ++-- 11 files changed, 36 insertions(+), 46 deletions(-) diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php index 723116a43..e88859590 100644 --- a/src/Dto/Translation.php +++ b/src/Dto/Translation.php @@ -21,8 +21,7 @@ public function __construct( private string $locale = 'en', private array $keys = [] - ) - { + ) { } public function getLocale(): string @@ -34,4 +33,4 @@ public function getKeys(): array { return $this->keys; } -} \ No newline at end of file +} diff --git a/src/Exception/NoRequestException.php b/src/Exception/NoRequestException.php index d86875cd4..a4cae39e1 100644 --- a/src/Exception/NoRequestException.php +++ b/src/Exception/NoRequestException.php @@ -4,22 +4,17 @@ /** * Pimcore * - * This source file is available under two different licenses: - * - GNU General Public License version 3 (GPLv3) + * This source file is available under following license: * - 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 + * @license http://www.pimcore.org/license PCL */ namespace Pimcore\Bundle\StudioApiBundle\Exception; use RuntimeException; - final class NoRequestException extends RuntimeException { - -} \ No newline at end of file +} diff --git a/src/Security/Trait/PublicTranslationTrait.php b/src/Security/Trait/PublicTranslationTrait.php index 6643a9e0d..671b25824 100644 --- a/src/Security/Trait/PublicTranslationTrait.php +++ b/src/Security/Trait/PublicTranslationTrait.php @@ -4,14 +4,11 @@ /** * Pimcore * - * This source file is available under two different licenses: - * - GNU General Public License version 3 (GPLv3) + * This source file is available under following license: * - 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 + * @license http://www.pimcore.org/license PCL */ namespace Pimcore\Bundle\StudioApiBundle\Security\Trait; @@ -22,6 +19,7 @@ trait PublicTranslationTrait { private const ARRAY_KEYS_INDEX = 'keys'; + private function voteOnTranslation(InputBag $payload): bool { $parameters = $payload->all(); @@ -38,4 +36,4 @@ private function voteOnTranslation(InputBag $payload): bool return true; } -} \ No newline at end of file +} diff --git a/src/Security/Voter/PublicTokenVoter.php b/src/Security/Voter/PublicTokenVoter.php index c76e78517..2de4b9c05 100644 --- a/src/Security/Voter/PublicTokenVoter.php +++ b/src/Security/Voter/PublicTokenVoter.php @@ -4,14 +4,11 @@ /** * Pimcore * - * This source file is available under two different licenses: - * - GNU General Public License version 3 (GPLv3) + * This source file is available under following license: * - 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 + * @license http://www.pimcore.org/license PCL */ namespace Pimcore\Bundle\StudioApiBundle\Security\Voter; @@ -67,4 +64,4 @@ private function voteOnRequest(Request $request, string $subject): bool default => false, }; } -} \ No newline at end of file +} diff --git a/src/Service/TranslatorService.php b/src/Service/TranslatorService.php index ffb59b778..2af962dbc 100644 --- a/src/Service/TranslatorService.php +++ b/src/Service/TranslatorService.php @@ -23,8 +23,8 @@ */ final class TranslatorService implements TranslatorServiceInterface { - private TranslatorBagInterface $translator; + private const DOMAIN = 'admin'; public function __construct( @@ -51,6 +51,7 @@ public function getTranslationsForKeys(string $locale, array $keys): Translation foreach ($keys as $key) { $translations[$key] = $this->translator->getCatalogue($locale)->get($key, self::DOMAIN); } + return new Translation($locale, $translations); } -} \ No newline at end of file +} diff --git a/src/Service/TranslatorServiceInterface.php b/src/Service/TranslatorServiceInterface.php index d498c3810..677d8d6b9 100644 --- a/src/Service/TranslatorServiceInterface.php +++ b/src/Service/TranslatorServiceInterface.php @@ -21,6 +21,6 @@ interface TranslatorServiceInterface { public function getAllTranslations(string $locale): Translation; - public function getTranslationsForKeys(string $locale, array $keys): Translation; -} \ No newline at end of file + public function getTranslationsForKeys(string $locale, array $keys): Translation; +} diff --git a/src/State/TranslationProcessor.php b/src/State/TranslationProcessor.php index 528cd1824..58fb7fc88 100644 --- a/src/State/TranslationProcessor.php +++ b/src/State/TranslationProcessor.php @@ -37,17 +37,15 @@ public function process( Operation $operation, array $uriVariables = [], array $context = [] - ): Translation - { - if ( - !$operation instanceof Post || - !$data instanceof Translation || - $operation->getUriTemplate() !== self::OPERATION_URI_TEMPLATE - ) { - // wrong operation - throw new OperationNotFoundException(); - } - + ): Translation { + if ( + !$operation instanceof Post || + !$data instanceof Translation || + $operation->getUriTemplate() !== self::OPERATION_URI_TEMPLATE + ) { + // wrong operation + throw new OperationNotFoundException(); + } if(empty($data->getKeys())) { return $this->translatorService->getAllTranslations($data->getLocale()); @@ -55,4 +53,4 @@ public function process( return $this->translatorService->getTranslationsForKeys($data->getLocale(), $data->getKeys()); } -} \ No newline at end of file +} diff --git a/src/Util/Constants/PublicTranslations.php b/src/Util/Constants/PublicTranslations.php index 70724de7b..c9ea4305d 100644 --- a/src/Util/Constants/PublicTranslations.php +++ b/src/Util/Constants/PublicTranslations.php @@ -13,11 +13,12 @@ namespace Pimcore\Bundle\StudioApiBundle\Util\Constants; -final readonly class PublicTranslations { +final readonly class PublicTranslations +{ public const PUBLIC_KEYS = [ 'username', 'password', 'login', - 'Forgot your password' + 'Forgot your password', ]; -} \ No newline at end of file +} diff --git a/tests/Unit/Dto/TranslationTest.php b/tests/Unit/Dto/TranslationTest.php index b48e49445..e0d1f3167 100644 --- a/tests/Unit/Dto/TranslationTest.php +++ b/tests/Unit/Dto/TranslationTest.php @@ -26,4 +26,4 @@ public function testTranslation(): void $this->assertCount(1, $translation->getKeys()); $this->assertContains('login', $translation->getKeys()); } -} \ No newline at end of file +} diff --git a/tests/Unit/Service/Translator/TranslatorServiceTest.php b/tests/Unit/Service/Translator/TranslatorServiceTest.php index b3cb0be39..51a6eda55 100644 --- a/tests/Unit/Service/Translator/TranslatorServiceTest.php +++ b/tests/Unit/Service/Translator/TranslatorServiceTest.php @@ -59,6 +59,7 @@ public function testGetTranslationsForKeys(): void private function mockTranslatorService(): TranslatorServiceInterface { $translator = $this->makeEmpty(Translator::class); + return new TranslatorService($translator); } -} \ No newline at end of file +} diff --git a/tests/Unit/State/TranslationProcessorTest.php b/tests/Unit/State/TranslationProcessorTest.php index 675b2d86b..04b682e6c 100644 --- a/tests/Unit/State/TranslationProcessorTest.php +++ b/tests/Unit/State/TranslationProcessorTest.php @@ -43,7 +43,6 @@ public function testWrongUriTemplate(): void /** * @throws Exception */ - public function testWrongOperation(): void { $translationProcessor = $this->mockTranslationProcessor(); @@ -77,6 +76,7 @@ public function testWrongData(): void private function mockTranslationProcessor(): TranslationProcessor { $translatorInterface = $this->makeEmpty(TranslatorServiceInterface::class); + return new TranslationProcessor($translatorInterface); } @@ -94,4 +94,4 @@ private function getGetOperation(): Get { return new Get(uriTemplate: '/translations'); } -} \ No newline at end of file +} From d5c0b618a99049393ee028c6c2862743e09d9c67 Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 12:11:36 +0100 Subject: [PATCH 11/14] Add security service --- config/services.yaml | 15 ++++-------- .../NonPublicTranslationException.php | 23 +++++++++++++++++++ src/Security/Trait/PublicTranslationTrait.php | 12 +++++----- src/Security/Voter/PublicTokenVoter.php | 19 ++++++++------- 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 src/Exception/NonPublicTranslationException.php diff --git a/config/services.yaml b/config/services.yaml index b9fb7d61c..19330252c 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -27,9 +27,9 @@ services: # Processors Pimcore\Bundle\StudioApiBundle\State\ResetPasswordProcessor: ~ - Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor: ~ Pimcore\Bundle\StudioApiBundle\State\Token\Create\Processor: ~ Pimcore\Bundle\StudioApiBundle\State\Token\Refresh\Processor: ~ + Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor: ~ # Filters Pimcore\Bundle\StudioApiBundle\Filter\AssetParentIdFilter: @@ -89,19 +89,12 @@ services: Pimcore\Bundle\StudioApiBundle\Service\TranslatorServiceInterface: class: Pimcore\Bundle\StudioApiBundle\Service\TranslatorService - #Voters - Pimcore\Bundle\StudioApiBundle\Security\Voter\PublicTokenVoter: - arguments: [ '@request_stack' ] - tags: - - { name: security.voter } - Pimcore\Bundle\StudioApiBundle\Service\TokenServiceInterface: class: Pimcore\Bundle\StudioApiBundle\Service\TokenService Pimcore\Bundle\StudioApiBundle\Service\SecurityServiceInterface: class: Pimcore\Bundle\StudioApiBundle\Service\SecurityService - #Voters Pimcore\Bundle\StudioApiBundle\Security\Voter\TokenVoter: arguments: ['@request_stack'] @@ -109,9 +102,9 @@ services: - { name: security.voter } Pimcore\Bundle\StudioApiBundle\Security\Voter\PublicTokenVoter: - arguments: [ '@request_stack' ] - tags: - - { name: security.voter } + arguments: [ '@request_stack' ] + tags: + - { name: security.voter } #Decorators Pimcore\Bundle\StudioApiBundle\ApiPlatform\OpenApiFactoryDecorator: diff --git a/src/Exception/NonPublicTranslationException.php b/src/Exception/NonPublicTranslationException.php new file mode 100644 index 000000000..6acd9130c --- /dev/null +++ b/src/Exception/NonPublicTranslationException.php @@ -0,0 +1,23 @@ +all(); - if(!array_key_exists(self::ARRAY_KEYS_INDEX, $parameters)) { + if (!array_key_exists(self::ARRAY_KEYS_INDEX, $parameters)) { return false; } - foreach($parameters[self::ARRAY_KEYS_INDEX] as $key) { - // Allow only public keys - if(!in_array($key, PublicTranslations::PUBLIC_KEYS, true)) { - return false; - } + $nonPublicTranslations = array_diff($parameters[self::ARRAY_KEYS_INDEX], PublicTranslations::PUBLIC_KEYS); + + if (!empty($nonPublicTranslations)) { + throw new NonPublicTranslationException(sprintf('You have requested non public keys: %s', implode(',', $nonPublicTranslations))); } return true; diff --git a/src/Security/Voter/PublicTokenVoter.php b/src/Security/Voter/PublicTokenVoter.php index 2de4b9c05..3e121668a 100644 --- a/src/Security/Voter/PublicTokenVoter.php +++ b/src/Security/Voter/PublicTokenVoter.php @@ -15,6 +15,8 @@ use Pimcore\Bundle\StudioApiBundle\Exception\NoRequestException; use Pimcore\Bundle\StudioApiBundle\Security\Trait\PublicTranslationTrait; +use Pimcore\Bundle\StudioApiBundle\Security\Trait\RequestTrait; +use Pimcore\Bundle\StudioApiBundle\Service\SecurityServiceInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; @@ -22,6 +24,7 @@ final class PublicTokenVoter extends Voter { + use RequestTrait; use PublicTranslationTrait; private const SUPPORTED_ATTRIBUTE = 'PUBLIC_API_PLATFORM'; @@ -30,6 +33,7 @@ final class PublicTokenVoter extends Voter public function __construct( private readonly RequestStack $requestStack, + private readonly SecurityServiceInterface $securityService ) { } @@ -41,20 +45,15 @@ protected function supports(string $attribute, mixed $subject): bool protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool { - $request = $this->getCurrentRequest(); + $request = $this->getCurrentRequest($this->requestStack); - // TODO Add security service once merged with PR#5 - return $this->voteOnRequest($request, $subject); - } + $authToken = $this->getAuthToken($request); - private function getCurrentRequest(): Request - { - $request = $this->requestStack->getCurrentRequest(); - if(!$request) { - throw new NoRequestException('No request found'); + if ($this->securityService->checkAuthToken($authToken)) { + return true; } - return $request; + return $this->voteOnRequest($request, $subject); } private function voteOnRequest(Request $request, string $subject): bool From cca665a0115c2a089b42d09002de6b4e6cb21458 Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 11:12:03 +0000 Subject: [PATCH 12/14] Apply php-cs-fixer changes --- src/Dto/Translation.php | 7 +++++-- src/Security/Trait/PublicTranslationTrait.php | 7 +++++-- src/Security/Voter/PublicTokenVoter.php | 8 +++++--- src/Service/TranslatorService.php | 7 +++++-- src/Service/TranslatorServiceInterface.php | 7 +++++-- src/State/TranslationProcessor.php | 7 +++++-- src/Util/Constants/PublicTranslations.php | 7 +++++-- tests/Unit/Dto/TranslationTest.php | 7 +++++-- tests/Unit/Service/Translator/TranslatorServiceTest.php | 7 +++++-- tests/Unit/State/TranslationProcessorTest.php | 7 +++++-- 10 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/Dto/Translation.php b/src/Dto/Translation.php index e88859590..a3afa84e5 100644 --- a/src/Dto/Translation.php +++ b/src/Dto/Translation.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Dto; diff --git a/src/Security/Trait/PublicTranslationTrait.php b/src/Security/Trait/PublicTranslationTrait.php index 2e930ead2..d7dd78791 100644 --- a/src/Security/Trait/PublicTranslationTrait.php +++ b/src/Security/Trait/PublicTranslationTrait.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Security\Trait; diff --git a/src/Security/Voter/PublicTokenVoter.php b/src/Security/Voter/PublicTokenVoter.php index 3e121668a..6c050e936 100644 --- a/src/Security/Voter/PublicTokenVoter.php +++ b/src/Security/Voter/PublicTokenVoter.php @@ -4,16 +4,18 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Security\Voter; -use Pimcore\Bundle\StudioApiBundle\Exception\NoRequestException; use Pimcore\Bundle\StudioApiBundle\Security\Trait\PublicTranslationTrait; use Pimcore\Bundle\StudioApiBundle\Security\Trait\RequestTrait; use Pimcore\Bundle\StudioApiBundle\Service\SecurityServiceInterface; diff --git a/src/Service/TranslatorService.php b/src/Service/TranslatorService.php index 2af962dbc..79133ba6b 100644 --- a/src/Service/TranslatorService.php +++ b/src/Service/TranslatorService.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Service; diff --git a/src/Service/TranslatorServiceInterface.php b/src/Service/TranslatorServiceInterface.php index 677d8d6b9..d8b7cab96 100644 --- a/src/Service/TranslatorServiceInterface.php +++ b/src/Service/TranslatorServiceInterface.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Service; diff --git a/src/State/TranslationProcessor.php b/src/State/TranslationProcessor.php index 58fb7fc88..e74b04c42 100644 --- a/src/State/TranslationProcessor.php +++ b/src/State/TranslationProcessor.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\State; diff --git a/src/Util/Constants/PublicTranslations.php b/src/Util/Constants/PublicTranslations.php index c9ea4305d..a4d2730d1 100644 --- a/src/Util/Constants/PublicTranslations.php +++ b/src/Util/Constants/PublicTranslations.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Util\Constants; diff --git a/tests/Unit/Dto/TranslationTest.php b/tests/Unit/Dto/TranslationTest.php index e0d1f3167..5258c50e2 100644 --- a/tests/Unit/Dto/TranslationTest.php +++ b/tests/Unit/Dto/TranslationTest.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Tests\Unit\Dto; diff --git a/tests/Unit/Service/Translator/TranslatorServiceTest.php b/tests/Unit/Service/Translator/TranslatorServiceTest.php index 51a6eda55..1342955ce 100644 --- a/tests/Unit/Service/Translator/TranslatorServiceTest.php +++ b/tests/Unit/Service/Translator/TranslatorServiceTest.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Tests\Unit\Service\Translator; diff --git a/tests/Unit/State/TranslationProcessorTest.php b/tests/Unit/State/TranslationProcessorTest.php index 04b682e6c..f72a7a48e 100644 --- a/tests/Unit/State/TranslationProcessorTest.php +++ b/tests/Unit/State/TranslationProcessorTest.php @@ -4,11 +4,14 @@ /** * Pimcore * - * This source file is available under following license: + * 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 PCL + * @license http://www.pimcore.org/license GPLv3 and PCL */ namespace Pimcore\Bundle\StudioApiBundle\Tests\Unit\State; From 6068794401ea0dc67a4869f5ffc27272f40a72c8 Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 12:13:14 +0100 Subject: [PATCH 13/14] Disable public voter for frontend testing --- config/api_platform/resources/translation.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/api_platform/resources/translation.yaml b/config/api_platform/resources/translation.yaml index 4f2d2d77c..bd7d920b4 100644 --- a/config/api_platform/resources/translation.yaml +++ b/config/api_platform/resources/translation.yaml @@ -1,6 +1,6 @@ resources: Pimcore\Bundle\StudioApiBundle\Dto\Translation: - security: 'is_granted("PUBLIC_API_PLATFORM", "translation")' + #security: 'is_granted("PUBLIC_API_PLATFORM", "translation")' operations: ApiPlatform\Metadata\Post: processor: Pimcore\Bundle\StudioApiBundle\State\TranslationProcessor From 6d4750046c7c7d384066021b7730e73dbbab8485 Mon Sep 17 00:00:00 2001 From: mattamon Date: Tue, 5 Mar 2024 12:18:45 +0100 Subject: [PATCH 14/14] Shorten lines --- src/Security/Trait/PublicTranslationTrait.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Security/Trait/PublicTranslationTrait.php b/src/Security/Trait/PublicTranslationTrait.php index d7dd78791..e35620455 100644 --- a/src/Security/Trait/PublicTranslationTrait.php +++ b/src/Security/Trait/PublicTranslationTrait.php @@ -31,10 +31,18 @@ private function voteOnTranslation(InputBag $payload): bool return false; } - $nonPublicTranslations = array_diff($parameters[self::ARRAY_KEYS_INDEX], PublicTranslations::PUBLIC_KEYS); + $nonPublicTranslations = array_diff( + $parameters[self::ARRAY_KEYS_INDEX], + PublicTranslations::PUBLIC_KEYS + ); if (!empty($nonPublicTranslations)) { - throw new NonPublicTranslationException(sprintf('You have requested non public keys: %s', implode(',', $nonPublicTranslations))); + throw new NonPublicTranslationException( + sprintf( + 'You have requested non public keys: %s', + implode(',', $nonPublicTranslations) + ) + ); } return true;