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

Commit

Permalink
Merge pull request #469 from beyondcode/refactor/cors
Browse files Browse the repository at this point in the history
[2.x] Per-app CORS
  • Loading branch information
rennokki authored Aug 18, 2020
2 parents 560bc6e + 21d37d9 commit 3b49bcd
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 86 deletions.
17 changes: 3 additions & 14 deletions config/websockets.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,12 @@
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
'allowed_origins' => [
//
],
],
],

/*
|--------------------------------------------------------------------------
| Allowed Origins
|--------------------------------------------------------------------------
|
| If not empty, you can whitelist certain origins that will be allowed
| to connect to the websocket server.
|
*/

'allowed_origins' => [
//
],

/*
|--------------------------------------------------------------------------
| Maximum Request Size
Expand Down
1 change: 1 addition & 0 deletions docs/basic-usage/pusher.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ You may add additional apps in your `config/websockets.php` file.
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
'allowed_origins' => [],
],
],
```
Expand Down
10 changes: 10 additions & 0 deletions src/Apps/App.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class App
/** @var bool */
public $statisticsEnabled = true;

/** @var array */
public $allowedOrigins = [];

public static function findById($appId)
{
return app(AppManager::class)->findById($appId);
Expand Down Expand Up @@ -106,4 +109,11 @@ public function enableStatistics(bool $enabled = true)

return $this;
}

public function setAllowedOrigins(array $allowedOrigins)
{
$this->allowedOrigins = $allowedOrigins;

return $this;
}
}
3 changes: 2 additions & 1 deletion src/Apps/ConfigAppManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ protected function instantiate(?array $appAttributes): ?App
$app
->enableClientMessages($appAttributes['enable_client_messages'])
->enableStatistics($appAttributes['enable_statistics'])
->setCapacity($appAttributes['capacity'] ?? null);
->setCapacity($appAttributes['capacity'] ?? null)
->setAllowedOrigins($appAttributes['allowed_origins'] ?? []);

return $app;
}
Expand Down
60 changes: 0 additions & 60 deletions src/Server/OriginCheck.php

This file was deleted.

8 changes: 3 additions & 5 deletions src/Server/WebSocketServerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,9 @@ public function createServer(): IoServer
$socket = new SecureServer($socket, $this->loop, config('websockets.ssl'));
}

$urlMatcher = new UrlMatcher($this->routes, new RequestContext);

$router = new Router($urlMatcher);

$app = new OriginCheck($router, config('websockets.allowed_origins', []));
$app = new Router(
new UrlMatcher($this->routes, new RequestContext)
);

$httpServer = new HttpServer($app, config('websockets.max_request_size_in_kb') * 1024);

Expand Down
12 changes: 12 additions & 0 deletions src/WebSockets/Exceptions/OriginNotAllowed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace BeyondCode\LaravelWebSockets\WebSockets\Exceptions;

class OriginNotAllowed extends WebSocketException
{
public function __construct(string $appKey)
{
$this->message = "The origin is not allowed for `{$appKey}`.";
$this->code = 4009;
}
}
19 changes: 19 additions & 0 deletions src/WebSockets/WebSocketHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use BeyondCode\LaravelWebSockets\QueryParameters;
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\ConnectionsOverCapacity;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\OriginNotAllowed;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\UnknownAppKey;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\WebSocketException;
use BeyondCode\LaravelWebSockets\WebSockets\Messages\PusherMessageFactory;
Expand All @@ -30,6 +31,7 @@ public function onOpen(ConnectionInterface $connection)
{
$this
->verifyAppKey($connection)
->verifyOrigin($connection)
->limitConcurrentConnections($connection)
->generateSocketId($connection)
->establishConnection($connection);
Expand Down Expand Up @@ -77,6 +79,23 @@ protected function verifyAppKey(ConnectionInterface $connection)
return $this;
}

