mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-25 19:03:13 +00:00
Use configured alchemy/symfony-cors instead of ApiCorsSubscriber
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea;
|
namespace Alchemy\Phrasea;
|
||||||
|
|
||||||
|
use Alchemy\Cors\Options\DefaultProvider;
|
||||||
|
use Alchemy\CorsProvider\CorsServiceProvider;
|
||||||
use Alchemy\Phrasea\Authorization\AuthorizationServiceProvider;
|
use Alchemy\Phrasea\Authorization\AuthorizationServiceProvider;
|
||||||
use Alchemy\Phrasea\Controller\Api\V1;
|
use Alchemy\Phrasea\Controller\Api\V1;
|
||||||
use Alchemy\Phrasea\Controller\Lightbox;
|
use Alchemy\Phrasea\Controller\Lightbox;
|
||||||
@@ -127,6 +129,7 @@ use Silex\Provider\SwiftmailerServiceProvider;
|
|||||||
use Silex\Provider\UrlGeneratorServiceProvider;
|
use Silex\Provider\UrlGeneratorServiceProvider;
|
||||||
use Silex\Provider\ValidatorServiceProvider;
|
use Silex\Provider\ValidatorServiceProvider;
|
||||||
use Silex\Provider\ServiceControllerServiceProvider;
|
use Silex\Provider\ServiceControllerServiceProvider;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
|
||||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||||
@@ -325,6 +328,29 @@ class Application extends SilexApplication
|
|||||||
$this->register(new PluginServiceProvider());
|
$this->register(new PluginServiceProvider());
|
||||||
$this->register(new PhraseaEventServiceProvider());
|
$this->register(new PhraseaEventServiceProvider());
|
||||||
$this->register(new ContentNegotiationServiceProvider());
|
$this->register(new ContentNegotiationServiceProvider());
|
||||||
|
$this->register(new CorsServiceProvider(), [
|
||||||
|
'alchemy_cors.debug' => $this['debug'],
|
||||||
|
'alchemy_cors.cache_path' => function (Application $app) {
|
||||||
|
return rtrim($app['cache.path'], '/\\') . '/alchemy_cors.cache.php';
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
$this['phraseanet.api_cors.options_provider'] = function (Application $app) {
|
||||||
|
$paths = [];
|
||||||
|
|
||||||
|
if (isset($app['phraseanet.configuration']['api_cors']['enabled'])) {
|
||||||
|
$config = $app['phraseanet.configuration']['api_cors'];
|
||||||
|
|
||||||
|
if ($config['enabled']) {
|
||||||
|
unset($config['enabled']);
|
||||||
|
|
||||||
|
$paths['/api/v\d+/'] = $config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultProvider($paths, []);
|
||||||
|
};
|
||||||
|
|
||||||
|
$this['alchemy_cors.options_providers'][] = 'phraseanet.api_cors.options_provider';
|
||||||
// allow access to oauth service from standard application scope
|
// allow access to oauth service from standard application scope
|
||||||
$this->register(new V1());
|
$this->register(new V1());
|
||||||
|
|
||||||
@@ -416,7 +442,7 @@ class Application extends SilexApplication
|
|||||||
);
|
);
|
||||||
|
|
||||||
$this['dispatcher'] = $this->share(
|
$this['dispatcher'] = $this->share(
|
||||||
$this->extend('dispatcher', function ($dispatcher, Application $app) {
|
$this->extend('dispatcher', function (EventDispatcherInterface $dispatcher, Application $app) {
|
||||||
$dispatcher->addListener(KernelEvents::RESPONSE, array($app, 'addUTF8Charset'), -128);
|
$dispatcher->addListener(KernelEvents::RESPONSE, array($app, 'addUTF8Charset'), -128);
|
||||||
$dispatcher->addSubscriber($app['phraseanet.logout-subscriber']);
|
$dispatcher->addSubscriber($app['phraseanet.logout-subscriber']);
|
||||||
$dispatcher->addSubscriber($app['phraseanet.locale-subscriber']);
|
$dispatcher->addSubscriber($app['phraseanet.locale-subscriber']);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ namespace Alchemy\Phrasea\Application;
|
|||||||
|
|
||||||
use Alchemy\Phrasea\Application as PhraseaApplication;
|
use Alchemy\Phrasea\Application as PhraseaApplication;
|
||||||
use Alchemy\Phrasea\Controller\Datafiles;
|
use Alchemy\Phrasea\Controller\Datafiles;
|
||||||
use Alchemy\Phrasea\Core\Event\Subscriber\ApiCorsSubscriber;
|
|
||||||
use Alchemy\Phrasea\Core\PhraseaEvents;
|
use Alchemy\Phrasea\Core\PhraseaEvents;
|
||||||
use Alchemy\Phrasea\Controller\Api\Oauth2;
|
use Alchemy\Phrasea\Controller\Api\Oauth2;
|
||||||
use Alchemy\Phrasea\Controller\Api\V1;
|
use Alchemy\Phrasea\Controller\Api\V1;
|
||||||
@@ -132,7 +131,6 @@ return call_user_func(function ($environment = PhraseaApplication::ENV_PROD) {
|
|||||||
$app->bindPluginRoutes('plugin.controller_providers.api');
|
$app->bindPluginRoutes('plugin.controller_providers.api');
|
||||||
|
|
||||||
$app['dispatcher']->addSubscriber(new ApiOauth2ErrorsSubscriber($app['phraseanet.exception_handler']));
|
$app['dispatcher']->addSubscriber(new ApiOauth2ErrorsSubscriber($app['phraseanet.exception_handler']));
|
||||||
$app['dispatcher']->addSubscriber(new ApiCorsSubscriber($app));
|
|
||||||
$app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_END, new ApiLoadEndEvent());
|
$app['dispatcher']->dispatch(PhraseaEvents::API_LOAD_END, new ApiLoadEndEvent());
|
||||||
|
|
||||||
return $app;
|
return $app;
|
||||||
|
|||||||
@@ -1,207 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file is part of Phraseanet
|
|
||||||
*
|
|
||||||
* (c) 2005-2015 Alchemy
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace Alchemy\Phrasea\Core\Event\Subscriber;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
||||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
|
||||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
|
||||||
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
|
|
||||||
|
|
||||||
class ApiCorsSubscriber implements EventSubscriberInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Simple headers as defined in the spec should always be accepted
|
|
||||||
*/
|
|
||||||
protected static $simpleHeaders = array(
|
|
||||||
'accept',
|
|
||||||
'accept-language',
|
|
||||||
'content-language',
|
|
||||||
'origin',
|
|
||||||
);
|
|
||||||
|
|
||||||
private $app;
|
|
||||||
private $options;
|
|
||||||
|
|
||||||
public function __construct(Application $app)
|
|
||||||
{
|
|
||||||
$this->app = $app;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getSubscribedEvents()
|
|
||||||
{
|
|
||||||
return array(
|
|
||||||
KernelEvents::REQUEST => array('onKernelRequest', 128),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onKernelRequest(GetResponseEvent $event)
|
|
||||||
{
|
|
||||||
if (!$this->app['phraseanet.configuration']['api_cors']['enabled']) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$request = $event->getRequest();
|
|
||||||
|
|
||||||
if (!preg_match('{api/v(\d+)}i', $request->getPathInfo() ?: '/')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// skip if not a CORS request
|
|
||||||
if (!$request->headers->has('Origin') || $request->headers->get('Origin') == $request->getSchemeAndHttpHost()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$options = array_merge(array(
|
|
||||||
'allow_credentials'=> false,
|
|
||||||
'allow_origin'=> array(),
|
|
||||||
'allow_headers'=> array(),
|
|
||||||
'allow_methods'=> array(),
|
|
||||||
'expose_headers'=> array(),
|
|
||||||
'max_age'=> 0,
|
|
||||||
'hosts'=> array(),
|
|
||||||
), $this->app['phraseanet.configuration']['api_cors']);
|
|
||||||
|
|
||||||
// skip if the host is not matching
|
|
||||||
if (!$this->checkHost($request, $options)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// perform preflight checks
|
|
||||||
if ('OPTIONS' === $request->getMethod()) {
|
|
||||||
$event->setResponse($this->getPreflightResponse($request, $options));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!$this->checkOrigin($request, $options)) {
|
|
||||||
$response = new Response('', 403, array('Access-Control-Allow-Origin' => 'null'));
|
|
||||||
$event->setResponse($response);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->app['dispatcher']->addListener(KernelEvents::RESPONSE, array($this, 'onKernelResponse'));
|
|
||||||
$this->options = $options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onKernelResponse(FilterResponseEvent $event)
|
|
||||||
{
|
|
||||||
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = $event->getResponse();
|
|
||||||
$request = $event->getRequest();
|
|
||||||
// add CORS response headers
|
|
||||||
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
|
|
||||||
if ($this->options['allow_credentials']) {
|
|
||||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
|
||||||
}
|
|
||||||
if ($this->options['expose_headers']) {
|
|
||||||
$response->headers->set('Access-Control-Expose-Headers', strtolower(implode(', ', $this->options['expose_headers'])));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getPreflightResponse(Request $request, array $options)
|
|
||||||
{
|
|
||||||
$response = new Response();
|
|
||||||
|
|
||||||
if ($options['allow_credentials']) {
|
|
||||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
|
||||||
}
|
|
||||||
if ($options['allow_methods']) {
|
|
||||||
$response->headers->set('Access-Control-Allow-Methods', implode(', ', $options['allow_methods']));
|
|
||||||
}
|
|
||||||
if ($options['allow_headers']) {
|
|
||||||
$headers = in_array('*', $options['allow_headers'])
|
|
||||||
? $request->headers->get('Access-Control-Request-Headers')
|
|
||||||
: implode(', ', array_map('strtolower', $options['allow_headers']));
|
|
||||||
$response->headers->set('Access-Control-Allow-Headers', $headers);
|
|
||||||
}
|
|
||||||
if ($options['max_age']) {
|
|
||||||
$response->headers->set('Access-Control-Max-Age', $options['max_age']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->checkOrigin($request, $options)) {
|
|
||||||
$response->headers->set('Access-Control-Allow-Origin', 'null');
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
|
|
||||||
|
|
||||||
// check request method
|
|
||||||
if (!in_array(strtoupper($request->headers->get('Access-Control-Request-Method')), $options['allow_methods'], true)) {
|
|
||||||
$response->setStatusCode(405);
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We have to allow the header in the case-set as we received it by the client.
|
|
||||||
* Firefox f.e. sends the LINK method as "Link", and we have to allow it like this or the browser will deny the
|
|
||||||
* request.
|
|
||||||
*/
|
|
||||||
if (!in_array($request->headers->get('Access-Control-Request-Method'), $options['allow_methods'], true)) {
|
|
||||||
$options['allow_methods'][] = $request->headers->get('Access-Control-Request-Method');
|
|
||||||
$response->headers->set('Access-Control-Allow-Methods', implode(', ', $options['allow_methods']));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check request headers
|
|
||||||
$headers = $request->headers->get('Access-Control-Request-Headers');
|
|
||||||
if ($options['allow_headers'] !== true && $headers) {
|
|
||||||
$headers = trim(strtolower($headers));
|
|
||||||
foreach (preg_split('{, *}', $headers) as $header) {
|
|
||||||
if (in_array($header, self::$simpleHeaders, true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!in_array($header, $options['allow_headers'], true)) {
|
|
||||||
$response->setStatusCode(400);
|
|
||||||
$response->setContent('Unauthorized header '.$header);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function checkOrigin(Request $request, array $options)
|
|
||||||
{
|
|
||||||
// check origin
|
|
||||||
$origin = $request->headers->get('Origin');
|
|
||||||
if (in_array('*', $options['allow_origin']) || in_array($origin, $options['allow_origin'])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function checkHost(Request $request, array $options)
|
|
||||||
{
|
|
||||||
if (count($options['hosts']) === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($options['hosts'] as $hostRegexp) {
|
|
||||||
if (preg_match('{'.$hostRegexp.'}i', $request->getHost())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,11 +11,11 @@
|
|||||||
|
|
||||||
namespace Alchemy\Phrasea\Core\Provider;
|
namespace Alchemy\Phrasea\Core\Provider;
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Alchemy\Phrasea\Core\Configuration\Configuration;
|
use Alchemy\Phrasea\Core\Configuration\Configuration;
|
||||||
use Alchemy\Phrasea\Core\Configuration\Compiler;
|
use Alchemy\Phrasea\Core\Configuration\Compiler;
|
||||||
use Silex\Application as SilexApplication;
|
use Silex\Application as SilexApplication;
|
||||||
use Silex\ServiceProviderInterface;
|
use Silex\ServiceProviderInterface;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Alchemy\Phrasea\Core\Event\Subscriber\TrustedProxySubscriber;
|
use Alchemy\Phrasea\Core\Event\Subscriber\TrustedProxySubscriber;
|
||||||
|
|
||||||
@@ -23,10 +23,10 @@ class ConfigurationServiceProvider implements ServiceProviderInterface
|
|||||||
{
|
{
|
||||||
public function register(SilexApplication $app)
|
public function register(SilexApplication $app)
|
||||||
{
|
{
|
||||||
$app['phraseanet.configuration.yaml-parser'] = $app->share(function (SilexApplication $app) {
|
$app['phraseanet.configuration.yaml-parser'] = $app->share(function () {
|
||||||
return new Yaml();
|
return new Yaml();
|
||||||
});
|
});
|
||||||
$app['phraseanet.configuration.compiler'] = $app->share(function (SilexApplication $app) {
|
$app['phraseanet.configuration.compiler'] = $app->share(function () {
|
||||||
return new Compiler();
|
return new Compiler();
|
||||||
});
|
});
|
||||||
$app['phraseanet.configuration.config-path'] = $app['root.path'] . '/config/configuration.yml';
|
$app['phraseanet.configuration.config-path'] = $app['root.path'] . '/config/configuration.yml';
|
||||||
@@ -49,7 +49,7 @@ class ConfigurationServiceProvider implements ServiceProviderInterface
|
|||||||
public function boot(SilexApplication $app)
|
public function boot(SilexApplication $app)
|
||||||
{
|
{
|
||||||
$app['dispatcher'] = $app->share(
|
$app['dispatcher'] = $app->share(
|
||||||
$app->extend('dispatcher', function ($dispatcher, SilexApplication $app) {
|
$app->extend('dispatcher', function (EventDispatcherInterface $dispatcher, SilexApplication $app) {
|
||||||
$dispatcher->addSubscriber(new TrustedProxySubscriber($app['phraseanet.configuration']));
|
$dispatcher->addSubscriber(new TrustedProxySubscriber($app['phraseanet.configuration']));
|
||||||
|
|
||||||
return $dispatcher;
|
return $dispatcher;
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Alchemy\Tests\Phrasea\Core\Event\Subscriber;
|
|
||||||
|
|
||||||
use Alchemy\Phrasea\Application;
|
|
||||||
use Symfony\Component\HttpKernel\Client;
|
|
||||||
use Alchemy\Phrasea\Core\Event\Subscriber\ApiCorsSubscriber;
|
|
||||||
|
|
||||||
class ApiCorsSubscriberTest extends \PHPUnit_Framework_TestCase
|
|
||||||
{
|
|
||||||
private $origin = 'http://dev.phrasea.net';
|
|
||||||
|
|
||||||
public function testHostRestriction()
|
|
||||||
{
|
|
||||||
$response = $this->request(array('enabled' => true, 'hosts' => array('http://api.domain.com')));
|
|
||||||
$this->assertArrayNotHasKey('access-control-allow-origin', $response->headers->all());
|
|
||||||
|
|
||||||
$response = $this->request(array('enabled' => true, 'hosts' => array('localhost')));
|
|
||||||
$this->assertArrayHasKey('access-control-allow-origin', $response->headers->all());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testExposeHeaders()
|
|
||||||
{
|
|
||||||
$response = $this->request(
|
|
||||||
array('enabled' => true, 'allow_origin' => array('*'), 'expose_headers' => array('HTTP_X_CUSTOM')),
|
|
||||||
'GET'
|
|
||||||
);
|
|
||||||
$this->assertArrayHasKey('access-control-expose-headers', $response->headers->all());
|
|
||||||
$this->assertEquals('http_x_custom', $response->headers->get('access-control-expose-headers'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllowMethods()
|
|
||||||
{
|
|
||||||
$response = $this->request(
|
|
||||||
array('enabled' => true, 'allow_origin' => array('*'), 'allow_methods' => array('GET', 'POST', 'PUT')),
|
|
||||||
'OPTIONS'
|
|
||||||
);
|
|
||||||
$this->assertArrayHasKey('access-control-allow-methods', $response->headers->all());
|
|
||||||
$this->assertEquals(implode(', ', array('GET', 'POST', 'PUT')), $response->headers->get('access-control-allow-methods'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllowHeaders()
|
|
||||||
{
|
|
||||||
$response = $this->request(
|
|
||||||
array('enabled' => true, 'allow_origin' => array('*'), 'allow_headers' => array('HTTP_X_CUSTOM')),
|
|
||||||
'OPTIONS'
|
|
||||||
);
|
|
||||||
$this->assertArrayHasKey('access-control-allow-headers', $response->headers->all());
|
|
||||||
$this->assertEquals('http_x_custom', $response->headers->get('access-control-allow-headers'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCORSIsEnable()
|
|
||||||
{
|
|
||||||
$response = $this->request(array('enabled' => true));
|
|
||||||
$this->assertArrayHasKey('access-control-allow-origin', $response->headers->all());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCORSIsDisable()
|
|
||||||
{
|
|
||||||
$response = $this->request(array('enabled' => false));
|
|
||||||
$this->assertArrayNotHasKey('access-control-allow-origin', $response->headers->all());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllowOrigin()
|
|
||||||
{
|
|
||||||
$response = $this->request(array('enabled' => true, 'allow_origin' => array('*')));
|
|
||||||
$this->assertArrayHasKey('access-control-allow-origin', $response->headers->all());
|
|
||||||
$this->assertEquals($this->origin, $response->headers->get('access-control-allow-origin'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCredentialIsEnabled()
|
|
||||||
{
|
|
||||||
$response = $this->request(array('enabled' => true, 'allow_credentials' => true, 'allow_origin' => array('*')));
|
|
||||||
$this->assertArrayHasKey('access-control-allow-credentials', $response->headers->all());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $conf
|
|
||||||
* @param string $method
|
|
||||||
* @param array $extraHeaders
|
|
||||||
*
|
|
||||||
* @return \Symfony\Component\HttpFoundation\Response
|
|
||||||
*/
|
|
||||||
private function request(array $conf, $method = 'GET', array $extraHeaders = array())
|
|
||||||
{
|
|
||||||
$app = new Application('test');
|
|
||||||
$app['phraseanet.configuration']['api_cors'] = $conf;
|
|
||||||
$app['dispatcher']->addSubscriber(new ApiCorsSubscriber($app));
|
|
||||||
$app->get('/api/v1/test-route', function () {
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
$client = new Client($app);
|
|
||||||
$client->request($method, '/api/v1/test-route',
|
|
||||||
array(),
|
|
||||||
array(),
|
|
||||||
array_merge(
|
|
||||||
$extraHeaders,
|
|
||||||
array(
|
|
||||||
'HTTP_Origin' => $this->origin,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $client->getResponse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user