Attempt revocation of JWT access when changing a server's owner

closes #2771
This commit is contained in:
Dane Everitt 2020-12-06 12:16:12 -08:00
parent af360d49dd
commit 11054de5b3
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
3 changed files with 53 additions and 25 deletions

View file

@ -135,7 +135,7 @@ class SubuserController extends ClientApiController
]); ]);
try { try {
$this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]); $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
} catch (DaemonConnectionException $exception) { } catch (DaemonConnectionException $exception) {
// Don't block this request if we can't connect to the Wings instance. Chances are it is // 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. // offline in this event and the token will be invalid anyways once Wings boots back.
@ -163,7 +163,7 @@ class SubuserController extends ClientApiController
$this->repository->delete($subuser->id); $this->repository->delete($subuser->id);
try { try {
$this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]); $this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
} catch (DaemonConnectionException $exception) { } catch (DaemonConnectionException $exception) {
// Don't block this request if we can't connect to the Wings instance. // 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]); Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);

View file

@ -3,6 +3,7 @@
namespace Pterodactyl\Repositories\Wings; namespace Pterodactyl\Repositories\Wings;
use Webmozart\Assert\Assert; use Webmozart\Assert\Assert;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use GuzzleHttp\Exception\TransferException; use GuzzleHttp\Exception\TransferException;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
@ -144,6 +145,21 @@ class DaemonServerRepository extends DaemonRepository
} }
} }
/**
* Revokes a single user's JTI by using their ID. This is simply a helper function to
* make it easier to revoke tokens on the fly. This ensures that the JTI key is formatted
* correctly and avoids any costly mistakes in the codebase.
*
* @param int $id
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/
public function revokeUserJTI(int $id): void
{
Assert::isInstanceOf($this->server, Server::class);
$this->revokeJTIs([ md5($id . $this->server->uuid) ]);
}
/** /**
* Revokes an array of JWT JTI's by marking any token generated before the current time on * Revokes an array of JWT JTI's by marking any token generated before the current time on
* the Wings instance as being invalid. * the Wings instance as being invalid.
@ -151,7 +167,7 @@ class DaemonServerRepository extends DaemonRepository
* @param array $jtis * @param array $jtis
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
*/ */
public function revokeJTIs(array $jtis): void protected function revokeJTIs(array $jtis): void
{ {
Assert::isInstanceOf($this->server, Server::class); Assert::isInstanceOf($this->server, Server::class);

View file

@ -2,10 +2,12 @@
namespace Pterodactyl\Services\Servers; namespace Pterodactyl\Services\Servers;
use Illuminate\Support\Arr;
use Pterodactyl\Models\Server; use Pterodactyl\Models\Server;
use Illuminate\Database\ConnectionInterface; use Illuminate\Database\ConnectionInterface;
use Pterodactyl\Traits\Services\ReturnsUpdatedModels; use Pterodactyl\Traits\Services\ReturnsUpdatedModels;
use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Wings\DaemonServerRepository;
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
class DetailsModificationService class DetailsModificationService
{ {
@ -17,22 +19,20 @@ class DetailsModificationService
private $connection; private $connection;
/** /**
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
*/ */
private $repository; private $serverRepository;
/** /**
* DetailsModificationService constructor. * DetailsModificationService constructor.
* *
* @param \Illuminate\Database\ConnectionInterface $connection * @param \Illuminate\Database\ConnectionInterface $connection
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository * @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $serverRepository
*/ */
public function __construct( public function __construct(ConnectionInterface $connection, DaemonServerRepository $serverRepository)
ConnectionInterface $connection, {
ServerRepository $repository
) {
$this->connection = $connection; $this->connection = $connection;
$this->repository = $repository; $this->serverRepository = $serverRepository;
} }
/** /**
@ -40,24 +40,36 @@ class DetailsModificationService
* *
* @param \Pterodactyl\Models\Server $server * @param \Pterodactyl\Models\Server $server
* @param array $data * @param array $data
* @return bool|\Pterodactyl\Models\Server * @return \Pterodactyl\Models\Server
* *
* @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Throwable
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
*/ */
public function handle(Server $server, array $data) public function handle(Server $server, array $data): Server
{ {
$this->connection->beginTransaction(); return $this->connection->transaction(function () use ($data, $server) {
$owner = $server->owner_id;
$response = $this->repository->setFreshModel($this->getUpdatedModel())->update($server->id, [ $server->forceFill([
'external_id' => array_get($data, 'external_id'), 'external_id' => Arr::get($data, 'external_id'),
'owner_id' => array_get($data, 'owner_id'), 'owner_id' => Arr::get($data, 'owner_id'),
'name' => array_get($data, 'name'), 'name' => Arr::get($data, 'name'),
'description' => array_get($data, 'description') ?? '', 'description' => Arr::get($data, 'description') ?? '',
], true, true); ])->saveOrFail();
$this->connection->commit(); // If the owner_id value is changed we need to revoke any tokens that exist for the server
// on the Wings instance so that the old owner no longer has any permission to access the
// websockets.
if ($server->owner_id !== $owner) {
try {
$this->serverRepository->setServer($server)->revokeUserJTI($owner);
} catch (DaemonConnectionException $exception) {
// Do nothing. A failure here is not ideal, but it is likely to be caused by Wings
// being offline, or in an entirely broken state. Remeber, these tokens reset every
// few minutes by default, we're just trying to help it along a little quicker.
}
}
return $response; return $server;
});
} }
} }