Simplify logic when a server is in an unsupported state
This commit is contained in:
parent
be26921fcc
commit
e30a765071
10 changed files with 76 additions and 294 deletions
30
app/Exceptions/Http/Server/ServerStateConflictException.php
Normal file
30
app/Exceptions/Http/Server/ServerStateConflictException.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Http\Server;
|
||||
|
||||
use Throwable;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
||||
|
||||
class ServerStateConflictException extends ConflictHttpException
|
||||
{
|
||||
/**
|
||||
* Exception thrown when the server is in an unsupported state for API access or
|
||||
* certain operations within the codebase.
|
||||
*/
|
||||
public function __construct(Server $server, Throwable $previous = null)
|
||||
{
|
||||
$message = 'This server is currently in an unsupported state, please try again later.';
|
||||
if ($server->isSuspended()) {
|
||||
$message = 'This server is currently suspended and the functionality requested is unavailable.';
|
||||
} elseif (!$server->isInstalled()) {
|
||||
$message = 'This server has not yet completed its installation process, please try again later.';
|
||||
} elseif ($server->status === Server::STATUS_RESTORING_BACKUP) {
|
||||
$message = 'This server is currently restoring from a backup, please try again later.';
|
||||
} elseif (!is_null($server->transfer)) {
|
||||
$message = 'This server is currently being transferred to a new machine, please try again later.';
|
||||
}
|
||||
|
||||
parent::__construct($message, $previous);
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions\Http\Server;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
|
||||
class ServerTransferringException extends HttpException
|
||||
{
|
||||
/**
|
||||
* ServerTransferringException constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Response::HTTP_CONFLICT, 'This server is currently being transferred to a new machine, please try again laster.');
|
||||
}
|
||||
}
|
|
@ -12,8 +12,6 @@ use Pterodactyl\Exceptions\Http\HttpForbiddenException;
|
|||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Services\Servers\GetUserPermissionsService;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerTransferringException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest;
|
||||
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
|
||||
|
||||
|
@ -98,16 +96,7 @@ class SftpAuthenticationController extends Controller
|
|||
}
|
||||
}
|
||||
|
||||
// Prevent SFTP access to servers that are being transferred.
|
||||
if (!is_null($server->transfer)) {
|
||||
throw new ServerTransferringException();
|
||||
}
|
||||
|
||||
// Remember, for security purposes, only reveal the existence of the server to people that
|
||||
// have provided valid credentials, and have permissions to know about it.
|
||||
if ($server->isSuspended() || !$server->isInstalled()) {
|
||||
throw new BadRequestHttpException('Server is not installed or is currently suspended.');
|
||||
}
|
||||
$server->validateCurrentState();
|
||||
|
||||
return new JsonResponse([
|
||||
'server' => $server->uuid,
|
||||
|
|
|
@ -28,7 +28,6 @@ use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess;
|
|||
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||
use Pterodactyl\Http\Middleware\Server\AccessingValidServer;
|
||||
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
|
||||
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
||||
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
|
||||
|
@ -106,7 +105,6 @@ class Kernel extends HttpKernel
|
|||
'auth' => Authenticate::class,
|
||||
'auth.basic' => AuthenticateWithBasicAuth::class,
|
||||
'guest' => RedirectIfAuthenticated::class,
|
||||
'server' => AccessingValidServer::class,
|
||||
'admin' => AdminAuthenticate::class,
|
||||
'csrf' => VerifyCsrfToken::class,
|
||||
'throttle' => ThrottleRequests::class,
|
||||
|
|
|
@ -6,10 +6,8 @@ use Closure;
|
|||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerTransferringException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException;
|
||||
|
||||
class AuthenticateServerAccess
|
||||
{
|
||||
|
@ -60,23 +58,17 @@ class AuthenticateServerAccess
|
|||
}
|
||||
}
|
||||
|
||||
if ($server->suspended && !$request->routeIs('api:client:server.resources')) {
|
||||
throw new BadRequestHttpException('This server is currently suspended and the functionality requested is unavailable.');
|
||||
}
|
||||
|
||||
// Still allow users to get information about their server if it is installing or being transferred.
|
||||
if (!$request->routeIs('api:client:server.view')) {
|
||||
if (!$server->isInstalled()) {
|
||||
// Throw an exception for all server routes; however if the user is an admin and requesting the
|
||||
// server details, don't throw the exception for them.
|
||||
if (!$user->root_admin || ($user->root_admin && !$request->routeIs($this->except))) {
|
||||
throw new ConflictHttpException('Server has not completed the installation process.');
|
||||
try {
|
||||
$server->validateCurrentState();
|
||||
} catch (ServerStateConflictException $exception) {
|
||||
// Still allow users to get information about their server if it is installing or
|
||||
// being transferred.
|
||||
if (!$request->routeIs('api:client:server.view')) {
|
||||
if ($server->isSuspended() && !$request->routeIs('api:client:server.resources')) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_null($server->transfer)) {
|
||||
if (!$user->root_admin || ($user->root_admin && !$request->routeIs($this->except))) {
|
||||
throw new ServerTransferringException();
|
||||
if (!$user->root_admin || !$request->routeIs($this->except)) {
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Middleware\Server;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerTransferringException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
class AccessingValidServer
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Routing\ResponseFactory
|
||||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* AccessingValidServer constructor.
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigRepository $config,
|
||||
ResponseFactory $response,
|
||||
ServerRepositoryInterface $repository
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a given user has permission to access a server.
|
||||
*
|
||||
* @return \Illuminate\Http\Response|mixed
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$attributes = $request->route()->parameter('server');
|
||||
$isApiRequest = $request->expectsJson() || $request->is(...$this->config->get('pterodactyl.json_routes', []));
|
||||
$server = $this->repository->getByUuid($attributes instanceof Server ? $attributes->uuid : $attributes);
|
||||
|
||||
if ($server->isSuspended()) {
|
||||
if ($isApiRequest) {
|
||||
throw new AccessDeniedHttpException('Server is suspended and cannot be accessed.');
|
||||
}
|
||||
|
||||
return $this->response->view('errors.suspended', [], 403);
|
||||
}
|
||||
|
||||
// Servers can have install statuses other than 1 or 0, so don't check
|
||||
// for a bool-type operator here.
|
||||
if (!$server->isInstalled()) {
|
||||
if ($isApiRequest) {
|
||||
throw new ConflictHttpException('Server is still completing the installation process.');
|
||||
}
|
||||
|
||||
return $this->response->view('errors.installing', [], 409);
|
||||
}
|
||||
|
||||
if (!is_null($server->transfer)) {
|
||||
if ($isApiRequest) {
|
||||
throw new ServerTransferringException();
|
||||
}
|
||||
|
||||
return $this->response->view('errors.transferring', [], 409);
|
||||
}
|
||||
|
||||
// Add server to the request attributes. This will replace sessions
|
||||
// as files are updated.
|
||||
$request->attributes->set('server', $server);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use Closure;
|
|||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Query\JoinClause;
|
||||
use Znck\Eloquent\Traits\BelongsToThrough;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerStateConflictException;
|
||||
|
||||
/**
|
||||
* @property int $id
|
||||
|
@ -371,4 +372,23 @@ class Server extends Model
|
|||
{
|
||||
return $this->hasMany(AuditLog::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server is currently in a user-accessible state. If not, an
|
||||
* exception is raised. This should be called whenever something needs to make
|
||||
* sure the server is not in a weird state that should block user access.
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Server\ServerStateConflictException
|
||||
*/
|
||||
public function validateCurrentState()
|
||||
{
|
||||
if (
|
||||
$this->isSuspended() ||
|
||||
!$this->isInstalled() ||
|
||||
$this->status === self::STATUS_RESTORING_BACKUP ||
|
||||
!is_null($this->transfer)
|
||||
) {
|
||||
throw new ServerStateConflictException($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use Webmozart\Assert\Assert;
|
|||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Database\ConnectionInterface;
|
||||
use Pterodactyl\Repositories\Wings\DaemonServerRepository;
|
||||
use Pterodactyl\Exceptions\Http\Server\ServerTransferringException;
|
||||
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
|
||||
|
||||
class SuspensionService
|
||||
{
|
||||
|
@ -55,7 +55,7 @@ class SuspensionService
|
|||
|
||||
// Check if the server is currently being transferred.
|
||||
if (!is_null($server->transfer)) {
|
||||
throw new ServerTransferringException();
|
||||
throw new ConflictHttpException('Cannot toggle suspension status on a server that is currently being transferred.');
|
||||
}
|
||||
|
||||
$this->connection->transaction(function () use ($action, $server, $isSuspending) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue