Skip to content

Commit 7a19019

Browse files
committed
Fix suspension/installed handling for servers
closes pterodactyl#891
1 parent b0c8390 commit 7a19019

File tree

7 files changed

+71
-29
lines changed

7 files changed

+71
-29
lines changed

app/Http/Controllers/API/Remote/SftpController.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
namespace Pterodactyl\Http\Controllers\Api\Remote;
44

55
use Illuminate\Http\Request;
6+
use Illuminate\Http\Response;
67
use Illuminate\Http\JsonResponse;
7-
use Illuminate\Auth\AuthenticationException;
88
use Pterodactyl\Http\Controllers\Controller;
99
use Illuminate\Foundation\Auth\ThrottlesLogins;
1010
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
1111
use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService;
12+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1213
use Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest;
1314

1415
class SftpController extends Controller
@@ -47,7 +48,7 @@ public function index(SftpAuthenticationFormRequest $request): JsonResponse
4748
if ($this->hasTooManyLoginAttempts($request)) {
4849
return response()->json([
4950
'error' => 'Logins throttled.',
50-
], 429);
51+
], Response::HTTP_TOO_MANY_REQUESTS);
5152
}
5253

5354
try {
@@ -59,14 +60,14 @@ public function index(SftpAuthenticationFormRequest $request): JsonResponse
5960
);
6061

6162
$this->clearLoginAttempts($request);
62-
} catch (AuthenticationException $exception) {
63+
} catch (BadRequestHttpException $exception) {
6364
return response()->json([
64-
'error' => 'Invalid credentials.',
65-
], 403);
65+
'error' => 'The server you are trying to access is not installed or is suspended.',
66+
], Response::HTTP_BAD_REQUEST);
6667
} catch (RecordNotFoundException $exception) {
6768
return response()->json([
68-
'error' => 'Invalid server.',
69-
], 404);
69+
'error' => 'Unable to locate a resource using the username and password provided.',
70+
], Response::HTTP_NOT_FOUND);
7071
}
7172

7273
return response()->json($data);

app/Http/Controllers/API/Remote/ValidateKeyController.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
namespace Pterodactyl\Http\Controllers\Api\Remote;
2626

2727
use Spatie\Fractal\Fractal;
28+
use Illuminate\Http\Response;
2829
use Pterodactyl\Http\Controllers\Controller;
2930
use Illuminate\Contracts\Foundation\Application;
3031
use Illuminate\Foundation\Testing\HttpException;
@@ -75,12 +76,11 @@ public function __construct(
7576
* @return array
7677
*
7778
* @throws \Illuminate\Foundation\Testing\HttpException
78-
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
7979
*/
8080
public function index($token)
8181
{
8282
if (! starts_with($token, DaemonKeyRepositoryInterface::INTERNAL_KEY_IDENTIFIER)) {
83-
throw new HttpException(501);
83+
throw new HttpException(Response::HTTP_NOT_IMPLEMENTED);
8484
}
8585

8686
try {
@@ -89,6 +89,10 @@ public function index($token)
8989
throw new NotFoundHttpException;
9090
}
9191

92+
if ($key->getRelation('server')->suspended || $key->getRelation('server')->installed !== 1) {
93+
throw new NotFoundHttpException;
94+
}
95+
9296
return $this->fractal->item($key, $this->app->make(ApiKeyTransformer::class), 'server')
9397
->serializeWith(JsonApiSerializer::class)
9498
->toArray();

app/Http/Controllers/Admin/ServersController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ public function toggleInstall(Server $server)
431431

432432
$this->repository->update($server->id, [
433433
'installed' => ! $server->installed,
434-
]);
434+
], true, true);
435435

436436
$this->alert->success(trans('admin/server.alerts.install_toggled'))->flash();
437437

app/Http/Controllers/Api/Application/Servers/ServerController.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ public function view(ServerWriteRequest $request): array
108108
* @return \Illuminate\Http\Response
109109
*
110110
* @throws \Pterodactyl\Exceptions\DisplayException
111-
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
112111
*/
113112
public function delete(ServerWriteRequest $request, Server $server, string $force = ''): Response
114113
{

app/Models/Server.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
9090
'startup' => 'string',
9191
'skip_scripts' => 'boolean',
9292
'image' => 'string|max:255',
93+
'installed' => 'boolean',
9394
];
9495

