diff --git a/composer.lock b/composer.lock index 132fba5188265a4c9295c7ca2bb8f50f5d09ecf4..eea55a3a39df37142e761d6a9fb8c16ac5f4a490 100644 --- a/composer.lock +++ b/composer.lock @@ -7,6 +7,49 @@ "hash": "7d101b08e5ae002d827cd42ae9a4e344", "content-hash": "60f7057617c6d995bf9946d0b12f0b5d", "packages": [ + { + "name": "asm89/stack-cors", + "version": "0.2.1", + "source": { + "type": "git", + "url": "https://github.com/asm89/stack-cors.git", + "reference": "2d77e77251a434e4527315313a672f5801b29fa2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/asm89/stack-cors/zipball/2d77e77251a434e4527315313a672f5801b29fa2", + "reference": "2d77e77251a434e4527315313a672f5801b29fa2", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/http-foundation": "~2.1", + "symfony/http-kernel": "~2.1" + }, + "type": "library", + "autoload": { + "psr-0": { + "Asm89\\Stack": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alexander", + "email": "iam.asm89@gmail.com" + } + ], + "description": "Cross-origin resource sharing library and stack middleware", + "homepage": "https://github.com/asm89/stack-cors", + "keywords": [ + "cors", + "stack" + ], + "time": "2014-07-28 07:22:35" + }, { "name": "composer/installers", "version": "v1.0.21", @@ -1061,7 +1104,7 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/1.0.0", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", "reference": "1.0.0", "shasum": "" }, @@ -2716,24 +2759,24 @@ "packages-dev": [ { "name": "behat/mink", - "version": "v1.7.1", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/minkphp/Mink.git", - "reference": "e6930b9c74693dff7f4e58577e1b1743399f3ff9" + "reference": "6c129030ec2cc029905cf969a56ca8f087b2dfdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/Mink/zipball/e6930b9c74693dff7f4e58577e1b1743399f3ff9", - "reference": "e6930b9c74693dff7f4e58577e1b1743399f3ff9", + "url": "https://api.github.com/repos/minkphp/Mink/zipball/6c129030ec2cc029905cf969a56ca8f087b2dfdf", + "reference": "6c129030ec2cc029905cf969a56ca8f087b2dfdf", "shasum": "" }, "require": { "php": ">=5.3.1", - "symfony/css-selector": "~2.1|~3.0" + "symfony/css-selector": "~2.1" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0" + "symfony/phpunit-bridge": "~2.7" }, "suggest": { "behat/mink-browserkit-driver": "extremely fast headless driver for Symfony\\Kernel-based apps (Sf2, Silex)", @@ -2770,31 +2813,31 @@ "testing", "web" ], - "time": "2016-03-05 08:26:18" + "time": "2015-09-20 20:24:03" }, { "name": "behat/mink-browserkit-driver", - "version": "v1.3.2", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/minkphp/MinkBrowserKitDriver.git", - "reference": "10e67fb4a295efcd62ea0bf16025a85ea19534fb" + "reference": "da47df1593dac132f04d24e7277ef40d33d9f201" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/10e67fb4a295efcd62ea0bf16025a85ea19534fb", - "reference": "10e67fb4a295efcd62ea0bf16025a85ea19534fb", + "url": "https://api.github.com/repos/minkphp/MinkBrowserKitDriver/zipball/da47df1593dac132f04d24e7277ef40d33d9f201", + "reference": "da47df1593dac132f04d24e7277ef40d33d9f201", "shasum": "" }, "require": { - "behat/mink": "^1.7.1@dev", + "behat/mink": "~1.7@dev", "php": ">=5.3.6", - "symfony/browser-kit": "~2.3|~3.0", - "symfony/dom-crawler": "~2.3|~3.0" + "symfony/browser-kit": "~2.3", + "symfony/dom-crawler": "~2.3" }, "require-dev": { "silex/silex": "~1.2", - "symfony/phpunit-bridge": "~2.7|~3.0" + "symfony/phpunit-bridge": "~2.7" }, "type": "mink-driver", "extra": { @@ -2826,20 +2869,20 @@ "browser", "testing" ], - "time": "2016-03-05 08:59:47" + "time": "2015-09-21 20:56:13" }, { "name": "behat/mink-goutte-driver", - "version": "v1.2.1", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/minkphp/MinkGoutteDriver.git", - "reference": "8b9ad6d2d95bc70b840d15323365f52fcdaea6ca" + "reference": "c8e254f127d6f2242b994afd4339fb62d471df3f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/8b9ad6d2d95bc70b840d15323365f52fcdaea6ca", - "reference": "8b9ad6d2d95bc70b840d15323365f52fcdaea6ca", + "url": "https://api.github.com/repos/minkphp/MinkGoutteDriver/zipball/c8e254f127d6f2242b994afd4339fb62d471df3f", + "reference": "c8e254f127d6f2242b994afd4339fb62d471df3f", "shasum": "" }, "require": { @@ -2849,7 +2892,7 @@ "php": ">=5.3.1" }, "require-dev": { - "symfony/phpunit-bridge": "~2.7|~3.0" + "symfony/phpunit-bridge": "~2.7" }, "type": "mink-driver", "extra": { @@ -2881,7 +2924,7 @@ "headless", "testing" ], - "time": "2016-03-05 09:04:22" + "time": "2015-09-21 21:31:11" }, { "name": "doctrine/instantiator", @@ -2939,24 +2982,24 @@ }, { "name": "fabpot/goutte", - "version": "v3.1.2", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/Goutte.git", - "reference": "3cbc6ed222422a28400e470050f14928a153207e" + "reference": "751a3dc5c4d86ec3e97c9f27133ef9694d9243cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/3cbc6ed222422a28400e470050f14928a153207e", - "reference": "3cbc6ed222422a28400e470050f14928a153207e", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/751a3dc5c4d86ec3e97c9f27133ef9694d9243cc", + "reference": "751a3dc5c4d86ec3e97c9f27133ef9694d9243cc", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.0", "php": ">=5.5.0", - "symfony/browser-kit": "~2.1|~3.0", - "symfony/css-selector": "~2.1|~3.0", - "symfony/dom-crawler": "~2.1|~3.0" + "symfony/browser-kit": "~2.1", + "symfony/css-selector": "~2.1", + "symfony/dom-crawler": "~2.1" }, "type": "application", "extra": { @@ -2984,7 +3027,7 @@ "keywords": [ "scraper" ], - "time": "2015-11-05 12:58:44" + "time": "2015-08-29 16:16:56" }, { "name": "jcalderonzumba/gastonjs", diff --git a/core/composer.json b/core/composer.json index 310964982c64d8694cdb053fb10096370e48ada7..7ae7f4f7380c5b5b8bac95977a7b30d015a8f736 100644 --- a/core/composer.json +++ b/core/composer.json @@ -31,7 +31,8 @@ "symfony/psr-http-message-bridge": "v0.2", "zendframework/zend-diactoros": "~1.1", "composer/semver": "~1.0", - "paragonie/random_compat": "~1.0" + "paragonie/random_compat": "~1.0", + "asm89/stack-cors": "~0.2.1" }, "require-dev": { "behat/mink": "~1.7", diff --git a/core/core.services.yml b/core/core.services.yml index 276e19d447050758e6952ef675104b834a7ac4e5..0e7eb586e931c3a28364a5b31329943a944455e6 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -33,6 +33,14 @@ parameters: - sftp - webcal - rtsp + cors.config: + enabled: false + allowedHeaders: [] + allowedMethods: [] + allowedOrigins: ['*'] + exposedHeaders: false + maxAge: false + supportsCredentials: false services: # Simple cache contexts, directly derived from the request context. cache_context.ip: @@ -703,6 +711,11 @@ services: - { name: http_middleware, priority: 50 } calls: - [setContainer, ['@service_container']] + http_middleware.cors: + class: Asm89\Stack\Cors + arguments: ['%cors.config%'] + tags: + - { name: http_middleware } psr7.http_foundation_factory: class: Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory psr7.http_message_factory: diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 013dec14d9b2bae3577bdd64804e1a5f99baa183..8a22bc50d4b6c8b70d6ee9ede8e98c45bbc32999 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -6,6 +6,7 @@ use Drupal\Core\Cache\ListCacheBinsPass; use Drupal\Core\DependencyInjection\Compiler\AuthenticationProviderPass; use Drupal\Core\DependencyInjection\Compiler\BackendCompilerPass; +use Drupal\Core\DependencyInjection\Compiler\CorsCompilerPass; use Drupal\Core\DependencyInjection\Compiler\GuzzleMiddlewarePass; use Drupal\Core\DependencyInjection\Compiler\ContextProvidersPass; use Drupal\Core\DependencyInjection\Compiler\ProxyServicesPass; @@ -64,6 +65,8 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new BackendCompilerPass()); + $container->addCompilerPass(new CorsCompilerPass()); + $container->addCompilerPass(new StackedKernelPass()); $container->addCompilerPass(new StackedSessionHandlerPass()); diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/CorsCompilerPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/CorsCompilerPass.php new file mode 100644 index 0000000000000000000000000000000000000000..207e094094f4870b401bf377972e95f75e6d53ee --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/CorsCompilerPass.php @@ -0,0 +1,31 @@ +getParameter('cors.config')) { + $enabled = !empty($cors_config['enabled']); + } + + // Remove the CORS middleware completly in case it was not enabled. + if (!$enabled) { + $container->removeDefinition('http_middleware.cors'); + } + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/HttpKernel/CorsIntegrationTest.php b/core/tests/Drupal/KernelTests/Core/HttpKernel/CorsIntegrationTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e73efa3b74f01706c249feea9b0239b8f9872ed2 --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/HttpKernel/CorsIntegrationTest.php @@ -0,0 +1,84 @@ +installSchema('system', 'router'); + \Drupal::service('router.builder')->rebuild(); + } + + public function testCrossSiteRequest() { + + // Test default parameters. + $cors_config = $this->container->getParameter('cors.config'); + $this->assertSame(FALSE, $cors_config['enabled']); + $this->assertSame([], $cors_config['allowedHeaders']); + $this->assertSame([], $cors_config['allowedMethods']); + $this->assertSame(['*'], $cors_config['allowedOrigins']); + + $this->assertSame(FALSE, $cors_config['exposedHeaders']); + $this->assertSame(FALSE, $cors_config['maxAge']); + $this->assertSame(FALSE, $cors_config['supportsCredentials']); + + // Configure the CORS stack to allow a specific set of origins, but don't + // specify an origin header. + $request = Request::create('/test-page'); + $request->headers->set('Origin', ''); + $cors_config['enabled'] = TRUE; + $cors_config['allowedOrigins'] = ['http://example.com']; + + $this->corsConfig = $cors_config; + $this->container->get('kernel')->rebuildContainer(); + + /** @var \Symfony\Component\HttpFoundation\Response $response */ + $response = $this->container->get('http_kernel')->handle($request); + $this->assertEquals(Response::HTTP_FORBIDDEN, $response->getStatusCode()); + $this->assertEquals('Not allowed.', $response->getContent()); + + // Specify a valid origin. + $request->headers->set('Origin', 'http://example.com'); + $response = $this->container->get('http_kernel')->handle($request); + $this->assertEquals(Response::HTTP_OK, $response->getStatusCode()); + } + + /** + * {@inheritdoc} + */ + public function alter(ContainerBuilder $container) { + if (isset($this->corsConfig)) { + $container->setParameter('cors.config', $this->corsConfig); + } + } + +} diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml index 23f6483cc72f3f073f034f1bdcfab66cefea801b..e1bbbc7e21f0b4c31e263c4a2e884871567de73d 100644 --- a/sites/default/default.services.yml +++ b/sites/default/default.services.yml @@ -153,3 +153,22 @@ parameters: - sftp - webcal - rtsp + + # Configure Cross-Site HTTP requests (CORS). + # Read https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS + # for more information about the topic in general. + # Note: By default the configuration is disabled. + cors.config: + enabled: false + # Specify allowed headers, like 'x-allowed-header'. + allowedHeaders: [] + # Specify allowed request methods, specify ['*'] to allow all possible ones. + allowedMethods: [] + # Configure requests allowed from specific origins. + allowedOrigins: ['*'] + # Sets the Access-Control-Expose-Headers header. + exposedHeaders: false + # Sets the Access-Control-Max-Age header. + maxAge: false + # Sets the Access-Control-Allow-Credentials header. + supportsCredentials: false