Skip to content

Commit 11054de

Browse files
committed
Attempt revocation of JWT access when changing a server's owner
closes pterodactyl#2771
1 parent af360d4 commit 11054de

File tree

3 files changed

+53
-25
lines changed

3 files changed

+53
-25
lines changed

app/Http/Controllers/Api/Client/Servers/SubuserController.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ public function update(UpdateSubuserRequest $request, Server $server): array
135135
]);
136136

137137
try {
138-
$this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]);
138+
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
139139
} catch (DaemonConnectionException $exception) {
140140
// Don't block this request if we can't connect to the Wings instance. Chances are it is
141141
// offline in this event and the token will be invalid anyways once Wings boots back.
@@ -163,7 +163,7 @@ public function delete(DeleteSubuserRequest $request, Server $server)
163163
$this->repository->delete($subuser->id);
164164

165165
try {
166-
$this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]);
166+
$this->serverRepository->setServer($server)->revokeUserJTI($subuser->user_id);
167167
} catch (DaemonConnectionException $exception) {
168168
// Don't block this request if we can't connect to the Wings instance.
169169
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);

app/Repositories/Wings/DaemonServerRepository.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Pterodactyl\Repositories\Wings;
44

55
use Webmozart\Assert\Assert;
6+
use Pterodactyl\Models\User;
67
use Pterodactyl\Models\Server;
78
use GuzzleHttp\Exception\TransferException;
89
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
@@ -144,14 +145,29 @@ public function requestArchive(): void
144145
}
145146
}
146147

148+
/**
149+
* Revokes a single user's JTI by using their ID. This is simply a helper function to
150+
* make it easier to revoke tokens on the fly. This ensures that the JTI key is formatted
151+
* correctly and avoids any costly mistakes in the codebase.
152+
*
153+
* @param int $id
154+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
155+
*/
156+
public function revokeUserJTI(int $id): void
157+
{
158+
Assert::isInstanceOf($this->server, Server::class);
159+
160+
$this->revokeJTIs([ md5($id . $this->server->uuid) ]);
161+
}
162+
147163
/**
148164
* Revokes an array of JWT JTI's by marking any token generated before the current time on
149165
* the Wings instance as being invalid.
150166
*
151167
* @param array $jtis
152168
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
153169
*/
154-
public function revokeJTIs(array $jtis): void
170+
protected function revokeJTIs(array $jtis): void
155171
{
156172
Assert::isInstanceOf($this->server, Server::class);
157173

app/Services/Servers/DetailsModificationService.php

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
namespace Pterodactyl\Services\Servers;
44

5+
use Illuminate\Support\Arr;
56
use Pterodactyl\Models\Server;
67
use Illuminate\Database\ConnectionInterface;
78
use Pterodactyl\Traits\Services\ReturnsUpdatedModels;
8-
use Pterodactyl\Repositories\Eloquent\ServerRepository;
9+
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
10+
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
911

1012
class DetailsModificationService
1113
{
@@ -17,47 +19,57 @@ class DetailsModificationService
1719
private $connection;
1820

1921
/**
20-
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
22+
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
2123
*/
22-
private $repository;
24+
private $serverRepository;
2325

2426
/**
2527
* DetailsModificationService constructor.
2628
*
2729
* @param \Illuminate\Database\ConnectionInterface $connection
28-
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
30+
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $serverRepository
2931
*/
30-
public function __construct(
31-
ConnectionInterface $connection,
32-
ServerRepository $repository
33-
) {
32+
public function __construct(ConnectionInterface $connection, DaemonServerRepository $serverRepository)
33+
{
3434
$this->connection = $connection;
35-
$this->repository = $repository;
35+
$this->serverRepository = $serverRepository;
3636
}
3737

3838
/**
3939
* Update the details for a single server instance.
4040
*
4141
* @param \Pterodactyl\Models\Server $server
4242
* @param array $data
43-
* @return bool|\Pterodactyl\Models\Server
43+
* @return \Pterodactyl\Models\Server
4444
*
45-
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
46-
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
45+
* @throws \Throwable
4746
*/
48-
public function handle(Server $server, array $data)
47+
public function handle(Server $server, array $data): Server
4948
{
50-
$this->connection->beginTransaction();
49+
return $this->connection->transaction(function () use ($data, $server) {
50+
$owner = $server->owner_id;
5151

52-
$response = $this->repository->setFreshModel($this->getUpdatedModel())->update($server->id, [
53-
'external_id' => array_get($data, 'external_id'),
54-
'owner_id' => array_get($data, 'owner_id'),
55-
'name' => array_get($data, 'name'),
56-
'description' => array_get($data, 'description') ?? '',
57-
], true, true);
52+
$server->forceFill([
53+
'external_id' => Arr::get($data, 'external_id'),
54+
'owner_id' => Arr::get($data, 'owner_id'),
55+
'name' => Arr::get($data, 'name'),
56+
'description' => Arr::get($data, 'description') ?? '',
57+
])->saveOrFail();
5858

59-
$this->connection->commit();
59+
// If the owner_id value is changed we need to revoke any tokens that exist for the server
60+
// on the Wings instance so that the old owner no longer has any permission to access the
61+
// websockets.
62+
if ($server->owner_id !== $owner) {
63+
try {
64+
$this->serverRepository->setServer($server)->revokeUserJTI($owner);
65+
} catch (DaemonConnectionException $exception) {
66+
// Do nothing. A failure here is not ideal, but it is likely to be caused by Wings
67+
// being offline, or in an entirely broken state. Remeber, these tokens reset every
68+
// few minutes by default, we're just trying to help it along a little quicker.
69+
}
70+
}
6071

61-
return $response;
72+
return $server;
73+
});
6274
}
6375
}

0 commit comments

Comments
 (0)