added CORS plugin
This commit is contained in:
parent
7481cca823
commit
cd9c891873
|
|
@ -0,0 +1 @@
|
|||
/vendor/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# How to contribute
|
||||
|
||||
Contributions to this project are highly welcome.
|
||||
|
||||
1. Submit your pull requests to the `develop` branch
|
||||
1. Adhere to the [PSR-2 coding](http://www.php-fig.org/psr/psr-2/) standard
|
||||
1. If you are not sure if your ideas are fit for this project, create an issue and ask
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2016 OFFLINE GmbH
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php namespace OFFLINE\CORS;
|
||||
|
||||
use OFFLINE\CORS\Classes\HandleCors;
|
||||
use OFFLINE\CORS\Classes\HandlePreflight;
|
||||
use OFFLINE\CORS\Classes\ServiceProvider;
|
||||
use OFFLINE\CORS\Models\Settings;
|
||||
use System\Classes\PluginBase;
|
||||
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
public function boot()
|
||||
{
|
||||
\App::register(ServiceProvider::class);
|
||||
|
||||
$this->app['Illuminate\Contracts\Http\Kernel']
|
||||
->prependMiddleware(HandleCors::class);
|
||||
|
||||
if (request()->isMethod('OPTIONS')) {
|
||||
$this->app['Illuminate\Contracts\Http\Kernel']
|
||||
->prependMiddleware(HandlePreflight::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function registerPermissions()
|
||||
{
|
||||
return [
|
||||
'offline.cors.manage' => [
|
||||
'label' => 'Can manage cors settings',
|
||||
'tab' => 'CORS',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function registerSettings()
|
||||
{
|
||||
return [
|
||||
'cors' => [
|
||||
'label' => 'CORS-Settings',
|
||||
'description' => 'Manage CORS headers',
|
||||
'category' => 'system::lang.system.categories.cms',
|
||||
'icon' => 'icon-code',
|
||||
'class' => Settings::class,
|
||||
'order' => 500,
|
||||
'keywords' => 'cors',
|
||||
'permissions' => ['offline.cors.manage'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# CORS plugin for October CMS
|
||||
|
||||
This plugin is based on [https://github.com/barryvdh/laravel-cors](https://github.com/barryvdh/laravel-cors/blob/master/config/cors.php).
|
||||
|
||||
All configuration for the plugin can be done via the backend settings.
|
||||
|
||||
The following cors headers are supported:
|
||||
|
||||
* Access-Control-Allow-Origin
|
||||
* Access-Control-Allow-Headers
|
||||
* Access-Control-Allow-Methods
|
||||
* Access-Control-Allow-Credentials
|
||||
* Access-Control-Expose-Headers
|
||||
* Access-Control-Max-Age
|
||||
|
||||
Currently these headers are sent for every request. There is no per-route configuration possible at this time.
|
||||
|
||||
## Setup
|
||||
|
||||
After installing the plugin visit the CORS settings page in your October CMS backend settings.
|
||||
|
||||
You can add `*` as an entry to `Allowed origins`, `Allowed headers` and `Allowed methods` to allow any kind of CORS request from everywhere.
|
||||
|
||||
It is advised to be more explicit about these settings. You can add values for each header via the repeater fields.
|
||||
|
||||
> It is important to set these intial settings once for the plugin to work as excpected!
|
||||
|
||||
### Filesystem configuration
|
||||
|
||||
As an alternative to the backend settings you can create a `config/config.php` file in the plugins root directory to configure it.
|
||||
|
||||
The filesystem configuration will overwrite any defined backend setting.
|
||||
|
||||
```php
|
||||
<?php
|
||||
// plugins/offline/cors/config/config.php
|
||||
return [
|
||||
'supportsCredentials' => true,
|
||||
'maxAge' => 3600,
|
||||
'allowedOrigins' => ['*'],
|
||||
'allowedHeaders' => ['*'],
|
||||
'allowedMethods' => ['GET', 'POST'],
|
||||
'exposedHeaders' => [''],
|
||||
];
|
||||
```
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
|
||||
/**
|
||||
* Based on asm89/stack-cors
|
||||
*/
|
||||
class Cors implements HttpKernelInterface
|
||||
{
|
||||
/**
|
||||
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* @var CorsService
|
||||
*/
|
||||
private $cors;
|
||||
|
||||
private $defaultOptions = [
|
||||
'allowedHeaders' => [],
|
||||
'allowedMethods' => [],
|
||||
'allowedOrigins' => [],
|
||||
'exposedHeaders' => false,
|
||||
'maxAge' => false,
|
||||
'supportsCredentials' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Cors constructor.
|
||||
*
|
||||
* @param HttpKernelInterface $app
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct(HttpKernelInterface $app, array $options = [])
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->cors = new CorsService(array_merge($this->defaultOptions, $options));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param int $type
|
||||
* @param bool $catch
|
||||
*
|
||||
* @return bool|Response
|
||||
*/
|
||||
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
|
||||
{
|
||||
if ( ! $this->cors->isCorsRequest($request)) {
|
||||
return $this->app->handle($request, $type, $catch);
|
||||
}
|
||||
|
||||
if ($this->cors->isPreflightRequest($request)) {
|
||||
return $this->cors->handlePreflightRequest($request);
|
||||
}
|
||||
|
||||
if ( ! $this->cors->isActualRequestAllowed($request)) {
|
||||
return new Response('Not allowed.', 403);
|
||||
}
|
||||
|
||||
$response = $this->app->handle($request, $type, $catch);
|
||||
|
||||
return $this->cors->addActualRequestHeaders($response, $request);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
/**
|
||||
* Based on asm89/stack-cors
|
||||
*/
|
||||
class CorsService
|
||||
{
|
||||
private $options;
|
||||
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $this->normalizeOptions($options);
|
||||
}
|
||||
|
||||
private function normalizeOptions(array $options = [])
|
||||
{
|
||||
$options += [
|
||||
'supportsCredentials' => false,
|
||||
'maxAge' => 0,
|
||||
];
|
||||
|
||||
// Make sure these values are arrays, if not specified in the backend settings.
|
||||
$arrayKeys = [
|
||||
'allowedOrigins',
|
||||
'allowedHeaders',
|
||||
'exposedHeaders',
|
||||
'allowedMethods',
|
||||
];
|
||||
|
||||
foreach ($arrayKeys as $key) {
|
||||
if (!$options[$key]) {
|
||||
$options[$key] = [];
|
||||
}
|
||||
}
|
||||
|
||||
// normalize array('*') to true
|
||||
if (in_array('*', $options['allowedOrigins'])) {
|
||||
$options['allowedOrigins'] = true;
|
||||
}
|
||||
if (in_array('*', $options['allowedHeaders'])) {
|
||||
$options['allowedHeaders'] = true;
|
||||
} else {
|
||||
$options['allowedHeaders'] = array_map('strtolower', $options['allowedHeaders']);
|
||||
}
|
||||
|
||||
if (in_array('*', $options['allowedMethods'])) {
|
||||
$options['allowedMethods'] = true;
|
||||
} else {
|
||||
$options['allowedMethods'] = array_map('strtoupper', $options['allowedMethods']);
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function isActualRequestAllowed(Request $request)
|
||||
{
|
||||
return $this->checkOrigin($request);
|
||||
}
|
||||
|
||||
public function isCorsRequest(Request $request)
|
||||
{
|
||||
return $request->headers->has('Origin') && $request->headers->get('Origin') !== $request->getSchemeAndHttpHost();
|
||||
}
|
||||
|
||||
public function isPreflightRequest(Request $request)
|
||||
{
|
||||
return $this->isCorsRequest($request)
|
||||
&& $request->getMethod() === 'OPTIONS'
|
||||
&& $request->headers->has('Access-Control-Request-Method');
|
||||
}
|
||||
|
||||
public function addActualRequestHeaders(Response $response, Request $request)
|
||||
{
|
||||
if ( ! $this->checkOrigin($request)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
|
||||
|
||||
if ( ! $response->headers->has('Vary')) {
|
||||
$response->headers->set('Vary', 'Origin');
|
||||
} else {
|
||||
$response->headers->set('Vary', $response->headers->get('Vary') . ', Origin');
|
||||
}
|
||||
|
||||
if ($this->options['supportsCredentials']) {
|
||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
|
||||
if ($this->options['exposedHeaders']) {
|
||||
$response->headers->set('Access-Control-Expose-Headers', implode(', ', $this->options['exposedHeaders']));
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function handlePreflightRequest(Request $request)
|
||||
{
|
||||
if (true !== $check = $this->checkPreflightRequestConditions($request)) {
|
||||
return $check;
|
||||
}
|
||||
|
||||
return $this->buildPreflightCheckResponse($request);
|
||||
}
|
||||
|
||||
private function buildPreflightCheckResponse(Request $request)
|
||||
{
|
||||
$response = new Response();
|
||||
|
||||
if ($this->options['supportsCredentials']) {
|
||||
$response->headers->set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
|
||||
$response->headers->set('Access-Control-Allow-Origin', $request->headers->get('Origin'));
|
||||
|
||||
if ($this->options['maxAge']) {
|
||||
$response->headers->set('Access-Control-Max-Age', $this->options['maxAge']);
|
||||
}
|
||||
|
||||
$allowMethods = $this->options['allowedMethods'] === true
|
||||
? strtoupper($request->headers->get('Access-Control-Request-Method'))
|
||||
: implode(', ', $this->options['allowedMethods']);
|
||||
$response->headers->set('Access-Control-Allow-Methods', $allowMethods);
|
||||
|
||||
$allowHeaders = $this->options['allowedHeaders'] === true
|
||||
? strtoupper($request->headers->get('Access-Control-Request-Headers'))
|
||||
: implode(', ', $this->options['allowedHeaders']);
|
||||
$response->headers->set('Access-Control-Allow-Headers', $allowHeaders);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
private function checkPreflightRequestConditions(Request $request)
|
||||
{
|
||||
if ( ! $this->checkOrigin($request)) {
|
||||
return $this->createBadRequestResponse(403, 'Origin not allowed');
|
||||
}
|
||||
|
||||
if ( ! $this->checkMethod($request)) {
|
||||
return $this->createBadRequestResponse(405, 'Method not allowed');
|
||||
}
|
||||
|
||||
$requestHeaders = [];
|
||||
// if allowedHeaders has been set to true ('*' allow all flag) just skip this check
|
||||
if ($this->options['allowedHeaders'] !== true && $request->headers->has('Access-Control-Request-Headers')) {
|
||||
$headers = strtolower($request->headers->get('Access-Control-Request-Headers'));
|
||||
$requestHeaders = explode(',', $headers);
|
||||
|
||||
foreach ($requestHeaders as $header) {
|
||||
if ( ! in_array(trim($header), $this->options['allowedHeaders'])) {
|
||||
return $this->createBadRequestResponse(403, 'Header not allowed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createBadRequestResponse($code, $reason = '')
|
||||
{
|
||||
return new Response($reason, $code);
|
||||
}
|
||||
|
||||
private function checkOrigin(Request $request)
|
||||
{
|
||||
if ($this->options['allowedOrigins'] === true) {
|
||||
// allow all '*' flag
|
||||
return true;
|
||||
}
|
||||
$origin = $request->headers->get('Origin');
|
||||
|
||||
foreach ($this->options['allowedOrigins'] as $allowedOrigin) {
|
||||
if (OriginMatcher::matches($allowedOrigin, $origin)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function checkMethod(Request $request)
|
||||
{
|
||||
if ($this->options['allowedMethods'] === true) {
|
||||
// allow all '*' flag
|
||||
return true;
|
||||
}
|
||||
|
||||
$requestMethod = strtoupper($request->headers->get('Access-Control-Request-Method'));
|
||||
|
||||
return in_array($requestMethod, $this->options['allowedMethods']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
use Closure;
|
||||
|
||||
class HandleCors
|
||||
{
|
||||
/**
|
||||
* The CORS service
|
||||
*
|
||||
* @var CorsService
|
||||
*/
|
||||
protected $cors;
|
||||
|
||||
/**
|
||||
* @param CorsService $cors
|
||||
*/
|
||||
public function __construct(CorsService $cors)
|
||||
{
|
||||
$this->cors = $cors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request. Based on Asm89\Stack\Cors by asm89
|
||||
* @see https://github.com/asm89/stack-cors/blob/master/src/Asm89/Stack/Cors.php
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ( ! $this->cors->isCorsRequest($request)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
if ( ! $this->cors->isActualRequestAllowed($request)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Http\Response $response */
|
||||
$response = $next($request);
|
||||
|
||||
return $this->cors->addActualRequestHeaders($response, $request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Http\Kernel;
|
||||
use Illuminate\Routing\Router;
|
||||
|
||||
class HandlePreflight
|
||||
{
|
||||
/**
|
||||
* @param CorsService $cors
|
||||
*/
|
||||
public function __construct(CorsService $cors, Router $router, Kernel $kernel)
|
||||
{
|
||||
$this->cors = $cors;
|
||||
$this->router = $router;
|
||||
$this->kernel = $kernel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming OPTIONS request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
$response = $next($request);
|
||||
if ($this->cors->isPreflightRequest($request) && $this->hasMatchingCorsRoute($request)) {
|
||||
$preflight = $this->cors->handlePreflightRequest($request);
|
||||
$response->headers->add($preflight->headers->all());
|
||||
}
|
||||
$response->setStatusCode(204);
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the current OPTIONS request matches a CORS-enabled route
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function hasMatchingCorsRoute($request)
|
||||
{
|
||||
// Check if CORS is added in a global middleware
|
||||
if ($this->kernel->hasMiddleware(HandleCors::class)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if CORS is added as a route middleware
|
||||
$request = clone $request;
|
||||
$request->setMethod($request->header('Access-Control-Request-Method'));
|
||||
|
||||
try {
|
||||
$route = $this->router->getRoutes()->match($request);
|
||||
// change of method name in laravel 5.3
|
||||
if (method_exists($this->router, 'gatherRouteMiddleware')) {
|
||||
$middleware = $this->router->gatherRouteMiddleware($route);
|
||||
} else {
|
||||
$middleware = $this->router->gatherRouteMiddlewares($route);
|
||||
}
|
||||
|
||||
return in_array(HandleCors::class, $middleware);
|
||||
} catch (\Exception $e) {
|
||||
app('log')->error($e);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
class OriginMatcher
|
||||
{
|
||||
|
||||
public static function matches($pattern, $origin)
|
||||
{
|
||||
if ($pattern === $origin) {
|
||||
return true;
|
||||
}
|
||||
$scheme = parse_url($origin, PHP_URL_SCHEME);
|
||||
$host = parse_url($origin, PHP_URL_HOST);
|
||||
$port = parse_url($origin, PHP_URL_PORT);
|
||||
|
||||
$schemePattern = static::parseOriginPattern($pattern, PHP_URL_SCHEME);
|
||||
$hostPattern = static::parseOriginPattern($pattern, PHP_URL_HOST);
|
||||
$portPattern = static::parseOriginPattern($pattern, PHP_URL_PORT);
|
||||
|
||||
$schemeMatches = static::schemeMatches($schemePattern, $scheme);
|
||||
$hostMatches = static::hostMatches($hostPattern, $host);
|
||||
$portMatches = static::portMatches($portPattern, $port);
|
||||
|
||||
return $schemeMatches && $hostMatches && $portMatches;
|
||||
}
|
||||
|
||||
public static function schemeMatches($pattern, $scheme)
|
||||
{
|
||||
return is_null($pattern) || $pattern === $scheme;
|
||||
}
|
||||
|
||||
public static function hostMatches($pattern, $host)
|
||||
{
|
||||
$patternComponents = array_reverse(explode('.', $pattern));
|
||||
$hostComponents = array_reverse(explode('.', $host));
|
||||
foreach ($patternComponents as $index => $patternComponent) {
|
||||
if ($patternComponent === '*') {
|
||||
return true;
|
||||
}
|
||||
if ( ! isset($hostComponents[$index])) {
|
||||
return false;
|
||||
}
|
||||
if ($hostComponents[$index] !== $patternComponent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return count($patternComponents) === count($hostComponents);
|
||||
}
|
||||
|
||||
public static function portMatches($pattern, $port)
|
||||
{
|
||||
if ($pattern === "*") {
|
||||
return true;
|
||||
}
|
||||
if ((string)$pattern === "") {
|
||||
return (string)$port === "";
|
||||
}
|
||||
if (preg_match('/\A\d+\z/', $pattern)) {
|
||||
return (string)$pattern === (string)$port;
|
||||
}
|
||||
if (preg_match('/\A(?P<from>\d+)-(?P<to>\d+)\z/', $pattern, $captured)) {
|
||||
return $captured['from'] <= $port && $port <= $captured['to'];
|
||||
}
|
||||
throw new \InvalidArgumentException("Invalid port pattern: ${pattern}");
|
||||
}
|
||||
|
||||
public static function parseOriginPattern($originPattern, $component = -1)
|
||||
{
|
||||
$matched = preg_match(
|
||||
'!\A
|
||||
(?: (?P<scheme> ([a-z][a-z0-9+\-.]*) ):// )?
|
||||
(?P<host> (?:\*|[\w-]+)(?:\.[\w-]+)* )
|
||||
(?: :(?P<port> (?: \*|\d+(?:-\d+)? ) ) )?
|
||||
\z!x',
|
||||
$originPattern,
|
||||
$captured
|
||||
);
|
||||
if ( ! $matched) {
|
||||
throw new \InvalidArgumentException("Invalid origin pattern ${originPattern}");
|
||||
}
|
||||
$components = [
|
||||
'scheme' => $captured['scheme'] ?: null,
|
||||
'host' => $captured['host'],
|
||||
'port' => array_key_exists('port', $captured) ? $captured['port'] : null,
|
||||
];
|
||||
switch ($component) {
|
||||
case -1:
|
||||
return $components;
|
||||
case PHP_URL_SCHEME:
|
||||
return $components['scheme'];
|
||||
case PHP_URL_HOST:
|
||||
return $components['host'];
|
||||
case PHP_URL_PORT:
|
||||
return $components['port'];
|
||||
}
|
||||
throw new \InvalidArgumentException("Invalid component: ${component}");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Classes;
|
||||
|
||||
use October\Rain\Support\ServiceProvider as BaseServiceProvider;
|
||||
use OFFLINE\CORS\Models\Settings;
|
||||
|
||||
class ServiceProvider extends BaseServiceProvider
|
||||
{
|
||||
/**
|
||||
* Indicates if loading of the provider is deferred.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $defer = false;
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->app->singleton(CorsService::class, function ($app) {
|
||||
return new CorsService($this->getSettings());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return default Settings
|
||||
*/
|
||||
protected function getSettings()
|
||||
{
|
||||
$supportsCredentials = (bool)$this->getConfigValue('supportsCredentials', false);
|
||||
$maxAge = (int)$this->getConfigValue('maxAge', 0);
|
||||
$allowedOrigins = $this->getConfigValue('allowedOrigins', []);
|
||||
$allowedHeaders = $this->getConfigValue('allowedHeaders', []);
|
||||
$allowedMethods = $this->getConfigValue('allowedMethods', []);
|
||||
$exposedHeaders = $this->getConfigValue('exposedHeaders', []);
|
||||
|
||||
return compact(
|
||||
'supportsCredentials',
|
||||
'allowedOrigins',
|
||||
'allowedHeaders',
|
||||
'allowedMethods',
|
||||
'exposedHeaders',
|
||||
'maxAge'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an effective config value.
|
||||
*
|
||||
* If a filesystem config is available it takes precedence
|
||||
* over the backend settings values.
|
||||
*
|
||||
* @param $key
|
||||
* @param null $default
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getConfigValue($key, $default = null)
|
||||
{
|
||||
return $this->filesystemConfig($key) ?: $this->getValues(Settings::get($key, $default));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the filesystem config value if available.
|
||||
*
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function filesystemConfig($key)
|
||||
{
|
||||
return config('offline.cors::' . $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the repeater field values.
|
||||
*
|
||||
* @param mixed $values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getValues($values)
|
||||
{
|
||||
return \is_array($values) ? collect($values)->pluck('value')->toArray() : $values;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "offline/oc-cors-plugin",
|
||||
"description": "Setup and manage Cross-Origin Resource Sharing headers in October CMS",
|
||||
"type": "october-plugin",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tobias Kündig",
|
||||
"email": "tobias@offline.swiss"
|
||||
}
|
||||
],
|
||||
"require": {}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?php return [
|
||||
'plugin' => [
|
||||
'name' => 'CORS',
|
||||
'description' => 'Verwalte Cross-Origin Resource Sharing Header in October CMS',
|
||||
],
|
||||
];
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?php return [
|
||||
'plugin' => [
|
||||
'name' => 'CORS',
|
||||
'description' => 'Setup and manage Cross-Origin Resource Sharing headers',
|
||||
],
|
||||
];
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
namespace OFFLINE\CORS\Models;
|
||||
|
||||
use Model;
|
||||
|
||||
class Settings extends Model
|
||||
{
|
||||
public $implement = ['System.Behaviors.SettingsModel'];
|
||||
public $settingsCode = 'offline_cors_settings';
|
||||
public $settingsFields = 'fields.yaml';
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
fields:
|
||||
supportsCredentials:
|
||||
label: Supports credentials
|
||||
type: switch
|
||||
comment: 'Set Access-Control-Allow-Credentials header to true'
|
||||
default: false
|
||||
span: left
|
||||
maxAge:
|
||||
label: Max age
|
||||
type: number
|
||||
comment: 'Set Access-Control-Max-Age to this value'
|
||||
default: 0
|
||||
span: right
|
||||
tabs:
|
||||
fields:
|
||||
allowedOrigins:
|
||||
label: Allowed origins
|
||||
tab: Allowed origins
|
||||
type: repeater
|
||||
span: left
|
||||
form:
|
||||
fields:
|
||||
value:
|
||||
type: text
|
||||
label: Origin
|
||||
allowedHeaders:
|
||||
label: Allowed headers
|
||||
tab: Allowed headers
|
||||
type: repeater
|
||||
span: left
|
||||
form:
|
||||
fields:
|
||||
value:
|
||||
type: text
|
||||
label: Header
|
||||
allowedMethods:
|
||||
label: Allowed methods
|
||||
tab: Allowed methods
|
||||
type: repeater
|
||||
span: left
|
||||
form:
|
||||
fields:
|
||||
value:
|
||||
type: text
|
||||
label: Method
|
||||
exposedHeaders:
|
||||
label: Exposed headers
|
||||
tab: Exposed headers
|
||||
type: repeater
|
||||
span: left
|
||||
form:
|
||||
fields:
|
||||
value:
|
||||
type: text
|
||||
label: Header
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
plugin:
|
||||
name: 'offline.cors::lang.plugin.name'
|
||||
description: 'offline.cors::lang.plugin.description'
|
||||
author: 'OFFLINE GmbH'
|
||||
icon: oc-icon-code
|
||||
homepage: ''
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
1.0.1:
|
||||
- Initial release.
|
||||
1.0.2:
|
||||
- Fixed backend settings label (thanks to LukeTowers)
|
||||
1.0.3:
|
||||
- Added support for filesystem configuration file / Added plugin to Packagist (https://packagist.org/packages/offline/oc-cors-plugin)
|
||||
1.0.4:
|
||||
- Fixed minor bug when running the plugin without custom settings
|
||||
1.0.5:
|
||||
- "Return proper 204 response code for preflight requests (thanks to @adrian-marinescu-ch on GitHub)"
|
||||
1.0.6:
|
||||
- "Dummy release to sync with Packagist version"
|
||||
1.0.7:
|
||||
- "Optimized compatibility with October v2"
|
||||
Loading…
Reference in New Issue