Skip to content

Commit 009f9c2

Browse files
committed
Revoke JWT JTIs when modifying a subuser's permissions
1 parent c4df534 commit 009f9c2

File tree

3 files changed

+69
-13
lines changed

3 files changed

+69
-13
lines changed

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

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
44

55
use Illuminate\Http\Request;
6-
use Pterodactyl\Models\User;
76
use Pterodactyl\Models\Server;
8-
use Pterodactyl\Models\Subuser;
97
use Illuminate\Http\JsonResponse;
108
use Pterodactyl\Models\Permission;
9+
use Illuminate\Support\Facades\Log;
1110
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
1211
use Pterodactyl\Services\Subusers\SubuserCreationService;
12+
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
1313
use Pterodactyl\Transformers\Api\Client\SubuserTransformer;
1414
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
15+
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
1516
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
1617
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest;
1718
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest;
@@ -29,20 +30,28 @@ class SubuserController extends ClientApiController
2930
*/
3031
private $creationService;
3132

33+
/**
34+
* @var \Pterodactyl\Repositories\Wings\DaemonServerRepository
35+
*/
36+
private $serverRepository;
37+
3238
/**
3339
* SubuserController constructor.
3440
*
3541
* @param \Pterodactyl\Repositories\Eloquent\SubuserRepository $repository
3642
* @param \Pterodactyl\Services\Subusers\SubuserCreationService $creationService
43+
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $serverRepository
3744
*/
3845
public function __construct(
3946
SubuserRepository $repository,
40-
SubuserCreationService $creationService
47+
SubuserCreationService $creationService,
48+
DaemonServerRepository $serverRepository
4149
) {
4250
parent::__construct();
4351

4452
$this->repository = $repository;
4553
$this->creationService = $creationService;
54+
$this->serverRepository = $serverRepository;
4655
}
4756

4857
/**
@@ -101,19 +110,38 @@ public function store(StoreSubuserRequest $request, Server $server)
101110
* Update a given subuser in the system for the server.
102111
*
103112
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\UpdateSubuserRequest $request
113+
* @param \Pterodactyl\Models\Server $server
104114
* @return array
105115
*
106116
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
107117
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
108118
*/
109-
public function update(UpdateSubuserRequest $request): array
119+
public function update(UpdateSubuserRequest $request, Server $server): array
110120
{
111121
/** @var \Pterodactyl\Models\Subuser $subuser */
112122
$subuser = $request->attributes->get('subuser');
113123

114-
$this->repository->update($subuser->id, [
115-
'permissions' => $this->getDefaultPermissions($request),
116-
]);
124+
$permissions = $this->getDefaultPermissions($request);
125+
$current = $subuser->permissions;
126+
127+
sort($permissions);
128+
sort($current);
129+
130+
// Only update the database and hit up the Wings instance to invalidate JTI's if the permissions
131+
// have actually changed for the user.
132+
if ($permissions !== $current) {
133+
$this->repository->update($subuser->id, [
134+
'permissions' => $this->getDefaultPermissions($request),
135+
]);
136+
137+
try {
138+
$this->serverRepository->setServer($server)->revokeJTIs([md5($subuser->user_id . $server->uuid)]);
139+
} catch (DaemonConnectionException $exception) {
140+
// Don't block this request if we can't connect to the Wings instance. Chances are it is
141+
// offline in this event and the token will be invalid anyways once Wings boots back.
142+
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
143+
}
144+
}
117145

118146
return $this->fractal->item($subuser->refresh())
119147
->transformWith($this->getTransformer(SubuserTransformer::class))
@@ -124,15 +152,23 @@ public function update(UpdateSubuserRequest $request): array
124152
* Removes a subusers from a server's assignment.
125153
*
126154
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest $request
155+
* @param \Pterodactyl\Models\Server $server
127156
* @return \Illuminate\Http\JsonResponse
128157
*/
129-
public function delete(DeleteSubuserRequest $request)
158+
public function delete(DeleteSubuserRequest $request, Server $server)
130159
{
131160
/** @var \Pterodactyl\Models\Subuser $subuser */
132161
$subuser = $request->attributes->get('subuser');
133162

134163
$this->repository->delete($subuser->id);
135164

165+
try {
166+
$this->serverRepository->revokeJTIs([md5($subuser->user_id . $server->uuid)]);
167+
} catch (DaemonConnectionException $exception) {
168+
// Don't block this request if we can't connect to the Wings instance.
169+
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
170+
}
171+
136172
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);
137173
}
138174

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function __invoke(ClientApiRequest $request, Server $server)
5959
}
6060

6161
$token = $this->jwtService
62-
->setExpiresAt(CarbonImmutable::now()->addMinutes(15))
62+
->setExpiresAt(CarbonImmutable::now()->addMinutes(10))
6363
->setClaims([
6464
'user_id' => $request->user()->id,
6565
'server_uuid' => $server->uuid,

app/Repositories/Wings/DaemonServerRepository.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,10 @@ public function suspend(bool $unsuspend = false): void
126126
}
127127

128128
/**
129-
* Requests the daemon to create a full archive of the server.
130-
* Once the daemon is finished they will send a POST request to
131-
* "/api/remote/servers/{uuid}/archive" with a boolean.
129+
* Requests the daemon to create a full archive of the server. Once the daemon is finished
130+
* they will send a POST request to "/api/remote/servers/{uuid}/archive" with a boolean.
132131
*
133-
* @throws DaemonConnectionException
132+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
134133
*/
135134
public function requestArchive(): void
136135
{
@@ -144,4 +143,25 @@ public function requestArchive(): void
144143
throw new DaemonConnectionException($exception);
145144
}
146145
}
146+
147+
/**
148+
* Revokes an array of JWT JTI's by marking any token generated before the current time on
149+
* the Wings instance as being invalid.
150+
*
151+
* @param array $jtis
152+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
153+
*/
154+
public function revokeJTIs(array $jtis): void
155+
{
156+
Assert::isInstanceOf($this->server, Server::class);
157+
158+
try {
159+
$this->getHttpClient()
160+
->post(sprintf('/api/servers/%s/ws/deny', $this->server->uuid), [
161+
'json' => ['jtis' => $jtis],
162+
]);
163+
} catch (TransferException $exception) {
164+
throw new DaemonConnectionException($exception);
165+
}
166+
}
147167
}

0 commit comments

Comments
 (0)