Skip to content
This repository has been archived by the owner on Feb 16, 2023. It is now read-only.

Commit

Permalink
Fork asm89/stock-cors
Browse files Browse the repository at this point in the history
  • Loading branch information
barryvdh committed Feb 11, 2016
1 parent 8cdcbed commit e88c20f
Show file tree
Hide file tree
Showing 13 changed files with 716 additions and 8 deletions.
28 changes: 28 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
language: php

php:
- 5.5.9
- 5.5
- 5.6
- 7.0
- hhvm

env:
global:
- setup=basic

matrix:
include:
- php: 5.5.9
env: setup=lowest
- php: 5.5.9
env: setup=stable

sudo: false

install:
- if [[ $setup = 'basic' ]]; then travis_retry composer install --no-interaction --prefer-source; fi
- if [[ $setup = 'stable' ]]; then travis_retry composer update --prefer-source --no-interaction --prefer-stable; fi
- if [[ $setup = 'lowest' ]]; then travis_retry composer update --prefer-source --no-interaction --prefer-lowest --prefer-stable; fi

script: vendor/bin/phpunit
5 changes: 3 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Copyright (c) 2013 Barryvdh
Based on NelmioCorsBundle by Nelmio / Jordi Boggiano: https://github.com/nelmio/NelmioCorsBundle
Copyright (c) 2013-2016 Barry vd. Heuvel

Copyright for portions of this project are held by [asm89 (Alexander)] as part of project asm89/stack-cors.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"require": {
"php": ">=5.5.9",
"illuminate/support": "5.1.x|5.2.x",
"asm89/stack-cors": "0.2.x|dev-master"
"symfony/http-foundation": "~2.7|~3.0",
"symfony/http-kernel": "~2.7|~3.0"
},
"autoload": {
"psr-4": {
Expand All @@ -23,5 +24,8 @@
"branch-alias": {
"dev-master": "0.8-dev"
}
},
"require-dev": {
"phpunit/phpunit": "^5.2"
}
}
16 changes: 16 additions & 0 deletions phpunit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

/*
|--------------------------------------------------------------------------
| Register The Composer Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/

require __DIR__.'/vendor/autoload.php';

25 changes: 25 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="phpunit.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
syntaxCheck="true"
verbose="true"
>
<testsuites>
<testsuite name="Cors Test Suite">
<directory>./tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory>./src</directory>
</whitelist>
</filter>
</phpunit>
2 changes: 1 addition & 1 deletion src/HandleCors.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php namespace Barryvdh\Cors;

use Closure;
use Asm89\Stack\CorsService;
use Barryvdh\Cors\Stack\CorsService;

class HandleCors
{
Expand Down
2 changes: 1 addition & 1 deletion src/HandlePreflight.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php namespace Barryvdh\Cors;

use Closure;
use Asm89\Stack\CorsService;
use Barryvdh\Cors\Stack\CorsService;
use Illuminate\Routing\Router;
use Illuminate\Contracts\Http\Kernel;

Expand Down
2 changes: 1 addition & 1 deletion src/HandlePreflightSimple.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?php namespace Barryvdh\Cors;

use Closure;
use Asm89\Stack\CorsService;
use Barryvdh\Cors\Stack\CorsService;

class HandlePreflightSimple
{
Expand Down
2 changes: 1 addition & 1 deletion src/LumenServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace Barryvdh\Cors;

use Asm89\Stack\CorsService;
use Barryvdh\Cors\Stack\CorsService;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;

Expand Down
2 changes: 1 addition & 1 deletion src/ServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php namespace Barryvdh\Cors;

use Asm89\Stack\CorsService;
use Barryvdh\Cors\Stack\CorsService;
use Illuminate\Contracts\Http\Kernel;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider as BaseServiceProvider;
Expand Down
58 changes: 58 additions & 0 deletions src/Stack/Cors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace Barryvdh\Cors\Stack;

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* Based on asm89/stack-cors
*/
class Cors implements HttpKernelInterface
{
/**
* @var \Symfony\Component\HttpKernel\HttpKernelInterface
*/
private $app;

/**
* @var \Asm89\Stack\CorsService
*/
private $cors;

private $defaultOptions = array(
'allowedHeaders' => array(),
'allowedMethods' => array(),
'allowedOrigins' => array(),
'exposedHeaders' => false,
'maxAge' => false,
'supportsCredentials' => false,
);

public function __construct(HttpKernelInterface $app, array $options = array())
{
$this->app = $app;
$this->cors = new CorsService(array_merge($this->defaultOptions, $options));

}

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);
}
}
181 changes: 181 additions & 0 deletions src/Stack/CorsService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
<?php

namespace Barryvdh\Cors\Stack;

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
* Based on asm89/stack-cors
*/
class CorsService
{
private $options;

public function __construct(array $options = array())
{
$this->options = $this->normalizeOptions($options);
}

private function normalizeOptions(array $options = array())
{

$options += array(
'allowedOrigins' => array(),
'supportsCredentials' => false,
'allowedHeaders' => array(),
'exposedHeaders' => array(),
'allowedMethods' => array(),
'maxAge' => 0,
);

// 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');
}

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 = array();
// 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');

return in_array($origin, $this->options['allowedOrigins']);
}

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']);
}

}
Loading

0 comments on commit e88c20f

Please sign in to comment.