Skip to content

Commit d50ea18

Browse files
committed
Add support for changing the server default allocation as a normal user
1 parent 5a3428f commit d50ea18

File tree

14 files changed

+308
-68
lines changed

14 files changed

+308
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
1313
* Ability to delete users and locations via the CLI.
1414
* You can now require 2FA for all users, admins only, or at will using a simple configuration in the Admin CP.
1515
* Added ability to export and import service options and their associated settings and environment variables via the Admin CP.
16+
* Default allocation for a server can be changed on the front-end by users. This includes two new subuser permissions as well.
1617

1718
### Changed
1819
* Theme colors and login pages updated to give a more unique feel to the project.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Pterodactyl\Exceptions\Service\Allocation;
4+
5+
use Pterodactyl\Exceptions\PterodactylException;
6+
7+
class AllocationDoesNotBelongToServerException extends PterodactylException
8+
{
9+
}

app/Http/Controllers/Server/DatabaseController.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,14 @@ public function __construct(DatabasePasswordService $passwordService, DatabaseRe
4141
*
4242
* @param \Illuminate\Http\Request $request
4343
* @return \Illuminate\View\View
44+
*
45+
* @throws \Illuminate\Auth\Access\AuthorizationException
4446
*/
4547
public function index(Request $request): View
4648
{
4749
$server = $request->attributes->get('server');
48-
$this->injectJavascript();
50+
$this->authorize('view-databases', $server);
51+
$this->setRequest($request)->injectJavascript();
4952

5053
return view('server.databases.index', [
5154
'databases' => $this->repository->getDatabasesForServer($server->id),
@@ -58,11 +61,14 @@ public function index(Request $request): View
5861
* @param \Illuminate\Http\Request $request
5962
* @return \Illuminate\Http\JsonResponse
6063
*
64+
* @throws \Illuminate\Auth\Access\AuthorizationException
6165
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
6266
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
6367
*/
6468
public function update(Request $request): JsonResponse
6569
{
70+
$this->authorize('reset-db-password', $request->attributes->get('server'));
71+
6672
$password = str_random(20);
6773
$this->passwordService->handle($request->attributes->get('database'), $password);
6874

app/Http/Controllers/Server/ServerController.php

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -86,28 +86,6 @@ public function getStartup(Request $request, $uuid)
8686
]);
8787
}
8888

89-
/**
90-
* Returns the database overview for a server.
91-
*
92-
* @param \Illuminate\Http\Request $request
93-
* @param string $uuid
94-
* @return \Illuminate\View\View
95-
*/
96-
public function getDatabases(Request $request, $uuid)
97-
{
98-
$server = Models\Server::byUuid($uuid);
99-
$this->authorize('view-databases', $server);
100-
101-
$server->load('node', 'databases.host');
102-
$server->js();
103-
104-
return view('server.settings.databases', [
105-
'server' => $server,
106-
'node' => $server->node,
107-
'databases' => $server->databases,
108-
]);
109-
}
110-
11189
/**
11290
* Returns the SFTP overview for a server.
11391
*
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\Server\Settings;
4+
5+
use Illuminate\View\View;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Response;
8+
use Illuminate\Http\JsonResponse;
9+
use Pterodactyl\Http\Controllers\Controller;
10+
use Pterodactyl\Contracts\Extensions\HashidsInterface;
11+
use Pterodactyl\Traits\Controllers\JavascriptInjection;
12+
use Pterodactyl\Services\Allocations\SetDefaultAllocationService;
13+
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
14+
use Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException;
15+
16+
class AllocationController extends Controller
17+
{
18+
use JavascriptInjection;
19+
20+
/**
21+
* @var \Pterodactyl\Services\Allocations\SetDefaultAllocationService
22+
*/
23+
private $defaultAllocationService;
24+
25+
/**
26+
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
27+
*/
28+
private $hashids;
29+
30+
/**
31+
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
32+
*/
33+
private $repository;
34+
35+
/**
36+
* AllocationController constructor.
37+
*
38+
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
39+
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
40+
* @param \Pterodactyl\Services\Allocations\SetDefaultAllocationService $defaultAllocationService
41+
*/
42+
public function __construct(
43+
AllocationRepositoryInterface $repository,
44+
HashidsInterface $hashids,
45+
SetDefaultAllocationService $defaultAllocationService
46+
) {
47+
$this->defaultAllocationService = $defaultAllocationService;
48+
$this->hashids = $hashids;
49+
$this->repository = $repository;
50+
}
51+
52+
/**
53+
* Render the allocation management overview page for a server.
54+
*
55+
* @param \Illuminate\Http\Request $request
56+
* @return \Illuminate\View\View
57+
*
58+
* @throws \Illuminate\Auth\Access\AuthorizationException
59+
*/
60+
public function index(Request $request): View
61+
{
62+
$server = $request->attributes->get('server');
63+
$this->authorize('view-allocations', $server);
64+
$this->setRequest($request)->injectJavascript();
65+
66+
return view('server.settings.allocation', [
67+
'allocations' => $this->repository->findWhere([['server_id', '=', $server->id]]),
68+
]);
69+
}
70+
71+
/**
72+
* Update the default allocation for a server.
73+
*
74+
* @param \Illuminate\Http\Request $request
75+
* @return \Illuminate\Http\JsonResponse
76+
*
77+
* @throws \Illuminate\Auth\Access\AuthorizationException
78+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
79+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
80+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
81+
*/
82+
public function update(Request $request): JsonResponse
83+
{
84+
$server = $request->attributes->get('server');
85+
$this->authorize('edit-allocation', $server);
86+
87+
$allocation = $this->hashids->decodeFirst($request->input('allocation'), 0);
88+
89+
try {
90+
$this->defaultAllocationService->handle($server->id, $allocation);
91+
} catch (AllocationDoesNotBelongToServerException $exception) {
92+
return response()->json(['error' => 'No matching allocation was located for this server.'], 404);
93+
}
94+
95+
return response()->json();
96+
}
97+
}

app/Models/Allocation.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ class Allocation extends Model implements CleansAttributes, ValidableContract
6464
'server_id' => 'nullable|exists:servers,id',
6565
];
6666

