Skip to content

Commit 2ee5fff

Browse files
authored
Panel 1.12.1 (#8)
* [🫸] Sync gh with prod New version branch: panel-1.12.0 created after panel update * Sync gh with prod * Sync gh with prod Updated panel to 1.12.1
1 parent d6c213b commit 2ee5fff

71 files changed

Lines changed: 1451 additions & 533 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,25 @@ 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+
## v1.12.1
7+
### Fixed
8+
* [CVE-2026-26016](https://github.com/pterodactyl/panel/security/advisories/GHSA-g7vw-f8p5-c728)
9+
* [GHSA-hr7j-63v7-vj7g](https://github.com/pterodactyl/panel/security/advisories/GHSA-hr7j-63v7-vj7g)
10+
* Fixes bug where presigned URLs would
11+
* Fixes issue where certain input values would cause the activity log screen to stop rendering properly due to improper element encoding.
12+
* Fixes improper display of unicode characters in console output.
13+
* Fixes page number not resetting when toggling between "Show My Servers" and "Show All Servers" on the dashboard.
14+
15+
### Changed
16+
* SFTP sessions are now revoked on nodes when a user changes their password or their account is deleted.
17+
* Remote node access tokens are now scoped to only allow access to servers that belong to the same node. Previously a node could access information and control the installation status for any server in the system.
18+
* The default rate limit for the client API was bumped from `128` to `256` requests per minute.
19+
20+
### Added
21+
* HTTP responses now include default security headers if not otherwise set.
22+
* Adds modal popup when running a Hytale server that requires additional auth.
23+
* Adds support for administrators to view any application API key that has been created, regardless of the owning account.
24+
625
## v1.12.0
726
### Fixed
827

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Pterodactyl\Contracts\Models;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
7+
interface Identifiable
8+
{
9+
public function scopeWhereIdentifier(Builder $builder, string $identifier): void;
10+
}

app/Contracts/Repository/ApiKeyRepositoryInterface.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,8 @@ interface ApiKeyRepositoryInterface extends RepositoryInterface
1212
*/
1313
public function getAccountKeys(User $user): Collection;
1414

15-
/**
16-
* Get all the application API keys that exist for a specific user.
17-
*/
18-
public function getApplicationKeys(User $user): Collection;
19-
2015
/**
2116
* Delete an account API key from the panel for a specific user.
2217
*/
2318
public function deleteAccountKey(User $user, string $identifier): int;
24-
25-
/**
26-
* Delete an application API key from the panel for a specific user.
27-
*/
28-
public function deleteApplicationKey(User $user, string $identifier): int;
2919
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Pterodactyl\Events\User;
4+
5+
use Pterodactyl\Models\User;
6+
use Illuminate\Foundation\Events\Dispatchable;
7+
8+
final class PasswordChanged
9+
{
10+
use Dispatchable;
11+
12+
public function __construct(public readonly User $user)
13+
{
14+
}
15+
}

app/Http/Controllers/Admin/ApiController.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
use Pterodactyl\Services\Acl\Api\AdminAcl;
1212
use Pterodactyl\Http\Controllers\Controller;
1313
use Pterodactyl\Services\Api\KeyCreationService;
14-
use Pterodactyl\Contracts\Repository\ApiKeyRepositoryInterface;
1514
use Pterodactyl\Http\Requests\Admin\Api\StoreApplicationApiKeyRequest;
1615

1716
class ApiController extends Controller
@@ -21,7 +20,6 @@ class ApiController extends Controller
2120
*/
2221
public function __construct(
2322
private AlertsMessageBag $alert,
24-
private ApiKeyRepositoryInterface $repository,
2523
private KeyCreationService $keyCreationService,
2624
) {
2725
}
@@ -32,7 +30,7 @@ public function __construct(
3230
public function index(Request $request): View
3331
{
3432
return view('admin.api.index', [
35-
'keys' => $this->repository->getApplicationKeys($request->user()),
33+
'keys' => ApiKey::query()->where('key_type', ApiKey::TYPE_APPLICATION)->get(),
3634
]);
3735
}
3836

@@ -78,7 +76,10 @@ public function store(StoreApplicationApiKeyRequest $request): RedirectResponse
7876
*/
7977
public function delete(Request $request, string $identifier): Response
8078
{
81-
$this->repository->deleteApplicationKey($request->user(), $identifier);
79+
ApiKey::query()
80+
->where('key_type', ApiKey::TYPE_APPLICATION)
81+
->where('identifier', $identifier)
82+
->delete();
8283

8384
return response('', 204);
8485
}

app/Http/Controllers/Admin/NodeAutoDeployController.php

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@
99
use Pterodactyl\Http\Controllers\Controller;
1010
use Illuminate\Contracts\Encryption\Encrypter;
1111
use Pterodactyl\Services\Api\KeyCreationService;
12-
use Pterodactyl\Repositories\Eloquent\ApiKeyRepository;
1312

1413
class NodeAutoDeployController extends Controller
1514
{
1615
/**
1716
* NodeAutoDeployController constructor.
1817
*/
1918
public function __construct(
20-
private ApiKeyRepository $repository,
2119
private Encrypter $encrypter,
2220
private KeyCreationService $keyCreationService,
2321
) {
@@ -31,17 +29,10 @@ public function __construct(
3129
*/
3230
public function __invoke(Request $request, Node $node): JsonResponse
3331
{
34-
/** @var ApiKey|null $key */
35-
$key = $this->repository->getApplicationKeys($request->user())
36-
->filter(function (ApiKey $key) {
37-
foreach ($key->getAttributes() as $permission => $value) {
38-
if ($permission === 'r_nodes' && $value === 1) {
39-
return true;
40-
}
41-
}
42-
43-
return false;
44-
})
32+
$key = ApiKey::query()
33+
->where('user_id', $request->user()->id)
34+
->where('key_type', ApiKey::TYPE_APPLICATION)
35+
->where('r_nodes', 1)
4536
->first();
4637

4738
// We couldn't find a key that exists for this user with only permission for

app/Http/Controllers/Api/Client/AccountController.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ public function updateEmail(UpdateEmailRequest $request): JsonResponse
5454
*/
5555
public function updatePassword(UpdatePasswordRequest $request): JsonResponse
5656
{
57-
$user = $this->updateService->handle($request->user(), $request->validated());
57+
$user = Activity::event('user:account.password-changed')->transaction(function () use ($request) {
58+
return $this->updateService->handle($request->user(), $request->validated());
59+
});
5860

5961
$guard = $this->manager->guard();
6062
// If you do not update the user in the session you'll end up working with a
@@ -68,8 +70,6 @@ public function updatePassword(UpdatePasswordRequest $request): JsonResponse
6870
$guard->logoutOtherDevices($request->input('password'));
6971
}
7072

71-
Activity::event('user:account.password-changed')->log();
72-
7373
return new JsonResponse([], Response::HTTP_NO_CONTENT);
7474
}
7575
}

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

Lines changed: 5 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
use Illuminate\Http\JsonResponse;
88
use Pterodactyl\Facades\Activity;
99
use Pterodactyl\Models\Permission;
10-
use Illuminate\Support\Facades\Log;
10+
use Pterodactyl\Jobs\RevokeSftpAccessJob;
1111
use Pterodactyl\Repositories\Eloquent\SubuserRepository;
1212
use Pterodactyl\Services\Subusers\SubuserCreationService;
1313
use Pterodactyl\Transformers\Api\Client\SubuserTransformer;
1414
use Pterodactyl\Repositories\Wings\DaemonRevocationRepository;
1515
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
16-
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
1716
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\GetSubuserRequest;
1817
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\StoreSubuserRequest;
1918
use Pterodactyl\Http\Requests\Api\Client\Servers\Subusers\DeleteSubuserRequest;
@@ -109,23 +108,12 @@ public function update(UpdateSubuserRequest $request, Server $server): array
109108
// Only update the database and hit up the Wings instance to invalidate JTI's if the permissions
110109
// have actually changed for the user.
111110
if ($permissions !== $current) {
112-
$log->transaction(function ($instance) use ($request, $subuser, $server) {
111+
$log->transaction(function () use ($request, $subuser, $server) {
113112
$this->repository->update($subuser->id, [
114113
'permissions' => $this->getDefaultPermissions($request),
115114
]);
116115

117-
try {
118-
$this->revocationRepository->setNode($server->node)->deauthorize(
119-
$subuser->user->uuid,
120-
[$server->uuid],
121-
);
122-
} catch (DaemonConnectionException $exception) {
123-
// Don't block this request if we can't connect to the Wings instance. Chances are it is
124-
// offline and the token will be invalid once Wings boots back.
125-
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
126-
127-
$instance->property('revoked', false);
128-
}
116+
RevokeSftpAccessJob::dispatch($subuser->user->uuid, $server);
129117
});
130118
}
131119

@@ -149,20 +137,10 @@ public function delete(DeleteSubuserRequest $request, Server $server): JsonRespo
149137
->property('email', $subuser->user->email)
150138
->property('revoked', true);
151139

152-
$log->transaction(function ($instance) use ($server, $subuser) {
140+
$log->transaction(function () use ($server, $subuser) {
153141
$subuser->delete();
154142

155-
try {
156-
$this->revocationRepository->setNode($server->node)->deauthorize(
157-
$subuser->user->uuid,
158-
[$server->uuid],
159-
);
160-
} catch (DaemonConnectionException $exception) {
161-
// Don't block this request if we can't connect to the Wings instance.
162-
Log::warning($exception, ['user_id' => $subuser->user_id, 'server_id' => $server->id]);
163-
164-
$instance->property('revoked', false);
165-
}
143+
RevokeSftpAccessJob::dispatch($subuser->user->uuid, $server);
166144
});
167145

168146
return new JsonResponse([], JsonResponse::HTTP_NO_CONTENT);

app/Http/Controllers/Api/Remote/Backups/BackupRemoteUploadController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function __invoke(Request $request, string $backup): JsonResponse
4949
// from messing with backups that they don't own.
5050
$server = $model->server;
5151
if ($server->node_id !== $node->id) {
52-
throw new HttpForbiddenException('You do not have permission to access that backup.');
52+
throw new HttpForbiddenException('Requesting node does not have permission to access this server.');
5353
}
5454

5555
// Prevent backups that have already been completed from trying to

app/Http/Controllers/Api/Remote/Backups/BackupStatusController.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public function index(ReportBackupCompleteRequest $request, string $backup): Jso
4545
/** @var \Pterodactyl\Models\Server $server */
4646
$server = $model->server;
4747
if ($server->node_id !== $node->id) {
48-
throw new HttpForbiddenException('You do not have permission to access that backup.');
48+
throw new HttpForbiddenException('Requesting node does not have permission to access this server.');
4949
}
5050

5151
if ($model->is_successful) {
@@ -95,6 +95,11 @@ public function restore(Request $request, string $backup): JsonResponse
9595
/** @var Backup $model */
9696
$model = Backup::query()->where('uuid', $backup)->firstOrFail();
9797

98+
$node = $request->attributes->get('node');
99+
if (! $model->server->node->is($node)) {
100+
throw new HttpForbiddenException('Requesting node does not have permission to access this server.');
101+
}
102+
98103
$model->server->update(['status' => null]);
99104

100105
Activity::event($request->boolean('successful') ? 'server:backup.restore-complete' : 'server.backup.restore-failed')

0 commit comments

Comments
 (0)