Fix handling of SFTP authorization; closes #1972
This commit is contained in:
parent
72ecac5236
commit
2cf1c7f712
3 changed files with 72 additions and 367 deletions
|
@ -3,12 +3,15 @@
|
|||
namespace Pterodactyl\Http\Controllers\Api\Remote;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ThrottlesLogins;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService;
|
||||
use Pterodactyl\Repositories\Eloquent\UserRepository;
|
||||
use Pterodactyl\Exceptions\Http\HttpForbiddenException;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Services\Servers\GetUserPermissionsService;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest;
|
||||
|
||||
|
@ -17,18 +20,35 @@ class SftpAuthenticationController extends Controller
|
|||
use ThrottlesLogins;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService
|
||||
* @var \Pterodactyl\Repositories\Eloquent\UserRepository
|
||||
*/
|
||||
private $authenticationService;
|
||||
private $userRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $serverRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\GetUserPermissionsService
|
||||
*/
|
||||
private $permissionsService;
|
||||
|
||||
/**
|
||||
* SftpController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Sftp\AuthenticateUsingPasswordService $authenticationService
|
||||
* @param \Pterodactyl\Services\Servers\GetUserPermissionsService $permissionsService
|
||||
* @param \Pterodactyl\Repositories\Eloquent\UserRepository $userRepository
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $serverRepository
|
||||
*/
|
||||
public function __construct(AuthenticateUsingPasswordService $authenticationService)
|
||||
{
|
||||
$this->authenticationService = $authenticationService;
|
||||
public function __construct(
|
||||
GetUserPermissionsService $permissionsService,
|
||||
UserRepository $userRepository,
|
||||
ServerRepository $serverRepository
|
||||
) {
|
||||
$this->userRepository = $userRepository;
|
||||
$this->serverRepository = $serverRepository;
|
||||
$this->permissionsService = $permissionsService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,7 +58,7 @@ class SftpAuthenticationController extends Controller
|
|||
* @param \Pterodactyl\Http\Requests\Api\Remote\SftpAuthenticationFormRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function __invoke(SftpAuthenticationFormRequest $request): JsonResponse
|
||||
{
|
||||
|
@ -52,33 +72,54 @@ class SftpAuthenticationController extends Controller
|
|||
];
|
||||
|
||||
$this->incrementLoginAttempts($request);
|
||||
|
||||
if ($this->hasTooManyLoginAttempts($request)) {
|
||||
return response()->json([
|
||||
'error' => 'Logins throttled.',
|
||||
], Response::HTTP_TOO_MANY_REQUESTS);
|
||||
return JsonResponse::create([
|
||||
'error' => 'Too many logins attempted too quickly.',
|
||||
], JsonResponse::HTTP_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $this->authenticationService->handle(
|
||||
$connection['username'],
|
||||
$request->input('password'),
|
||||
object_get($request->attributes->get('node'), 'id', 0),
|
||||
empty($connection['server']) ? null : $connection['server']
|
||||
/** @var \Pterodactyl\Models\Node $node */
|
||||
$node = $request->attributes->get('node');
|
||||
if (empty($connection['server'])) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
/** @var \Pterodactyl\Models\User $user */
|
||||
$user = $this->userRepository->findFirstWhere([
|
||||
['username', '=', $connection['username']],
|
||||
]);
|
||||
|
||||
$server = $this->serverRepository->getByUuid($connection['server'] ?? '');
|
||||
if (! password_verify($request->input('password'), $user->password) || $server->node_id !== $node->id) {
|
||||
throw new HttpForbiddenException(
|
||||
'Authorization credentials were not correct, please try again.'
|
||||
);
|
||||
|
||||
$this->clearLoginAttempts($request);
|
||||
} catch (BadRequestHttpException $exception) {
|
||||
return response()->json([
|
||||
'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' => 'Unable to locate a resource using the username and password provided.',
|
||||
], Response::HTTP_NOT_FOUND);
|
||||
}
|
||||
|
||||
return response()->json($data);
|
||||
if (! $user->root_admin && $server->owner_id !== $user->id) {
|
||||
$permissions = $this->permissionsService->handle($server, $user);
|
||||
|
||||
if (! in_array(Permission::ACTION_FILE_SFTP, $permissions)) {
|
||||
throw new HttpForbiddenException(
|
||||
'You do not have permission to access SFTP for this server.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Remeber, 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->installed !== 1 || $server->suspended) {
|
||||
throw new BadRequestHttpException(
|
||||
'Server is not installed or is currently suspended.'
|
||||
);
|
||||
}
|
||||
|
||||
return JsonResponse::create([
|
||||
'server' => $server->uuid,
|
||||
// Deprecated, but still needed at the moment for Wings.
|
||||
'token' => '',
|
||||
'permissions' => $permissions ?? ['*'],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Sftp;
|
||||
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
|
||||
class AuthenticateUsingPasswordService
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
|
||||
*/
|
||||
private $keyProviderService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||
*/
|
||||
private $userRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
||||
*/
|
||||
private $subuserRepository;
|
||||
|
||||
/**
|
||||
* AuthenticateUsingPasswordService constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $subuserRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
|
||||
*/
|
||||
public function __construct(
|
||||
DaemonKeyProviderService $keyProviderService,
|
||||
ServerRepositoryInterface $repository,
|
||||
SubuserRepositoryInterface $subuserRepository,
|
||||
UserRepositoryInterface $userRepository
|
||||
) {
|
||||
$this->keyProviderService = $keyProviderService;
|
||||
$this->repository = $repository;
|
||||
$this->subuserRepository = $subuserRepository;
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to authenticate a provided username and password and determine if they
|
||||
* have permission to access a given server. This function does not account for
|
||||
* subusers currently. Only administrators and server owners can login to access
|
||||
* their files at this time.
|
||||
*
|
||||
* Server must exist on the node that the API call is being made from in order for a
|
||||
* valid response to be provided.
|
||||
*
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @param int $node
|
||||
* @param string|null $server
|
||||
* @return array
|
||||
*
|
||||
* @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
|
||||
{
|
||||
if (is_null($server)) {
|
||||
throw new RecordNotFoundException;
|
||||
}
|
||||
|
||||
$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', 'installed', 'suspended'])->getByUuid($server);
|
||||
if ($server->node_id !== $node) {
|
||||
throw new RecordNotFoundException;
|
||||
}
|
||||
|
||||
if (! $user->root_admin && $server->owner_id !== $user->id) {
|
||||
$subuser = $this->subuserRepository->getWithPermissionsUsingUserAndServer($user->id, $server->id);
|
||||
$permissions = $subuser->getRelation('permissions')->pluck('permission')->toArray();
|
||||
|
||||
if (! in_array('access-sftp', $permissions)) {
|
||||
throw new RecordNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
if ($server->installed !== 1 || $server->suspended) {
|
||||
throw new BadRequestHttpException;
|
||||
}
|
||||
|
||||
return [
|
||||
'server' => $server->uuid,
|
||||
'token' => $this->keyProviderService->handle($server, $user),
|
||||
'permissions' => $permissions ?? ['*'],
|
||||
];
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue