From 70b60c972ed13fa1819ef3611521e9a2f6fbf459 Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Fri, 23 Jun 2023 19:09:14 +0200 Subject: [PATCH 1/4] fix(Preview): Check strict `_preview` param value (allowing `0`, `false` values to disable preview mode) --- .../src/Preview/EventSubscriber/PreviewModeSubscriber.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/RoadizCoreBundle/src/Preview/EventSubscriber/PreviewModeSubscriber.php b/lib/RoadizCoreBundle/src/Preview/EventSubscriber/PreviewModeSubscriber.php index cc7c3410..2850b83f 100644 --- a/lib/RoadizCoreBundle/src/Preview/EventSubscriber/PreviewModeSubscriber.php +++ b/lib/RoadizCoreBundle/src/Preview/EventSubscriber/PreviewModeSubscriber.php @@ -62,7 +62,11 @@ public function onKernelRequest(RequestEvent $event): void if ( $event->isMainRequest() && $request->query->has(static::QUERY_PARAM_NAME) && - (bool) ($request->query->get(static::QUERY_PARAM_NAME, 0)) === true + \in_array( + $request->query->get(static::QUERY_PARAM_NAME, 0), + ['true', true, '1', 1, 'on', 'yes', 'y'], + true + ) ) { $request->attributes->set('preview', true); } From 449c9f9fcdb2114e6c13804be696d558ac7efc49 Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Fri, 23 Jun 2023 19:12:03 +0200 Subject: [PATCH 2/4] feat(Preview): Added OpenAPI decorator to document `_preview` query param and JWT security --- lib/RoadizCoreBundle/config/services.yaml | 6 +++ .../src/Api/OpenApi/PreviewDecorator.php | 54 +++++++++++++++++++ .../src/Api/OpenApi/WebResponseDecorator.php | 22 +++++--- 3 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 lib/RoadizCoreBundle/src/Api/OpenApi/PreviewDecorator.php diff --git a/lib/RoadizCoreBundle/config/services.yaml b/lib/RoadizCoreBundle/config/services.yaml index 3d7308cf..8335e6aa 100644 --- a/lib/RoadizCoreBundle/config/services.yaml +++ b/lib/RoadizCoreBundle/config/services.yaml @@ -119,6 +119,12 @@ services: public: true + RZ\Roadiz\CoreBundle\Api\OpenApi\PreviewDecorator: + decorates: 'api_platform.openapi.factory' + decoration_priority: 100 + arguments: [ '@RZ\Roadiz\CoreBundle\Api\OpenApi\PreviewDecorator.inner' ] + autoconfigure: false + RZ\Roadiz\CoreBundle\Api\OpenApi\JwtDecorator: decorates: 'api_platform.openapi.factory' arguments: [ '@RZ\Roadiz\CoreBundle\Api\OpenApi\JwtDecorator.inner' ] diff --git a/lib/RoadizCoreBundle/src/Api/OpenApi/PreviewDecorator.php b/lib/RoadizCoreBundle/src/Api/OpenApi/PreviewDecorator.php new file mode 100644 index 00000000..f63c706e --- /dev/null +++ b/lib/RoadizCoreBundle/src/Api/OpenApi/PreviewDecorator.php @@ -0,0 +1,54 @@ +decorated = $decorated; + } + + public function __invoke(array $context = []): OpenApi + { + $openApi = ($this->decorated)($context); + /** @var PathItem[] $paths */ + $paths = $openApi->getPaths()->getPaths(); + // For each GET path, add a new query parameter `_preview` to force preview mode + foreach ($paths as $path => $pathItem) { + $operation = $pathItem->getGet(); + if (null !== $operation) { + $responses = $operation->getResponses(); + $responses['401'] = new Model\Response( + description: 'Invalid JWT Token' + ); + + $newOperation = $operation->withParameters([ + ...$operation->getParameters(), + (new Model\Parameter( + '_preview', + 'query', + 'Enables preview mode (requires a valid bearer JWT token)', + false + ))->withSchema(['type' => 'boolean'])->withExample('1') + ])->withSecurity([ + ...$operation->getSecurity() ?? [], + ['JWT' => []] + ])->withResponses($responses); + $openApi->getPaths()->addPath($path, $pathItem->withGet($newOperation)); + } + } + + return $openApi; + } +} diff --git a/lib/RoadizCoreBundle/src/Api/OpenApi/WebResponseDecorator.php b/lib/RoadizCoreBundle/src/Api/OpenApi/WebResponseDecorator.php index c44d1c2f..da54e849 100644 --- a/lib/RoadizCoreBundle/src/Api/OpenApi/WebResponseDecorator.php +++ b/lib/RoadizCoreBundle/src/Api/OpenApi/WebResponseDecorator.php @@ -21,17 +21,25 @@ public function __construct( public function __invoke(array $context = []): OpenApi { $openApi = ($this->decorated)($context); - $schemas = $openApi->getComponents()->getSchemas(); $pathItem = $openApi->getPaths()->getPath('/api/web_response_by_path'); $operation = $pathItem->getGet(); $openApi->getPaths()->addPath('/api/web_response_by_path', $pathItem->withGet( - $operation->withParameters([new Model\Parameter( - 'path', - 'query', - 'Resource path, or `/` for home page', - true, - )]) + $operation->withParameters([ + // override completely parameters + new Model\Parameter( + 'path', + 'query', + 'Resource path, or `/` for home page', + true, + ), + (new Model\Parameter( + '_preview', + 'query', + 'Enables preview mode (requires a valid bearer JWT token)', + false + ))->withSchema(['type' => 'boolean'])->withExample('1') + ]) )); return $openApi; From 9aac1d71ccc60f55aa6870ff29f1b7958e6b65ef Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Fri, 23 Jun 2023 19:17:33 +0200 Subject: [PATCH 3/4] chore: Ignore covariance phpstan errors on Doctrine collections --- lib/Documents/phpstan.neon | 2 ++ lib/RoadizCompatBundle/phpstan.neon | 2 ++ lib/RoadizCoreBundle/phpstan.neon | 2 ++ lib/RoadizFontBundle/phpstan.neon | 2 ++ lib/RoadizRozierBundle/phpstan.neon | 2 ++ lib/RoadizUserBundle/phpstan.neon | 2 ++ lib/Rozier/phpstan.neon | 2 ++ phpstan.neon | 2 ++ 8 files changed, 16 insertions(+) diff --git a/lib/Documents/phpstan.neon b/lib/Documents/phpstan.neon index aba15b5f..273466e4 100644 --- a/lib/Documents/phpstan.neon +++ b/lib/Documents/phpstan.neon @@ -15,6 +15,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkMissingIterableValueType: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/RoadizCompatBundle/phpstan.neon b/lib/RoadizCompatBundle/phpstan.neon index d1f675bb..f4795d77 100644 --- a/lib/RoadizCompatBundle/phpstan.neon +++ b/lib/RoadizCompatBundle/phpstan.neon @@ -24,6 +24,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/RoadizCoreBundle/phpstan.neon b/lib/RoadizCoreBundle/phpstan.neon index bedd1ca4..8785b98d 100644 --- a/lib/RoadizCoreBundle/phpstan.neon +++ b/lib/RoadizCoreBundle/phpstan.neon @@ -24,6 +24,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/RoadizFontBundle/phpstan.neon b/lib/RoadizFontBundle/phpstan.neon index 80dda276..b42b4032 100644 --- a/lib/RoadizFontBundle/phpstan.neon +++ b/lib/RoadizFontBundle/phpstan.neon @@ -24,6 +24,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/RoadizRozierBundle/phpstan.neon b/lib/RoadizRozierBundle/phpstan.neon index dfc8587a..782b3a3e 100644 --- a/lib/RoadizRozierBundle/phpstan.neon +++ b/lib/RoadizRozierBundle/phpstan.neon @@ -26,6 +26,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/RoadizUserBundle/phpstan.neon b/lib/RoadizUserBundle/phpstan.neon index 675f70d0..4f702f48 100644 --- a/lib/RoadizUserBundle/phpstan.neon +++ b/lib/RoadizUserBundle/phpstan.neon @@ -24,6 +24,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/lib/Rozier/phpstan.neon b/lib/Rozier/phpstan.neon index eed7061a..353be6b9 100644 --- a/lib/Rozier/phpstan.neon +++ b/lib/Rozier/phpstan.neon @@ -26,6 +26,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false diff --git a/phpstan.neon b/phpstan.neon index 8391eb7b..273669c3 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -45,6 +45,8 @@ parameters: - '#Property ([a-zA-Z\\\:\$]+) type mapping mismatch: property can contain ([a-zA-Z\\\&\>\<]+)Interface\>? but database expects ([a-zA-Z\\\&\>\<]+)#' - '#type mapping mismatch: database can contain array\|bool\|float\|int\|JsonSerializable\|stdClass\|string\|null but property expects array\|null#' - '#Doctrine\\ORM\\Mapping\\GeneratedValue constructor expects#' + - '#type mapping mismatch: property can contain Doctrine\\Common\\Collections\\Collection]+> but database expects Doctrine\\Common\\Collections\\Collection&iterable<[^\>]+>#' + - '#should return Doctrine\\Common\\Collections\\Collection]+Interface> but returns Doctrine\\Common\\Collections\\Collection]+>#' reportUnmatchedIgnoredErrors: false checkGenericClassInNonGenericObjectType: false From 9f462fee5ce6eb7a594082aa8d42a706f616faed Mon Sep 17 00:00:00 2001 From: Ambroise Maupate Date: Fri, 23 Jun 2023 19:18:59 +0200 Subject: [PATCH 4/4] chore: Bumped --- CHANGELOG.md | 12 ++++++++++++ lib/RoadizCoreBundle/config/services.yaml | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51131c93..fa64d234 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## [v2.1.20](https://github.com/roadiz/core-bundle-dev-app/compare/v2.1.19...v2.1.20) (2023-06-23) + + +### Features + +* **Preview:** Added OpenAPI decorator to document `_preview` query param and JWT security ([449c9f9](https://github.com/roadiz/core-bundle-dev-app/commit/449c9f9fcdb2114e6c13804be696d558ac7efc49)) + + +### Bug Fixes + +* **Preview:** Check strict `_preview` param value (allowing `0`, `false` values to disable preview mode) ([70b60c9](https://github.com/roadiz/core-bundle-dev-app/commit/70b60c972ed13fa1819ef3611521e9a2f6fbf459)) + ## [v2.1.19](https://github.com/roadiz/core-bundle-dev-app/compare/v2.1.18...v2.1.19) (2023-06-23) diff --git a/lib/RoadizCoreBundle/config/services.yaml b/lib/RoadizCoreBundle/config/services.yaml index 8335e6aa..ee01d1c9 100644 --- a/lib/RoadizCoreBundle/config/services.yaml +++ b/lib/RoadizCoreBundle/config/services.yaml @@ -1,6 +1,6 @@ --- parameters: - roadiz_core.cms_version: '2.1.19' + roadiz_core.cms_version: '2.1.20' roadiz_core.cms_version_prefix: 'main' env(APP_NAMESPACE): "roadiz" env(APP_VERSION): "0.1.0"