67+
/**
68+
* Return a hashid encoded string to represent the ID of the allocation.
69+
*
70+
* @return string
71+
*/
72+
public function getHashidAttribute()
73+
{
74+
return app()->make('hashids')->encode($this->id);
75+
}
76+
6777
/**
6878
* Accessor to automatically provide the IP alias if defined.
6979
*

app/Models/Permission.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ class Permission extends Model implements CleansAttributes, ValidableContract
8686
'delete-subuser' => null,
8787
],
8888
'server' => [
89-
'set-connection' => null,
89+
'view-allocations' => null,
90+
'edit-allocation' => null,
9091
'view-startup' => null,
9192
'edit-startup' => null,
9293
],
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
<?php
2+
3+
namespace Pterodactyl\Services\Allocations;
4+
5+
use Pterodactyl\Models\Server;
6+
use Pterodactyl\Models\Allocation;
7+
use GuzzleHttp\Exception\RequestException;
8+
use Illuminate\Database\ConnectionInterface;
9+
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
10+
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
11+
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
12+
use Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException;
13+
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonRepositoryInterface;
14+
15+
class SetDefaultAllocationService
16+
{
17+
/**
18+
* @var \Illuminate\Database\ConnectionInterface
19+
*/
20+
private $connection;
21+
22+
/**
23+
* @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface
24+
*/
25+
private $daemonRepository;
26+
27+
/**
28+
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
29+
*/
30+
private $repository;
31+
32+
/**
33+
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
34+
*/
35+
private $serverRepository;
36+
37+
/**
38+
* SetDefaultAllocationService constructor.
39+
*
40+
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
41+
* @param \Illuminate\Database\ConnectionInterface $connection
42+
* @param \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface $daemonRepository
43+
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
44+
*/
45+
public function __construct(
46+
AllocationRepositoryInterface $repository,
47+
ConnectionInterface $connection,
48+
DaemonRepositoryInterface $daemonRepository,
49+
ServerRepositoryInterface $serverRepository
50+
) {
51+
$this->connection = $connection;
52+
$this->daemonRepository = $daemonRepository;
53+
$this->repository = $repository;
54+
$this->serverRepository = $serverRepository;
55+
}
56+
57+
/**
58+
* Update the default allocation for a server only if that allocation is currently
59+
* assigned to the specified server.
60+
*
61+
* @param int|\Pterodactyl\Models\Server $server
62+
* @param int $allocation
63+
* @return \Pterodactyl\Models\Allocation
64+
*
65+
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
66+
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
67+
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
68+
* @throws \Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException
69+
*/
70+
public function handle($server, int $allocation): Allocation
71+
{
72+
if (! $server instanceof Server) {
73+
$server = $this->serverRepository->find($server);
74+
}
75+
76+
$allocations = $this->repository->findWhere([['server_id', '=', $server->id]]);
77+
$model = $allocations->filter(function ($model) use ($allocation) {
78+
return $model->id === $allocation;
79+
})->first();
80+
81+
if (! $model instanceof Allocation) {
82+
throw new AllocationDoesNotBelongToServerException;
83+
}
84+
85+
$this->connection->beginTransaction();
86+
$this->serverRepository->withoutFresh()->update($server->id, ['allocation_id' => $model->id]);
87+
88+
// Update on the daemon.
89+
try {
90+
$this->daemonRepository->setAccessServer($server->uuid)->setNode($server->node_id)->update([
91+
'build' => [
92+
'default' => [
93+
'ip' => $model->ip,
94+
'port' => $model->port,
95+
],
96+
'ports|overwrite' => $allocations->groupBy('ip')->map(function ($item) {
97+
return $item->pluck('port');
98+
})->toArray(),
99+
],
100+
]);
101+
102+
$this->connection->commit();
103+
} catch (RequestException $exception) {
104+
$this->connection->rollBack();
105+
throw new DaemonConnectionException($exception);
106+
}
107+
108+
return $model;
109+
}
110+
}

