diff --git a/CHANGELOG.md b/CHANGELOG.md index 990cc2937..67b19c3a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines. * Ability to delete users and locations via the CLI. * You can now require 2FA for all users, admins only, or at will using a simple configuration in the Admin CP. * Added ability to export and import service options and their associated settings and environment variables via the Admin CP. +* Default allocation for a server can be changed on the front-end by users. This includes two new subuser permissions as well. ### Changed * Theme colors and login pages updated to give a more unique feel to the project. diff --git a/app/Exceptions/Service/Allocation/AllocationDoesNotBelongToServerException.php b/app/Exceptions/Service/Allocation/AllocationDoesNotBelongToServerException.php new file mode 100644 index 000000000..81f056b56 --- /dev/null +++ b/app/Exceptions/Service/Allocation/AllocationDoesNotBelongToServerException.php @@ -0,0 +1,9 @@ +attributes->get('server'); - $this->injectJavascript(); + $this->authorize('view-databases', $server); + $this->setRequest($request)->injectJavascript(); return view('server.databases.index', [ 'databases' => $this->repository->getDatabasesForServer($server->id), @@ -58,11 +61,14 @@ class DatabaseController extends Controller * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\JsonResponse * + * @throws \Illuminate\Auth\Access\AuthorizationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function update(Request $request): JsonResponse { + $this->authorize('reset-db-password', $request->attributes->get('server')); + $password = str_random(20); $this->passwordService->handle($request->attributes->get('database'), $password); diff --git a/app/Http/Controllers/Server/ServerController.php b/app/Http/Controllers/Server/ServerController.php index 9b4208319..65700b24f 100644 --- a/app/Http/Controllers/Server/ServerController.php +++ b/app/Http/Controllers/Server/ServerController.php @@ -86,28 +86,6 @@ class ServerController extends Controller ]); } - /** - * Returns the database overview for a server. - * - * @param \Illuminate\Http\Request $request - * @param string $uuid - * @return \Illuminate\View\View - */ - public function getDatabases(Request $request, $uuid) - { - $server = Models\Server::byUuid($uuid); - $this->authorize('view-databases', $server); - - $server->load('node', 'databases.host'); - $server->js(); - - return view('server.settings.databases', [ - 'server' => $server, - 'node' => $server->node, - 'databases' => $server->databases, - ]); - } - /** * Returns the SFTP overview for a server. * diff --git a/app/Http/Controllers/Server/Settings/AllocationController.php b/app/Http/Controllers/Server/Settings/AllocationController.php new file mode 100644 index 000000000..18a42f963 --- /dev/null +++ b/app/Http/Controllers/Server/Settings/AllocationController.php @@ -0,0 +1,97 @@ +defaultAllocationService = $defaultAllocationService; + $this->hashids = $hashids; + $this->repository = $repository; + } + + /** + * Render the allocation management overview page for a server. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\View\View + * + * @throws \Illuminate\Auth\Access\AuthorizationException + */ + public function index(Request $request): View + { + $server = $request->attributes->get('server'); + $this->authorize('view-allocations', $server); + $this->setRequest($request)->injectJavascript(); + + return view('server.settings.allocation', [ + 'allocations' => $this->repository->findWhere([['server_id', '=', $server->id]]), + ]); + } + + /** + * Update the default allocation for a server. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + * + * @throws \Illuminate\Auth\Access\AuthorizationException + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + */ + public function update(Request $request): JsonResponse + { + $server = $request->attributes->get('server'); + $this->authorize('edit-allocation', $server); + + $allocation = $this->hashids->decodeFirst($request->input('allocation'), 0); + + try { + $this->defaultAllocationService->handle($server->id, $allocation); + } catch (AllocationDoesNotBelongToServerException $exception) { + return response()->json(['error' => 'No matching allocation was located for this server.'], 404); + } + + return response()->json(); + } +} diff --git a/app/Models/Allocation.php b/app/Models/Allocation.php index 9593a7744..bb77647d9 100644 --- a/app/Models/Allocation.php +++ b/app/Models/Allocation.php @@ -64,6 +64,16 @@ class Allocation extends Model implements CleansAttributes, ValidableContract 'server_id' => 'nullable|exists:servers,id', ]; + /** + * Return a hashid encoded string to represent the ID of the allocation. + * + * @return string + */ + public function getHashidAttribute() + { + return app()->make('hashids')->encode($this->id); + } + /** * Accessor to automatically provide the IP alias if defined. * diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 1fc57cc57..61b67e487 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -86,7 +86,8 @@ class Permission extends Model implements CleansAttributes, ValidableContract 'delete-subuser' => null, ], 'server' => [ - 'set-connection' => null, + 'view-allocations' => null, + 'edit-allocation' => null, 'view-startup' => null, 'edit-startup' => null, ], diff --git a/app/Services/Allocations/SetDefaultAllocationService.php b/app/Services/Allocations/SetDefaultAllocationService.php new file mode 100644 index 000000000..66a858be3 --- /dev/null +++ b/app/Services/Allocations/SetDefaultAllocationService.php @@ -0,0 +1,110 @@ +connection = $connection; + $this->daemonRepository = $daemonRepository; + $this->repository = $repository; + $this->serverRepository = $serverRepository; + } + + /** + * Update the default allocation for a server only if that allocation is currently + * assigned to the specified server. + * + * @param int|\Pterodactyl\Models\Server $server + * @param int $allocation + * @return \Pterodactyl\Models\Allocation + * + * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException + * @throws \Pterodactyl\Exceptions\Model\DataValidationException + * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException + */ + public function handle($server, int $allocation): Allocation + { + if (! $server instanceof Server) { + $server = $this->serverRepository->find($server); + } + + $allocations = $this->repository->findWhere([['server_id', '=', $server->id]]); + $model = $allocations->filter(function ($model) use ($allocation) { + return $model->id === $allocation; + })->first(); + + if (! $model instanceof Allocation) { + throw new AllocationDoesNotBelongToServerException; + } + + $this->connection->beginTransaction(); + $this->serverRepository->withoutFresh()->update($server->id, ['allocation_id' => $model->id]); + + // Update on the daemon. + try { + $this->daemonRepository->setAccessServer($server->uuid)->setNode($server->node_id)->update([ + 'build' => [ + 'default' => [ + 'ip' => $model->ip, + 'port' => $model->port, + ], + 'ports|overwrite' => $allocations->groupBy('ip')->map(function ($item) { + return $item->pluck('port'); + })->toArray(), + ], + ]); + + $this->connection->commit(); + } catch (RequestException $exception) { + $this->connection->rollBack(); + throw new DaemonConnectionException($exception); + } + + return $model; + } +} diff --git a/app/Traits/Controllers/JavascriptInjection.php b/app/Traits/Controllers/JavascriptInjection.php index cce577819..7c7ee3c16 100644 --- a/app/Traits/Controllers/JavascriptInjection.php +++ b/app/Traits/Controllers/JavascriptInjection.php @@ -14,17 +14,34 @@ use Illuminate\Http\Request; trait JavascriptInjection { + /** + * @var \Illuminate\Http\Request + */ + private $request; + + /** + * Set the request object to use when injecting JS. + * + * @param \Illuminate\Http\Request $request + * @return $this + */ + public function setRequest(Request $request) + { + $this->request = $request; + + return $this; + } + /** * Injects server javascript into the page to be used by other services. * - * @param array $args - * @param bool $overwrite - * @param \Illuminate\Http\Request|null $request + * @param array $args + * @param bool $overwrite * @return array */ - public function injectJavascript($args = [], $overwrite = false, Request $request = null) + public function injectJavascript($args = [], $overwrite = false) { - $request = $request ?? app()->make(Request::class); + $request = $this->request ?? app()->make(Request::class); $server = $request->attributes->get('server'); $token = $request->attributes->get('server_token'); diff --git a/resources/lang/en/navigation.php b/resources/lang/en/navigation.php index 5693d825d..8435eba77 100644 --- a/resources/lang/en/navigation.php +++ b/resources/lang/en/navigation.php @@ -20,7 +20,7 @@ return [ 'subusers' => 'Subusers', 'schedules' => 'Schedules', 'configuration' => 'Configuration', - 'port_allocations' => 'Port Allocations', + 'port_allocations' => 'Allocation Settings', 'sftp_settings' => 'SFTP Settings', 'startup_parameters' => 'Startup Parameters', 'databases' => 'Databases', diff --git a/resources/lang/en/server.php b/resources/lang/en/server.php index ec570e4a8..489803b87 100644 --- a/resources/lang/en/server.php +++ b/resources/lang/en/server.php @@ -189,9 +189,13 @@ return [ 'title' => 'Delete Subuser', 'description' => 'Allows a user to delete other subusers on the server.', ], - 'set_connection' => [ - 'title' => 'Set Default Connection', - 'description' => 'Allows user to set the default connection used for a server as well as view avaliable ports.', + 'view_allocations' => [ + 'title' => 'View Allocations', + 'description' => 'Allows user to view all of the IPs and ports assigned to a server.', + ], + 'edit_allocation' => [ + 'title' => 'Edit Default Connection', + 'description' => 'Allows user to change the default connection allocation to use for a server.', ], 'view_startup' => [ 'title' => 'View Startup Command', diff --git a/resources/themes/pterodactyl/layouts/master.blade.php b/resources/themes/pterodactyl/layouts/master.blade.php index 1a53c4089..9d48dc9e1 100644 --- a/resources/themes/pterodactyl/layouts/master.blade.php +++ b/resources/themes/pterodactyl/layouts/master.blade.php @@ -166,7 +166,7 @@ @endcan - @if(Gate::allows('view-startup', $server) || Gate::allows('view-sftp', $server) || Gate::allows('view-databases', $server) || Gate::allows('view-allocation', $server)) + @if(Gate::allows('view-startup', $server) || Gate::allows('view-sftp', $server) || Gate::allows('view-allocation', $server))
  • @if($allocation->id === $server->allocation_id) - @lang('strings.primary') + @lang('strings.primary') @else - @lang('strings.make_primary') + @lang('strings.make_primary') @endif @@ -60,6 +60,9 @@ +
    @@ -79,37 +82,39 @@ @parent {!! Theme::js('js/frontend/server.socket.js') !!} @endsection diff --git a/routes/server.php b/routes/server.php index 6386b6618..fc658b673 100644 --- a/routes/server.php +++ b/routes/server.php @@ -18,9 +18,11 @@ Route::get('/console', 'ConsoleController@console')->name('server.console'); | */ Route::group(['prefix' => 'settings'], function () { + Route::get('/allocation', 'Settings\AllocationController@index')->name('server.settings.allocation'); + Route::patch('/allocation', 'Settings\AllocationController@update'); + Route::get('/sftp', 'ServerController@getSFTP')->name('server.settings.sftp'); Route::get('/startup', 'ServerController@getStartup')->name('server.settings.startup'); - Route::get('/allocation', 'ServerController@getAllocation')->name('server.settings.allocation'); Route::post('/sftp', 'ServerController@postSettingsSFTP'); Route::post('/startup', 'ServerController@postSettingsStartup');