Skip to content

Commit ac8b7fe

Browse files
authored
Merge branch 'develop' into matthewpi/server-details-patch-1
2 parents 8241ea5 + 5d23d89 commit ac8b7fe

30 files changed

+407
-117
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ This file is a running track of new features and fixes to each version of the pa
33

44
This project follows [Semantic Versioning](http://semver.org) guidelines.
55

6+
## Unreleased
7+
### Fixed
8+
* Fixes the application API unable to return server's variables.
9+
610
## v1.1.3
711
### Fixed
812
* Server bulk power actions command will no longer attempt to run commands against installing or suspended servers.

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public function __construct(
6060
*/
6161
public function index(GetBackupsRequest $request, Server $server)
6262
{
63-
return $this->fractal->collection($server->backups()->paginate(20))
63+
$limit = min($request->query('per_page') ?? 20, 50);
64+
return $this->fractal->collection($server->backups()->paginate($limit))
6465
->transformWith($this->getTransformer(BackupTransformer::class))
6566
->toArray();
6667
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest;
1717
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest;
1818
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest;
19+
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ChmodFilesRequest;
1920
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest;
2021
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CompressFilesRequest;
2122
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DecompressFilesRequest;
@@ -263,6 +264,25 @@ public function delete(DeleteFileRequest $request, Server $server): JsonResponse
263264
return new JsonResponse([], Response::HTTP_NO_CONTENT);
264265
}
265266

267+
/**
268+
* Updates file permissions for file(s) in the given root directory.
269+
*
270+
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\ChmodFilesRequest $request
271+
* @param \Pterodactyl\Models\Server $server
272+
* @return \Illuminate\Http\JsonResponse
273+
*
274+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
275+
*/
276+
public function chmod(ChmodFilesRequest $request, Server $server): JsonResponse
277+
{
278+
$this->fileRepository->setServer($server)
279+
->chmodFiles(
280+
$request->input('root'), $request->input('files')
281+
);
282+
283+
return new JsonResponse([], Response::HTTP_NO_CONTENT);
284+
}
285+
266286
/**
267287
* Encodes a given file name & path in a format that should work for a good majority
268288
* of file names without too much confusing logic.

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]);
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
4+
5+
use Pterodactyl\Models\Permission;
6+
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
7+
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
8+
9+
class ChmodFilesRequest extends ClientApiRequest implements ClientPermissionsRequest
10+
{
11+
/**
12+
* @return string
13+
*/
14+
public function permission(): string
15+
{
16+
return Permission::ACTION_FILE_UPDATE;
17+
}
18+
19+
/**
20+
* @return array
21+
*/
22+
public function rules(): array
23+
{
24+
return [
25+
'root' => 'required|nullable|string',
26+
'files' => 'required|array',
27+
'files.*.file' => 'required|string',
28+
'files.*.mode' => 'required|numeric',
29+
];
30+
}
31+
}

app/Repositories/Wings/DaemonFileRepository.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,32 @@ public function decompressFile(?string $root, string $file): ResponseInterface
269269
throw new DaemonConnectionException($exception);
270270
}
271271
}
272+
273+
/**
274+
* Chmods the given files.
275+
*
276+
* @param string|null $root
277+
* @param array $files
278+
* @return \Psr\Http\Message\ResponseInterface
279+
*
280+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
281+
*/
282+
public function chmodFiles(?string $root, array $files): ResponseInterface
283+
{
284+
Assert::isInstanceOf($this->server, Server::class);
285+
286+
try {
287+
return $this->getHttpClient()->post(
288+
sprintf('/api/servers/%s/files/chmod', $this->server->uuid),
289+
[
290+
'json' => [
291+
'root' => $root ?? '/',
292+
'files' => $files,
293+
],
294+
]
295+
);
296+
} catch (TransferException $exception) {
297+
throw new DaemonConnectionException($exception);
298+
}
299+
}
272300
}

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/Nodes/NodeUpdateService.php

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Support\Str;
66
use Pterodactyl\Models\Node;
7+
use Illuminate\Support\Facades\Log;
78
use GuzzleHttp\Exception\ConnectException;
89
use Illuminate\Database\ConnectionInterface;
910
use Illuminate\Contracts\Encryption\Encrypter;
@@ -90,11 +91,17 @@ public function handle(Node $node, array $data, bool $resetToken = false)
9091

9192
$this->configurationRepository->setNode($node)->update($updated);
9293
} catch (DaemonConnectionException $exception) {
93-
if (! is_null($exception->getPrevious()) && $exception->getPrevious() instanceof ConnectException) {
94-
return [$updated, true];
95-
}
94+
Log::warning($exception, ['node_id' => $node->id]);
9695

97-
throw $exception;
96+
// Never actually throw these exceptions up the stack. If we were able to change the settings
97+
// but something went wrong with Wings we just want to store the update and let the user manually
98+
// make changes as needed.
99+
//
100+
// This avoids issues with proxies such as CloudFlare which will see Wings as offline and then
101+
// inject their own response pages, causing this logic to get fucked up.
102+
//
103+
// @see https://github.com/pterodactyl/panel/issues/2712
104+
return [ $updated, true ];
98105
}
99106

100107
return [$updated, false];

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
}

app/Transformers/Api/Application/ServerVariableTransformer.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Pterodactyl\Transformers\Api\Application;
44

5-
use Pterodactyl\Models\ServerVariable;
5+
use Pterodactyl\Models\EggVariable;
66
use Pterodactyl\Services\Acl\Api\AdminAcl;
77

88
class ServerVariableTransformer extends BaseTransformer
@@ -27,22 +27,22 @@ public function getResourceName(): string
2727
/**
2828
* Return a generic transformed server variable array.
2929
*
30-
* @param \Pterodactyl\Models\ServerVariable $variable
30+
* @param \Pterodactyl\Models\EggVariable $variable
3131
* @return array
3232
*/
33-
public function transform(ServerVariable $variable)
33+
public function transform(EggVariable $variable)
3434
{
3535
return $variable->toArray();
3636
}
3737

3838
/**
3939
* Return the parent service variable data.
4040
*
41-
* @param \Pterodactyl\Models\ServerVariable $variable
41+
* @param \Pterodactyl\Models\EggVariable $variable
4242
* @return \League\Fractal\Resource\Item|\League\Fractal\Resource\NullResource
4343
* @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException
4444
*/
45-
public function includeParent(ServerVariable $variable)
45+
public function includeParent(EggVariable $variable)
4646
{
4747
if (! $this->authorize(AdminAcl::RESOURCE_EGGS)) {
4848
return $this->null();

0 commit comments

Comments
 (0)