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

Commit 3b49bcd

Browse files
authored
Merge pull request #469 from beyondcode/refactor/cors
[2.x] Per-app CORS
2 parents 560bc6e + 21d37d9 commit 3b49bcd

File tree

11 files changed

+104
-86
lines changed

11 files changed

+104
-86
lines changed

config/websockets.php

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -84,23 +84,12 @@
8484
'capacity' => null,
8585
'enable_client_messages' => false,
8686
'enable_statistics' => true,
87+
'allowed_origins' => [
88+
//
89+
],
8790
],
8891
],
8992

90-
/*
91-
|--------------------------------------------------------------------------
92-
| Allowed Origins
93-
|--------------------------------------------------------------------------
94-
|
95-
| If not empty, you can whitelist certain origins that will be allowed
96-
| to connect to the websocket server.
97-
|
98-
*/
99-
100-
'allowed_origins' => [
101-
//
102-
],
103-
10493
/*
10594
|--------------------------------------------------------------------------
10695
| Maximum Request Size

docs/basic-usage/pusher.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ You may add additional apps in your `config/websockets.php` file.
7474
'capacity' => null,
7575
'enable_client_messages' => false,
7676
'enable_statistics' => true,
77+
'allowed_origins' => [],
7778
],
7879
],
7980
```

src/Apps/App.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ class App
3333
/** @var bool */
3434
public $statisticsEnabled = true;
3535

36+
/** @var array */
37+
public $allowedOrigins = [];
38+
3639
public static function findById($appId)
3740
{
3841
return app(AppManager::class)->findById($appId);
@@ -106,4 +109,11 @@ public function enableStatistics(bool $enabled = true)
106109

107110
return $this;
108111
}
112+
113+
public function setAllowedOrigins(array $allowedOrigins)
114+
{
115+
$this->allowedOrigins = $allowedOrigins;
116+
117+
return $this;
118+
}
109119
}

src/Apps/ConfigAppManager.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ protected function instantiate(?array $appAttributes): ?App
7878
$app
7979
->enableClientMessages($appAttributes['enable_client_messages'])
8080
->enableStatistics($appAttributes['enable_statistics'])
81-
->setCapacity($appAttributes['capacity'] ?? null);
81+
->setCapacity($appAttributes['capacity'] ?? null)
82+
->setAllowedOrigins($appAttributes['allowed_origins'] ?? []);
8283

8384
return $app;
8485
}

src/Server/OriginCheck.php

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/Server/WebSocketServerFactory.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,9 @@ public function createServer(): IoServer
7979
$socket = new SecureServer($socket, $this->loop, config('websockets.ssl'));
8080
}
8181

82-
$urlMatcher = new UrlMatcher($this->routes, new RequestContext);
83-
84-
$router = new Router($urlMatcher);
85-
86-
$app = new OriginCheck($router, config('websockets.allowed_origins', []));
82+
$app = new Router(
83+
new UrlMatcher($this->routes, new RequestContext)
84+
);
8785

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace BeyondCode\LaravelWebSockets\WebSockets\Exceptions;
4+
5+
class OriginNotAllowed extends WebSocketException
6+
{
7+
public function __construct(string $appKey)
8+
{
9+
$this->message = "The origin is not allowed for `{$appKey}`.";
10+
$this->code = 4009;
11+
}
12+
}

src/WebSockets/WebSocketHandler.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use BeyondCode\LaravelWebSockets\QueryParameters;
99
use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager;
1010
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\ConnectionsOverCapacity;
11+
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\OriginNotAllowed;
1112
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\UnknownAppKey;
1213
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\WebSocketException;
1314
use BeyondCode\LaravelWebSockets\WebSockets\Messages\PusherMessageFactory;
@@ -30,6 +31,7 @@ public function onOpen(ConnectionInterface $connection)
3031
{
3132
$this
3233
->verifyAppKey($connection)
34+
->verifyOrigin($connection)
3335
->limitConcurrentConnections($connection)
3436
->generateSocketId($connection)
3537
->establishConnection($connection);
@@ -77,6 +79,23 @@ protected function verifyAppKey(ConnectionInterface $connection)
7779
return $this;
7880
}
7981

