From 60f6e86b8bc951150f0c2d3387bd80ddeba50fb0 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 11 Apr 2020 15:35:32 -0700 Subject: [PATCH 1/3] Remove all of the old references to unused interfaces outside the test code --- .php_cs | 1 + .../Server/BulkPowerActionCommand.php | 13 +-- .../Server/BulkReinstallActionCommand.php | 24 ++-- .../Daemon/BaseRepositoryInterface.php | 68 ----------- .../Daemon/CommandRepositoryInterface.php | 19 --- .../ConfigurationRepositoryInterface.php | 19 --- .../Daemon/FileRepositoryInterface.php | 86 -------------- .../Daemon/PowerRepositoryInterface.php | 26 ----- .../Daemon/ServerRepositoryInterface.php | 83 ------------- .../Api/Remote/ValidateKeyController.php | 100 ---------------- app/Jobs/Schedule/RunTaskJob.php | 72 ++++-------- app/Providers/RepositoryServiceProvider.php | 17 --- .../SetDefaultAllocationService.php | 110 ------------------ .../RevokeMultipleDaemonKeysService.php | 89 -------------- 14 files changed, 44 insertions(+), 683 deletions(-) delete mode 100644 app/Contracts/Repository/Daemon/BaseRepositoryInterface.php delete mode 100644 app/Contracts/Repository/Daemon/CommandRepositoryInterface.php delete mode 100644 app/Contracts/Repository/Daemon/ConfigurationRepositoryInterface.php delete mode 100644 app/Contracts/Repository/Daemon/FileRepositoryInterface.php delete mode 100644 app/Contracts/Repository/Daemon/PowerRepositoryInterface.php delete mode 100644 app/Contracts/Repository/Daemon/ServerRepositoryInterface.php delete mode 100644 app/Http/Controllers/Api/Remote/ValidateKeyController.php delete mode 100644 app/Services/Allocations/SetDefaultAllocationService.php delete mode 100644 app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php diff --git a/.php_cs b/.php_cs index 8a52ffee..e72e8e70 100644 --- a/.php_cs +++ b/.php_cs @@ -47,6 +47,7 @@ return PhpCsFixer\Config::create() 'psr4' => true, 'random_api_migration' => true, 'single_line_throw' => false, + 'single_trait_insert_per_statement' => false, 'standardize_not_equals' => true, 'ternary_to_null_coalescing' => true, 'yoda_style' => [ diff --git a/app/Console/Commands/Server/BulkPowerActionCommand.php b/app/Console/Commands/Server/BulkPowerActionCommand.php index 5e728a03..38387990 100644 --- a/app/Console/Commands/Server/BulkPowerActionCommand.php +++ b/app/Console/Commands/Server/BulkPowerActionCommand.php @@ -5,14 +5,14 @@ namespace Pterodactyl\Console\Commands\Server; use Illuminate\Console\Command; use GuzzleHttp\Exception\RequestException; use Illuminate\Validation\ValidationException; -use Pterodactyl\Repositories\Daemon\PowerRepository; use Illuminate\Validation\Factory as ValidatorFactory; +use Pterodactyl\Repositories\Wings\DaemonPowerRepository; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; class BulkPowerActionCommand extends Command { /** - * @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface + * @var \Pterodactyl\Repositories\Wings\DaemonPowerRepository */ private $powerRepository; @@ -42,27 +42,26 @@ class BulkPowerActionCommand extends Command /** * BulkPowerActionCommand constructor. * - * @param \Pterodactyl\Repositories\Daemon\PowerRepository $powerRepository + * @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository * @param \Illuminate\Validation\Factory $validator */ public function __construct( - PowerRepository $powerRepository, + DaemonPowerRepository $powerRepository, ServerRepositoryInterface $repository, ValidatorFactory $validator ) { parent::__construct(); - $this->powerRepository = $powerRepository; $this->repository = $repository; $this->validator = $validator; + $this->powerRepository = $powerRepository; } /** * Handle the bulk power request. * * @throws \Illuminate\Validation\ValidationException - * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException */ public function handle() { @@ -105,7 +104,7 @@ class BulkPowerActionCommand extends Command $this->powerRepository ->setNode($server->node) ->setServer($server) - ->sendSignal($action); + ->send($action); } catch (RequestException $exception) { $this->output->error(trans('command/messages.server.power.action_failed', [ 'name' => $server->name, diff --git a/app/Console/Commands/Server/BulkReinstallActionCommand.php b/app/Console/Commands/Server/BulkReinstallActionCommand.php index b15fbfaa..a56cefc7 100644 --- a/app/Console/Commands/Server/BulkReinstallActionCommand.php +++ b/app/Console/Commands/Server/BulkReinstallActionCommand.php @@ -12,8 +12,8 @@ namespace Pterodactyl\Console\Commands\Server; use Webmozart\Assert\Assert; use Illuminate\Console\Command; use GuzzleHttp\Exception\RequestException; +use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Wings\DaemonServerRepository; -use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; use Pterodactyl\Services\Servers\ServerConfigurationStructureService; class BulkReinstallActionCommand extends Command @@ -21,23 +21,23 @@ class BulkReinstallActionCommand extends Command /** * @var \Pterodactyl\Services\Servers\ServerConfigurationStructureService */ - protected $configurationStructureService; + private $configurationStructureService; /** - * @var \Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface + * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository */ - protected $daemonRepository; + private $daemonRepository; + + /** + * @var \Pterodactyl\Repositories\Eloquent\ServerRepository + */ + private $repository; /** * @var string */ protected $description = 'Reinstall a single server, all servers on a node, or all servers on the panel.'; - /** - * @var \Pterodactyl\Repositories\Wings\DaemonServerRepository - */ - protected $repository; - /** * @var string */ @@ -50,12 +50,12 @@ class BulkReinstallActionCommand extends Command * * @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonRepository * @param \Pterodactyl\Services\Servers\ServerConfigurationStructureService $configurationStructureService - * @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository + * @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository */ public function __construct( DaemonServerRepository $daemonRepository, ServerConfigurationStructureService $configurationStructureService, - ServerRepositoryInterface $repository + ServerRepository $repository ) { parent::__construct(); @@ -101,7 +101,7 @@ class BulkReinstallActionCommand extends Command /** * Return the servers to be reinstalled. * - * @return \Illuminate\Database\Eloquent\Collection + * @return \Illuminate\Support\Collection */ private function getServersToProcess() { diff --git a/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php b/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php deleted file mode 100644 index 791dcc81..00000000 --- a/app/Contracts/Repository/Daemon/BaseRepositoryInterface.php +++ /dev/null @@ -1,68 +0,0 @@ -. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -namespace Pterodactyl\Http\Controllers\Api\Remote; - -use Spatie\Fractal\Fractal; -use Illuminate\Http\Response; -use Pterodactyl\Http\Controllers\Controller; -use Illuminate\Contracts\Foundation\Application; -use Illuminate\Foundation\Testing\HttpException; -use League\Fractal\Serializer\JsonApiSerializer; -use Pterodactyl\Transformers\Daemon\ApiKeyTransformer; -use Pterodactyl\Exceptions\Repository\RecordNotFoundException; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; - -class ValidateKeyController extends Controller -{ - /** - * @var \Illuminate\Contracts\Foundation\Application - */ - protected $app; - - /** - * @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface - */ - protected $daemonKeyRepository; - - /** - * @var \Spatie\Fractal\Fractal - */ - protected $fractal; - - /** - * ValidateKeyController constructor. - * - * @param \Illuminate\Contracts\Foundation\Application $app - * @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $daemonKeyRepository - * @param \Spatie\Fractal\Fractal $fractal - */ - public function __construct( - Application $app, - DaemonKeyRepositoryInterface $daemonKeyRepository, - Fractal $fractal - ) { - $this->app = $app; - $this->daemonKeyRepository = $daemonKeyRepository; - $this->fractal = $fractal; - } - - /** - * Return the server(s) and permissions associated with an API key. - * - * @param string $token - * @return array - * - * @throws \Illuminate\Foundation\Testing\HttpException - */ - public function index($token) - { - if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) { - throw new HttpException(Response::HTTP_NOT_IMPLEMENTED); - } - - try { - $key = $this->daemonKeyRepository->getKeyWithServer($token); - } catch (RecordNotFoundException $exception) { - throw new NotFoundHttpException; - } - - if ($key->getRelation('server')->suspended || $key->getRelation('server')->installed !== 1) { - throw new NotFoundHttpException; - } - - return $this->fractal->item($key, $this->app->make(ApiKeyTransformer::class), 'server') - ->serializeWith(JsonApiSerializer::class) - ->toArray(); - } -} diff --git a/app/Jobs/Schedule/RunTaskJob.php b/app/Jobs/Schedule/RunTaskJob.php index 0b09102a..bab5b9f6 100644 --- a/app/Jobs/Schedule/RunTaskJob.php +++ b/app/Jobs/Schedule/RunTaskJob.php @@ -3,33 +3,25 @@ namespace Pterodactyl\Jobs\Schedule; use Exception; -use Cake\Chronos\Chronos; +use Carbon\Carbon; use Pterodactyl\Jobs\Job; use InvalidArgumentException; +use Illuminate\Container\Container; use Illuminate\Queue\SerializesModels; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\DispatchesJobs; +use Pterodactyl\Repositories\Eloquent\TaskRepository; +use Pterodactyl\Repositories\Wings\DaemonPowerRepository; +use Pterodactyl\Repositories\Wings\DaemonCommandRepository; use Pterodactyl\Contracts\Repository\TaskRepositoryInterface; use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; class RunTaskJob extends Job implements ShouldQueue { use DispatchesJobs, InteractsWithQueue, SerializesModels; - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface - */ - protected $commandRepository; - - /** - * @var \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface - */ - protected $powerRepository; - /** * @var int */ @@ -41,7 +33,7 @@ class RunTaskJob extends Job implements ShouldQueue public $task; /** - * @var \Pterodactyl\Contracts\Repository\TaskRepositoryInterface + * @var \Pterodactyl\Repositories\Eloquent\TaskRepository */ protected $taskRepository; @@ -61,28 +53,24 @@ class RunTaskJob extends Job implements ShouldQueue /** * Run the job and send actions to the daemon running the server. * - * @param \Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface $commandRepository + * @param \Pterodactyl\Repositories\Wings\DaemonCommandRepository $commandRepository * @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService - * @param \Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface $powerRepository - * @param \Pterodactyl\Contracts\Repository\TaskRepositoryInterface $taskRepository + * @param \Pterodactyl\Repositories\Wings\DaemonPowerRepository $powerRepository + * @param \Pterodactyl\Repositories\Eloquent\TaskRepository $taskRepository * * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\Daemon\InvalidPowerSignalException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function handle( - CommandRepositoryInterface $commandRepository, + DaemonCommandRepository $commandRepository, DaemonKeyProviderService $keyProviderService, - PowerRepositoryInterface $powerRepository, - TaskRepositoryInterface $taskRepository + DaemonPowerRepository $powerRepository, + TaskRepository $taskRepository ) { - $this->commandRepository = $commandRepository; - $this->powerRepository = $powerRepository; $this->taskRepository = $taskRepository; $task = $this->taskRepository->getTaskForJobProcess($this->task); $server = $task->getRelation('server'); - $user = $server->getRelation('user'); // Do not process a task that is not set to active. if (! $task->getRelation('schedule')->is_active) { @@ -95,14 +83,10 @@ class RunTaskJob extends Job implements ShouldQueue // Perform the provided task against the daemon. switch ($task->action) { case 'power': - $this->powerRepository->setServer($server) - ->setToken($keyProviderService->handle($server, $user)) - ->sendSignal($task->payload); + $powerRepository->setServer($server)->send($task->payload); break; case 'command': - $this->commandRepository->setServer($server) - ->setToken($keyProviderService->handle($server, $user)) - ->send($task->payload); + $commandRepository->setServer($server)->send($task->payload); break; default: throw new InvalidArgumentException('Cannot run a task that points to a non-existent action.'); @@ -115,10 +99,7 @@ class RunTaskJob extends Job implements ShouldQueue /** * Handle a failure while sending the action to the daemon or otherwise processing the job. * - * @param null|\Exception $exception - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @param \Exception|null $exception */ public function failed(Exception $exception = null) { @@ -149,28 +130,25 @@ class RunTaskJob extends Job implements ShouldQueue /** * Marks the parent schedule as being complete. - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ private function markScheduleComplete() { - $repository = app()->make(ScheduleRepositoryInterface::class); - $repository->withoutFreshModel()->update($this->schedule, [ - 'is_processing' => false, - 'last_run_at' => Chronos::now()->toDateTimeString(), - ]); + Container::getInstance() + ->make(ScheduleRepositoryInterface::class) + ->withoutFreshModel() + ->update($this->schedule, [ + 'is_processing' => false, + 'last_run_at' => Carbon::now()->toDateTimeString(), + ]); } /** * Mark a specific task as no longer being queued. - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ private function markTaskNotQueued() { - $repository = app()->make(TaskRepositoryInterface::class); - $repository->update($this->task, ['is_queued' => false]); + Container::getInstance() + ->make(TaskRepositoryInterface::class) + ->update($this->task, ['is_queued' => false]); } } diff --git a/app/Providers/RepositoryServiceProvider.php b/app/Providers/RepositoryServiceProvider.php index 6ee5a8d5..ef8e6cb0 100644 --- a/app/Providers/RepositoryServiceProvider.php +++ b/app/Providers/RepositoryServiceProvider.php @@ -13,15 +13,12 @@ use Pterodactyl\Repositories\Eloquent\ApiKeyRepository; use Pterodactyl\Repositories\Eloquent\ServerRepository; use Pterodactyl\Repositories\Eloquent\SessionRepository; use Pterodactyl\Repositories\Eloquent\SubuserRepository; -use Pterodactyl\Repositories\Wings\DaemonFileRepository; use Pterodactyl\Repositories\Eloquent\DatabaseRepository; use Pterodactyl\Repositories\Eloquent\LocationRepository; use Pterodactyl\Repositories\Eloquent\ScheduleRepository; use Pterodactyl\Repositories\Eloquent\SettingsRepository; -use Pterodactyl\Repositories\Wings\DaemonPowerRepository; use Pterodactyl\Repositories\Eloquent\DaemonKeyRepository; use Pterodactyl\Repositories\Eloquent\AllocationRepository; -use Pterodactyl\Repositories\Wings\DaemonCommandRepository; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; use Pterodactyl\Repositories\Eloquent\EggVariableRepository; use Pterodactyl\Contracts\Repository\NestRepositoryInterface; @@ -39,18 +36,11 @@ use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface; use Pterodactyl\Contracts\Repository\LocationRepositoryInterface; use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface; use Pterodactyl\Contracts\Repository\SettingsRepositoryInterface; -use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository; use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface; use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface; use Pterodactyl\Contracts\Repository\EggVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\PowerRepositoryInterface; use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\CommandRepositoryInterface; use Pterodactyl\Contracts\Repository\ServerVariableRepositoryInterface; -use Pterodactyl\Contracts\Repository\Daemon\ConfigurationRepositoryInterface; -use Pterodactyl\Repositories\Wings\DaemonServerRepository as DaemonServerRepository; -use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface as DaemonServerRepositoryInterface; class RepositoryServiceProvider extends ServiceProvider { @@ -79,12 +69,5 @@ class RepositoryServiceProvider extends ServiceProvider $this->app->bind(SubuserRepositoryInterface::class, SubuserRepository::class); $this->app->bind(TaskRepositoryInterface::class, TaskRepository::class); $this->app->bind(UserRepositoryInterface::class, UserRepository::class); - - // Daemon Repositories - $this->app->bind(ConfigurationRepositoryInterface::class, DaemonConfigurationRepository::class); - $this->app->bind(CommandRepositoryInterface::class, DaemonCommandRepository::class); - $this->app->bind(DaemonServerRepositoryInterface::class, DaemonServerRepository::class); - $this->app->bind(FileRepositoryInterface::class, DaemonFileRepository::class); - $this->app->bind(PowerRepositoryInterface::class, DaemonPowerRepository::class); } } diff --git a/app/Services/Allocations/SetDefaultAllocationService.php b/app/Services/Allocations/SetDefaultAllocationService.php deleted file mode 100644 index b05b1c48..00000000 --- a/app/Services/Allocations/SetDefaultAllocationService.php +++ /dev/null @@ -1,110 +0,0 @@ -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->withoutFreshModel()->update($server->id, ['allocation_id' => $model->id]); - - // Update on the daemon. - try { - $this->daemonRepository->setServer($server)->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/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php b/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php deleted file mode 100644 index 84237f94..00000000 --- a/app/Services/DaemonKeys/RevokeMultipleDaemonKeysService.php +++ /dev/null @@ -1,89 +0,0 @@ -daemonRepository = $daemonRepository; - $this->repository = $repository; - } - - /** - * Grab all of the keys that exist for a single user and delete them from all - * daemon's that they are assigned to. If connection fails, this function will - * return an error. - * - * @param \Pterodactyl\Models\User $user - * @param bool $ignoreConnectionErrors - */ - public function handle(User $user, bool $ignoreConnectionErrors = false) - { - $keys = $this->repository->getKeysForRevocation($user); - - $keys->groupBy('node.id')->each(function ($group, $nodeId) use ($ignoreConnectionErrors) { - try { - $this->daemonRepository->setNode(collect($group)->first()->getRelation('node'))->revokeAccessKey(collect($group)->pluck('secret')->toArray()); - } catch (RequestException $exception) { - if (! $ignoreConnectionErrors) { - throw new DaemonConnectionException($exception); - } - - $this->setConnectionException($nodeId, $exception); - } - - $this->repository->deleteKeys(collect($group)->pluck('id')->toArray()); - }); - } - - /** - * Returns an array of exceptions that were returned by the handle function. - * - * @return RequestException[] - */ - public function getExceptions() - { - return $this->exceptions; - } - - /** - * Add an exception for a node to the array. - * - * @param int $node - * @param \GuzzleHttp\Exception\RequestException $exception - */ - protected function setConnectionException(int $node, RequestException $exception) - { - $this->exceptions[$node] = $exception; - } -} From 1327bbbbe547ae9cd4b86650fbad7b642687dce4 Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 11 Apr 2020 16:33:15 -0700 Subject: [PATCH 2/3] Fix logic to update a daemon's configuration --- .../Wings/DaemonConfigurationRepository.php | 11 ++- app/Services/Nodes/NodeUpdateService.php | 74 +++++++------------ 2 files changed, 34 insertions(+), 51 deletions(-) diff --git a/app/Repositories/Wings/DaemonConfigurationRepository.php b/app/Repositories/Wings/DaemonConfigurationRepository.php index 90b50477..f1b83cac 100644 --- a/app/Repositories/Wings/DaemonConfigurationRepository.php +++ b/app/Repositories/Wings/DaemonConfigurationRepository.php @@ -2,6 +2,7 @@ namespace Pterodactyl\Repositories\Wings; +use Pterodactyl\Models\Node; use GuzzleHttp\Exception\TransferException; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; @@ -25,17 +26,19 @@ class DaemonConfigurationRepository extends DaemonRepository } /** - * Updates the configuration information for a daemon. + * Updates the configuration information for a daemon. Updates the information for + * this instance using a passed-in model. This allows us to change plenty of information + * in the model, and still use the old, pre-update model to actually make the HTTP request. * - * @param array $attributes + * @param \Pterodactyl\Models\Node $node * @return \Psr\Http\Message\ResponseInterface * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException */ - public function update(array $attributes = []) + public function update(?Node $node) { try { return $this->getHttpClient()->post( - '/api/update', array_merge($this->node->getConfiguration(), $attributes) + '/api/update', ['json' => $node->getConfiguration()] ); } catch (TransferException $exception) { throw new DaemonConnectionException($exception); diff --git a/app/Services/Nodes/NodeUpdateService.php b/app/Services/Nodes/NodeUpdateService.php index 8ce610df..9582c4e1 100644 --- a/app/Services/Nodes/NodeUpdateService.php +++ b/app/Services/Nodes/NodeUpdateService.php @@ -5,11 +5,10 @@ namespace Pterodactyl\Services\Nodes; use Illuminate\Support\Str; use Pterodactyl\Models\Node; use GuzzleHttp\Exception\ConnectException; -use GuzzleHttp\Exception\RequestException; use Illuminate\Database\ConnectionInterface; use Illuminate\Contracts\Encryption\Encrypter; +use Pterodactyl\Repositories\Eloquent\NodeRepository; use Pterodactyl\Repositories\Daemon\ConfigurationRepository; -use Pterodactyl\Contracts\Repository\NodeRepositoryInterface; use Pterodactyl\Repositories\Wings\DaemonConfigurationRepository; use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException; use Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException; @@ -21,11 +20,6 @@ class NodeUpdateService */ private $connection; - /** - * @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface - */ - private $repository; - /** * @var \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository */ @@ -36,24 +30,29 @@ class NodeUpdateService */ private $encrypter; + /** + * @var \Pterodactyl\Repositories\Eloquent\NodeRepository + */ + private $repository; + /** * UpdateService constructor. * * @param \Illuminate\Database\ConnectionInterface $connection * @param \Illuminate\Contracts\Encryption\Encrypter $encrypter * @param \Pterodactyl\Repositories\Wings\DaemonConfigurationRepository $configurationRepository - * @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository + * @param \Pterodactyl\Repositories\Eloquent\NodeRepository $repository */ public function __construct( ConnectionInterface $connection, Encrypter $encrypter, DaemonConfigurationRepository $configurationRepository, - NodeRepositoryInterface $repository + NodeRepository $repository ) { $this->connection = $connection; - $this->repository = $repository; $this->configurationRepository = $configurationRepository; $this->encrypter = $encrypter; + $this->repository = $repository; } /** @@ -64,55 +63,36 @@ class NodeUpdateService * @param bool $resetToken * * @return \Pterodactyl\Models\Node - * - * @throws \Pterodactyl\Exceptions\Model\DataValidationException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException - * @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException - * @throws \Pterodactyl\Exceptions\Service\Node\ConfigurationNotPersistedException + * @throws \Throwable */ public function handle(Node $node, array $data, bool $resetToken = false) { if ($resetToken) { - $data['daemon_token'] = Str::random(Node::DAEMON_TOKEN_LENGTH); - $data['daemon_token_id'] = $this->encrypter->encrypt( - Str::random(Node::DAEMON_TOKEN_ID_LENGTH) - ); + $data['daemon_token'] = $this->encrypter->encrypt(Str::random(Node::DAEMON_TOKEN_LENGTH)); + $data['daemon_token_id'] = Str::random(Node::DAEMON_TOKEN_ID_LENGTH); } - $this->connection->beginTransaction(); + [$updated, $exception] = $this->connection->transaction(function () use ($data, $node) { + /** @var \Pterodactyl\Models\Node $updated */ + $updated = $this->repository->withFreshModel()->update($node->id, $data, true, true); - /** @var \Pterodactyl\Models\Node $updatedModel */ - $updatedModel = $this->repository->update($node->id, $data); + try { + $this->configurationRepository->setNode($node)->update($updated); + } catch (DaemonConnectionException $exception) { + if (! is_null($exception->getPrevious()) && $exception->getPrevious() instanceof ConnectException) { + return [$updated, true]; + } - try { - if ($resetToken) { - // We need to clone the new model and set it's authentication token to be the - // old one so we can connect. Then we will pass the new token through as an - // override on the call. - $cloned = $updatedModel->replicate(['daemon_token']); - $cloned->setAttribute('daemon_token', $node->getAttribute('daemon_token')); - - $this->configurationRepository->setNode($cloned)->update([ - 'daemon_token_id' => $updatedModel->daemon_token_id, - 'daemon_token' => $updatedModel->getDecryptedKey(), - ]); - } else { - $this->configurationRepository->setNode($updatedModel)->update(); + throw $exception; } - $this->connection->commit(); - } catch (RequestException $exception) { - // Failed to connect to the Daemon. Let's go ahead and save the configuration - // and let the user know they'll need to manually update. - if ($exception instanceof ConnectException) { - $this->connection->commit(); + return [$updated, false]; + }); - throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); - } - - throw new DaemonConnectionException($exception); + if ($exception) { + throw new ConfigurationNotPersistedException(trans('exceptions.node.daemon_off_config_updated')); } - return $updatedModel; + return $updated; } } From 3d224993cd88259cb6fbb330d6f25d9afcfe68bc Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 11 Apr 2020 18:27:47 -0700 Subject: [PATCH 3/3] Fix logic for parsing egg data to not explode with nested objects --- app/Services/Eggs/EggConfigurationService.php | 164 +++++++++++------- 1 file changed, 99 insertions(+), 65 deletions(-) diff --git a/app/Services/Eggs/EggConfigurationService.php b/app/Services/Eggs/EggConfigurationService.php index 2650be92..96b6a72b 100644 --- a/app/Services/Eggs/EggConfigurationService.php +++ b/app/Services/Eggs/EggConfigurationService.php @@ -1,23 +1,17 @@ . - * - * This software is licensed under the terms of the MIT license. - * https://opensource.org/licenses/MIT - */ namespace Pterodactyl\Services\Eggs; use Illuminate\Support\Arr; use Illuminate\Support\Str; -use Pterodactyl\Models\Egg; use Pterodactyl\Models\Server; use Pterodactyl\Contracts\Repository\EggRepositoryInterface; use Pterodactyl\Services\Servers\ServerConfigurationStructureService; class EggConfigurationService { + private const NOT_MATCHED = '__no_match'; + /** * @var \Pterodactyl\Contracts\Repository\EggRepositoryInterface */ @@ -109,63 +103,8 @@ class EggConfigurationService // can property map the egg placeholders to values. $structure = $this->configurationStructureService->handle($server, true); - foreach ($configs as $file => $data) { - foreach ($data->find ?? [] as &$value) { - preg_match('/^{{(?.*)}}$/', $value, $matches); - - if (! $key = $matches['key'] ?? null) { - continue; - } - - // Matched something in {{server.X}} format, now replace that with the actual - // value from the server properties. - // - // The Daemon supports server.X, env.X, and config.X placeholders. - if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) { - continue; - } - - // We don't want to do anything with config keys since the Daemon will need to handle - // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker - // interface to proxy through, but the Panel would be unaware of that. - if (Str::startsWith($key, 'config.')) { - $value = "{{{$key}}}"; - continue; - } - - // The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their - // respective values on the Daemon side. Ensure that anything referencing those properly - // replaces them with the matching config value. - switch ($key) { - case 'server.build.env.SERVER_MEMORY': - case 'env.SERVER_MEMORY': - $key = 'server.build.memory'; - break; - case 'server.build.env.SERVER_IP': - case 'env.SERVER_IP': - $key = 'server.build.default.ip'; - break; - case 'server.build.env.SERVER_PORT': - case 'env.SERVER_PORT': - $key = 'server.build.default.port'; - break; - } - - // Replace anything starting with "server." with the value out of the server configuration - // array that used to be created for the old daemon. - if (Str::startsWith($key, 'server.')) { - $value = Arr::get( - $structure, preg_replace('/^server\./', '', $key), '' - ); - continue; - } - - // Finally, replace anything starting with env. with the expected environment - // variable from the server configuration. - $value = Arr::get( - $structure, preg_replace('/^env\./', 'build.env.', $key), '' - ); - } + foreach ($configs as $file => &$data) { + $this->iterate($data->find, $structure); } $response = []; @@ -194,4 +133,99 @@ class EggConfigurationService return $response; } + + /** + * @param string $value + * @param array $structure + * @return string|null + */ + protected function matchAndReplaceKeys(string $value, array $structure): ?string + { + preg_match('/{{(?.*)}}/', $value, $matches); + + if (! $key = $matches['key'] ?? null) { + return self::NOT_MATCHED; + } + + // Matched something in {{server.X}} format, now replace that with the actual + // value from the server properties. + // + // The Daemon supports server.X, env.X, and config.X placeholders. + if (! Str::startsWith($key, ['server.', 'env.', 'config.'])) { + return self::NOT_MATCHED; + } + + // We don't want to do anything with config keys since the Daemon will need to handle + // that. For example, the Spigot egg uses "config.docker.interface" to identify the Docker + // interface to proxy through, but the Panel would be unaware of that. + if (Str::startsWith($key, 'config.')) { + return "{{{$key}}}"; + } + + // The legacy Daemon would set SERVER_MEMORY, SERVER_IP, and SERVER_PORT with their + // respective values on the Daemon side. Ensure that anything referencing those properly + // replaces them with the matching config value. + switch ($key) { + case 'server.build.env.SERVER_MEMORY': + case 'env.SERVER_MEMORY': + $key = 'server.build.memory'; + break; + case 'server.build.env.SERVER_IP': + case 'env.SERVER_IP': + $key = 'server.build.default.ip'; + break; + case 'server.build.env.SERVER_PORT': + case 'env.SERVER_PORT': + $key = 'server.build.default.port'; + break; + } + + // Replace anything starting with "server." with the value out of the server configuration + // array that used to be created for the old daemon. + if (Str::startsWith($key, 'server.')) { + $plucked = Arr::get( + $structure, preg_replace('/^server\./', '', $key), '' + ); + + return preg_replace('/{{(.*)}}/', $plucked, $value); + } + + // Finally, replace anything starting with env. with the expected environment + // variable from the server configuration. + $plucked = Arr::get( + $structure, preg_replace('/^env\./', 'build.env.', $key), '' + ); + + return preg_replace('/{{(.*)}}/', $plucked, $value); + } + + /** + * Iterates over a set of "find" values for a given file in the parser configuration. If + * the value of the line match is something iterable, continue iterating, otherwise perform + * a match & replace. + * + * @param mixed $data + * @param array $structure + */ + private function iterate(&$data, array $structure) + { + if (! is_iterable($data) && ! is_object($data)) { + return; + } + + foreach ($data as &$value) { + if (is_iterable($value) || is_object($value)) { + $this->iterate($value, $structure); + + continue; + } + + $response = $this->matchAndReplaceKeys($value, $structure); + if ($response === self::NOT_MATCHED) { + continue; + } + + $value = $response; + } + } }