├── NOTICE ├── src ├── Providers │ ├── LumenServiceProvider.php │ └── LaravelServiceProvider.php ├── Settings │ └── Settings.php ├── Adapters │ └── IlluminateRequestToPsr7.php └── CorsMiddleware.php ├── composer.json ├── config └── cors-illuminate.php ├── README.md └── LICENSE /NOTICE: -------------------------------------------------------------------------------- 1 | CORS (Cross-Origin Resource Sharing) support for Laravel and Lumen 2 | 3 | Copyright 2015-2020 info@neomerx.com 4 | 5 | This product includes software developed at Neomerx (www.neomerx.com). 6 | -------------------------------------------------------------------------------- /src/Providers/LumenServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->configure(self::CONFIG_FILE_NAME_WO_EXT); 39 | 40 | parent::configureCorsAnalyzer(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neomerx/cors-illuminate", 3 | "description": "CORS (Cross-Origin Resource Sharing) support for Laravel and Lumen", 4 | "keywords": [ 5 | "Cross-Origin Resource Sharing", 6 | "Cross Origin Resource Sharing", 7 | "CORS", 8 | "Laravel", 9 | "Lumen", 10 | "neomerx" 11 | ], 12 | "homepage": "https://github.com/neomerx/cors-illuminate", 13 | "support": { 14 | "issues": "https://github.com/neomerx/cors-illuminate/issues" 15 | }, 16 | "license": "Apache-2.0", 17 | "authors": [ 18 | { 19 | "name": "neomerx", 20 | "email": "info@neomerx.com" 21 | } 22 | ], 23 | "require": { 24 | "php": ">=7.2.0", 25 | "psr/http-message": "^1.0", 26 | "psr/log": "^1.0", 27 | "illuminate/http": "^6.0", 28 | "neomerx/cors-psr7": "^2.0" 29 | }, 30 | "require-dev": { 31 | "phpunit/phpunit": "^8.0", 32 | "mockery/mockery": "^1.3", 33 | "scrutinizer/ocular": "^1.7", 34 | "phpmd/phpmd": "^2.8", 35 | "squizlabs/php_codesniffer": "^3.0" 36 | }, 37 | "minimum-stability": "stable", 38 | "autoload": { 39 | "psr-4": { 40 | "Neomerx\\CorsIlluminate\\": "src/" 41 | } 42 | }, 43 | "autoload-dev": { 44 | "psr-4": { 45 | "Neomerx\\Tests\\CorsIlluminate\\": "tests/" 46 | } 47 | }, 48 | "scripts": { 49 | "test": ["@test-unit", "@test-cs", "@test-md"], 50 | "test-unit": "phpdbg -qrr ./vendor/bin/phpunit --coverage-text", 51 | "test-cs": "./vendor/bin/phpcs -p -s --standard=PSR2 ./src ./tests", 52 | "test-md": "./vendor/bin/phpmd ./src text codesize,controversial,cleancode,design,unusedcode,naming", 53 | "hhvm": "hhvm ./vendor/phpunit/phpunit/phpunit" 54 | }, 55 | "extra": { 56 | "laravel": { 57 | "providers": [ 58 | "Neomerx\\CorsIlluminate\\Providers\\LaravelServiceProvider" 59 | ] 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /config/cors-illuminate.php: -------------------------------------------------------------------------------- 1 | false, 11 | 12 | /** 13 | * Could be string or array. If specified as array (recommended for better performance) it should 14 | * be in parse_url() result format. 15 | */ 16 | S::KEY_SERVER_ORIGIN => [ 17 | 'scheme' => 'http', 18 | 'host' => 'localhost', 19 | 'port' => 8080, 20 | ], 21 | 22 | /** 23 | * A list of allowed request origins (no trail slashes). 24 | * If value is not on the list it is considered as not allowed. 25 | * If you want to allow all origins remove/comment this section. 26 | */ 27 | S::KEY_ALLOWED_ORIGINS => [ 28 | 'http://localhost', 29 | ], 30 | 31 | /** 32 | * A list of allowed request methods. * If value is not on the list it is considered as not allowed. 33 | * 34 | * Security Note: you have to remember CORS is not access control system and you should not expect all cross-origin 35 | * requests will have pre-flights. For so-called 'simple' methods with so-called 'simple' headers request 36 | * will be made without pre-flight. Thus you can not restrict such requests with CORS and should use other means. 37 | * For example method 'GET' without any headers or with only 'simple' headers will not have pre-flight request so 38 | * disabling it will not restrict access to resource(s). 39 | * 40 | * You can read more on 'simple' methods at http://www.w3.org/TR/cors/#simple-method 41 | */ 42 | S::KEY_ALLOWED_METHODS => [ 43 | 'GET', 44 | 'PATCH', 45 | 'POST', 46 | 'PUT', 47 | 'DELETE', 48 | ], 49 | 50 | /** 51 | * A list of allowed request headers. If value is not on the list it is considered as not allowed. 52 | * 53 | * Security Note: you have to remember CORS is not access control system and you should not expect all cross-origin 54 | * requests will have pre-flights. For so-called 'simple' methods with so-called 'simple' headers request 55 | * will be made without pre-flight. Thus you can not restrict such requests with CORS and should use other means. 56 | * For example method 'GET' without any headers or with only 'simple' headers will not have pre-flight request so 57 | * disabling it will not restrict access to resource(s). 58 | * 59 | * You can read more on 'simple' headers at http://www.w3.org/TR/cors/#simple-header 60 | */ 61 | S::KEY_ALLOWED_HEADERS => [ 62 | 'Content-Type', 63 | ], 64 | 65 | /** 66 | * A list of headers (case insensitive) which will be made accessible to user agent (browser) in response. 67 | */ 68 | S::KEY_EXPOSED_HEADERS => [ 69 | 'Content-Type', 70 | ], 71 | 72 | /** 73 | * If access with credentials is supported by the resource. 74 | */ 75 | S::KEY_IS_USING_CREDENTIALS => false, 76 | 77 | /** 78 | * Pre-flight response cache max period in seconds. 79 | */ 80 | S::KEY_FLIGHT_CACHE_MAX_AGE => 0, 81 | 82 | /** 83 | * If allowed methods should be added to pre-flight response when 'simple' method is requested. 84 | */ 85 | S::KEY_IS_FORCE_ADD_METHODS => false, 86 | 87 | /** 88 | * If allowed headers should be added when request headers are 'simple' and 89 | * non of them is 'Content-Type'. 90 | */ 91 | S::KEY_IS_FORCE_ADD_HEADERS => false, 92 | 93 | /** 94 | * If request 'Host' header should be checked against server's origin. 95 | */ 96 | S::KEY_IS_CHECK_HOST => false, 97 | 98 | ]; 99 | -------------------------------------------------------------------------------- /src/Settings/Settings.php: -------------------------------------------------------------------------------- 1 | isLogEnabled; 79 | } 80 | 81 | /** 82 | * @return self 83 | */ 84 | public function enableLog(): self 85 | { 86 | $this->isLogEnabled = true; 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * @return self 93 | */ 94 | public function disableLog(): self 95 | { 96 | $this->isLogEnabled = false; 97 | 98 | return $this; 99 | } 100 | 101 | /** 102 | * @inheritDoc 103 | */ 104 | public function getData(): array 105 | { 106 | $data = parent::getData(); 107 | 108 | // check we won't override any parent properties 109 | \assert(\array_key_exists(static::CORS_ILLUMINATE_SETTINGS_CACHE_KEY_IS_LOG_ENABLED, $data) === false); 110 | 111 | $data[static::CORS_ILLUMINATE_SETTINGS_CACHE_KEY_IS_LOG_ENABLED] = $this->isLogEnabled(); 112 | 113 | return $data; 114 | } 115 | 116 | /** 117 | * @inheritDoc 118 | */ 119 | public function setData(array $data): CorsSettings 120 | { 121 | $isLogEnabled = \boolval( 122 | $data[static::CORS_ILLUMINATE_SETTINGS_CACHE_KEY_IS_LOG_ENABLED] ?? static::DEFAULT_IS_LOG_ENABLED 123 | ); 124 | $isLogEnabled === true ? $this->enableLog() : $this->disableLog(); 125 | 126 | return parent::setData($data); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/Adapters/IlluminateRequestToPsr7.php: -------------------------------------------------------------------------------- 1 | request = $request; 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function getMethod() 57 | { 58 | return $this->request->getMethod(); 59 | } 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | public function hasHeader($name) 65 | { 66 | return $this->request->headers->has($name); 67 | } 68 | 69 | /** 70 | * @inheritdoc 71 | */ 72 | public function getHeader($name) 73 | { 74 | return $this->request->headers->get($name, null, false); 75 | } 76 | 77 | /** 78 | * @inheritdoc 79 | */ 80 | public function getProtocolVersion() 81 | { 82 | throw new LogicException('Method is not implemented'); 83 | } 84 | 85 | /** 86 | * @inheritdoc 87 | */ 88 | public function withProtocolVersion($version) 89 | { 90 | throw new LogicException('Method is not implemented'); 91 | } 92 | 93 | /** 94 | * @inheritdoc 95 | */ 96 | public function getHeaders() 97 | { 98 | throw new LogicException('Method is not implemented'); 99 | } 100 | 101 | /** 102 | * @inheritdoc 103 | */ 104 | public function getHeaderLine($name) 105 | { 106 | throw new LogicException('Method is not implemented'); 107 | } 108 | 109 | /** 110 | * @inheritdoc 111 | */ 112 | public function withHeader($name, $value) 113 | { 114 | throw new LogicException('Method is not implemented'); 115 | } 116 | 117 | /** 118 | * @inheritdoc 119 | */ 120 | public function withAddedHeader($name, $value) 121 | { 122 | throw new LogicException('Method is not implemented'); 123 | } 124 | 125 | /** 126 | * @inheritdoc 127 | */ 128 | public function withoutHeader($name) 129 | { 130 | throw new LogicException('Method is not implemented'); 131 | } 132 | 133 | /** 134 | * @inheritdoc 135 | */ 136 | public function getBody() 137 | { 138 | throw new LogicException('Method is not implemented'); 139 | } 140 | 141 | /** 142 | * @inheritdoc 143 | */ 144 | public function withBody(StreamInterface $body) 145 | { 146 | throw new LogicException('Method is not implemented'); 147 | } 148 | 149 | /** 150 | * @inheritdoc 151 | */ 152 | public function getRequestTarget() 153 | { 154 | throw new LogicException('Method is not implemented'); 155 | } 156 | 157 | /** 158 | * @inheritdoc 159 | */ 160 | public function withRequestTarget($requestTarget) 161 | { 162 | throw new LogicException('Method is not implemented'); 163 | } 164 | 165 | /** 166 | * @inheritdoc 167 | */ 168 | public function withMethod($method) 169 | { 170 | throw new LogicException('Method is not implemented'); 171 | } 172 | 173 | /** 174 | * @inheritdoc 175 | */ 176 | public function getUri() 177 | { 178 | throw new LogicException('Method is not implemented'); 179 | } 180 | 181 | /** 182 | * @inheritdoc 183 | * 184 | * @SuppressWarnings(PHPMD.BooleanArgumentFlag) 185 | */ 186 | public function withUri(UriInterface $uri, $preserveHost = false) 187 | { 188 | throw new LogicException('Method is not implemented'); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/CorsMiddleware.php: -------------------------------------------------------------------------------- 1 | analyzer = $analyzer; 53 | $this->container = $container; 54 | } 55 | 56 | /** 57 | * Handle an incoming request. 58 | * 59 | * @param Request $request 60 | * @param Closure $next 61 | * 62 | * @return mixed 63 | */ 64 | public function handle(Request $request, Closure $next) 65 | { 66 | $cors = $this->getCorsAnalysis($request); 67 | 68 | switch ($cors->getRequestType()) { 69 | case AnalysisResultInterface::TYPE_REQUEST_OUT_OF_CORS_SCOPE: 70 | $response = $next($request); 71 | break; 72 | 73 | case AnalysisResultInterface::TYPE_PRE_FLIGHT_REQUEST: 74 | $headers = $this->getPrepareCorsHeaders($cors->getResponseHeaders()); 75 | $response = new Response(null, Response::HTTP_OK, $headers); 76 | break; 77 | 78 | case AnalysisResultInterface::TYPE_ACTUAL_REQUEST: 79 | /** @var Response $response */ 80 | $response = $next($request); 81 | // merge CORS headers to response 82 | foreach ($this->getPrepareCorsHeaders($cors->getResponseHeaders()) as $name => $value) { 83 | $response->headers->set($name, $value, false); 84 | } 85 | break; 86 | 87 | default: 88 | $response = $this->getResponseOnError($cors); 89 | break; 90 | } 91 | 92 | return $response; 93 | } 94 | 95 | /** 96 | * You can override this method in order to customize error reply. 97 | * 98 | * @param AnalysisResultInterface $analysisResult 99 | * 100 | * @return Response 101 | */ 102 | protected function getResponseOnError(AnalysisResultInterface $analysisResult): Response 103 | { 104 | // avoid unused warning 105 | $analysisResult ?: null; 106 | 107 | return new Response(null, Response::HTTP_BAD_REQUEST); 108 | } 109 | 110 | /** 111 | * This method saves analysis result in Illuminate Container for 112 | * using it in other parts of the application (e.g. in exception handler). 113 | * 114 | * @param Request $request 115 | * 116 | * @return AnalysisResultInterface 117 | */ 118 | protected function getCorsAnalysis(Request $request): AnalysisResultInterface 119 | { 120 | $analysis = $this->analyzer->analyze($this->getRequestAdapter($request)); 121 | $this->container->instance(AnalysisResultInterface::class, $analysis); 122 | 123 | return $analysis; 124 | } 125 | 126 | /** 127 | * You can override this method to replace IlluminateRequestToPsr7 adapter with another one. 128 | * 129 | * @param Request $request 130 | * 131 | * @return RequestInterface 132 | */ 133 | protected function getRequestAdapter(Request $request): RequestInterface 134 | { 135 | return new IlluminateRequestToPsr7($request); 136 | } 137 | 138 | /** 139 | * There is an issue with IE which cannot work with multiple 'Access-Control-Expose-Headers' and 140 | * requires it them to be comma separated. Chrome and Firefox seem to be not affected. 141 | * 142 | * @param array $headers 143 | * 144 | * @return array 145 | * 146 | * @see https://github.com/neomerx/cors-psr7/issues/31 147 | */ 148 | protected function getPrepareCorsHeaders(array $headers): array 149 | { 150 | if (array_key_exists(CorsResponseHeaders::EXPOSE_HEADERS, $headers) === true) { 151 | $headers[CorsResponseHeaders::EXPOSE_HEADERS] = 152 | implode(', ', $headers[CorsResponseHeaders::EXPOSE_HEADERS]); 153 | } 154 | 155 | return $headers; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Project Management](https://img.shields.io/badge/project-management-blue.svg)](https://waffle.io/neomerx/cors-illuminate) 2 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/neomerx/cors-illuminate/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/neomerx/cors-illuminate/?branch=master) 3 | [![Code Coverage](https://scrutinizer-ci.com/g/neomerx/cors-illuminate/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/neomerx/cors-illuminate/?branch=master) 4 | [![Build Status](https://travis-ci.org/neomerx/cors-illuminate.svg?branch=master)](https://travis-ci.org/neomerx/cors-illuminate) 5 | [![License](https://img.shields.io/packagist/l/neomerx/cors-illuminate.svg)](https://packagist.org/packages/neomerx/cors-illuminate) 6 | 7 | ## Description 8 | 9 | This package adds [Cross-Origin Resource Sharing](http://www.w3.org/TR/cors/) (CORS) support to your Laravel application. 10 | 11 | The package is based on [Framework agnostic (PSR-7) CORS implementation](https://github.com/neomerx/cors-psr7). 12 | 13 | The **current version V3** is designed for Laravel 6 or higher. If you use lower Laravel version please use **V2**. 14 | 15 | ## Install 16 | 17 | ### 1 Composer 18 | 19 | ``` 20 | composer require neomerx/cors-illuminate 21 | ``` 22 | 23 | ### 2.1 Laravel 24 | 25 | > For Lumen skip this step and see step 2.2 26 | 27 | Create a config file by executing 28 | 29 | ``` 30 | php artisan vendor:publish --provider="Neomerx\CorsIlluminate\Providers\LaravelServiceProvider" 31 | ``` 32 | 33 | it will create `config/cors-illuminate.php` file in you application. 34 | 35 | Add CORS middleware to your HTTP stack at `app/Http/Kernel.php` file. The middleware should be added to `$middleware` list which is executed for all routes (even non declared in your routes file). Preferably before 'heavy' middleware for performance reasons. 36 | 37 | ```php 38 | class Kernel extends HttpKernel 39 | { 40 | ... 41 | 42 | protected $middleware = [ 43 | \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, 44 | \Neomerx\CorsIlluminate\CorsMiddleware::class, // <== add this line 45 | 46 | ... 47 | ]; 48 | 49 | ... 50 | } 51 | ``` 52 | 53 | > Next see step 3 54 | 55 | ### 2.2 Lumen 56 | 57 | > For Laravel skip this step 58 | 59 | In `bootstrap/app.php` add CORS to global middleware list 60 | 61 | ```php 62 | $app->middleware([ 63 | ... 64 | \Neomerx\CorsIlluminate\CorsMiddleware::class, 65 | ]); 66 | ``` 67 | 68 | and register CORS provider 69 | 70 | ```php 71 | $app->register(\Neomerx\CorsIlluminate\Providers\LumenServiceProvider::class); 72 | ``` 73 | 74 | As Lumen does not support `vendor:publish` command file `vendor/neomerx/cors-illuminate/config/cors-illuminate.php` have to be manually copied to `config/cors-illuminate.php`. 75 | 76 | > Next see step 3 77 | 78 | ### 3 Configuration 79 | 80 | [Configuration file](config/cors-illuminate.php) is extensively commented so it will be easy for you to set up it for your needs. First settings you need to configure are server origin (URL) and allowed origins 81 | 82 | ```php 83 | ... 84 | 85 | /** 86 | * Could be string or array. If specified as array (recommended for 87 | * better performance) it should be in parse_url() result format. 88 | */ 89 | Settings::KEY_SERVER_ORIGIN => [ 90 | 'scheme' => 'http', 91 | 'host' => 'localhost', 92 | 'port' => 1337, 93 | ], 94 | 95 | /** 96 | * A list of allowed request origins (no trail slashes). 97 | * If value is not on the list it is considered as not allowed. 98 | * If you want to allow all origins remove/comment this section. 99 | */ 100 | Settings::KEY_ALLOWED_ORIGINS => [ 101 | 'http://localhost:4200', 102 | ], 103 | 104 | ... 105 | ``` 106 | 107 | ## Exceptions and CORS headers 108 | 109 | When exceptions are thrown and responses are created in [Laravel/Lumen exception handlers](https://laravel.com/docs/6.x/errors) middleware will be excluded from handling responses. It means CORS middleware will not add its CORS headers to responses. For this reason CORS results (including headers) are registered in [Laravel/Lumen Container](https://laravel.com/docs/6.x/container) and made accessible from any part of your application including exception handlers. 110 | 111 | Code sample for reading CORS headers 112 | 113 | ```php 114 | use Neomerx\Cors\Contracts\AnalysisResultInterface; 115 | 116 | $corsHeaders = []; 117 | if (app()->resolved(AnalysisResultInterface::class) === true) { 118 | /** @var AnalysisResultInterface $result */ 119 | $result = app(AnalysisResultInterface::class); 120 | $corsHeaders = $result->getResponseHeaders(); 121 | } 122 | ``` 123 | 124 | ## Customization 125 | 126 | This package provides a number of ways how its behaviour could be customized. 127 | 128 | The following methods of class `CorsMiddleware` could be replaced in descendant classes 129 | - `getResponseOnError` You can override this method in order to customize error reply. 130 | - `getCorsAnalysis` You can override this method to modify how CORS analysis result is saved to Illuminate Container. 131 | - `getRequestAdapter` You can override this method to replace `IlluminateRequestToPsr7` adapter with another one. 132 | 133 | Additionally a custom [AnalysisStrategyInterface](https://github.com/neomerx/cors-psr7/blob/master/src/Contracts/AnalysisStrategyInterface.php) could be injected by 134 | - overriding `getCreateAnalysisStrategyClosure` method in `ServiceProvider` for Laravel/Lumen 135 | - using [Laravel/Lumen Container binding](https://laravel.com/docs/6.x/container) for interface `AnalysisStrategyInterface` 136 | 137 | Also custom [AnalyzerInterface](https://github.com/neomerx/cors-psr7/blob/master/src/Contracts/AnalyzerInterface.php) could be injected by 138 | - overriding `getCreateAnalyzerClosure` method in `ServiceProvider` for Laravel/Lumen 139 | - using [Laravel/Lumen Container binding](https://laravel.com/docs/6.x/container) for interface `AnalyzerInterface` 140 | 141 | ## Testing 142 | 143 | ``` 144 | composer test 145 | ``` 146 | 147 | ## Contributing 148 | 149 | Pull requests for documentation and code improvements (PSR-2, tests) are welcome. 150 | 151 | ## Versioning 152 | 153 | This package is using [Semantic Versioning](http://semver.org/). 154 | 155 | ## License 156 | 157 | Apache License (Version 2.0). Please see [License File](LICENSE) for more information. 158 | -------------------------------------------------------------------------------- /src/Providers/LaravelServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigs(); 54 | $this->configureCorsAnalyzer(); 55 | } 56 | 57 | /** 58 | * Perform post-registration booting of services. 59 | * 60 | * @return void 61 | */ 62 | public function boot() 63 | { 64 | $this->registerPublishConfig(); 65 | } 66 | 67 | /** 68 | * Merge default config and config from application `config` folder. 69 | * 70 | * @return void 71 | */ 72 | protected function mergeConfigs(): void 73 | { 74 | $repo = $this->getConfigRepository(); 75 | $config = $repo->get(static::CONFIG_FILE_NAME_WO_EXT, []); 76 | $base = $this->getBaseConfig(); 77 | $result = $config + $base; 78 | $repo->set(static::CONFIG_FILE_NAME_WO_EXT, $result); 79 | } 80 | 81 | /** 82 | * @return void 83 | */ 84 | protected function registerPublishConfig(): void 85 | { 86 | $publishPath = $this->app['path.config'] . DIRECTORY_SEPARATOR . static::CONFIG_FILE_NAME_WO_EXT . '.php'; 87 | $this->publishes([ 88 | $this->getConfigPath() => $publishPath, 89 | ]); 90 | } 91 | 92 | /** 93 | * @return void 94 | */ 95 | protected function configureCorsAnalyzer(): void 96 | { 97 | $this->app->bind(AnalysisStrategyInterface::class, $this->getCreateAnalysisStrategyClosure()); 98 | $this->app->bind(AnalyzerInterface::class, $this->getCreateAnalyzerClosure()); 99 | } 100 | 101 | /** 102 | * @return Closure 103 | */ 104 | protected function getCreateAnalysisStrategyClosure(): Closure 105 | { 106 | return function (): AnalysisStrategyInterface { 107 | $data = $this->getSettingsData(); 108 | $strategy = (new Settings())->setData($data); 109 | 110 | return $strategy; 111 | }; 112 | } 113 | 114 | /** 115 | * @return Closure 116 | * 117 | * @SuppressWarnings(PHPMD.StaticAccess) 118 | */ 119 | protected function getCreateAnalyzerClosure(): Closure 120 | { 121 | return function ($app): AnalyzerInterface { 122 | /** @var AnalysisStrategyInterface $strategy */ 123 | $strategy = $app[AnalysisStrategyInterface::class]; 124 | $analyzer = Analyzer::instance($strategy); 125 | 126 | /** @var Settings $strategy */ 127 | 128 | if ($strategy->isLogEnabled() === true) { 129 | /** @var LoggerInterface $logger */ 130 | $logger = $app[LoggerInterface::class]; 131 | $analyzer->setLogger($logger); 132 | } 133 | 134 | return $analyzer; 135 | }; 136 | } 137 | 138 | /** 139 | * @return string 140 | */ 141 | protected function getConfigPath(): string 142 | { 143 | $root = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; 144 | $path = $root . 'config' . DIRECTORY_SEPARATOR . static::CONFIG_FILE_NAME_WO_EXT . '.php'; 145 | 146 | return $path; 147 | } 148 | 149 | /** 150 | * @return array 151 | * 152 | * PHPMD do not work with ['key' => $var] = ...; 153 | * @SuppressWarnings(PHPMD.UndefinedVariable) 154 | */ 155 | protected function getSettingsData(): array 156 | { 157 | if ($this->settingsData === false) { 158 | $configFile = $this->getConfigRepository()->get(static::CONFIG_FILE_NAME_WO_EXT, []); 159 | 160 | // server origin should be in parse_url() result format. 161 | \assert( 162 | \array_key_exists(Settings::KEY_SERVER_ORIGIN, $configFile) && 163 | \is_array($configFile[Settings::KEY_SERVER_ORIGIN]), 164 | 'Server origin must be array in `parse_url()` format.' 165 | ); 166 | \assert(\array_key_exists('scheme', $configFile[Settings::KEY_SERVER_ORIGIN])); 167 | \assert(\array_key_exists('host', $configFile[Settings::KEY_SERVER_ORIGIN])); 168 | \assert(\array_key_exists('port', $configFile[Settings::KEY_SERVER_ORIGIN])); 169 | [ 170 | 'scheme' => $serverOriginScheme, 171 | 'host' => $serverOriginHost, 172 | 'port' => $serverOriginPort, 173 | ] = $configFile[Settings::KEY_SERVER_ORIGIN]; 174 | 175 | $settings = new Settings(); 176 | $settings->init($serverOriginScheme, $serverOriginHost, $serverOriginPort); 177 | 178 | $origins = $configFile[Settings::KEY_ALLOWED_ORIGINS] ?? null; 179 | $origins !== null ? $settings->setAllowedOrigins($origins) : $settings->enableAllOriginsAllowed(); 180 | 181 | $settings->setAllowedMethods($configFile[Settings::KEY_ALLOWED_METHODS] ?? []); 182 | $settings->setAllowedHeaders($configFile[Settings::KEY_ALLOWED_HEADERS] ?? []); 183 | $settings->setExposedHeaders($configFile[Settings::KEY_EXPOSED_HEADERS] ?? []); 184 | $settings->setPreFlightCacheMaxAge($configFile[Settings::KEY_FLIGHT_CACHE_MAX_AGE] ?? 0); 185 | 186 | \boolval($configFile[Settings::KEY_IS_USING_CREDENTIALS] ?? false) === true ? 187 | $settings->setCredentialsSupported() : $settings->setCredentialsNotSupported(); 188 | 189 | \boolval($configFile[Settings::KEY_IS_FORCE_ADD_METHODS] ?? false) === true ? 190 | $settings->enableAddAllowedMethodsToPreFlightResponse() : 191 | $settings->disableAddAllowedMethodsToPreFlightResponse(); 192 | 193 | \boolval($configFile[Settings::KEY_IS_FORCE_ADD_HEADERS] ?? false) === true ? 194 | $settings->enableAddAllowedHeadersToPreFlightResponse() : 195 | $settings->disableAddAllowedHeadersToPreFlightResponse(); 196 | 197 | \boolval($configFile[Settings::KEY_IS_CHECK_HOST] ?? false) === true ? 198 | $settings->enableCheckHost() : $settings->disableCheckHost(); 199 | 200 | 201 | \boolval($configFile[Settings::KEY_LOGS_ENABLED] ?? false) === true ? 202 | $settings->enableLog() : $settings->disableLog(); 203 | 204 | $this->settingsData = $settings->getData(); 205 | } 206 | 207 | return $this->settingsData; 208 | } 209 | 210 | /** 211 | * @return Repository 212 | */ 213 | protected function getConfigRepository() 214 | { 215 | /** @var Repository $config */ 216 | $config = $this->app['config']; 217 | 218 | return $config; 219 | } 220 | 221 | /** 222 | * @return array 223 | */ 224 | protected function getBaseConfig(): array 225 | { 226 | $path = $this->getConfigPath(); 227 | /** @noinspection PhpIncludeInspection */ 228 | $base = require $path; 229 | 230 | return $base; 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | --------------------------------------------------------------------------------