Merge branch 'master' into develop

This commit is contained in:
Dane Everitt 2019-08-04 13:49:26 -07:00
commit 81143e231a
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
41 changed files with 303 additions and 190 deletions

View file

@ -102,7 +102,10 @@ class BulkPowerActionCommand extends Command
$bar->clear();
try {
$this->powerRepository->setServer($server)->sendSignal($action);
$this->powerRepository
->setNode($server->node)
->setServer($server)
->sendSignal($action);
} catch (RequestException $exception) {
$this->output->error(trans('command/messages.server.power.action_failed', [
'name' => $server->name,

View file

@ -2,6 +2,7 @@
namespace Pterodactyl\Http\Controllers\Admin;
use Exception;
use PDOException;
use Illuminate\View\View;
use Pterodactyl\Models\DatabaseHost;
@ -118,17 +119,22 @@ class DatabaseController extends Controller
* @param \Pterodactyl\Http\Requests\Admin\DatabaseHostFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function create(DatabaseHostFormRequest $request): RedirectResponse
{
try {
$host = $this->creationService->handle($request->normalize());
} catch (PDOException $ex) {
$this->alert->danger($ex->getMessage())->flash();
} catch (Exception $exception) {
if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
return redirect()->route('admin.databases');
redirect()->route('admin.databases')->withInput($request->validated());
} else {
throw $exception;
}
}
$this->alert->success('Successfully created a new database host on the system.')->flash();
@ -143,8 +149,7 @@ class DatabaseController extends Controller
* @param \Pterodactyl\Models\DatabaseHost $host
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function update(DatabaseHostFormRequest $request, DatabaseHost $host): RedirectResponse
{
@ -153,9 +158,17 @@ class DatabaseController extends Controller
try {
$this->updateService->handle($host->id, $request->normalize());
$this->alert->success('Database host was updated successfully.')->flash();
} catch (PDOException $ex) {
$this->alert->danger($ex->getMessage())->flash();
$redirect->withInput($request->normalize());
} catch (Exception $exception) {
// Catch any SQL related exceptions and display them back to the user, otherwise just
// throw the exception like normal and move on with it.
if ($exception instanceof PDOException || $exception->getPrevious() instanceof PDOException) {
$this->alert->danger(
sprintf('There was an error while trying to connect to the host or while executing a query: "%s"', $exception->getMessage())
)->flash();
$redirect->withInput($request->normalize());
} else {
throw $exception;
}
}
return $redirect;

View file

@ -516,7 +516,7 @@ class ServersController extends Controller
$this->buildModificationService->handle($server, $request->only([
'allocation_id', 'add_allocations', 'remove_allocations',
'memory', 'swap', 'io', 'cpu', 'disk',
'database_limit', 'allocation_limit',
'database_limit', 'allocation_limit', 'oom_disabled',
]));
$this->alert->success(trans('admin/server.alerts.build_updated'))->flash();
@ -589,8 +589,7 @@ class ServersController extends Controller
* @param int $server
* @return \Illuminate\Http\RedirectResponse
*
* @throws \Exception
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Throwable
*/
public function resetDatabasePassword(Request $request, $server)
{
@ -599,7 +598,7 @@ class ServersController extends Controller
['id', '=', $request->input('database')],
]);
$this->databasePasswordService->handle($database, str_random(24));
$this->databasePasswordService->handle($database);
return response('', 204);
}

View file

@ -87,12 +87,11 @@ class DatabaseController extends ApplicationApiController
* @param \Pterodactyl\Http\Requests\Api\Application\Servers\Databases\ServerDatabaseWriteRequest $request
* @return \Illuminate\Http\Response
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function resetPassword(ServerDatabaseWriteRequest $request): Response
{
$this->databasePasswordService->handle($request->getModel(Database::class), str_random(24));
$this->databasePasswordService->handle($request->getModel(Database::class));
return response('', 204);
}

View file

@ -35,9 +35,28 @@ class ClientController extends ClientApiController
*/
public function index(GetServersRequest $request): array
{
$servers = $this->repository
// Check for the filter parameter on the request.
switch ($request->input('filter')) {
case 'all':
$filter = User::FILTER_LEVEL_ALL;
break;
case 'admin':
$filter = User::FILTER_LEVEL_ADMIN;
break;
case 'owner':
$filter = User::FILTER_LEVEL_OWNER;
break;
case 'subuser-of':
default:
$filter = User::FILTER_LEVEL_SUBUSER;
break;
}
$servers = $this->repository->
->setSearchTerm($request->input('query'))
->filterUserAccessServers($request->user(), User::FILTER_LEVEL_ALL);
->filterUserAccessServers(
$request->user(), $filter, config('pterodactyl.paginate.frontend.servers')
);
return $this->fractal->collection($servers)
->transformWith($this->getTransformer(ServerTransformer::class))

View file

@ -40,7 +40,7 @@ class StoreAllocationRequest extends ApplicationApiRequest
return [
'allocation_ip' => $data['ip'],
'allocation_ports' => $data['ports'],
'allocation_alias' => $data['alias'],
'allocation_alias' => $data['alias'] ?? null,
];
}
}

View file

@ -41,6 +41,7 @@ class StoreServerRequest extends ApplicationApiRequest
'startup' => $rules['startup'],
'environment' => 'present|array',
'skip_scripts' => 'sometimes|boolean',
'oom_disabled' => 'sometimes|boolean',
// Resource limitations
'limits' => 'required|array',

View file

@ -18,6 +18,7 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
return [
'allocation' => $rules['allocation_id'],
'oom_disabled' => $rules['oom_disabled'],
'limits' => 'sometimes|array',
'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true),

View file

@ -28,6 +28,16 @@ class Server extends Model implements CleansAttributes, ValidableContract
*/
protected $table = 'servers';
/**
* Default values when creating the model. We want to switch to disabling OOM killer
* on server instances unless the user specifies otherwise in the request.
*
* @var array
*/
protected $attributes = [
'oom_disabled' => true,
];
/**
* The attributes that should be mutated to dates.
*
@ -53,6 +63,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'swap' => 'required',
'io' => 'required',
'cpu' => 'required',
'oom_disabled' => 'sometimes',
'disk' => 'required',
'nest_id' => 'required',
'egg_id' => 'required',
@ -79,6 +90,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'swap' => 'numeric|min:-1',
'io' => 'numeric|between:10,1000',
'cpu' => 'numeric|min:0',
'oom_disabled' => 'boolean',
'disk' => 'numeric|min:0',
'allocation_id' => 'bail|unique:servers|exists:allocations,id',
'nest_id' => 'exists:nests,id',
@ -107,7 +119,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
'disk' => 'integer',
'io' => 'integer',
'cpu' => 'integer',
'oom_disabled' => 'integer',
'oom_disabled' => 'boolean',
'allocation_id' => 'integer',
'nest_id' => 'integer',
'egg_id' => 'integer',
@ -164,11 +176,11 @@ class Server extends Model implements CleansAttributes, ValidableContract
/**
* Gets the subusers associated with a server.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
* @return \Illuminate\Database\Eloquent\Relations\HasManyThrough
*/
public function subusers()
{
return $this->hasMany(Subuser::class);
return $this->hasManyThrough(User::class, Subuser::class, 'server_id', 'id', 'id', 'user_id');
}
/**

View file

@ -153,7 +153,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
public function assignUserToDatabase(string $database, string $username, string $remote): bool
{
return $this->run(sprintf(
'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, EXECUTE ON `%s`.* TO `%s`@`%s`',
'GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, INDEX, LOCK TABLES, EXECUTE ON `%s`.* TO `%s`@`%s`',
$database,
$username,
$remote

View file

@ -2,8 +2,9 @@
namespace Pterodactyl\Services\Databases;
use Webmozart\Assert\Assert;
use Exception;
use Pterodactyl\Models\Database;
use Illuminate\Support\Facades\Log;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Contracts\Encryption\Encrypter;
use Pterodactyl\Extensions\DynamicDatabaseConnection;
@ -55,35 +56,39 @@ class DatabasePasswordService
* Updates a password for a given database.
*
* @param \Pterodactyl\Models\Database|int $database
* @param string $password
* @return bool
* @return string
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function handle($database, string $password): bool
public function handle(Database $database): string
{
if (! $database instanceof Database) {
Assert::integerish($database);
$password = str_random(24);
// Given a random string of characters, randomly loop through the characters and replace some
// with special characters to avoid issues with MySQL password requirements on some servers.
try {
for ($i = 0; $i < random_int(2, 6); $i++) {
$character = ['!', '@', '=', '.', '+', '^'][random_int(0, 5)];
$database = $this->repository->find($database);
$password = substr_replace($password, $character, random_int(0, 23), 1);
}
} catch (Exception $exception) {
// Just log the error and hope for the best at this point.
Log::error($exception);
}
$this->dynamic->set('dynamic', $database->database_host_id);
$this->connection->beginTransaction();
$this->connection->transaction(function () use ($database, $password) {
$this->dynamic->set('dynamic', $database->database_host_id);
$updated = $this->repository->withoutFreshModel()->update($database->id, [
'password' => $this->encrypter->encrypt($password),
]);
$this->repository->withoutFreshModel()->update($database->id, [
'password' => $this->encrypter->encrypt($password),
]);
$this->repository->dropUser($database->username, $database->remote);
$this->repository->createUser($database->username, $database->remote, $password);
$this->repository->assignUserToDatabase($database->database, $database->username, $database->remote);
$this->repository->flush();
$this->repository->dropUser($database->username, $database->remote);
$this->repository->createUser($database->username, $database->remote, $password);
$this->repository->assignUserToDatabase($database->database, $database->username, $database->remote);
$this->repository->flush();
});
unset($password);
$this->connection->commit();
return $updated;
return $password;
}
}

View file

@ -65,28 +65,26 @@ class HostCreationService
* @param array $data
* @return \Pterodactyl\Models\DatabaseHost
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function handle(array $data): DatabaseHost
{
$this->connection->beginTransaction();
return $this->connection->transaction(function () use ($data) {
$host = $this->repository->create([
'password' => $this->encrypter->encrypt(array_get($data, 'password')),
'name' => array_get($data, 'name'),
'host' => array_get($data, 'host'),
'port' => array_get($data, 'port'),
'username' => array_get($data, 'username'),
'max_databases' => null,
'node_id' => array_get($data, 'node_id'),
]);
$host = $this->repository->create([
'password' => $this->encrypter->encrypt(array_get($data, 'password')),
'name' => array_get($data, 'name'),
'host' => array_get($data, 'host'),
'port' => array_get($data, 'port'),
'username' => array_get($data, 'username'),
'max_databases' => null,
'node_id' => array_get($data, 'node_id'),
]);
// Confirm access using the provided credentials before saving data.
$this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
// Confirm access using the provided credentials before saving data.
$this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
$this->connection->commit();
return $host;
return $host;
});
}
}

View file

@ -71,10 +71,9 @@ class HostUpdateService
*
* @param int $hostId
* @param array $data
* @return mixed
* @return \Pterodactyl\Models\DatabaseHost
*
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Throwable
*/
public function handle(int $hostId, array $data): DatabaseHost
{
@ -84,13 +83,12 @@ class HostUpdateService
unset($data['password']);
}
$this->connection->beginTransaction();
$host = $this->repository->update($hostId, $data);
return $this->connection->transaction(function () use ($data, $hostId) {
$host = $this->repository->update($hostId, $data);
$this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
$this->dynamic->set('dynamic', $host);
$this->databaseManager->connection('dynamic')->select('SELECT 1 FROM dual');
$this->connection->commit();
return $host;
return $host;
});
}
}

View file

@ -85,6 +85,7 @@ class BuildModificationService
}
$server = $this->repository->withFreshModel()->update($server->id, [
'oom_disabled' => array_get($data, 'oom_disabled'),
'memory' => array_get($data, 'memory'),
'swap' => array_get($data, 'swap'),
'io' => array_get($data, 'io'),
@ -97,6 +98,7 @@ class BuildModificationService
$allocations = $this->allocationRepository->findWhere([['server_id', '=', $server->id]]);
$build['oom_disabled'] = $server->oom_disabled;
$build['memory'] = (int) $server->memory;
$build['swap'] = (int) $server->swap;
$build['io'] = (int) $server->io;

View file

@ -70,6 +70,7 @@ class ServerConfigurationStructureService
return $item->pluck('port');
})->toArray(),
'env' => $this->environment->handle($server),
'oom_disabled' => $server->oom_disabled,
'memory' => (int) $server->memory,
'swap' => (int) $server->swap,
'io' => (int) $server->io,

View file

@ -227,7 +227,7 @@ class ServerCreationService
'disk' => array_get($data, 'disk'),
'io' => array_get($data, 'io'),
'cpu' => array_get($data, 'cpu'),
'oom_disabled' => false,
'oom_disabled' => array_get($data, 'oom_disabled', true),
'allocation_id' => array_get($data, 'allocation_id'),
'nest_id' => array_get($data, 'nest_id'),
'egg_id' => array_get($data, 'egg_id'),

View file

@ -71,7 +71,7 @@ class TwoFactorSetupService
'totp_secret' => $this->encrypter->encrypt($secret),
]);
$company = $this->config->get('app.name');
$company = preg_replace('/\s/', '', $this->config->get('app.name'));
return sprintf(
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',