app/Traits/Controllers/JavascriptInjection.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,34 @@
1414

1515
trait JavascriptInjection
1616
{
17+
/**
18+
* @var \Illuminate\Http\Request
19+
*/
20+
private $request;
21+
22+
/**
23+
* Set the request object to use when injecting JS.
24+
*
25+
* @param \Illuminate\Http\Request $request
26+
* @return $this
27+
*/
28+
public function setRequest(Request $request)
29+
{
30+
$this->request = $request;
31+
32+
return $this;
33+
}
34+
1735
/**
1836
* Injects server javascript into the page to be used by other services.
1937
*
20-
* @param array $args
21-
* @param bool $overwrite
22-
* @param \Illuminate\Http\Request|null $request
38+
* @param array $args
39+
* @param bool $overwrite
2340
* @return array
2441
*/
25-
public function injectJavascript($args = [], $overwrite = false, Request $request = null)
42+
public function injectJavascript($args = [], $overwrite = false)
2643
{
27-
$request = $request ?? app()->make(Request::class);
44+
$request = $this->request ?? app()->make(Request::class);
2845
$server = $request->attributes->get('server');
2946
$token = $request->attributes->get('server_token');
3047

resources/lang/en/navigation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
'subusers' => 'Subusers',
2121
'schedules' => 'Schedules',
2222
'configuration' => 'Configuration',
23-
'port_allocations' => 'Port Allocations',
23+
'port_allocations' => 'Allocation Settings',
2424
'sftp_settings' => 'SFTP Settings',
2525
'startup_parameters' => 'Startup Parameters',
2626
'databases' => 'Databases',

0 commit comments

Comments
 (0)