This commit is contained in:
Marcel Pociot 2018-11-22 21:36:25 +01:00
parent 07046c1fdd
commit abeef421b5
5 changed files with 91 additions and 12 deletions

View File

@ -29,6 +29,7 @@
"illuminate/http": "5.6.*|5.7.*", "illuminate/http": "5.6.*|5.7.*",
"illuminate/routing": "5.6.*|5.7.*", "illuminate/routing": "5.6.*|5.7.*",
"illuminate/support": "5.6.*|5.7.*", "illuminate/support": "5.6.*|5.7.*",
"symfony/http-kernel": "~4.0",
"symfony/psr-http-message-bridge": "^1.1" "symfony/psr-http-message-bridge": "^1.1"
}, },
"require-dev": { "require-dev": {

View File

@ -2,17 +2,21 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use Exception;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use GuzzleHttp\Psr7 as gPsr;
use GuzzleHttp\Psr7\Response;
use Ratchet\ConnectionInterface; use Ratchet\ConnectionInterface;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use GuzzleHttp\Psr7\ServerRequest; use GuzzleHttp\Psr7\ServerRequest;
use Ratchet\Http\HttpServerInterface; use Ratchet\Http\HttpServerInterface;
use Psr\Http\Message\RequestInterface; use Psr\Http\Message\RequestInterface;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
abstract class EchoController implements HttpServerInterface abstract class EchoController implements HttpServerInterface
{ {
public function onOpen(ConnectionInterface $conn, RequestInterface $request = null) public function onOpen(ConnectionInterface $connection, RequestInterface $request = null)
{ {
$queryParameters = []; $queryParameters = [];
parse_str($request->getUri()->getQuery(), $queryParameters); parse_str($request->getUri()->getQuery(), $queryParameters);
@ -25,22 +29,42 @@ abstract class EchoController implements HttpServerInterface
$request->getProtocolVersion() $request->getProtocolVersion()
))->withQueryParams($queryParameters); ))->withQueryParams($queryParameters);
$response = $this(Request::createFromBase((new HttpFoundationFactory)->createRequest($serverRequest)));
$conn->send(JsonResponse::create($response)->send()); $laravelRequest = Request::createFromBase((new HttpFoundationFactory)->createRequest($serverRequest));
$conn->close();
// Always verify the app id
$this->verifyAppId($laravelRequest->appId);
$response = $this($laravelRequest);
$connection->send(JsonResponse::create($response)->send());
$connection->close();
} }
function onMessage(ConnectionInterface $from, $msg) function onMessage(ConnectionInterface $from, $msg)
{ {
} }
function onClose(ConnectionInterface $conn) function onClose(ConnectionInterface $connection)
{ {
} }
function onError(ConnectionInterface $conn, \Exception $e) function onError(ConnectionInterface $connection, Exception $exception)
{ {
if ($exception instanceof HttpException)
{
$response = new Response($exception->getStatusCode(), $exception->getHeaders(), $exception->getMessage());
$connection->send(gPsr\str($response));
$connection->close();
}
}
public function verifyAppId(string $appId)
{
if ($appId !== config('broadcasting.connections.pusher.app_id')) {
throw new HttpException(401, 'Invalid App ID provided.');
}
} }
abstract public function __invoke(Request $request); abstract public function __invoke(Request $request);

View File

@ -0,0 +1,54 @@
<?php
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignatureException;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
class FetchChannel extends EchoController
{
/** @var \BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager */
protected $channelManager;
public function __construct(ChannelManager $channelManager)
{
$this->channelManager = $channelManager;
}
public function __invoke(Request $request)
{
$this->verifySignature($request);
foreach ($request->json()->get('channels', []) as $channelId) {
$channel = $this->channelManager->find($request->appId, $channelId);
optional($channel)->broadcast([
'channel' => $channelId,
'event' => $request->json()->get('name'),
'data' => $request->json()->get('data'),
]);
}
return $request->json()->all();
}
protected function verifySignature(Request $request)
{
$bodyMd5 = md5($request->getContent());
$signature =
"POST\n/apps/{$request->appId}/events\n".
"auth_key={$request->auth_key}".
"&auth_timestamp={$request->auth_timestamp}".
"&auth_version={$request->auth_version}".
"&body_md5={$bodyMd5}";
$authSignature = hash_hmac('sha256', $signature, config('broadcasting.connections.pusher.secret'));
if ($authSignature !== $request->get('auth_signature')) {
throw new HttpException(401, 'Invalid authentication signature.');
}
}
}

View File

@ -2,11 +2,11 @@
namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers; namespace BeyondCode\LaravelWebSockets\LaravelEcho\Http\Controllers;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Exceptions\InvalidSignatureException;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
use BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager;
class EventController extends EchoController class TriggerEvent extends EchoController
{ {
/** @var \BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager */ /** @var \BeyondCode\LaravelWebSockets\LaravelEcho\Pusher\Channels\ChannelManager */
protected $channelManager; protected $channelManager;
@ -47,7 +47,7 @@ class EventController extends EchoController
$authSignature = hash_hmac('sha256', $signature, config('broadcasting.connections.pusher.secret')); $authSignature = hash_hmac('sha256', $signature, config('broadcasting.connections.pusher.secret'));
if ($authSignature !== $request->get('auth_signature')) { if ($authSignature !== $request->get('auth_signature')) {
throw new InvalidSignatureException(); throw new HttpException(401, 'Invalid auth signature provided.');
} }
} }
} }

View File

@ -71,10 +71,10 @@ class Router
// TODO: fleshen out http API // TODO: fleshen out http API
$this->get('/apps/{appId}/status', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/status', LaravelEcho\Http\Controllers\StatusController::class);
$this->get('/apps/{appId}/channels', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels', LaravelEcho\Http\Controllers\StatusController::class);
$this->get('/apps/{appId}/channels/{channelName}', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels/{channelName}', LaravelEcho\Http\Controllers\FetchChannel::class);
$this->get('/apps/{appId}/channels/{channelName}/users', LaravelEcho\Http\Controllers\StatusController::class); $this->get('/apps/{appId}/channels/{channelName}/users', LaravelEcho\Http\Controllers\StatusController::class);
$this->post('/apps/{appId}/events', LaravelEcho\Http\Controllers\EventController::class); $this->post('/apps/{appId}/events', LaravelEcho\Http\Controllers\TriggerEvent::class);
} }
/** /**