protected function verifyOrigin(ConnectionInterface $connection)
{
if (! $connection->app->allowedOrigins) {
return $this;
}

$header = (string) ($connection->httpRequest->getHeader('Origin')[0] ?? null);

$origin = parse_url($header, PHP_URL_HOST) ?: $header;

if (! $header || ! in_array($origin, $connection->app->allowedOrigins)) {
throw new OriginNotAllowed($connection->app->key);
}

return $this;
}

protected function limitConcurrentConnections(ConnectionInterface $connection)
{
if (! is_null($capacity = $connection->app->capacity)) {
Expand Down
2 changes: 1 addition & 1 deletion tests/ClientProviders/ConfigAppManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public function it_can_get_apps_from_the_config_file()
{
$apps = $this->appManager->all();

$this->assertCount(1, $apps);
$this->assertCount(2, $apps);

/** @var $app */
$app = $apps[0];
Expand Down
37 changes: 36 additions & 1 deletion tests/ConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use BeyondCode\LaravelWebSockets\Apps\App;
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\ConnectionsOverCapacity;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\OriginNotAllowed;
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\UnknownAppKey;

class ConnectionTest extends TestCase
Expand All @@ -14,7 +15,7 @@ public function unknown_app_keys_can_not_connect()
{
$this->expectException(UnknownAppKey::class);

$this->pusherServer->onOpen($this->getWebSocketConnection('/?appKey=test'));
$this->pusherServer->onOpen($this->getWebSocketConnection('test'));
}

/** @test */
Expand Down Expand Up @@ -65,4 +66,38 @@ public function ping_returns_pong()

$connection->assertSentEvent('pusher:pong');
}

/** @test */
public function origin_validation_should_fail_for_no_origin()
{
$this->expectException(OriginNotAllowed::class);

$connection = $this->getWebSocketConnection('TestOrigin');

$this->pusherServer->onOpen($connection);

$connection->assertSentEvent('pusher:connection_established');
}

/** @test */
public function origin_validation_should_fail_for_wrong_origin()
{
$this->expectException(OriginNotAllowed::class);

$connection = $this->getWebSocketConnection('TestOrigin', ['Origin' => 'https://google.ro']);

$this->pusherServer->onOpen($connection);

$connection->assertSentEvent('pusher:connection_established');
}

/** @test */
public function origin_validation_should_pass_for_the_right_origin()
{
$connection = $this->getWebSocketConnection('TestOrigin', ['Origin' => 'https://test.origin.com']);

$this->pusherServer->onOpen($connection);

$connection->assertSentEvent('pusher:connection_established');
}
}
21 changes: 17 additions & 4 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,19 @@ protected function getEnvironmentSetUp($app)
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
'allowed_origins' => [],
],
[
'name' => 'Origin Test App',
'id' => '1234',
'key' => 'TestOrigin',
'secret' => 'TestSecret',
'capacity' => null,
'enable_client_messages' => false,
'enable_statistics' => true,
'allowed_origins' => [
'test.origin.com',
],
],
]);

Expand Down Expand Up @@ -107,20 +120,20 @@ protected function getEnvironmentSetUp($app)
}
}

protected function getWebSocketConnection(string $url = '/?appKey=TestKey'): Connection
protected function getWebSocketConnection(string $appKey = 'TestKey', array $headers = []): Connection
{
$connection = new Connection();

$connection->httpRequest = new Request('GET', $url);
$connection->httpRequest = new Request('GET', "/?appKey={$appKey}", $headers);

return $connection;
}

protected function getConnectedWebSocketConnection(array $channelsToJoin = [], string $url = '/?appKey=TestKey'): Connection
protected function getConnectedWebSocketConnection(array $channelsToJoin = [], string $appKey = 'TestKey', array $headers = []): Connection
{
$connection = new Connection();

$connection->httpRequest = new Request('GET', $url);
$connection->httpRequest = new Request('GET', "/?appKey={$appKey}", $headers);

$this->pusherServer->onOpen($connection);

Expand Down

0 comments on commit 3b49bcd

Please sign in to comment.