Skip to content
Snippets Groups Projects
Commit aa77d5ee authored by Mario Gunter Simao's avatar Mario Gunter Simao :fencer:
Browse files

Initial commit

parent b4aa7eda
No related branches found
No related tags found
1 merge request!1Deploy v0.1.0
Pipeline #3629586 passed
vendor/
*.cache
composer.lock
reports/
workflow:
rules:
- if: $CI_MERGE_REQUEST_IID
- if: $CI_COMMIT_TAG
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
variables:
PHP_IMAGE: php:7.2
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- vendor/
before_script:
# Install composer
- curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
# Install libraries needed by composer
- apt-get update && apt-get install -y git && apt-get install -y unzip
# Intall Xdebug
- pecl install xdebug-2.8.1 && docker-php-ext-enable xdebug
stages:
- build
- test
build:
stage: build
image: $PHP_IMAGE
script:
- composer install
test:
stage: test
image: $PHP_IMAGE
script:
- composer run test:ci
artifacts:
when: always
reports:
junit:
- reports/phpunit.xml
- reports/phpcs.xml
- reports/psalm.xml
# cors-middleware
# CORS Middleware
A simple [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware for handling CORS.
## Installation
Install using Composer:
```bash
composer require glance-project/cors-middleware
```
## Usage
This middleware can be used with any framework compatible with PSR-7 and PSR-15.
On the following examples, Slim will be used.
### Basic usage
On most of the cases, the middleware can be used out of the box.
```php
<?php
use Glance\CorsMiddleware\CorsMiddleware;
$app = new \Slim\App();
$corsMiddleware = CorsMiddleware::create();
$app->add($corsMiddleware);
```
It will add the following headers to your response:
```
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH
```
If the request has the method `OPTIONS` and empty response will be returned.
### Custom origins
```php
$corsMiddleware = CorsMiddleware::create()
->withAllowedOrigins(["localhost"]);
```
### Custom headers
```php
$corsMiddleware = CorsMiddleware::create()
->withAllowedHeaders(["Content-Type", "Api-Key"]);
```
### Custom methods
```php
$corsMiddleware = CorsMiddleware::create()
->withAllowedMethods(["GET", "POST"]);
```
\ No newline at end of file
{
"name": "glance-project/cors-middleware",
"description": "A simple PSR middleware to handle CORS",
"type": "library",
"license": "proprietary",
"config": {
"platform-check": false,
"sort-packages": true
},
"authors": [
{
"name": "Mario Gunter Simao",
"email": "mario.simao@cern.ch"
}
],
"scripts": {
"test": [
"@test:standard",
"@test:types",
"@test:unit"
],
"test:ci": [
"@test:ci:unit",
"@test:ci:standard",
"@test:types"
],
"test:types": "psalm --show-info=true --no-diff",
"test:standard": "phpcs src/ tests/ --standard=psr12",
"test:unit": "phpunit tests --configuration phpunit.xml",
"test:ci:standard": "phpcs src/ tests/ --standard=psr12 --report=junit > reports/phpcs.xml",
"test:ci:unit": "phpunit tests --configuration phpunit.xml --coverage-text --colors=never --log-junit reports/phpunit.xml"
},
"autoload": {
"psr-4": {
"GlanceProject\\CorsMiddleware\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"GlanceProject\\CorsMiddleware\\Tests\\": "tests/"
}
},
"require": {
"php": "^7.2",
"psr/http-server-middleware": "^1.0",
"psr/http-factory": "^1.0",
"php-http/discovery": "^1.14"
},
"require-dev": {
"dq5studios/psalm-junit": "^2.0",
"nyholm/psr7": "^1.5",
"phpunit/phpunit": "^8.5",
"squizlabs/php_codesniffer": "^3.5",
"vimeo/psalm": "^4.1"
}
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
colors="true"
>
<testsuites>
<testsuite name="Unit Tests">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
</phpunit>
<?xml version="1.0"?>
<psalm
errorLevel="1"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src"/>
<ignoreFiles>
<directory name="vendor"/>
</ignoreFiles>
</projectFiles>
<plugins>
<pluginClass class="DQ5Studios\PsalmJunit\Plugin">
<filepath>reports/psalm.xml</filepath>
</pluginClass>
</plugins>
</psalm>
<?php
namespace GlanceProject\CorsMiddleware;
use Http\Discovery\Psr17FactoryDiscovery;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CorsMiddleware implements MiddlewareInterface
{
private $responseFactory;
private $allowedOrigins;
private $allowedHeaders;
private $allowedMethods;
private $bypassMethods;
/**
* @param ResponseFactoryInterface $responseFactory
* @param string[] $allowedOrigins
* @param string[] $allowedHeaders
* @param string[] $allowedMethods
* @param string[] $bypassMethods
*/
private function __construct(
ResponseFactoryInterface $responseFactory,
array $allowedOrigins,
array $allowedHeaders,
array $allowedMethods,
array $bypassMethods
) {
$this->responseFactory = $responseFactory;
$this->allowedOrigins = $allowedOrigins;
$this->allowedHeaders = $allowedHeaders;
$this->allowedMethods = $allowedMethods;
$this->bypassMethods = $bypassMethods;
}
public static function create(): self
{
return new self(
Psr17FactoryDiscovery::findResponseFactory(),
["*"],
["Content-Type", "Authorization"],
["GET", "POST", "PUT", "DELETE", "PATCH"],
["OPTIONS"]
);
}
/**
* Replace allowed origins
*
* Default value: "*"
*
* @param string[] $allowedOrigins
*
* @return self
*/
public function withAllowedOrigins(array $allowedOrigins): self
{
return new self(
$this->responseFactory,
$allowedOrigins,
$this->allowedHeaders,
$this->allowedMethods,
$this->bypassMethods
);
}
/**
* Replace allowed headers
*
* Default value: "Content-Type, Authorization"
*
* @param string[] $allowedHeaders
*
* @return self
*/
public function withAllowedHeaders(array $allowedHeaders): self
{
return new self(
$this->responseFactory,
$this->allowedOrigins,
$allowedHeaders,
$this->allowedMethods,
$this->bypassMethods
);
}
/**
* Replace allowed methods
*
* Default value: "GET, POST, PUT, DELTE, PATCH"
*
* @param string[] $allowedMethods
*
* @return self
*/
public function withAllowedMethods(array $allowedMethods): self
{
return new self(
$this->responseFactory,
$this->allowedOrigins,
$this->allowedHeaders,
$allowedMethods,
$this->bypassMethods
);
}
/**
* Replace bypass methods.
*
* Default value: "OPTIONS"
*
* @param string[] $bypassMethods
*
* @return self
*/
public function withBypassMethods(array $bypassMethods): self
{
return new self(
$this->responseFactory,
$this->allowedOrigins,
$this->allowedHeaders,
$this->allowedMethods,
$bypassMethods
);
}
public function process(Request $request, RequestHandlerInterface $handler): Response
{
$response = $this->responseFactory->createResponse();
$bypass = in_array($request->getMethod(), $this->bypassMethods);
if (!$bypass) {
$response = $handler->handle($request);
}
return $response
->withHeader("Access-Control-Allow-Origin", implode(", ", $this->allowedOrigins))
->withHeader("Access-Control-Allow-Headers", implode(", ", $this->allowedHeaders))
->withHeader("Access-Control-Allow-Methods", implode(", ", $this->allowedMethods));
}
}
<?php
namespace GlanceProject\CorsMiddleware\Tests\Unit;
use GlanceProject\CorsMiddleware\CorsMiddleware;
use Nyholm\Psr7\Response;
use Nyholm\Psr7\ServerRequest;
use PHPUnit\Framework\TestCase;
use Psr\Http\Server\RequestHandlerInterface;
class CorsMiddlewareTest extends TestCase
{
public function testMiddleware(): void
{
$request = new ServerRequest("GET", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response());
$middleware = CorsMiddleware::create();
$response = $middleware->process($request, $handler);
$allowedOrigins = $response->getHeader("Access-Control-Allow-Origin");
$allowedHeaders = $response->getHeader("Access-Control-Allow-Headers");
$allowedMethods = $response->getHeader("Access-Control-Allow-Methods");
$this->assertSame([ "*" ], $allowedOrigins);
$this->assertSame([ "Content-Type, Authorization" ], $allowedHeaders);
$this->assertSame([ "GET, POST, PUT, DELETE, PATCH" ], $allowedMethods);
}
public function testOptionsRequest(): void
{
$request = new ServerRequest("OPTIONS", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response(400));
$middleware = CorsMiddleware::create();
$response = $middleware->process($request, $handler);
$allowedOrigins = $response->getHeader("Access-Control-Allow-Origin");
$allowedHeaders = $response->getHeader("Access-Control-Allow-Headers");
$allowedMethods = $response->getHeader("Access-Control-Allow-Methods");
$this->assertSame(200, $response->getStatusCode());
$this->assertSame([ "*" ], $allowedOrigins);
$this->assertSame([ "Content-Type, Authorization" ], $allowedHeaders);
$this->assertSame([ "GET, POST, PUT, DELETE, PATCH" ], $allowedMethods);
}
public function testCustomAllowedOrigins(): void
{
$request = new ServerRequest("GET", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response(200));
$customOrigins = [ "http://localhost:8080", "http://glance.cern.ch/client" ];
$middleware = CorsMiddleware::create()
->withAllowedOrigins($customOrigins);
$response = $middleware->process($request, $handler);
$allowedOrigins = $response->getHeader("Access-Control-Allow-Origin");
$this->assertSame([ "http://localhost:8080, http://glance.cern.ch/client" ], $allowedOrigins);
}
public function testCustomAllowedHeaders(): void
{
$request = new ServerRequest("GET", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response(200));
$customHeaders = [ "Content-Type", "Api-Key" ];
$middleware = CorsMiddleware::create()
->withAllowedHeaders($customHeaders);
$response = $middleware->process($request, $handler);
$allowedHeaders = $response->getHeader("Access-Control-Allow-Headers");
$this->assertSame([ "Content-Type, Api-Key" ], $allowedHeaders);
}
public function testCustomAllowedMethods(): void
{
$request = new ServerRequest("GET", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response(200));
$customMethods = [ "GET", "POST" ];
$middleware = CorsMiddleware::create()
->withAllowedMethods($customMethods);
$response = $middleware->process($request, $handler);
$allowedMethods = $response->getHeader("Access-Control-Allow-Methods");
$this->assertSame([ "GET, POST" ], $allowedMethods);
}
public function testCustomBypassMethods(): void
{
$request = new ServerRequest("MYOPTIONS", "http://glance.cern.ch/api/members");
$handler = $this->createMock(RequestHandlerInterface::class);
$handler->method("handle")->willReturn(new Response(400));
$customMethods = [ "MYOPTIONS" ];
$middleware = CorsMiddleware::create()
->withBypassMethods($customMethods);
$response = $middleware->process($request, $handler);
$this->assertSame(200, $response->getStatusCode());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment