diff --git a/src/HttpApi/Controllers/Controller.php b/src/HttpApi/Controllers/Controller.php index f88c49c640..43f2ebd01d 100644 --- a/src/HttpApi/Controllers/Controller.php +++ b/src/HttpApi/Controllers/Controller.php @@ -3,6 +3,7 @@ namespace BeyondCode\LaravelWebSockets\HttpApi\Controllers; use Exception; +use Pusher\Pusher; use Illuminate\Http\Request; use GuzzleHttp\Psr7\Response; use Ratchet\ConnectionInterface; @@ -84,18 +85,21 @@ public function ensureValidAppId(string $appId) protected function ensureValidSignature(Request $request) { - $signature = - "{$request->getMethod()}\n/{$request->path()}\n". - "auth_key={$request->get('auth_key')}". - "&auth_timestamp={$request->get('auth_timestamp')}". - "&auth_version={$request->get('auth_version')}"; + /* + * The `auth_signature` & `body_md5` parameters are not included when calculating the `auth_signature` value. + * + * The `appId`, `appKey` & `channelName` parameters are actually route paramaters and are never supplied by the client. + */ + $params = array_except($request->query(), ['auth_signature', 'body_md5', 'appId', 'appKey', 'channelName']); if ($request->getContent() !== '') { - $bodyMd5 = md5($request->getContent()); - - $signature .= "&body_md5={$bodyMd5}"; + $params['body_md5'] = md5($request->getContent()); } + ksort($params); + + $signature = "{$request->getMethod()}\n/{$request->path()}\n".Pusher::array_implode('=', '&', $params); + $authSignature = hash_hmac('sha256', $signature, App::findById($request->get('appId'))->secret); if ($authSignature !== $request->get('auth_signature')) { diff --git a/tests/HttpApi/FetchChannelTest.php b/tests/HttpApi/FetchChannelTest.php index cabc459204..50fcaf1316 100644 --- a/tests/HttpApi/FetchChannelTest.php +++ b/tests/HttpApi/FetchChannelTest.php @@ -2,6 +2,7 @@ namespace BeyondCode\LaravelWebSockets\Tests\HttpApi; +use Pusher\Pusher; use GuzzleHttp\Psr7\Request; use Illuminate\Http\JsonResponse; use BeyondCode\LaravelWebSockets\Tests\TestCase; @@ -19,21 +20,15 @@ public function invalid_signatures_can_not_access_the_api() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; + $requestPath = '/apps/1234/channel/my-channel'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'my-channel', + ]; - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath); - $signature = - "GET\n/apps/1234/channels\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; - - $auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret'); - - $request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelController::class); @@ -48,21 +43,15 @@ public function it_returns_the_channel_information() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; - - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $requestPath = '/apps/1234/channel/my-channel'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'my-channel', + ]; - $signature = - "GET\n/apps/1234/channel/my-channel\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); - - $request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelController::class); @@ -87,21 +76,15 @@ public function it_returns_404_for_invalid_channels() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; - - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); - - $signature = - "GET\n/apps/1234/channel/my-channel\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $requestPath = '/apps/1234/channel/invalid-channel'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'invalid-channel', + ]; - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=invalid-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelController::class); diff --git a/tests/HttpApi/FetchChannelsTest.php b/tests/HttpApi/FetchChannelsTest.php index 94a6eb83b7..809250709a 100644 --- a/tests/HttpApi/FetchChannelsTest.php +++ b/tests/HttpApi/FetchChannelsTest.php @@ -2,6 +2,7 @@ namespace BeyondCode\LaravelWebSockets\Tests\HttpApi; +use Pusher\Pusher; use GuzzleHttp\Psr7\Request; use Illuminate\Http\JsonResponse; use BeyondCode\LaravelWebSockets\Tests\TestCase; @@ -19,21 +20,14 @@ public function invalid_signatures_can_not_access_the_api() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; + $requestPath = '/apps/1234/channels'; + $routeParams = [ + 'appId' => '1234', + ]; - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath); - $signature = - "GET\n/apps/1234/channels\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; - - $auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret'); - - $request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelsController::class); @@ -49,21 +43,14 @@ public function it_returns_the_channel_information() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; - - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); - - $signature = - "GET\n/apps/1234/channels\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $requestPath = '/apps/1234/channels'; + $routeParams = [ + 'appId' => '1234', + ]; - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelsController::class); @@ -82,25 +69,58 @@ public function it_returns_the_channel_information() } /** @test */ - public function it_returns_empty_object_for_no_channels_found() + public function it_returns_the_channel_information_for_prefix() { + $this->joinPresenceChannel('presence-global.1'); + $this->joinPresenceChannel('presence-global.1'); + $this->joinPresenceChannel('presence-global.2'); + $this->joinPresenceChannel('presence-notglobal.2'); + $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; + $requestPath = '/apps/1234/channels'; + $routeParams = [ + 'appId' => '1234', + ]; + + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath, [ + 'filter_by_prefix' => 'presence-global', + ]); - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); + + $controller = app(FetchChannelsController::class); + + $controller->onOpen($connection, $request); + + /** @var JsonResponse $response */ + $response = array_pop($connection->sentRawData); + + $this->assertSame([ + 'channels' => [ + 'presence-global.1' => [ + 'user_count' => 2, + ], + 'presence-global.2' => [ + 'user_count' => 1, + ], + ], + ], json_decode($response->getContent(), true)); + } + + /** @test */ + public function it_returns_empty_object_for_no_channels_found() + { + $connection = new Connection(); - $signature = - "GET\n/apps/1234/channels\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $requestPath = '/apps/1234/channels'; + $routeParams = [ + 'appId' => '1234', + ]; - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $request = new Request('GET', "/apps/1234/channels?appId=1234&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchChannelsController::class); diff --git a/tests/HttpApi/FetchUsersTest.php b/tests/HttpApi/FetchUsersTest.php index 543aeb0b5c..35a97444a3 100644 --- a/tests/HttpApi/FetchUsersTest.php +++ b/tests/HttpApi/FetchUsersTest.php @@ -2,6 +2,7 @@ namespace BeyondCode\LaravelWebSockets\Tests\HttpApi; +use Pusher\Pusher; use GuzzleHttp\Psr7\Request; use BeyondCode\LaravelWebSockets\Tests\TestCase; use BeyondCode\LaravelWebSockets\Tests\Mocks\Connection; @@ -18,21 +19,15 @@ public function invalid_signatures_can_not_access_the_api() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; + $requestPath = '/apps/1234/channel/my-channel'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'my-channel', + ]; - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $queryString = Pusher::build_auth_query_string('TestKey', 'InvalidSecret', 'GET', $requestPath); - $signature = - "GET\n/apps/1234/channels\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; - - $auth_signature = hash_hmac('sha256', $signature, 'InvalidSecret'); - - $request = new Request('GET', "/apps/1234/channel/my-channel?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchUsersController::class); @@ -49,21 +44,15 @@ public function it_only_returns_data_for_presence_channels() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; - - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); - - $signature = - "GET\n/apps/1234/channel/my-channel/users\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $requestPath = '/apps/1234/channel/my-channel/users'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'my-channel', + ]; - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=my-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchUsersController::class); @@ -80,21 +69,15 @@ public function it_returns_404_for_invalid_channels() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; + $requestPath = '/apps/1234/channel/invalid-channel/users'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'invalid-channel', + ]; - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $signature = - "GET\n/apps/1234/channel/my-channel/users\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; - - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); - - $request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=invalid-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchUsersController::class); @@ -108,21 +91,15 @@ public function it_returns_connected_user_information() $connection = new Connection(); - $auth_key = 'TestKey'; - $auth_timestamp = time(); - $auth_version = '1.0'; - - $queryParameters = http_build_query(compact('auth_key', 'auth_timestamp', 'auth_version')); - - $signature = - "GET\n/apps/1234/channel/my-channel/users\n". - "auth_key={$auth_key}". - "&auth_timestamp={$auth_timestamp}". - "&auth_version={$auth_version}"; + $requestPath = '/apps/1234/channel/presence-channel/users'; + $routeParams = [ + 'appId' => '1234', + 'channelName' => 'presence-channel', + ]; - $auth_signature = hash_hmac('sha256', $signature, 'TestSecret'); + $queryString = Pusher::build_auth_query_string('TestKey', 'TestSecret', 'GET', $requestPath); - $request = new Request('GET', "/apps/1234/channel/my-channel/users?appId=1234&channelName=presence-channel&auth_signature={$auth_signature}&{$queryParameters}"); + $request = new Request('GET', "{$requestPath}?{$queryString}&".http_build_query($routeParams)); $controller = app(FetchUsersController::class);