From 0e75027c121b28c6aed8ab690d0219573bfdcaa4 Mon Sep 17 00:00:00 2001 From: Charles Date: Wed, 17 Jun 2026 06:42:03 -0400 Subject: [PATCH] Add Jwt Scopes from upstream (#2397) --- app/Enums/NodeJwtScope.php | 12 +++++++++++ .../Resources/Files/Pages/DownloadFiles.php | 2 ++ app/Filament/Server/Widgets/ServerConsole.php | 2 ++ .../Api/Client/Servers/FileController.php | 2 ++ .../Client/Servers/FileUploadController.php | 2 ++ .../Client/Servers/WebsocketController.php | 2 ++ app/Livewire/NodeClientConnectivity.php | 2 ++ app/Services/Backups/DownloadLinkService.php | 2 ++ app/Services/Nodes/NodeJWTService.php | 20 +++++++++++++++++-- .../Servers/TransferServerService.php | 4 +++- 10 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 app/Enums/NodeJwtScope.php diff --git a/app/Enums/NodeJwtScope.php b/app/Enums/NodeJwtScope.php new file mode 100644 index 000000000..9747a420a --- /dev/null +++ b/app/Enums/NodeJwtScope.php @@ -0,0 +1,12 @@ +nodeJWTService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setScopes(NodeJwtScope::FileDownload) ->setUser(user()) ->setClaims([ 'file_path' => rawurldecode($path), diff --git a/app/Filament/Server/Widgets/ServerConsole.php b/app/Filament/Server/Widgets/ServerConsole.php index 3a2ec30a2..7a909a3f0 100644 --- a/app/Filament/Server/Widgets/ServerConsole.php +++ b/app/Filament/Server/Widgets/ServerConsole.php @@ -2,6 +2,7 @@ namespace App\Filament\Server\Widgets; +use App\Enums\NodeJwtScope; use App\Enums\SubuserPermission; use App\Exceptions\Http\HttpForbiddenException; use App\Livewire\AlertBanner; @@ -54,6 +55,7 @@ class ServerConsole extends Widget return $this->nodeJWTService ->setExpiresAt(now()->addMinutes(10)->toImmutable()) + ->setScopes(NodeJwtScope::Websocket) ->setUser($this->user) ->setClaims([ 'server_uuid' => $this->server->uuid, diff --git a/app/Http/Controllers/Api/Client/Servers/FileController.php b/app/Http/Controllers/Api/Client/Servers/FileController.php index 26cde684c..74ef32ef5 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api\Client\Servers; +use App\Enums\NodeJwtScope; use App\Facades\Activity; use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Requests\Api\Client\Servers\Files\ChmodFilesRequest; @@ -93,6 +94,7 @@ class FileController extends ClientApiController { $token = $this->jwtService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setScopes(NodeJwtScope::FileDownload) ->setUser($request->user()) ->setClaims([ 'file_path' => rawurldecode($request->get('file')), diff --git a/app/Http/Controllers/Api/Client/Servers/FileUploadController.php b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php index 3e4ba47e7..6baeda8ac 100644 --- a/app/Http/Controllers/Api/Client/Servers/FileUploadController.php +++ b/app/Http/Controllers/Api/Client/Servers/FileUploadController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api\Client\Servers; +use App\Enums\NodeJwtScope; use App\Http\Controllers\Api\Client\ClientApiController; use App\Http\Requests\Api\Client\Servers\Files\UploadFileRequest; use App\Models\Server; @@ -45,6 +46,7 @@ class FileUploadController extends ClientApiController { $token = $this->jwtService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setScopes(NodeJwtScope::FileUpload) ->setUser($user) ->setClaims(['server_uuid' => $server->uuid]) ->handle($server->node, $user->id . $server->uuid); diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php index baf5493ec..3dbd86267 100644 --- a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers\Api\Client\Servers; +use App\Enums\NodeJwtScope; use App\Enums\SubuserPermission; use App\Exceptions\Http\HttpForbiddenException; use App\Http\Controllers\Api\Client\ClientApiController; @@ -58,6 +59,7 @@ class WebsocketController extends ClientApiController $token = $this->jwtService ->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) + ->setScopes(NodeJwtScope::Websocket) ->setUser($request->user()) ->setClaims([ 'server_uuid' => $server->uuid, diff --git a/app/Livewire/NodeClientConnectivity.php b/app/Livewire/NodeClientConnectivity.php index 2006d550e..a04bb4e58 100644 --- a/app/Livewire/NodeClientConnectivity.php +++ b/app/Livewire/NodeClientConnectivity.php @@ -2,6 +2,7 @@ namespace App\Livewire; +use App\Enums\NodeJwtScope; use App\Enums\TablerIcon; use App\Models\Node; use App\Services\Nodes\NodeJWTService; @@ -47,6 +48,7 @@ class NodeClientConnectivity extends Component $wsToken = $this->nodeJWTService ->setExpiresAt(now()->addMinute()->toImmutable()) + ->setScopes(NodeJwtScope::Websocket) ->setUser($user) ->setClaims([ 'server_uuid' => $server->uuid, diff --git a/app/Services/Backups/DownloadLinkService.php b/app/Services/Backups/DownloadLinkService.php index 0b3eaba15..ccd80dcd6 100644 --- a/app/Services/Backups/DownloadLinkService.php +++ b/app/Services/Backups/DownloadLinkService.php @@ -2,6 +2,7 @@ namespace App\Services\Backups; +use App\Enums\NodeJwtScope; use App\Extensions\Backups\BackupManager; use App\Extensions\Filesystem\S3Filesystem; use App\Models\Backup; @@ -28,6 +29,7 @@ class DownloadLinkService $token = $this->jwtService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setScopes(NodeJwtScope::BackupDownload) ->setUser($user) ->setClaims([ 'backup_uuid' => $backup->uuid, diff --git a/app/Services/Nodes/NodeJWTService.php b/app/Services/Nodes/NodeJWTService.php index 0eaae4c58..63c4ae9ce 100644 --- a/app/Services/Nodes/NodeJWTService.php +++ b/app/Services/Nodes/NodeJWTService.php @@ -2,6 +2,7 @@ namespace App\Services\Nodes; +use App\Enums\NodeJwtScope; use App\Extensions\Lcobucci\JWT\Encoding\TimestampDates; use App\Models\Node; use App\Models\User; @@ -12,6 +13,7 @@ use Lcobucci\JWT\Configuration; use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\UnencryptedToken; +use Webmozart\Assert\Assert; class NodeJWTService { @@ -24,6 +26,9 @@ class NodeJWTService private ?string $subject = null; + /** @var NodeJwtScope[] */ + private array $scopes; + /** * Set the claims to include in this JWT. * @@ -64,9 +69,11 @@ class NodeJWTService /** * Generate a new JWT for a given node. */ - public function handle(Node $node, ?string $identifiedBy, string $algo = 'sha256'): UnencryptedToken + public function handle(Node $node, ?string $identifiedBy): UnencryptedToken { - $identifier = hash($algo, $identifiedBy); + Assert::notEmpty($this->scopes, 'Cannot generate a JWT without providing at least one scope.'); + + $identifier = hash('sha256', $identifiedBy); $config = Configuration::forSymmetricSigner(new Sha256(), InMemory::plainText($node->daemon_token)); $builder = $config->builder(new TimestampDates()) @@ -93,8 +100,17 @@ class NodeJWTService $builder = $builder->withClaim('user_uuid', $this->user->uuid); } + $builder = $builder->withClaim('scope', implode(' ', array_map(fn ($scope) => $scope->value, $this->scopes))); + return $builder ->withClaim('unique_id', Str::random()) ->getToken($config->signer(), $config->signingKey()); } + + public function setScopes(NodeJwtScope ...$scopes): self + { + $this->scopes = $scopes; + + return $this; + } } diff --git a/app/Services/Servers/TransferServerService.php b/app/Services/Servers/TransferServerService.php index 05a0fcc25..c5ac48caf 100644 --- a/app/Services/Servers/TransferServerService.php +++ b/app/Services/Servers/TransferServerService.php @@ -2,6 +2,7 @@ namespace App\Services\Servers; +use App\Enums\NodeJwtScope; use App\Models\Allocation; use App\Models\Backup; use App\Models\Node; @@ -99,8 +100,9 @@ class TransferServerService // Generate a token for the destination node that the source node can use to authenticate with. $token = $this->nodeJWTService ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setScopes(NodeJwtScope::ServerTransfer) ->setSubject($server->uuid) - ->handle($transfer->newNode, $server->uuid, 'sha256'); + ->handle($transfer->newNode, $server->uuid); // Notify the source node of the pending outgoing transfer. $this->notify($transfer, $token, $backup_uuid);