From 88380ccfbdf47400d038c3fd8c0cc7bde99f033e Mon Sep 17 00:00:00 2001 From: Dominik Zawadzki Date: Tue, 4 Jul 2023 09:18:53 +0200 Subject: [PATCH 1/4] CORS support --- src/DependencyInjection/services.php | 8 +++++ src/Hub/Middleware/CorsMiddleware.php | 32 +++++++++++++++++++ .../Hub/Middleware/CorsMiddlewareTest.php | 27 ++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 src/Hub/Middleware/CorsMiddleware.php create mode 100644 tests/Unit/Hub/Middleware/CorsMiddlewareTest.php diff --git a/src/DependencyInjection/services.php b/src/DependencyInjection/services.php index 3287727..41213d8 100644 --- a/src/DependencyInjection/services.php +++ b/src/DependencyInjection/services.php @@ -13,6 +13,7 @@ use Freddie\Hub\Hub; use Freddie\Hub\HubControllerInterface; use Freddie\Hub\HubInterface; +use Freddie\Hub\Middleware\CorsMiddleware; use Freddie\Hub\Middleware\HttpExceptionConverterMiddleware; use Freddie\Hub\Middleware\TokenExtractorMiddleware; use Freddie\Hub\Transport\TransportFactory; @@ -111,6 +112,7 @@ $services ->set(App::class) ->args([ + service(CorsMiddleware::class), service(HttpExceptionConverterMiddleware::class), service(TokenExtractorMiddleware::class), ]); @@ -124,6 +126,12 @@ $services ->set(Factory::class); + $services + ->set(CorsMiddleware::class) + ->args([ + param('env(string:default::CORS_ORIGINS)') + ]); + $services ->set(Configuration::class) ->factory(service(ConfigurationFactory::class)) diff --git a/src/Hub/Middleware/CorsMiddleware.php b/src/Hub/Middleware/CorsMiddleware.php new file mode 100644 index 0000000..0bc090e --- /dev/null +++ b/src/Hub/Middleware/CorsMiddleware.php @@ -0,0 +1,32 @@ +corsOrigin) { + $this->corsOrigin = 'null'; + } + } + + public function __invoke( + ServerRequestInterface $request, + callable $next + ): ResponseInterface + { + $response = $next($request); + + return $response->withAddedHeader('Access-Control-Allow-Origin', $this->corsOrigin) + ->withAddedHeader('Access-Control-Allow-Headers', '*') + ->withAddedHeader('Access-Control-Allow-Methods', '*') + ->withStatus(Response::STATUS_OK); + } +} \ No newline at end of file diff --git a/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php new file mode 100644 index 0000000..8946ae5 --- /dev/null +++ b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php @@ -0,0 +1,27 @@ + $expectedResponse); + $request = new \React\Http\Message\ServerRequest('POST', './well-known/mercure'); + + // When + $response = handle($app, $request); + + // Then + expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe($corsOrigins); + expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*'); + expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*'); +}); \ No newline at end of file From 88585d875544873117f4ed570e1c514e8fd02be6 Mon Sep 17 00:00:00 2001 From: Dominik Zawadzki Date: Mon, 14 Oct 2024 16:51:36 +0200 Subject: [PATCH 2/4] PR fixes --- src/Hub/Middleware/CorsMiddleware.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/Hub/Middleware/CorsMiddleware.php b/src/Hub/Middleware/CorsMiddleware.php index 0bc090e..05cf984 100644 --- a/src/Hub/Middleware/CorsMiddleware.php +++ b/src/Hub/Middleware/CorsMiddleware.php @@ -6,27 +6,30 @@ use Psr\Http\Message\ServerRequestInterface; use React\Http\Message\Response; -class CorsMiddleware +final class CorsMiddleware { public function __construct( - private ?string $corsOrigin - ) - { - if (!$this->corsOrigin) { - $this->corsOrigin = 'null'; - } + private readonly ?string $corsOrigin = '*', + ) { } public function __invoke( ServerRequestInterface $request, - callable $next - ): ResponseInterface - { + callable $next + ): ResponseInterface { $response = $next($request); - return $response->withAddedHeader('Access-Control-Allow-Origin', $this->corsOrigin) + $corsOrigin = $this->corsOrigin ?? $this->getOrigin($request); + + return $response->withAddedHeader('Access-Control-Allow-Origin', $corsOrigin) ->withAddedHeader('Access-Control-Allow-Headers', '*') ->withAddedHeader('Access-Control-Allow-Methods', '*') + ->withAddedHeader('Access-Control-Allow-Credentials', 'true') ->withStatus(Response::STATUS_OK); } -} \ No newline at end of file + + private function getOrigin(ServerRequestInterface $request): string + { + return $request->getHeaderLine('Origin') ?: 'null'; + } +} From ace8c6e672a53be7404d5579fec8991955027bb1 Mon Sep 17 00:00:00 2001 From: Dominik Zawadzki Date: Fri, 1 Nov 2024 16:40:55 +0100 Subject: [PATCH 3/4] fix code style and integration test --- .../Hub/Middleware/CorsMiddlewareTest.php | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php index 8946ae5..8568b54 100644 --- a/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php +++ b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php @@ -7,8 +7,10 @@ use FrameworkX\App; use Freddie\Hub\Middleware\CorsMiddleware; use React\Http\Message\Response; + use function expect; use function Freddie\Tests\handle; +use function it; it('decorate response with Cross-Origin Resource Sharing mechanism', function () { // Given @@ -24,4 +26,22 @@ expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe($corsOrigins); expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*'); expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*'); -}); \ No newline at end of file + expect($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); +}); + +it('decorate response with Cross-Origin Resource Sharing mechanism with default corsOrigin setup', function () { + // Given + $corsOrigins = null; + $expectedResponse = new Response(400); + $app = new App(new CorsMiddleware($corsOrigins), fn () => $expectedResponse); + $request = new \React\Http\Message\ServerRequest('POST', './well-known/mercure'); + + // When + $response = handle($app, $request); + + // Then + expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe('null'); + expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*'); + expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*'); + expect($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); +}); From b56697e2075444fa26e9ab80e4001f01035db0db Mon Sep 17 00:00:00 2001 From: Beno!t POLASZEK Date: Tue, 5 Nov 2024 15:34:12 +0100 Subject: [PATCH 4/4] Fix: cosmetics --- .../Hub/Middleware/CorsMiddlewareTest.php | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php index 8568b54..9e8c1d2 100644 --- a/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php +++ b/tests/Unit/Hub/Middleware/CorsMiddlewareTest.php @@ -7,41 +7,42 @@ use FrameworkX\App; use Freddie\Hub\Middleware\CorsMiddleware; use React\Http\Message\Response; +use React\Http\Message\ServerRequest; use function expect; use function Freddie\Tests\handle; use function it; -it('decorate response with Cross-Origin Resource Sharing mechanism', function () { +it('decorates response with CORS mechanism', function () { // Given $corsOrigins = 'http://testdomain.local'; $expectedResponse = new Response(200); $app = new App(new CorsMiddleware($corsOrigins), fn () => $expectedResponse); - $request = new \React\Http\Message\ServerRequest('POST', './well-known/mercure'); + $request = new ServerRequest('POST', './well-known/mercure'); // When $response = handle($app, $request); // Then - expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe($corsOrigins); - expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*'); - expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*'); - expect($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); + expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe($corsOrigins) + ->and($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*') + ->and($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*') + ->and($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); }); -it('decorate response with Cross-Origin Resource Sharing mechanism with default corsOrigin setup', function () { +it('decorates response with CORS mechanism with default corsOrigin setup', function () { // Given $corsOrigins = null; $expectedResponse = new Response(400); $app = new App(new CorsMiddleware($corsOrigins), fn () => $expectedResponse); - $request = new \React\Http\Message\ServerRequest('POST', './well-known/mercure'); + $request = new ServerRequest('POST', './well-known/mercure'); // When $response = handle($app, $request); // Then - expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe('null'); - expect($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*'); - expect($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*'); - expect($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); + expect($response->getHeaderLine('Access-Control-Allow-Origin'))->toBe('null') + ->and($response->getHeaderLine('Access-Control-Allow-Methods'))->toBe('*') + ->and($response->getHeaderLine('Access-Control-Allow-Headers'))->toBe('*') + ->and($response->getHeaderLine('Access-Control-Allow-Credentials'))->toBe('true'); });