Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle private network requests #13

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ This package can be used as a library. You can use it in your framework using:

- [Stack middleware](http://stackphp.com/): https://github.com/asm89/stack-cors
- [Laravel](https://laravel.com): https://github.com/fruitcake/laravel-cors


### Options

Expand All @@ -38,6 +38,7 @@ This package can be used as a library. You can use it in your framework using:
| exposedHeaders | Sets the Access-Control-Expose-Headers response header. | `[]` |
| maxAge | Sets the Access-Control-Max-Age response header. | `0` |
| supportsCredentials | Sets the Access-Control-Allow-Credentials header. | `false` |
| allowPrivateNetwork | Sets the Access-Control-Allow-Private-Network header. | `false` |

The _allowedMethods_ and _allowedHeaders_ options are case-insensitive.

Expand All @@ -62,6 +63,7 @@ $cors = new CorsService([
'exposedHeaders' => ['Content-Encoding'],
'maxAge' => 0,
'supportsCredentials' => false,
'allowPrivateNetwork' => false,
]);

$cors->addActualRequestHeaders(Response $response, $origin);
Expand Down
14 changes: 14 additions & 0 deletions src/CorsService.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
* 'allowedOrigins'?: string[],
* 'allowedOriginsPatterns'?: string[],
* 'supportsCredentials'?: bool,
* 'allowPrivateNetwork'? : bool,
* 'allowedHeaders'?: string[],
* 'allowedMethods'?: string[],
* 'exposedHeaders'?: string[]|false,
* 'maxAge'?: int|bool|null,
* 'allowed_origins'?: string[],
* 'allowed_origins_patterns'?: string[],
* 'supports_credentials'?: bool,
* 'allow_private_network'? : bool,
* 'allowed_headers'?: string[],
* 'allowed_methods'?: string[],
* 'exposed_headers'?: string[]|false,
Expand All @@ -48,6 +50,7 @@ class CorsService
/** @var string[] */
private array $exposedHeaders = [];
private bool $supportsCredentials = false;
private bool $allowPrivateNetwork = false;
private ?int $maxAge = 0;

private bool $allowAllOrigins = false;
Expand Down Expand Up @@ -76,6 +79,8 @@ public function setOptions(array $options): void
$this->allowedHeaders = $options['allowedHeaders'] ?? $options['allowed_headers'] ?? $this->allowedHeaders;
$this->supportsCredentials =
$options['supportsCredentials'] ?? $options['supports_credentials'] ?? $this->supportsCredentials;
$this->allowPrivateNetwork =
$options['allowPrivateNetwork'] ?? $options['allow_private_network'] ?? $this->allowPrivateNetwork;

$maxAge = $this->maxAge;
if (array_key_exists('maxAge', $options)) {
Expand Down Expand Up @@ -162,6 +167,8 @@ public function addPreflightRequestHeaders(Response $response, Request $request)
$this->configureAllowedHeaders($response, $request);

$this->configureMaxAge($response, $request);

$this->configurePrivateNetwork($response, $request);
}

return $response;
Expand Down Expand Up @@ -258,6 +265,13 @@ private function configureAllowCredentials(Response $response, Request $request)
}
}

private function configurePrivateNetwork(Response $response, Request $request): void
{
if ($request->headers->get('Access-Control-Request-Private-Network') === 'true' && $this->allowPrivateNetwork) {
$response->headers->set('Access-Control-Allow-Private-Network', 'true');
}
}

private function configureExposedHeaders(Response $response, Request $request): void
{
if ($this->exposedHeaders) {
Expand Down
14 changes: 14 additions & 0 deletions tests/CorsServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* 'allowedOrigins': string[],
* 'allowedOriginsPatterns': string[],
* 'supportsCredentials': bool,
* 'allowPrivateNetwork' : bool,
* 'allowedHeaders': string[],
* 'allowedMethods': string[],
* 'exposedHeaders': string[],
Expand All @@ -44,6 +45,7 @@ public function itCanHaveOptions(): void
'allowedMethods' => ['PUT'],
'maxAge' => 684,
'supportsCredentials' => true,
'allowPrivateNetwork' => true,
'exposedHeaders' => ['x-custom-2'],
];

Expand All @@ -59,6 +61,7 @@ public function itCanHaveOptions(): void
$this->assertEquals($options['allowedMethods'], $normalized['allowedMethods']);
$this->assertEquals($options['maxAge'], $normalized['maxAge']);
$this->assertEquals($options['supportsCredentials'], $normalized['supportsCredentials']);
$this->assertEquals($options['allowPrivateNetwork'], $normalized['allowPrivateNetwork']);
$this->assertEquals($options['exposedHeaders'], $normalized['exposedHeaders']);
}

