Handle token expiring and token expired websocket events (#755)

* handle `token expiring` and `token expired` events

* fix "getToken"

* Move logic to Widget instead of blade & add user check

---------

Co-authored-by: RMartinOscar <40749467+RMartinOscar@users.noreply.github.com>
This commit is contained in:
Boy132 2024-12-03 23:54:40 +01:00 committed by GitHub
parent bbfdee356b
commit 6d42a15ec3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 48 additions and 26 deletions

View File

@ -2,8 +2,12 @@
namespace App\Filament\Server\Widgets; namespace App\Filament\Server\Widgets;
use App\Exceptions\Http\HttpForbiddenException;
use App\Models\Permission;
use App\Models\Server; use App\Models\Server;
use App\Models\User; use App\Models\User;
use App\Services\Nodes\NodeJWTService;
use App\Services\Servers\GetUserPermissionsService;
use Filament\Widgets\Widget; use Filament\Widgets\Widget;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
use Livewire\Attributes\On; use Livewire\Attributes\On;
@ -26,6 +30,33 @@ class ServerConsole extends Widget
public string $input = ''; public string $input = '';
protected function getToken(): string
{
if (!$this->user || !$this->server || $this->user->cannot(Permission::ACTION_WEBSOCKET_CONNECT, $this->server)) {
throw new HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
}
// @phpstan-ignore-next-line
$permissions = app(GetUserPermissionsService::class)->handle($this->server, $this->user);
// @phpstan-ignore-next-line
return app(NodeJWTService::class)
->setExpiresAt(now()->addMinutes(10)->toImmutable())
->setUser($this->user)
->setClaims([
'server_uuid' => $this->server->uuid,
'permissions' => $permissions,
])
->handle($this->server->node, $this->user->id . $this->server->uuid)->toString();
}
protected function getSocket(): string
{
$socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $this->server->node->getConnectionAddress());
$socket .= sprintf('/api/servers/%s/ws', $this->server->uuid);
return $socket;
}
public function up(): void public function up(): void
{ {
$this->historyIndex = min($this->historyIndex + 1, count($this->history) - 1); $this->historyIndex = min($this->historyIndex + 1, count($this->history) - 1);

View File

@ -1,7 +1,11 @@
<x-filament::widget> <x-filament::widget>
@assets @assets
<script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.js" integrity="sha512-Gujw5GajF5is3nMoGv9X+tCMqePLL/60qvAv1LofUZTV9jK8ENbM9L+maGmOsNzuZaiuyc/fpph1KT9uR5w3CQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.js"
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.css" integrity="sha512-AbNrj/oSHJaILgcdnkYm+DQ08SqVbZ8jlkJbFyyS1WDcAaXAcAfxJnCH69el7oVgTwVwyA5u5T+RdFyUykrV3Q==" crossorigin="anonymous" referrerpolicy="no-referrer" /> integrity="sha512-Gujw5GajF5is3nMoGv9X+tCMqePLL/60qvAv1LofUZTV9jK8ENbM9L+maGmOsNzuZaiuyc/fpph1KT9uR5w3CQ=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/xterm/5.5.0/xterm.css"
integrity="sha512-AbNrj/oSHJaILgcdnkYm+DQ08SqVbZ8jlkJbFyyS1WDcAaXAcAfxJnCH69el7oVgTwVwyA5u5T+RdFyUykrV3Q=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
@endassets @endassets
<div id="terminal" wire:ignore></div> <div id="terminal" wire:ignore></div>
@ -27,7 +31,7 @@
cursorStyle: 'underline', cursorStyle: 'underline',
allowTransparency: true, allowTransparency: true,
rows: 35, rows: 35,
cols: 110, cols: 110
// theme: theme, // theme: theme,
}; };
@ -57,28 +61,8 @@
const handlePowerChangeEvent = (state) => const handlePowerChangeEvent = (state) =>
terminal.writeln(TERMINAL_PRELUDE + 'Server marked as ' + state + '...\u001b[0m'); terminal.writeln(TERMINAL_PRELUDE + 'Server marked as ' + state + '...\u001b[0m');
@php const socket = new WebSocket("{{ $this->getSocket() }}");
if ($user->cannot(\App\Models\Permission::ACTION_WEBSOCKET_CONNECT, $server)) { let token = '{{ $this->getToken() }}';
throw new \App\Exceptions\Http\HttpForbiddenException('You do not have permission to connect to this server\'s websocket.');
}
$permissions = app(\App\Services\Servers\GetUserPermissionsService::class)->handle($server, $user);
$socket = str_replace(['https://', 'http://'], ['wss://', 'ws://'], $server->node->getConnectionAddress());
$socket .= sprintf('/api/servers/%s/ws', $server->uuid);
$token = app(\App\Services\Nodes\NodeJWTService::class)
->setExpiresAt(now()->addMinutes(10)->toImmutable())
->setUser($user)
->setClaims([
'server_uuid' => $server->uuid,
'permissions' => $permissions,
])
->handle($server->node, $user->id . $server->uuid);
@endphp
const socket = new WebSocket("{{ $socket }}");
const token = '{{ $token->toString() }}';
socket.onmessage = function(websocketMessageEvent) { socket.onmessage = function(websocketMessageEvent) {
let eventData = JSON.parse(websocketMessageEvent.data); let eventData = JSON.parse(websocketMessageEvent.data);
@ -106,7 +90,14 @@
})); }));
} }
// TODO: handle "token expiring" and "token expired" if (eventData.event === 'token expiring' || eventData.event === 'token expired') {
token = '{{ $this->getToken() }}';
socket.send(JSON.stringify({
'event': 'auth',
'args': [token]
}));
}
}; };
socket.onopen = (event) => { socket.onopen = (event) => {