diff --git a/app/Http/Controllers/API/Remote/SftpController.php b/app/Http/Controllers/API/Remote/SftpController.php index 04fa432ce..2e12d4e51 100644 --- a/app/Http/Controllers/API/Remote/SftpController.php +++ b/app/Http/Controllers/API/Remote/SftpController.php @@ -3,12 +3,13 @@ namespace Pterodactyl\Http\Controllers\Api\Remote; use Illuminate\Http\Request; +use Illuminate\Http\Response; use Illuminate\Http\JsonResponse; -use Illuminate\Auth\AuthenticationException; use Pterodactyl\Http\Controllers\Controller; use Illuminate\Foundation\Auth\ThrottlesLogins; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest; class SftpController extends Controller @@ -47,7 +48,7 @@ class SftpController extends Controller if ($this->hasTooManyLoginAttempts($request)) { return response()->json([ 'error' => 'Logins throttled.', - ], 429); + ], Response::HTTP_TOO_MANY_REQUESTS); } try { @@ -59,14 +60,14 @@ class SftpController extends Controller ); $this->clearLoginAttempts($request); - } catch (AuthenticationException $exception) { + } catch (BadRequestHttpException $exception) { return response()->json([ - 'error' => 'Invalid credentials.', - ], 403); + 'error' => 'The server you are trying to access is not installed or is suspended.', + ], Response::HTTP_BAD_REQUEST); } catch (RecordNotFoundException $exception) { return response()->json([ - 'error' => 'Invalid server.', - ], 404); + 'error' => 'Unable to locate a resource using the username and password provided.', + ], Response::HTTP_NOT_FOUND); } return response()->json($data); diff --git a/app/Http/Controllers/API/Remote/ValidateKeyController.php b/app/Http/Controllers/API/Remote/ValidateKeyController.php index 4bf70c183..86a02cbce 100644 --- a/app/Http/Controllers/API/Remote/ValidateKeyController.php +++ b/app/Http/Controllers/API/Remote/ValidateKeyController.php @@ -25,6 +25,7 @@ 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; @@ -75,12 +76,11 @@ class ValidateKeyController extends Controller * @return array * * @throws \Illuminate\Foundation\Testing\HttpException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function index($token) { if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) { - throw new HttpException(501); + throw new HttpException(Response::HTTP_NOT_IMPLEMENTED); } try { @@ -89,6 +89,10 @@ class ValidateKeyController extends Controller 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/Http/Controllers/Admin/ServersController.php b/app/Http/Controllers/Admin/ServersController.php index 5ec2c14d9..c64e76711 100644 --- a/app/Http/Controllers/Admin/ServersController.php +++ b/app/Http/Controllers/Admin/ServersController.php @@ -431,7 +431,7 @@ class ServersController extends Controller $this->repository->update($server->id, [ 'installed' => ! $server->installed, - ]); + ], true, true); $this->alert->success(trans('admin/server.alerts.install_toggled'))->flash(); diff --git a/app/Http/Controllers/Api/Application/Servers/ServerController.php b/app/Http/Controllers/Api/Application/Servers/ServerController.php index fd6d62f89..f869393af 100644 --- a/app/Http/Controllers/Api/Application/Servers/ServerController.php +++ b/app/Http/Controllers/Api/Application/Servers/ServerController.php @@ -108,7 +108,6 @@ class ServerController extends ApplicationApiController * @return \Illuminate\Http\Response * * @throws \Pterodactyl\Exceptions\DisplayException - * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response { diff --git a/app/Models/Server.php b/app/Models/Server.php index ac3bd64f3..5bc10a3aa 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -90,6 +90,7 @@ class Server extends Model implements CleansAttributes, ValidableContract 'startup' => 'string', 'skip_scripts' => 'boolean', 'image' => 'string|max:255', + 'installed' => 'boolean', ]; /** diff --git a/app/Services/Sftp/AuthenticateUsingPasswordService.php b/app/Services/Sftp/AuthenticateUsingPasswordService.php index 1cf64bd8e..0c2f39c5d 100644 --- a/app/Services/Sftp/AuthenticateUsingPasswordService.php +++ b/app/Services/Sftp/AuthenticateUsingPasswordService.php @@ -2,11 +2,11 @@ namespace Pterodactyl\Services\Sftp; -use Illuminate\Auth\AuthenticationException; use Pterodactyl\Contracts\Repository\UserRepositoryInterface; use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService; use Pterodactyl\Exceptions\Repository\RecordNotFoundException; use Pterodactyl\Contracts\Repository\ServerRepositoryInterface; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; class AuthenticateUsingPasswordService { @@ -53,13 +53,13 @@ class AuthenticateUsingPasswordService * * @param string $username * @param string $password - * @param string|null $server * @param int $node + * @param string|null $server * @return array * - * @throws \Illuminate\Auth\AuthenticationException * @throws \Pterodactyl\Exceptions\Model\DataValidationException * @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException + * @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException */ public function handle(string $username, string $password, int $node, string $server = null): array { @@ -67,21 +67,20 @@ class AuthenticateUsingPasswordService throw new RecordNotFoundException; } - try { - $user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]); - - if (! password_verify($password, $user->password)) { - throw new AuthenticationException; - } - } catch (RecordNotFoundException $exception) { - throw new AuthenticationException; + $user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]); + if (! password_verify($password, $user->password)) { + throw new RecordNotFoundException; } - $server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid'])->getByUuid($server); + $server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->getByUuid($server); if ($server->node_id !== $node || (! $user->root_admin && $server->owner_id !== $user->id)) { throw new RecordNotFoundException; } + if ($server->installed !== 1 || $server->suspended) { + throw new BadRequestHttpException; + } + return [ 'server' => $server->uuid, 'token' => $this->keyProviderService->handle($server, $user), diff --git a/tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php b/tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php index 26584522f..9575fe779 100644 --- a/tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php +++ b/tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php @@ -52,7 +52,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); - $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf(); + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); $this->keyProviderService->shouldReceive('handle')->with($server, $user)->once()->andReturn('server_token'); @@ -77,7 +77,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); - $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf(); + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); $this->keyProviderService->shouldReceive('handle')->with($server, $user)->once()->andReturn('server_token'); @@ -104,7 +104,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase * Test that an exception is thrown if the user account exists but the wrong * credentials are passed. * - * @expectedException \Illuminate\Auth\AuthenticationException + * @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function testExceptionIsThrownIfUserDetailsAreIncorrect() { @@ -119,7 +119,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase /** * Test that an exception is thrown if no user account is found. * - * @expectedException \Illuminate\Auth\AuthenticationException + * @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException */ public function testExceptionIsThrownIfNoUserAccountIsFound() { @@ -143,7 +143,7 @@ class AuthenticateUsingPasswordServiceTest extends TestCase $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); - $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf(); + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); $this->getService()->handle($user->username, 'password', 1, $server->uuidShort); @@ -163,7 +163,45 @@ class AuthenticateUsingPasswordServiceTest extends TestCase $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); - $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf(); + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); + $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); + + $this->getService()->handle($user->username, 'password', 1, $server->uuidShort); + } + + /** + * Test that a suspended server throws an exception. + * + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + public function testSuspendedServer() + { + $user = factory(User::class)->make(['root_admin' => 1]); + $server = factory(Server::class)->make(['node_id' => 1, 'owner_id' => $user->id + 1, 'suspended' => 1]); + + $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); + $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); + + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); + $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); + + $this->getService()->handle($user->username, 'password', 1, $server->uuidShort); + } + + /** + * Test that a server that is not yet installed throws an exception. + * + * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException + */ + public function testNotInstalledServer() + { + $user = factory(User::class)->make(['root_admin' => 1]); + $server = factory(Server::class)->make(['node_id' => 1, 'owner_id' => $user->id + 1, 'installed' => 0]); + + $this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf(); + $this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user); + + $this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf(); $this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server); $this->getService()->handle($user->username, 'password', 1, $server->uuidShort);