wip
This commit is contained in:
parent
b46cfadaa2
commit
f62ac8fd56
|
|
@ -4,3 +4,4 @@ vendor
|
|||
coverage
|
||||
.phpunit.result.cache
|
||||
.idea/
|
||||
database.sqlite
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace BeyondCode\LaravelWebSockets\Contracts;
|
||||
|
||||
use BeyondCode\LaravelWebSockets\PubSub\Broadcasters\RedisPusherBroadcaster;
|
||||
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
|
||||
use Pusher\Pusher;
|
||||
|
||||
trait PushesToPusher
|
||||
{
|
||||
/**
|
||||
* Get the right Pusher broadcaster for the used driver.
|
||||
*
|
||||
* @param array $app
|
||||
* @return \Illuminate\Broadcasting\Broadcasters\Broadcaster
|
||||
*/
|
||||
public function getPusherBroadcaster(array $app)
|
||||
{
|
||||
if (config('websockets.replication.driver') === 'redis') {
|
||||
return new RedisPusherBroadcaster(
|
||||
new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.websockets.options', [])),
|
||||
$app['id'],
|
||||
app('redis'),
|
||||
config('broadcasting.connections.websockets.connection', null)
|
||||
);
|
||||
}
|
||||
|
||||
return new PusherBroadcaster(
|
||||
new Pusher($app['key'], $app['secret'], $app['id'], config('broadcasting.connections.pusher.options', [])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,15 @@
|
|||
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
|
||||
|
||||
use BeyondCode\LaravelWebSockets\Apps\App;
|
||||
use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher;
|
||||
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
|
||||
use Illuminate\Http\Request;
|
||||
use Pusher\Pusher;
|
||||
|
||||
class AuthenticateDashboard
|
||||
{
|
||||
use PushesToPusher;
|
||||
|
||||
/**
|
||||
* Find the app by using the header
|
||||
* and then reconstruct the PusherBroadcaster
|
||||
|
|
@ -21,12 +24,11 @@ class AuthenticateDashboard
|
|||
{
|
||||
$app = App::findById($request->header('x-app-id'));
|
||||
|
||||
$broadcaster = new PusherBroadcaster(new Pusher(
|
||||
$app->key,
|
||||
$app->secret,
|
||||
$app->id,
|
||||
[]
|
||||
));
|
||||
$broadcaster = $this->getPusherBroadcaster([
|
||||
'key' => $app->key,
|
||||
'secret' => $app->secret,
|
||||
'id' =>$app->id,
|
||||
]);
|
||||
|
||||
/*
|
||||
* Since the dashboard itself is already secured by the
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers;
|
||||
|
||||
use BeyondCode\LaravelWebSockets\Statistics\Rules\AppId;
|
||||
use BeyondCode\LaravelWebSockets\Contracts\PushesToPusher;
|
||||
use Illuminate\Broadcasting\Broadcasters\PusherBroadcaster;
|
||||
use Illuminate\Http\Request;
|
||||
use Pusher\Pusher;
|
||||
|
||||
class SendMessage
|
||||
{
|
||||
use PushesToPusher;
|
||||
|
||||
/**
|
||||
* Send the message to the requested channel.
|
||||
*
|
||||
|
|
@ -17,7 +20,7 @@ class SendMessage
|
|||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
$request->validate([
|
||||
'appId' => ['required', new AppId],
|
||||
'key' => 'required|string',
|
||||
'secret' => 'required|string',
|
||||
|
|
@ -26,30 +29,18 @@ class SendMessage
|
|||
'data' => 'required|json',
|
||||
]);
|
||||
|
||||
$this->getPusherBroadcaster($validated)->broadcast(
|
||||
[$validated['channel']],
|
||||
$validated['event'],
|
||||
json_decode($validated['data'], true)
|
||||
$broadcaster = $this->getPusherBroadcaster([
|
||||
'key' => $request->key,
|
||||
'secret' => $request->secret,
|
||||
'id' => $request->appId,
|
||||
]);
|
||||
|
||||
$broadcaster->broadcast(
|
||||
[$request->channel],
|
||||
$request->event,
|
||||
json_decode($request->data, true)
|
||||
);
|
||||
|
||||
return 'ok';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pusher broadcaster for the current request.
|
||||
*
|
||||
* @param array $validated
|
||||
* @return \Illuminate\Broadcasting\Broadcasters\PusherBroadcaster
|
||||
*/
|
||||
protected function getPusherBroadcaster(array $validated): PusherBroadcaster
|
||||
{
|
||||
$pusher = new Pusher(
|
||||
$validated['key'],
|
||||
$validated['secret'],
|
||||
$validated['appId'],
|
||||
config('broadcasting.connections.pusher.options', [])
|
||||
);
|
||||
|
||||
return new PusherBroadcaster($pusher);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ class WebSocketsServiceProvider extends ServiceProvider
|
|||
], function () {
|
||||
Route::get('/', ShowDashboard::class)->name('dashboard');
|
||||
Route::get('/api/{appId}/statistics', [DashboardApiController::class, 'getStatistics'])->name('statistics');
|
||||
Route::post('auth', AuthenticateDashboard::class)->name('auth');
|
||||
Route::post('event', SendMessage::class)->name('send');
|
||||
Route::post('/auth', AuthenticateDashboard::class)->name('auth');
|
||||
Route::post('/event', SendMessage::class)->name('send');
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
|
@ -140,7 +140,7 @@ class WebSocketsServiceProvider extends ServiceProvider
|
|||
protected function registerDashboardGate()
|
||||
{
|
||||
Gate::define('viewWebSocketsDashboard', function ($user = null) {
|
||||
return $this->app->environment(['local', 'testing']);
|
||||
return $this->app->environment('local');
|
||||
});
|
||||
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
|
||||
|
||||
use BeyondCode\LaravelWebSockets\Tests\TestCase;
|
||||
use BeyondCode\LaravelWebSockets\Tests\Models\User;
|
||||
use BeyondCode\LaravelWebSockets\Tests\Mocks\Message;
|
||||
|
||||
class AuthTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function can_authenticate_dashboard_over_channel()
|
||||
{
|
||||
$connection = $this->getConnectedWebSocketConnection(['test-channel']);
|
||||
|
||||
$this->pusherServer->onOpen($connection);
|
||||
|
||||
$this->actingAs(factory(User::class)->create())
|
||||
->json('POST', route('laravel-websockets.auth'), [
|
||||
'socket_id' => $connection->socketId,
|
||||
'channel_name' => 'test-channel',
|
||||
], ['x-app-id' => '1234'])
|
||||
->seeJsonStructure([
|
||||
'auth',
|
||||
'channel_data',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function can_authenticate_dashboard_over_private_channel()
|
||||
{
|
||||
$connection = $this->getWebSocketConnection();
|
||||
|
||||
$this->pusherServer->onOpen($connection);
|
||||
|
||||
$signature = "{$connection->socketId}:private-channel";
|
||||
|
||||
$hashedAppSecret = hash_hmac('sha256', $signature, $connection->app->secret);
|
||||
|
||||
$message = new Message([
|
||||
'event' => 'pusher:subscribe',
|
||||
'data' => [
|
||||
'auth' => "{$connection->app->key}:{$hashedAppSecret}",
|
||||
'channel' => 'private-channel',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->pusherServer->onMessage($connection, $message);
|
||||
|
||||
$connection->assertSentEvent('pusher_internal:subscription_succeeded', [
|
||||
'channel' => 'private-channel',
|
||||
]);
|
||||
|
||||
$this->actingAs(factory(User::class)->create())
|
||||
->json('POST', route('laravel-websockets.auth'), [
|
||||
'socket_id' => $connection->socketId,
|
||||
'channel_name' => 'private-test-channel',
|
||||
], ['x-app-id' => '1234'])
|
||||
->seeJsonStructure([
|
||||
'auth',
|
||||
]);
|
||||
}
|
||||
|
||||
/** @test */
|
||||
public function can_authenticate_dashboard_over_presence_channel()
|
||||
{
|
||||
$connection = $this->getWebSocketConnection();
|
||||
|
||||
$this->pusherServer->onOpen($connection);
|
||||
|
||||
$channelData = [
|
||||
'user_id' => 1,
|
||||
'user_info' => [
|
||||
'name' => 'Marcel',
|
||||
],
|
||||
];
|
||||
|
||||
$signature = "{$connection->socketId}:presence-channel:".json_encode($channelData);
|
||||
|
||||
$message = new Message([
|
||||
'event' => 'pusher:subscribe',
|
||||
'data' => [
|
||||
'auth' => $connection->app->key.':'.hash_hmac('sha256', $signature, $connection->app->secret),
|
||||
'channel' => 'presence-channel',
|
||||
'channel_data' => json_encode($channelData),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->pusherServer->onMessage($connection, $message);
|
||||
|
||||
$this->actingAs(factory(User::class)->create())
|
||||
->json('POST', route('laravel-websockets.auth'), [
|
||||
'socket_id' => $connection->socketId,
|
||||
'channel_name' => 'presence-channel',
|
||||
], ['x-app-id' => '1234'])
|
||||
->seeJsonStructure([
|
||||
'auth',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,13 @@
|
|||
namespace BeyondCode\LaravelWebSockets\Tests\Dashboard;
|
||||
|
||||
use BeyondCode\LaravelWebSockets\Tests\TestCase;
|
||||
use BeyondCode\LaravelWebSockets\Tests\Models\User;
|
||||
|
||||
class DashboardTest extends TestCase
|
||||
{
|
||||
/** @test */
|
||||
public function cant_see_dashboard_without_authorization()
|
||||
{
|
||||
config(['app.env' => 'production']);
|
||||
|
||||
$this->get(route('laravel-websockets.dashboard'))
|
||||
->assertResponseStatus(403);
|
||||
}
|
||||
|
|
@ -18,7 +17,8 @@ class DashboardTest extends TestCase
|
|||
/** @test */
|
||||
public function can_see_dashboard()
|
||||
{
|
||||
$this->get(route('laravel-websockets.dashboard'))
|
||||
$this->actingAs(factory(User::class)->create())
|
||||
->get(route('laravel-websockets.dashboard'))
|
||||
->assertResponseOk()
|
||||
->see('WebSockets Dashboard');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace BeyondCode\LaravelWebSockets\Tests\Models;
|
||||
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
'password', 'remember_token',
|
||||
];
|
||||
}
|
||||
|
|
@ -46,6 +46,12 @@ abstract class TestCase extends BaseTestCase
|
|||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->resetDatabase();
|
||||
|
||||
$this->loadLaravelMigrations(['--database' => 'sqlite']);
|
||||
|
||||
$this->withFactories(__DIR__.'/database/factories');
|
||||
|
||||
$this->pusherServer = $this->app->make(config('websockets.handlers.websocket'));
|
||||
|
||||
$this->channelManager = $this->app->make(ChannelManager::class);
|
||||
|
|
@ -69,6 +75,7 @@ abstract class TestCase extends BaseTestCase
|
|||
{
|
||||
return [
|
||||
\BeyondCode\LaravelWebSockets\WebSocketsServiceProvider::class,
|
||||
TestServiceProvider::class,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -79,6 +86,16 @@ abstract class TestCase extends BaseTestCase
|
|||
{
|
||||
$app['config']->set('app.key', 'wslxrEFGWY6GfGhvN9L3wH3KSRJQQpBD');
|
||||
|
||||
$app['config']->set('auth.providers.users.model', Models\User::class);
|
||||
|
||||
$app['config']->set('database.default', 'sqlite');
|
||||
|
||||
$app['config']->set('database.connections.sqlite', [
|
||||
'driver' => 'sqlite',
|
||||
'database' => __DIR__.'/database.sqlite',
|
||||
'prefix' => '',
|
||||
]);
|
||||
|
||||
$app['config']->set('websockets.apps', [
|
||||
[
|
||||
'name' => 'Test App',
|
||||
|
|
@ -307,4 +324,14 @@ abstract class TestCase extends BaseTestCase
|
|||
->make(ReplicationInterface::class)
|
||||
->getPublishClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the database.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function resetDatabase()
|
||||
{
|
||||
file_put_contents(__DIR__.'/database.sqlite', null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace BeyondCode\LaravelWebSockets\Tests;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class TestServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Boot the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Gate::define('viewWebSocketsDashboard', function ($user = null) {
|
||||
return ! is_null($user);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the service provider.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Model Factories
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This directory should contain each of the model factory definitions for
|
||||
| your application. Factories provide a convenient way to generate new
|
||||
| model instances for testing / seeding your application's database.
|
||||
|
|
||||
*/
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
$factory->define(\BeyondCode\LaravelWebSockets\Tests\Models\User::class, function () {
|
||||
return [
|
||||
'name' => 'Name'.Str::random(5),
|
||||
'email' => Str::random(5).'@gmail.com',
|
||||
'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
|
||||
'remember_token' => Str::random(10),
|
||||
];
|
||||
});
|
||||
Loading…
Reference in New Issue