Spaces:
No application file
No application file
namespace Mautic\Middleware; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpKernel\HttpKernelInterface; | |
class CORSMiddleware implements HttpKernelInterface, PrioritizedMiddlewareInterface | |
{ | |
use ConfigAwareTrait; | |
public const PRIORITY = 1000; | |
/** | |
* @var array | |
*/ | |
protected $corsHeaders = [ | |
'Access-Control-Allow-Origin' => '*', | |
'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Authorization', | |
'Access-Control-Allow-Methods' => 'PUT, GET, POST, DELETE, OPTIONS', | |
'Access-Control-Allow-Credentials' => 'true', | |
'Access-Control-Max-Age' => 10 * 60 * 60, // 10 min, max age for Chrome | |
]; | |
/** | |
* @var bool | |
*/ | |
protected $requestOriginIsValid = false; | |
/** | |
* @var bool | |
*/ | |
protected $restrictCORSDomains = true; | |
/** | |
* @var array | |
*/ | |
protected $validCORSDomains = []; | |
/** | |
* @var HttpKernelInterface | |
*/ | |
protected $app; | |
public function __construct(HttpKernelInterface $app) | |
{ | |
$this->app = $app; | |
$this->config = $this->getConfig(); | |
$this->restrictCORSDomains = array_key_exists('cors_restrict_domains', $this->config) ? (bool) $this->config['cors_restrict_domains'] : true; | |
$this->validCORSDomains = array_key_exists('cors_valid_domains', $this->config) ? (array) $this->config['cors_valid_domains'] : []; | |
} | |
public function handle(Request $request, $type = self::MAIN_REQUEST, $catch = true) | |
{ | |
$this->corsHeaders['Access-Control-Allow-Origin'] = $this->getAllowOriginHeaderValue($request); | |
// Capture all OPTIONS requests | |
if ('OPTIONS' === $request->getMethod()) { | |
$response = new Response('', Response::HTTP_NO_CONTENT); | |
// If this is a valid OPTIONS request, set the CORS headers on the Response and exit. | |
if ( | |
$this->requestOriginIsValid | |
&& $request->headers->has('Access-Control-Request-Headers') | |
&& $request->headers->has('Origin') | |
) { | |
foreach ($this->corsHeaders as $header => $value) { | |
$response->headers->set($header, $value); | |
} | |
} | |
return $response; | |
} | |
$response = $this->app->handle($request, $type, $catch); | |
// Add standard CORS headers to any XHR | |
if ($request->isXmlHttpRequest()) { | |
foreach ($this->corsHeaders as $header => $value) { | |
$response->headers->set($header, $value); | |
} | |
} | |
return $response; | |
} | |
/** | |
* Get the value for the Access-Control-Allow-Origin header | |
* based on the Request and local configuration options. | |
* | |
* @return string|null | |
*/ | |
private function getAllowOriginHeaderValue(Request $request) | |
{ | |
$origin = $request->headers->get('Origin'); | |
// If we're not restricting domains, set the header to the request origin | |
if (!$this->restrictCORSDomains || in_array($origin, $this->validCORSDomains)) { | |
$this->requestOriginIsValid = true; | |
return $origin; | |
} | |
// Check the domains using shell wildcard patterns | |
$validCorsDomainFilter = function ($validCorsDomain) use ($origin) { | |
if (null === $origin) { | |
return null; | |
} | |
return fnmatch($validCorsDomain, $origin, FNM_CASEFOLD); | |
}; | |
if (array_filter($this->validCORSDomains, $validCorsDomainFilter)) { | |
$this->requestOriginIsValid = true; | |
$this->corsHeaders['Vary'] = 'Origin'; | |
return $origin; | |
} | |
$this->requestOriginIsValid = false; | |
return null; | |
} | |
public function getPriority() | |
{ | |
return self::PRIORITY; | |
} | |
} | |