82+
protected function verifyOrigin(ConnectionInterface $connection)
83+
{
84+
if (! $connection->app->allowedOrigins) {
85+
return $this;
86+
}
87+
88+
$header = (string) ($connection->httpRequest->getHeader('Origin')[0] ?? null);
89+
90+
$origin = parse_url($header, PHP_URL_HOST) ?: $header;
91+
92+
if (! $header || ! in_array($origin, $connection->app->allowedOrigins)) {
93+
throw new OriginNotAllowed($connection->app->key);
94+
}
95+
96+
return $this;
97+
}
98+
8099
protected function limitConcurrentConnections(ConnectionInterface $connection)
81100
{
82101
if (! is_null($capacity = $connection->app->capacity)) {

tests/ClientProviders/ConfigAppManagerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function it_can_get_apps_from_the_config_file()
2222
{
2323
$apps = $this->appManager->all();
2424

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

2727
/** @var $app */
2828
$app = $apps[0];

tests/ConnectionTest.php

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use BeyondCode\LaravelWebSockets\Apps\App;
66
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
77
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\ConnectionsOverCapacity;
8+
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\OriginNotAllowed;
89
use BeyondCode\LaravelWebSockets\WebSockets\Exceptions\UnknownAppKey;
910

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

17-
$this->pusherServer->onOpen($this->getWebSocketConnection('/?appKey=test'));
18+
$this->pusherServer->onOpen($this->getWebSocketConnection('test'));
1819
}
1920

2021
/** @test */
@@ -65,4 +66,38 @@ public function ping_returns_pong()
6566

6667
$connection->assertSentEvent('pusher:pong');
6768
}
69+
70+
/** @test */
71+
public function origin_validation_should_fail_for_no_origin()
72+
{
73+
$this->expectException(OriginNotAllowed::class);
74+
75+
$connection = $this->getWebSocketConnection('TestOrigin');
76+
77+
$this->pusherServer->onOpen($connection);
78+
79+
$connection->assertSentEvent('pusher:connection_established');
80+
}
81+
82+
/** @test */
83+
public function origin_validation_should_fail_for_wrong_origin()
84+
{
85+
$this->expectException(OriginNotAllowed::class);
86+
87+
$connection = $this->getWebSocketConnection('TestOrigin', ['Origin' => 'https://google.ro']);
88+
89+
$this->pusherServer->onOpen($connection);
90+
91+
$connection->assertSentEvent('pusher:connection_established');
92+
}
93+
94+
/** @test */
95+
public function origin_validation_should_pass_for_the_right_origin()
96+
{
97+
$connection = $this->getWebSocketConnection('TestOrigin', ['Origin' => 'https://test.origin.com']);
98+
99+
$this->pusherServer->onOpen($connection);
100+
101+
$connection->assertSentEvent('pusher:connection_established');
102+
}
68103
}

tests/TestCase.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ protected function getEnvironmentSetUp($app)
7070
'capacity' => null,
7171
'enable_client_messages' => false,
7272
'enable_statistics' => true,
73+
'allowed_origins' => [],
74+
],
75+
[
76+
'name' => 'Origin Test App',
77+
'id' => '1234',
78+
'key' => 'TestOrigin',
79+
'secret' => 'TestSecret',
80+
'capacity' => null,
81+
'enable_client_messages' => false,
82+
'enable_statistics' => true,
83+
'allowed_origins' => [
84+
'test.origin.com',
85+
],
7386
],
7487
]);
7588

@@ -107,20 +120,20 @@ protected function getEnvironmentSetUp($app)
107120
}
108121
}
109122

110-
protected function getWebSocketConnection(string $url = '/?appKey=TestKey'): Connection
123+
protected function getWebSocketConnection(string $appKey = 'TestKey', array $headers = []): Connection
111124
{
112125
$connection = new Connection();
113126

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

116129
return $connection;
117130
}
118131

119-
protected function getConnectedWebSocketConnection(array $channelsToJoin = [], string $url = '/?appKey=TestKey'): Connection
132+
protected function getConnectedWebSocketConnection(array $channelsToJoin = [], string $appKey = 'TestKey', array $headers = []): Connection
120133
{
121134
$connection = new Connection();
122135

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

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

0 commit comments

Comments
 (0)