diff --git a/app/Http/Controllers/Api/Client/Servers/SubuserController.php b/app/Http/Controllers/Api/Client/Servers/SubuserController.php index d8bdcc40a..f51503d0f 100644 --- a/app/Http/Controllers/Api/Client/Servers/SubuserController.php +++ b/app/Http/Controllers/Api/Client/Servers/SubuserController.php @@ -3,15 +3,16 @@ namespace Pterodactyl\Http\Controllers\Api\Client\Servers; use Illuminate\Http\Request; -use Pterodactyl\Models\User; use Pterodactyl\Models\Server; -use Pterodactyl\Models\Subuser; use Illuminate\Http\JsonResponse; use Pterodactyl\Models\Permission; +use Illuminate\Support\Facades\Log; use Pterodactyl\Repositories\Eloquent\SubuserRepository; use Pterodactyl\Services\Subusers\SubuserCreationService; +use Pterodactyl\Repositories\Wings\DaemonServerRepository; use Pterodactyl\Transformers\Api\Client\SubuserTransformer; use Pterodactyl\Http\Controllers\Api\Client\ClientApiController; +use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest; use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest; @@ -29,20 +30,28 @@ class SubuserController extends ClientApiController */ private $creationService; + /** + * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository + */ + private $serverRepository; + /** * SubuserController constructor. * * @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository * @param \Pterodactyl\Services\Subusers\SubuserCreationService $creationService + * @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $serverRepository */ public function __construct( SubuserRepository $repository, - SubuserCreationService $creationService + SubuserCreationService $creationService, + DaemonServerRepository $serverRepository ) { parent::__construct(); $this->repository = $repository; $this->creationService = $creationService; + $this->serverRepository = $serverRepository; } /** @@ -101,19 +110,38 @@ class SubuserController extends ClientApiController * Update a given subuser in the system for the server. * * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest $request + * @param \Pterodactyl\Models\Server $server * @return array * * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ - public function update(UpdateSubuserRequest $request): array + public function update(UpdateSubuserRequest $request, Server $server): array { /** @var \Pterodactyl\Models\Subuser $subuser */ $subuser = $request->attributes->get('subuser'); - $this->repository->update($subuser->id, [ - 'permissions' => $this->getDefaultPermissions($request), - ]); + $permissions = $this->getDefaultPermissions($request); + $current = $subuser->permissions; + + sort($permissions); + sort($current); + + // Only update the database and hit up the Wings instance to invalidate JTI's if the permissions + // have actually changed for the user. + if ($permissions !== $current) { + $this->repository->update($subuser->id, [ + 'permissions' => $this->getDefaultPermissions($request), + ]); + + try { + $this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. Chances are it is + // offline in this event and the token will be invalid anyways once Wings boots back. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + } + } return $this->fractal->item($subuser->refresh()) ->transformWith($this->getTransformer(SubuserTransformer::class)) @@ -124,15 +152,23 @@ class SubuserController extends ClientApiController * Removes a subusers from a server's assignment. * * @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest $request + * @param \Pterodactyl\Models\Server $server * @return \Illuminate\Http\JsonResponse */ - public function delete(DeleteSubuserRequest $request) + public function delete(DeleteSubuserRequest $request, Server $server) { /** @var \Pterodactyl\Models\Subuser $subuser */ $subuser = $request->attributes->get('subuser'); $this->repository->delete($subuser->id); + try { + $this->serverRepository->revokeJTIs([md5($subuser->user_id . $server->uuid)]); + } catch (DaemonConnectionException $exception) { + // Don't block this request if we can't connect to the Wings instance. + Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]); + } + return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT); } diff --git a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php index a176f66fc..f18b47634 100644 --- a/app/Http/Controllers/Api/Client/Servers/WebsocketController.php +++ b/app/Http/Controllers/Api/Client/Servers/WebsocketController.php @@ -59,7 +59,7 @@ class WebsocketController extends ClientApiController } $token = $this->jwtService - ->setExpiresAt(CarbonImmutable::now()->addMinutes(15)) + ->setExpiresAt(CarbonImmutable::now()->addMinutes(10)) ->setClaims([ 'user_id' => $request->user()->id, 'server_uuid' => $server->uuid, diff --git a/app/Repositories/Wings/DaemonServerRepository.php b/app/Repositories/Wings/DaemonServerRepository.php index abb5dae4e..22c90d6c5 100644 --- a/app/Repositories/Wings/DaemonServerRepository.php +++ b/app/Repositories/Wings/DaemonServerRepository.php @@ -126,11 +126,10 @@ class DaemonServerRepository extends DaemonRepository } /** - * Requests the daemon to create a full archive of the server. - * Once the daemon is finished they will send a POST request to - * "/api/remote/servers/{uuid}/archive" with a boolean. + * Requests the daemon to create a full archive of the server. Once the daemon is finished + * they will send a POST request to "/api/remote/servers/{uuid}/archive" with a boolean. * - * @throws DaemonConnectionException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ public function requestArchive(): void { @@ -144,4 +143,25 @@ class DaemonServerRepository extends DaemonRepository throw new DaemonConnectionException($exception); } } + + /** + * Revokes an array of JWT JTI's by marking any token generated before the current time on + * the Wings instance as being invalid. + * + * @param array $jtis + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + */ + public function revokeJTIs(array $jtis): void + { + Assert::isInstanceOf($this->server, Server::class); + + try { + $this->getHttpClient() + ->post(sprintf('/api/servers/%s/ws/deny', $this->server->uuid), [ + 'json' => ['jtis' => $jtis], + ]); + } catch (TransferException $exception) { + throw new DaemonConnectionException($exception); + } + } }