Merge branch 'develop' into feature/server-mounts
This commit is contained in:
commit
29876e023b
166 changed files with 5482 additions and 4130 deletions
|
@ -1,87 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* 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\Services\DaemonKeys;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
||||
|
||||
class DaemonKeyCreationService
|
||||
{
|
||||
/**
|
||||
* @var \Carbon\Carbon
|
||||
*/
|
||||
protected $carbon;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* DaemonKeyCreationService constructor.
|
||||
*
|
||||
* @param \Carbon\Carbon $carbon
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
Carbon $carbon,
|
||||
ConfigRepository $config,
|
||||
DaemonKeyRepositoryInterface $repository
|
||||
) {
|
||||
$this->carbon = $carbon;
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new daemon key to be used when connecting to a daemon.
|
||||
*
|
||||
* @param int $server
|
||||
* @param int $user
|
||||
* @return string
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function handle(int $server, int $user)
|
||||
{
|
||||
$secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40);
|
||||
|
||||
$this->repository->withoutFreshModel()->create([
|
||||
'user_id' => $user,
|
||||
'server_id' => $server,
|
||||
'secret' => $secret,
|
||||
'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(),
|
||||
]);
|
||||
|
||||
return $secret;
|
||||
}
|
||||
}
|
|
@ -1,121 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* 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\Services\DaemonKeys;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
||||
|
||||
class DaemonKeyProviderService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService
|
||||
*/
|
||||
private $keyCreationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService
|
||||
*/
|
||||
private $keyUpdateService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
||||
*/
|
||||
private $subuserRepository;
|
||||
|
||||
/**
|
||||
* GetDaemonKeyService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyCreationService $keyCreationService
|
||||
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyUpdateService $keyUpdateService
|
||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
|
||||
*/
|
||||
public function __construct(
|
||||
DaemonKeyCreationService $keyCreationService,
|
||||
DaemonKeyRepositoryInterface $repository,
|
||||
DaemonKeyUpdateService $keyUpdateService,
|
||||
SubuserRepositoryInterface $subuserRepository
|
||||
) {
|
||||
$this->keyCreationService = $keyCreationService;
|
||||
$this->keyUpdateService = $keyUpdateService;
|
||||
$this->repository = $repository;
|
||||
$this->subuserRepository = $subuserRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the access key for a user on a specific server.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @param bool $updateIfExpired
|
||||
* @return string
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle(Server $server, User $user, $updateIfExpired = true): string
|
||||
{
|
||||
try {
|
||||
$key = $this->repository->findFirstWhere([
|
||||
['user_id', '=', $user->id],
|
||||
['server_id', '=', $server->id],
|
||||
]);
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
// If key doesn't exist but we are an admin or the server owner,
|
||||
// create it.
|
||||
if ($user->root_admin || $user->id === $server->owner_id) {
|
||||
return $this->keyCreationService->handle($server->id, $user->id);
|
||||
}
|
||||
|
||||
// Check if user is a subuser for this server. Ideally they should always have
|
||||
// a record associated with them in the database, but we should still handle
|
||||
// that potentiality here.
|
||||
//
|
||||
// If no subuser is found, a RecordNotFoundException will be thrown, thus handling
|
||||
// the parent error as well.
|
||||
$subuser = $this->subuserRepository->findFirstWhere([
|
||||
['user_id', '=', $user->id],
|
||||
['server_id', '=', $server->id],
|
||||
]);
|
||||
|
||||
return $this->keyCreationService->handle($subuser->server_id, $subuser->user_id);
|
||||
}
|
||||
|
||||
if (! $updateIfExpired || Carbon::now()->diffInSeconds($key->expires_at, false) > 0) {
|
||||
return $key->secret;
|
||||
}
|
||||
|
||||
return $this->keyUpdateService->handle($key->id);
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
<?php
|
||||
/*
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* 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\Services\DaemonKeys;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface;
|
||||
|
||||
class DaemonKeyUpdateService
|
||||
{
|
||||
/**
|
||||
* @var \Carbon\Carbon
|
||||
*/
|
||||
protected $carbon;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* DaemonKeyUpdateService constructor.
|
||||
*
|
||||
* @param \Carbon\Carbon $carbon
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\DaemonKeyRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
Carbon $carbon,
|
||||
ConfigRepository $config,
|
||||
DaemonKeyRepositoryInterface $repository
|
||||
) {
|
||||
$this->carbon = $carbon;
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a daemon key to expire the previous one.
|
||||
*
|
||||
* @param int $key
|
||||
* @return string
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle($key)
|
||||
{
|
||||
Assert::integerish($key, 'First argument passed to handle must be an integer, received %s.');
|
||||
|
||||
$secret = DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER . str_random(40);
|
||||
$this->repository->withoutFreshModel()->update($key, [
|
||||
'secret' => $secret,
|
||||
'expires_at' => $this->carbon->now()->addMinutes($this->config->get('pterodactyl.api.key_expire_time'))->toDateTimeString(),
|
||||
]);
|
||||
|
||||
return $secret;
|
||||
}
|
||||
}
|
|
@ -3,19 +3,22 @@
|
|||
namespace Pterodactyl\Services\Databases;
|
||||
|
||||
use Exception;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Helpers\Utilities;
|
||||
use Illuminate\Database\DatabaseManager;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Extensions\DynamicDatabaseConnection;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException;
|
||||
use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
||||
|
||||
class DatabaseManagementService
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Database\DatabaseManager
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Extensions\DynamicDatabaseConnection
|
||||
|
@ -33,84 +36,113 @@ class DatabaseManagementService
|
|||
private $repository;
|
||||
|
||||
/**
|
||||
* Determines if the service should validate the user's ability to create an additional
|
||||
* database for this server. In almost all cases this should be true, but to keep things
|
||||
* flexible you can also set it to false and create more databases than the server is
|
||||
* allocated.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $useRandomHost = false;
|
||||
protected $validateDatabaseLimit = true;
|
||||
|
||||
/**
|
||||
* CreationService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\DatabaseManager $database
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Extensions\DynamicDatabaseConnection $dynamic
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
*/
|
||||
public function __construct(
|
||||
DatabaseManager $database,
|
||||
ConnectionInterface $connection,
|
||||
DynamicDatabaseConnection $dynamic,
|
||||
DatabaseRepositoryInterface $repository,
|
||||
Encrypter $encrypter
|
||||
) {
|
||||
$this->database = $database;
|
||||
$this->connection = $connection;
|
||||
$this->dynamic = $dynamic;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set wether or not this class should validate that the server has enough slots
|
||||
* left before creating the new database.
|
||||
*
|
||||
* @param bool $validate
|
||||
* @return $this
|
||||
*/
|
||||
public function setValidateDatabaseLimit(bool $validate): self
|
||||
{
|
||||
$this->validateDatabaseLimit = $validate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database that is linked to a specific host.
|
||||
*
|
||||
* @param int $server
|
||||
* @param \Pterodactyl\Models\Server $server
|
||||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Throwable
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
|
||||
*/
|
||||
public function create($server, array $data)
|
||||
public function create(Server $server, array $data)
|
||||
{
|
||||
$data['server_id'] = $server;
|
||||
$data['database'] = sprintf('s%d_%s', $server, $data['database']);
|
||||
$data['username'] = sprintf('u%d_%s', $server, str_random(10));
|
||||
$data['password'] = $this->encrypter->encrypt(
|
||||
Utilities::randomStringWithSpecialCharacters(24)
|
||||
);
|
||||
if (! config('pterodactyl.client_features.databases.enabled')) {
|
||||
throw new DatabaseClientFeatureNotEnabledException;
|
||||
}
|
||||
|
||||
if ($this->validateDatabaseLimit) {
|
||||
// If the server has a limit assigned and we've already reached that limit, throw back
|
||||
// an exception and kill the process.
|
||||
if (! is_null($server->database_limit) && $server->databases()->count() >= $server->database_limit) {
|
||||
throw new TooManyDatabasesException;
|
||||
}
|
||||
}
|
||||
|
||||
$data = array_merge($data, [
|
||||
'server_id' => $server->id,
|
||||
'database' => sprintf('s%d_%s', $server->id, $data['database']),
|
||||
'username' => sprintf('u%d_%s', $server->id, str_random(10)),
|
||||
'password' => $this->encrypter->encrypt(
|
||||
Utilities::randomStringWithSpecialCharacters(24)
|
||||
),
|
||||
]);
|
||||
|
||||
$database = null;
|
||||
|
||||
$this->database->beginTransaction();
|
||||
try {
|
||||
$database = $this->repository->createIfNotExists($data);
|
||||
$this->dynamic->set('dynamic', $data['database_host_id']);
|
||||
return $this->connection->transaction(function () use ($data, &$database) {
|
||||
$database = $this->repository->createIfNotExists($data);
|
||||
$this->dynamic->set('dynamic', $data['database_host_id']);
|
||||
|
||||
$this->repository->createDatabase($database->database);
|
||||
$this->repository->createUser(
|
||||
$database->username,
|
||||
$database->remote,
|
||||
$this->encrypter->decrypt($database->password),
|
||||
$database->max_connections
|
||||
);
|
||||
$this->repository->assignUserToDatabase(
|
||||
$database->database,
|
||||
$database->username,
|
||||
$database->remote
|
||||
);
|
||||
$this->repository->flush();
|
||||
$this->repository->createDatabase($database->database);
|
||||
$this->repository->createUser(
|
||||
$database->username, $database->remote, $this->encrypter->decrypt($database->password), $database->max_connections
|
||||
);
|
||||
$this->repository->assignUserToDatabase($database->database, $database->username, $database->remote);
|
||||
$this->repository->flush();
|
||||
|
||||
$this->database->commit();
|
||||
} catch (Exception $ex) {
|
||||
return $database;
|
||||
});
|
||||
} catch (Exception $exception) {
|
||||
try {
|
||||
if (isset($database) && $database instanceof Database) {
|
||||
if ($database instanceof Database) {
|
||||
$this->repository->dropDatabase($database->database);
|
||||
$this->repository->dropUser($database->username, $database->remote);
|
||||
$this->repository->flush();
|
||||
}
|
||||
} catch (Exception $exTwo) {
|
||||
// ignore an exception
|
||||
} catch (Exception $exception) {
|
||||
// Do nothing here. We've already encountered an issue before this point so no
|
||||
// reason to prioritize this error over the initial one.
|
||||
}
|
||||
|
||||
$this->database->rollBack();
|
||||
throw $ex;
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
return $database;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,9 +6,7 @@ use Pterodactyl\Models\Server;
|
|||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException;
|
||||
use Pterodactyl\Exceptions\Service\Database\NoSuitableDatabaseHostException;
|
||||
use Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException;
|
||||
|
||||
class DeployServerDatabaseService
|
||||
{
|
||||
|
@ -49,20 +47,12 @@ class DeployServerDatabaseService
|
|||
* @param array $data
|
||||
* @return \Pterodactyl\Models\Database
|
||||
*
|
||||
* @throws \Throwable
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\TooManyDatabasesException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(Server $server, array $data): Database
|
||||
{
|
||||
if (! config('pterodactyl.client_features.databases.enabled')) {
|
||||
throw new DatabaseClientFeatureNotEnabledException;
|
||||
}
|
||||
|
||||
$databases = $this->repository->findCountWhere([['server_id', '=', $server->id]]);
|
||||
if (! is_null($server->database_limit) && $databases >= $server->database_limit) {
|
||||
throw new TooManyDatabasesException;
|
||||
}
|
||||
|
||||
$allowRandom = config('pterodactyl.client_features.databases.allow_random');
|
||||
$hosts = $this->databaseHostRepository->setColumns(['id'])->findWhere([
|
||||
['node_id', '=', $server->node_id],
|
||||
|
@ -81,7 +71,7 @@ class DeployServerDatabaseService
|
|||
|
||||
$host = $hosts->random();
|
||||
|
||||
return $this->managementService->create($server->id, [
|
||||
return $this->managementService->create($server, [
|
||||
'database_host_id' => $host->id,
|
||||
'database' => array_get($data, 'database'),
|
||||
'remote' => array_get($data, 'remote'),
|
||||
|
|
|
@ -76,7 +76,16 @@ class EggImporterService
|
|||
public function handle(UploadedFile $file, int $nest): Egg
|
||||
{
|
||||
if ($file->getError() !== UPLOAD_ERR_OK || ! $file->isFile()) {
|
||||
throw new InvalidFileUploadException(trans('exceptions.nest.importer.file_error'));
|
||||
throw new InvalidFileUploadException(
|
||||
sprintf(
|
||||
'The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)',
|
||||
$file->getFilename(),
|
||||
$file->isFile() ? 'true' : 'false',
|
||||
$file->isValid() ? 'true' : 'false',
|
||||
$file->getError(),
|
||||
$file->getErrorMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$parsed = json_decode($file->openFile()->fread($file->getSize()));
|
||||
|
|
|
@ -57,7 +57,16 @@ class EggUpdateImporterService
|
|||
public function handle(int $egg, UploadedFile $file)
|
||||
{
|
||||
if ($file->getError() !== UPLOAD_ERR_OK || ! $file->isFile()) {
|
||||
throw new InvalidFileUploadException(trans('exceptions.nest.importer.file_error'));
|
||||
throw new InvalidFileUploadException(
|
||||
sprintf(
|
||||
'The selected file ["%s"] was not in a valid format to import. (is_file: %s is_valid: %s err_code: %s err: %s)',
|
||||
$file->getFilename(),
|
||||
$file->isFile() ? 'true' : 'false',
|
||||
$file->isValid() ? 'true' : 'false',
|
||||
$file->getError(),
|
||||
$file->getErrorMessage()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$parsed = json_decode($file->openFile()->fread($file->getSize()));
|
||||
|
|
|
@ -24,7 +24,7 @@ class AssetHashService
|
|||
private $application;
|
||||
|
||||
/**
|
||||
* @var null|array
|
||||
* @var array|null
|
||||
*/
|
||||
protected static $manifest;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Pterodactyl\Services\Helpers;
|
|||
|
||||
use Exception;
|
||||
use GuzzleHttp\Client;
|
||||
use Cake\Chronos\Chronos;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Exceptions\Service\Helper\CdnVersionFetchingException;
|
||||
|
@ -120,7 +120,7 @@ class SoftwareVersionService
|
|||
*/
|
||||
protected function cacheVersionData()
|
||||
{
|
||||
return $this->cache->remember(self::VERSION_CACHE_KEY, Chronos::now()->addMinutes(config()->get('pterodactyl.cdn.cache_time', 60)), function () {
|
||||
return $this->cache->remember(self::VERSION_CACHE_KEY, CarbonImmutable::now()->addMinutes(config()->get('pterodactyl.cdn.cache_time', 60)), function () {
|
||||
try {
|
||||
$response = $this->client->request('GET', config()->get('pterodactyl.cdn.url'));
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace Pterodactyl\Services\Nodes;
|
|||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Support\Str;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Encryption\Encrypter;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
||||
|
||||
class NodeCreationService
|
||||
|
@ -16,14 +16,14 @@ class NodeCreationService
|
|||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Encryption\Encrypter
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* CreationService constructor.
|
||||
*
|
||||
* @param \Illuminate\Encryption\Encrypter $encrypter
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(Encrypter $encrypter, NodeRepositoryInterface $repository)
|
||||
|
|
|
@ -71,8 +71,8 @@ class BuildModificationService
|
|||
* @return \Pterodactyl\Models\Server
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function handle(Server $server, array $data)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ class BuildModificationService
|
|||
}
|
||||
}
|
||||
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
/* @var \Pterodactyl\Models\Server $server */
|
||||
$server = $this->repository->withFreshModel()->update($server->id, [
|
||||
'oom_disabled' => array_get($data, 'oom_disabled'),
|
||||
'memory' => array_get($data, 'memory'),
|
||||
|
|
|
@ -4,8 +4,8 @@ namespace Pterodactyl\Services\Servers;
|
|||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
||||
class ReinstallServerService
|
||||
{
|
||||
|
@ -17,27 +17,27 @@ class ReinstallServerService
|
|||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
private $database;
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ReinstallService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $database
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Pterodactyl\Repositories\Wings\DaemonServerRepository $daemonServerRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $database,
|
||||
ConnectionInterface $connection,
|
||||
DaemonServerRepository $daemonServerRepository,
|
||||
ServerRepositoryInterface $repository
|
||||
ServerRepository $repository
|
||||
) {
|
||||
$this->daemonServerRepository = $daemonServerRepository;
|
||||
$this->database = $database;
|
||||
$this->connection = $connection;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
|
@ -51,14 +51,14 @@ class ReinstallServerService
|
|||
*/
|
||||
public function reinstall(Server $server)
|
||||
{
|
||||
$this->database->transaction(function () use ($server) {
|
||||
$this->repository->withoutFreshModel()->update($server->id, [
|
||||
return $this->connection->transaction(function () use ($server) {
|
||||
$updated = $this->repository->update($server->id, [
|
||||
'installed' => Server::STATUS_INSTALLING,
|
||||
]);
|
||||
], true, true);
|
||||
|
||||
$this->daemonServerRepository->setServer($server)->reinstall();
|
||||
});
|
||||
|
||||
return $server->refresh();
|
||||
return $updated;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -310,8 +310,6 @@ class ServerCreationService
|
|||
return $allocation->node_id;
|
||||
}
|
||||
|
||||
/** @noinspection PhpDocMissingThrowsInspection */
|
||||
|
||||
/**
|
||||
* Create a unique UUID and UUID-Short combo for a server.
|
||||
*
|
||||
|
@ -319,7 +317,6 @@ class ServerCreationService
|
|||
*/
|
||||
private function generateUniqueUuidCombo(): string
|
||||
{
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$uuid = Uuid::uuid4()->toString();
|
||||
|
||||
if (! $this->repository->isUniqueUuidCombo($uuid, substr($uuid, 0, 8))) {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pterodactyl\Services\Servers;
|
||||
|
||||
use Exception;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
|
@ -109,7 +110,15 @@ class ServerDeletionService
|
|||
|
||||
$this->connection->transaction(function () use ($server) {
|
||||
$this->databaseRepository->setColumns('id')->findWhere([['server_id', '=', $server->id]])->each(function ($item) {
|
||||
$this->databaseManagementService->delete($item->id);
|
||||
try {
|
||||
$this->databaseManagementService->delete($item->id);
|
||||
} catch (Exception $exception) {
|
||||
if ($this->force) {
|
||||
$this->writer->warning($exception);
|
||||
} else {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$this->repository->delete($server->id);
|
||||
|
|
|
@ -3,10 +3,13 @@
|
|||
namespace Pterodactyl\Services\Users;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
use Pterodactyl\Models\User;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository;
|
||||
use Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid;
|
||||
|
||||
class ToggleTwoFactorService
|
||||
|
@ -26,21 +29,37 @@ class ToggleTwoFactorService
|
|||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository
|
||||
*/
|
||||
private $recoveryTokenRepository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Database\ConnectionInterface
|
||||
*/
|
||||
private $connection;
|
||||
|
||||
/**
|
||||
* ToggleTwoFactorService constructor.
|
||||
*
|
||||
* @param \Illuminate\Database\ConnectionInterface $connection
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \PragmaRX\Google2FA\Google2FA $google2FA
|
||||
* @param \Pterodactyl\Repositories\Eloquent\RecoveryTokenRepository $recoveryTokenRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ConnectionInterface $connection,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
RecoveryTokenRepository $recoveryTokenRepository,
|
||||
UserRepositoryInterface $repository
|
||||
) {
|
||||
$this->encrypter = $encrypter;
|
||||
$this->google2FA = $google2FA;
|
||||
$this->repository = $repository;
|
||||
$this->recoveryTokenRepository = $recoveryTokenRepository;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,32 +68,60 @@ class ToggleTwoFactorService
|
|||
* @param \Pterodactyl\Models\User $user
|
||||
* @param string $token
|
||||
* @param bool|null $toggleState
|
||||
* @return bool
|
||||
* @return string[]
|
||||
*
|
||||
* @throws \Throwable
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\User\TwoFactorAuthenticationTokenInvalid
|
||||
*/
|
||||
public function handle(User $user, string $token, bool $toggleState = null): bool
|
||||
public function handle(User $user, string $token, bool $toggleState = null): array
|
||||
{
|
||||
$secret = $this->encrypter->decrypt($user->totp_secret);
|
||||
|
||||
$isValidToken = $this->google2FA->verifyKey($secret, $token, config()->get('pterodactyl.auth.2fa.window'));
|
||||
|
||||
if (! $isValidToken) {
|
||||
throw new TwoFactorAuthenticationTokenInvalid(
|
||||
'The token provided is not valid.'
|
||||
);
|
||||
throw new TwoFactorAuthenticationTokenInvalid('The token provided is not valid.');
|
||||
}
|
||||
|
||||
$this->repository->withoutFreshModel()->update($user->id, [
|
||||
'totp_authenticated_at' => Carbon::now(),
|
||||
'use_totp' => (is_null($toggleState) ? ! $user->use_totp : $toggleState),
|
||||
]);
|
||||
return $this->connection->transaction(function () use ($user, $toggleState) {
|
||||
// Now that we're enabling 2FA on the account, generate 10 recovery tokens for the account
|
||||
// and store them hashed in the database. We'll return them to the caller so that the user
|
||||
// can see and save them.
|
||||
//
|
||||
// If a user is unable to login with a 2FA token they can provide one of these backup codes
|
||||
// which will then be marked as deleted from the database and will also bypass 2FA protections
|
||||
// on their account.
|
||||
$tokens = [];
|
||||
if ((! $toggleState && ! $user->use_totp) || $toggleState) {
|
||||
$inserts = [];
|
||||
for ($i = 0; $i < 10; $i++) {
|
||||
$token = Str::random(10);
|
||||
|
||||
return true;
|
||||
$inserts[] = [
|
||||
'user_id' => $user->id,
|
||||
'token' => password_hash($token, PASSWORD_DEFAULT),
|
||||
];
|
||||
|
||||
$tokens[] = $token;
|
||||
}
|
||||
|
||||
// Before inserting any new records make sure all of the old ones are deleted to avoid
|
||||
// any issues or storing an unnecessary number of tokens in the database.
|
||||
$this->recoveryTokenRepository->deleteWhere(['user_id' => $user->id]);
|
||||
|
||||
// Bulk insert the hashed tokens.
|
||||
$this->recoveryTokenRepository->insert($inserts);
|
||||
}
|
||||
|
||||
$this->repository->withoutFreshModel()->update($user->id, [
|
||||
'totp_authenticated_at' => Carbon::now(),
|
||||
'use_totp' => (is_null($toggleState) ? ! $user->use_totp : $toggleState),
|
||||
]);
|
||||
|
||||
return $tokens;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class TwoFactorSetupService
|
|||
'totp_secret' => $this->encrypter->encrypt($secret),
|
||||
]);
|
||||
|
||||
$company = preg_replace('/\s/', '', $this->config->get('app.name'));
|
||||
$company = urlencode(preg_replace('/\s/', '', $this->config->get('app.name')));
|
||||
|
||||
return sprintf(
|
||||
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',
|
||||
|
|
Reference in a new issue