Expand All @@ -80,6 +83,7 @@ public function itCanSetOptions(): void
'allowedMethods' => ['PUT'],
'maxAge' => 684,
'supportsCredentials' => true,
'allowPrivateNetwork' => true,
'exposedHeaders' => ['x-custom-2'],
];

Expand All @@ -93,6 +97,7 @@ public function itCanSetOptions(): void
$this->assertEquals($options['allowedMethods'], $normalized['allowedMethods']);
$this->assertEquals($options['maxAge'], $normalized['maxAge']);
$this->assertEquals($options['supportsCredentials'], $normalized['supportsCredentials']);
$this->assertEquals($options['allowPrivateNetwork'], $normalized['allowPrivateNetwork']);
$this->assertEquals($options['exposedHeaders'], $normalized['exposedHeaders']);
}

Expand All @@ -115,6 +120,7 @@ public function itCanOverwriteSetOptions(): void
'allowedMethods' => ['PUT'],
'maxAge' => 684,
'supportsCredentials' => true,
'allowPrivateNetwork' => true,
'exposedHeaders' => ['x-custom-2'],
];

Expand All @@ -128,6 +134,7 @@ public function itCanOverwriteSetOptions(): void
$this->assertEquals($options['allowedMethods'], $normalized['allowedMethods']);
$this->assertEquals($options['maxAge'], $normalized['maxAge']);
$this->assertEquals($options['supportsCredentials'], $normalized['supportsCredentials']);
$this->assertEquals($options['allowPrivateNetwork'], $normalized['allowPrivateNetwork']);
$this->assertEquals($options['exposedHeaders'], $normalized['exposedHeaders']);
}

Expand All @@ -148,6 +155,7 @@ public function itCanHaveNoOptions(): void
$this->assertEquals([], $normalized['exposedHeaders']);
$this->assertEquals(0, $normalized['maxAge']);
$this->assertEquals(false, $normalized['supportsCredentials']);
$this->assertEquals(false, $normalized['allowPrivateNetwork']);
}

/**
Expand All @@ -167,6 +175,7 @@ public function itCanHaveEmptyOptions(): void
$this->assertEquals([], $normalized['exposedHeaders']);
$this->assertEquals(0, $normalized['maxAge']);
$this->assertEquals(false, $normalized['supportsCredentials']);
$this->assertEquals(false, $normalized['allowPrivateNetwork']);
}

/**
Expand Down Expand Up @@ -275,6 +284,7 @@ public function itNormalizesUnderscoreOptions(): void
'allowed_methods' => ['PUT'],
'max_age' => 684,
'supports_credentials' => true,
'allow_private_network' => true,
'exposed_headers' => ['x-custom-2'],
];

Expand All @@ -294,6 +304,10 @@ public function itNormalizesUnderscoreOptions(): void
$options['supports_credentials'],
$this->getOptionsFromService($service)['supportsCredentials']
);
$this->assertEquals(
$options['allow_private_network'],
$this->getOptionsFromService($service)['allowPrivateNetwork']
);
}

/**
Expand Down
38 changes: 38 additions & 0 deletions tests/CorsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,44 @@ public function itDoesntSetAccessControlAllowOriginWithoutOrigin(): void
$this->assertFalse($response->headers->has('Access-Control-Allow-Origin'));
}

/**
* @test
*/
public function itSetsAllowPrivateNetworkWhenAllowed(): void
{
$app = $this->createStackedApp(array('allowPrivateNetwork' => true));
$request = $this->createValidPreflightRequest();
$request->headers->set('Access-Control-Request-Private-Network', 'true');

$response = $app->handle($request);
$this->assertTrue($response->headers->has('Access-Control-Allow-Private-Network'));
}

/**
* @test
*/
public function itDoesntSetAllowPrivateNetworkWhenNotAllowed(): void
{
$app = $this->createStackedApp(array('allowPrivateNetwork' => false));
$request = $this->createValidPreflightRequest();
$request->headers->set('Access-Control-Request-Private-Network', 'true');

$response = $app->handle($request);
$this->assertFalse($response->headers->has('Access-Control-Allow-Private-Network'));
}

/**
* @test
*/
public function itDoesntSetAllowPrivateNetworkWhenNotRequested(): void
{
$app = $this->createStackedApp(array('allowPrivateNetwork' => true));
$request = $this->createValidPreflightRequest();

$response = $app->handle($request);
$this->assertFalse($response->headers->has('Access-Control-Allow-Private-Network'));
}

private function createValidActualRequest(): Request
{
$request = new Request();
Expand Down