9596
/**

app/Services/Sftp/AuthenticateUsingPasswordService.php

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
namespace Pterodactyl\Services\Sftp;
44

5-
use Illuminate\Auth\AuthenticationException;
65
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
76
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
87
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
98
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
9+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1010

1111
class AuthenticateUsingPasswordService
1212
{
@@ -53,35 +53,34 @@ public function __construct(
5353
*
5454
* @param string $username
5555
* @param string $password
56-
* @param string|null $server
5756
* @param int $node
57+
* @param string|null $server
5858
* @return array
5959
*
60-
* @throws \Illuminate\Auth\AuthenticationException
6160
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
6261
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
62+
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
6363
*/
6464
public function handle(string $username, string $password, int $node, string $server = null): array
6565
{
6666
if (is_null($server)) {
6767
throw new RecordNotFoundException;
6868
}
6969

70-
try {
71-
$user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]);
72-
73-
if (! password_verify($password, $user->password)) {
74-
throw new AuthenticationException;
75-
}
76-
} catch (RecordNotFoundException $exception) {
77-
throw new AuthenticationException;
70+
$user = $this->userRepository->setColumns(['id', 'root_admin', 'password'])->findFirstWhere([['username', '=', $username]]);
71+
if (! password_verify($password, $user->password)) {
72+
throw new RecordNotFoundException;
7873
}
7974

80-
$server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid'])->getByUuid($server);
75+
$server = $this->repository->setColumns(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->getByUuid($server);
8176
if ($server->node_id !== $node || (! $user->root_admin && $server->owner_id !== $user->id)) {
8277
throw new RecordNotFoundException;
8378
}
8479

80+
if ($server->installed !== 1 || $server->suspended) {
81+
throw new BadRequestHttpException;
82+
}
83+
8584
return [
8685
'server' => $server->uuid,
8786
'token' => $this->keyProviderService->handle($server, $user),

tests/Unit/Services/Sftp/AuthenticateUsingPasswordServiceTest.php

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public function testNonAdminAccountIsAuthenticated()
5252
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
5353
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
5454

55-
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf();
55+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
5656
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
5757

5858
$this->keyProviderService->shouldReceive('handle')->with($server, $user)->once()->andReturn('server_token');
@@ -77,7 +77,7 @@ public function testAdminAccountIsAuthenticated()
7777
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
7878
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
7979

80-
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf();
80+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
8181
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
8282

8383
$this->keyProviderService->shouldReceive('handle')->with($server, $user)->once()->andReturn('server_token');
@@ -104,7 +104,7 @@ public function testExceptionIsThrownIfNoServerIsProvided()
104104
* Test that an exception is thrown if the user account exists but the wrong
105105
* credentials are passed.
106106
*
107-
* @expectedException \Illuminate\Auth\AuthenticationException
107+
* @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException
108108
*/
109109
public function testExceptionIsThrownIfUserDetailsAreIncorrect()
110110
{
@@ -119,7 +119,7 @@ public function testExceptionIsThrownIfUserDetailsAreIncorrect()
119119
/**
120120
* Test that an exception is thrown if no user account is found.
121121
*
122-
* @expectedException \Illuminate\Auth\AuthenticationException
122+
* @expectedException \Pterodactyl\Exceptions\Repository\RecordNotFoundException
123123
*/
124124
public function testExceptionIsThrownIfNoUserAccountIsFound()
125125
{
@@ -143,7 +143,7 @@ public function testExceptionIsThrownIfUserDoesNotOwnServer()
143143
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
144144
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
145145

146-
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf();
146+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
147147
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
148148

149149
$this->getService()->handle($user->username, 'password', 1, $server->uuidShort);
@@ -163,7 +163,45 @@ public function testExceptionIsThrownIfServerDoesNotExistOnCurrentNode()
163163
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
164164
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
165165

166-
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid'])->once()->andReturnSelf();
166+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
167+
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
168+
169+
$this->getService()->handle($user->username, 'password', 1, $server->uuidShort);
170+
}
171+
172+
/**
173+
* Test that a suspended server throws an exception.
174+
*
175+
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
176+
*/
177+
public function testSuspendedServer()
178+
{
179+
$user = factory(User::class)->make(['root_admin' => 1]);
180+
$server = factory(Server::class)->make(['node_id' => 1, 'owner_id' => $user->id + 1, 'suspended' => 1]);
181+
182+
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
183+
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
184+
185+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
186+
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
187+
188+
$this->getService()->handle($user->username, 'password', 1, $server->uuidShort);
189+
}
190+
191+
/**
192+
* Test that a server that is not yet installed throws an exception.
193+
*
194+
* @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
195+
*/
196+
public function testNotInstalledServer()
197+
{
198+
$user = factory(User::class)->make(['root_admin' => 1]);
199+
$server = factory(Server::class)->make(['node_id' => 1, 'owner_id' => $user->id + 1, 'installed' => 0]);
200+
201+
$this->userRepository->shouldReceive('setColumns')->with(['id', 'root_admin', 'password'])->once()->andReturnSelf();
202+
$this->userRepository->shouldReceive('findFirstWhere')->with([['username', '=', $user->username]])->once()->andReturn($user);
203+
204+
$this->repository->shouldReceive('setColumns')->with(['id', 'node_id', 'owner_id', 'uuid', 'installed', 'suspended'])->once()->andReturnSelf();
167205
$this->repository->shouldReceive('getByUuid')->with($server->uuidShort)->once()->andReturn($server);
168206

169207
$this->getService()->handle($user->username, 'password', 1, $server->uuidShort);

0 commit comments

Comments
 (0)