Merge branch 'develop' into feature/bulk-reinstall-command
This commit is contained in:
commit
215351eeb3
308 changed files with 18740 additions and 3400 deletions
|
@ -178,9 +178,7 @@ class AppSettingsCommand extends Command
|
|||
if ($askForRedisPassword) {
|
||||
$this->output->comment(trans('command/messages.environment.app.redis_pass_help'));
|
||||
$this->variables['REDIS_PASSWORD'] = $this->option('redis-pass') ?? $this->output->askHidden(
|
||||
trans('command/messages.environment.app.redis_password'), function () {
|
||||
return '';
|
||||
}
|
||||
trans('command/messages.environment.app.redis_password')
|
||||
);
|
||||
}
|
||||
|
||||
|
|
15
app/Contracts/Http/ClientPermissionsRequest.php
Normal file
15
app/Contracts/Http/ClientPermissionsRequest.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Contracts\Http;
|
||||
|
||||
interface ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* Returns the permissions string indicating which permission should be used to
|
||||
* validate that the authenticated user has permission to perform this action aganist
|
||||
* the given resource (server).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string;
|
||||
}
|
|
@ -13,19 +13,18 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
|||
* @param string $path
|
||||
* @return \stdClass
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getFileStat(string $path): stdClass;
|
||||
|
||||
/**
|
||||
* Return the contents of a given file if it can be edited in the Panel.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $path
|
||||
* @param int|null $notLargerThan
|
||||
* @return string
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
*/
|
||||
public function getContent(string $path): string;
|
||||
public function getContent(string $path, int $notLargerThan = null): string;
|
||||
|
||||
/**
|
||||
* Save new contents to a given file.
|
||||
|
@ -34,7 +33,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
|||
* @param string $content
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function putContent(string $path, string $content): ResponseInterface;
|
||||
|
||||
|
@ -44,7 +43,41 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
|||
* @param string $path
|
||||
* @return array
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getDirectory(string $path): array;
|
||||
|
||||
/**
|
||||
* Creates a new directory for the server in the given $path.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function createDirectory(string $name, string $path): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Renames or moves a file on the remote machine.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function renameFile(string $from, string $to): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Copy a given file and give it a unique name.
|
||||
*
|
||||
* @param string $location
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function copyFile(string $location): ResponseInterface;
|
||||
|
||||
/**
|
||||
* Delete a file or folder for the server.
|
||||
*
|
||||
* @param string $location
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function deleteFile(string $location): ResponseInterface;
|
||||
}
|
||||
|
|
|
@ -55,16 +55,6 @@ interface NodeRepositoryInterface extends RepositoryInterface, SearchableInterfa
|
|||
*/
|
||||
public function loadNodeAllocations(Node $node, bool $refresh = false): Node;
|
||||
|
||||
/**
|
||||
* Return a node with all of the servers attached to that node.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Pterodactyl\Models\Node
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function getNodeServers(int $id): Node;
|
||||
|
||||
/**
|
||||
* Return a collection of nodes for all locations to use in server creation UI.
|
||||
*
|
||||
|
|
|
@ -161,4 +161,14 @@ interface ServerRepositoryInterface extends RepositoryInterface, SearchableInter
|
|||
* @return int
|
||||
*/
|
||||
public function getSuspendedServersCount(): int;
|
||||
|
||||
/**
|
||||
* Returns all of the servers that exist for a given node in a paginated response.
|
||||
*
|
||||
* @param int $node
|
||||
* @param int $limit
|
||||
*
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
public function loadAllServersForNode(int $node, int $limit): LengthAwarePaginator;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use Exception;
|
|||
use PDOException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Database\Connection;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Session\TokenMismatchException;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
@ -14,6 +15,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
|
|||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
|
@ -137,6 +139,41 @@ class Handler extends ExceptionHandler
|
|||
*/
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
$connections = Container::getInstance()->make(Connection::class);
|
||||
|
||||
// If we are currently wrapped up inside a transaction, we will roll all the way
|
||||
// back to the beginning. This needs to happen, otherwise session data does not
|
||||
// get properly persisted.
|
||||
//
|
||||
// This is kind of a hack, and ideally things like this should be handled as
|
||||
// much as possible at the code level, but there are a lot of spots that do a
|
||||
// ton of actions and were written before this bug discovery was made.
|
||||
//
|
||||
// @see https://github.com/pterodactyl/panel/pull/1468
|
||||
if ($connections->transactionLevel()) {
|
||||
$connections->rollBack(0);
|
||||
}
|
||||
|
||||
// Because of some breaking change snuck into a Laravel update that didn't get caught
|
||||
// by any of the tests, exceptions implementing the HttpExceptionInterface get marked
|
||||
// as being HttpExceptions, but aren't actually implementing the HttpException abstract.
|
||||
//
|
||||
// This is incredibly annoying because we can't just temporarily override the handler to
|
||||
// allow these (at least without taking on a high maintenance cost). Laravel 5.8 fixes this,
|
||||
// so when we update (or have updated) this code can be removed.
|
||||
//
|
||||
// @see https://github.com/laravel/framework/pull/25975
|
||||
// @todo remove this code when upgrading to Laravel 5.8
|
||||
if ($exception instanceof HttpExceptionInterface && ! $exception instanceof HttpException) {
|
||||
$exception = new HttpException(
|
||||
$exception->getStatusCode(),
|
||||
$exception->getMessage(),
|
||||
$exception,
|
||||
$exception->getHeaders(),
|
||||
$exception->getCode()
|
||||
);
|
||||
}
|
||||
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
|
@ -231,7 +268,7 @@ class Handler extends ExceptionHandler
|
|||
protected function unauthenticated($request, AuthenticationException $exception)
|
||||
{
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['error' => 'Unauthenticated.'], 401);
|
||||
return response()->json(self::convertToArray($exception), 401);
|
||||
}
|
||||
|
||||
return redirect()->guest(route('auth.login'));
|
||||
|
|
|
@ -2,19 +2,28 @@
|
|||
|
||||
namespace Pterodactyl\Exceptions\Repository;
|
||||
|
||||
class RecordNotFoundException extends RepositoryException
|
||||
use Illuminate\Http\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
|
||||
|
||||
class RecordNotFoundException extends RepositoryException implements HttpExceptionInterface
|
||||
{
|
||||
/**
|
||||
* Handle request to render this exception to a user. Returns the default
|
||||
* 404 page view.
|
||||
* Returns the status code.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return int
|
||||
*/
|
||||
public function render($request)
|
||||
public function getStatusCode()
|
||||
{
|
||||
if (! config('app.debug')) {
|
||||
return response()->view('errors.404', [], 404);
|
||||
}
|
||||
return Response::HTTP_NOT_FOUND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns response headers.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getHeaders()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ use Pterodactyl\Services\Allocations\AssignmentService;
|
|||
use Pterodactyl\Services\Helpers\SoftwareVersionService;
|
||||
use Pterodactyl\Http\Requests\Admin\Node\NodeFormRequest;
|
||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Admin\Node\AllocationFormRequest;
|
||||
use Pterodactyl\Services\Allocations\AllocationDeletionService;
|
||||
use Pterodactyl\Contracts\Repository\LocationRepositoryInterface;
|
||||
|
@ -32,6 +33,11 @@ use Pterodactyl\Http\Requests\Admin\Node\AllocationAliasFormRequest;
|
|||
|
||||
class NodesController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Allocations\AllocationDeletionService
|
||||
*/
|
||||
protected $allocationDeletionService;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
|
@ -72,6 +78,11 @@ class NodesController extends Controller
|
|||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
protected $serverRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Nodes\NodeUpdateService
|
||||
*/
|
||||
|
@ -81,10 +92,6 @@ class NodesController extends Controller
|
|||
* @var \Pterodactyl\Services\Helpers\SoftwareVersionService
|
||||
*/
|
||||
protected $versionService;
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Allocations\AllocationDeletionService
|
||||
*/
|
||||
private $allocationDeletionService;
|
||||
|
||||
/**
|
||||
* NodesController constructor.
|
||||
|
@ -98,6 +105,7 @@ class NodesController extends Controller
|
|||
* @param \Pterodactyl\Services\Nodes\NodeDeletionService $deletionService
|
||||
* @param \Pterodactyl\Contracts\Repository\LocationRepositoryInterface $locationRepository
|
||||
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $serverRepository
|
||||
* @param \Pterodactyl\Services\Nodes\NodeUpdateService $updateService
|
||||
* @param \Pterodactyl\Services\Helpers\SoftwareVersionService $versionService
|
||||
*/
|
||||
|
@ -111,6 +119,7 @@ class NodesController extends Controller
|
|||
NodeDeletionService $deletionService,
|
||||
LocationRepositoryInterface $locationRepository,
|
||||
NodeRepositoryInterface $repository,
|
||||
ServerRepositoryInterface $serverRepository,
|
||||
NodeUpdateService $updateService,
|
||||
SoftwareVersionService $versionService
|
||||
) {
|
||||
|
@ -123,6 +132,7 @@ class NodesController extends Controller
|
|||
$this->deletionService = $deletionService;
|
||||
$this->locationRepository = $locationRepository;
|
||||
$this->repository = $repository;
|
||||
$this->serverRepository = $serverRepository;
|
||||
$this->updateService = $updateService;
|
||||
$this->versionService = $versionService;
|
||||
}
|
||||
|
@ -178,8 +188,6 @@ class NodesController extends Controller
|
|||
*
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function viewIndex(Node $node)
|
||||
{
|
||||
|
@ -235,19 +243,17 @@ class NodesController extends Controller
|
|||
/**
|
||||
* Shows the server listing page for a specific node.
|
||||
*
|
||||
* @param int $node
|
||||
* @param \Pterodactyl\Models\Node $node
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function viewServers($node)
|
||||
public function viewServers(Node $node)
|
||||
{
|
||||
$node = $this->repository->getNodeServers($node);
|
||||
$servers = $this->serverRepository->loadAllServersForNode($node->id, 25);
|
||||
Javascript::put([
|
||||
'node' => collect($node->makeVisible('daemonSecret'))->only(['scheme', 'fqdn', 'daemonListen', 'daemonSecret']),
|
||||
]);
|
||||
|
||||
return view('admin.nodes.view.servers', ['node' => $node]);
|
||||
return view('admin.nodes.view.servers', ['node' => $node, 'servers' => $servers]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -599,7 +599,7 @@ class ServersController extends Controller
|
|||
['id', '=', $request->input('database')],
|
||||
]);
|
||||
|
||||
$this->databasePasswordService->handle($database, str_random(20));
|
||||
$this->databasePasswordService->handle($database, str_random(24));
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ use Pterodactyl\Transformers\Api\Application\LocationTransformer;
|
|||
use Pterodactyl\Http\Controllers\Api\Application\ApplicationApiController;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\GetLocationsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\DeleteLocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\StoreLocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\DeleteLocationRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\Locations\UpdateLocationRequest;
|
||||
|
||||
class LocationController extends ApplicationApiController
|
||||
|
|
73
app/Http/Controllers/Api/Client/AccountController.php
Normal file
73
app/Http/Controllers/Api/Client/AccountController.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Services\Users\UserUpdateService;
|
||||
use Pterodactyl\Transformers\Api\Client\AccountTransformer;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest;
|
||||
|
||||
class AccountController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Users\UserUpdateService
|
||||
*/
|
||||
private $updateService;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Users\UserUpdateService $updateService
|
||||
*/
|
||||
public function __construct(UserUpdateService $updateService)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->updateService = $updateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function index(Request $request): array
|
||||
{
|
||||
return $this->fractal->item($request->user())
|
||||
->transformWith($this->getTransformer(AccountTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the authenticated user's email address.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Account\UpdateEmailRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function updateEmail(UpdateEmailRequest $request): Response
|
||||
{
|
||||
$this->updateService->handle($request->user(), $request->validated());
|
||||
|
||||
return response('', Response::HTTP_CREATED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the authenticated user's password.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Account\UpdatePasswordRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function updatePassword(UpdatePasswordRequest $request): Response
|
||||
{
|
||||
$this->updateService->handle($request->user(), $request->validated());
|
||||
|
||||
return response('', Response::HTTP_CREATED);
|
||||
}
|
||||
}
|
|
@ -35,7 +35,9 @@ class ClientController extends ClientApiController
|
|||
*/
|
||||
public function index(GetServersRequest $request): array
|
||||
{
|
||||
$servers = $this->repository->filterUserAccessServers($request->user(), User::FILTER_LEVEL_SUBUSER, config('pterodactyl.paginate.frontend.servers'));
|
||||
$servers = $this->repository
|
||||
->setSearchTerm($request->input('query'))
|
||||
->filterUserAccessServers($request->user(), User::FILTER_LEVEL_ALL);
|
||||
|
||||
return $this->fractal->collection($servers)
|
||||
->transformWith($this->getTransformer(ServerTransformer::class))
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Transformers\Api\Client\DatabaseTransformer;
|
||||
use Pterodactyl\Services\Databases\DatabaseManagementService;
|
||||
use Pterodactyl\Services\Databases\DeployServerDatabaseService;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Databases\GetDatabasesRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Databases\StoreDatabaseRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Databases\DeleteDatabaseRequest;
|
||||
|
||||
class DatabaseController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DeployServerDatabaseService
|
||||
*/
|
||||
private $deployDatabaseService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
|
||||
*/
|
||||
private $managementService;
|
||||
|
||||
/**
|
||||
* DatabaseController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $managementService
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\Databases\DeployServerDatabaseService $deployDatabaseService
|
||||
*/
|
||||
public function __construct(
|
||||
DatabaseManagementService $managementService,
|
||||
DatabaseRepositoryInterface $repository,
|
||||
DeployServerDatabaseService $deployDatabaseService
|
||||
) {
|
||||
parent::__construct();
|
||||
|
||||
$this->deployDatabaseService = $deployDatabaseService;
|
||||
$this->repository = $repository;
|
||||
$this->managementService = $managementService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Databases\GetDatabasesRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetDatabasesRequest $request): array
|
||||
{
|
||||
$databases = $this->repository->getDatabasesForServer($request->getModel(Server::class)->id);
|
||||
|
||||
return $this->fractal->collection($databases)
|
||||
->transformWith($this->getTransformer(DatabaseTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new database for the given server and return it.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Databases\StoreDatabaseRequest $request
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
|
||||
*/
|
||||
public function store(StoreDatabaseRequest $request): array
|
||||
{
|
||||
$database = $this->deployDatabaseService->handle($request->getModel(Server::class), $request->validated());
|
||||
|
||||
return $this->fractal->item($database)
|
||||
->parseIncludes(['password'])
|
||||
->transformWith($this->getTransformer(DatabaseTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Databases\DeleteDatabaseRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function delete(DeleteDatabaseRequest $request): Response
|
||||
{
|
||||
$this->managementService->delete($request->getModel(Database::class)->id);
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
186
app/Http/Controllers/Api/Client/Servers/FileController.php
Normal file
186
app/Http/Controllers/Api/Client/Servers/FileController.php
Normal file
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CopyFileRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest;
|
||||
|
||||
class FileController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Factory
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
|
||||
*/
|
||||
private $fileRepository;
|
||||
|
||||
/**
|
||||
* FileController constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
*/
|
||||
public function __construct(ConfigRepository $config, FileRepositoryInterface $fileRepository, CacheRepository $cache)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->fileRepository = $fileRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a listing of files in a given directory.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function listDirectory(ListFilesRequest $request): JsonResponse
|
||||
{
|
||||
return JsonResponse::create([
|
||||
'contents' => $this->fileRepository->setServer($request->getModel(Server::class))->getDirectory(
|
||||
$request->get('directory') ?? '/'
|
||||
),
|
||||
'editable' => $this->config->get('pterodactyl.files.editable', []),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of a specified file for the user.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getFileContents(GetFileContentsRequest $request): Response
|
||||
{
|
||||
return Response::create(
|
||||
$this->fileRepository->setServer($request->getModel(Server::class))->getContent(
|
||||
$request->get('file'), $this->config->get('pterodactyl.files.max_edit_size')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the contents of the specified file to the server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function writeFileContents(WriteFileContentRequest $request): Response
|
||||
{
|
||||
$this->fileRepository->setServer($request->getModel(Server::class))->putContent(
|
||||
$request->get('file'),
|
||||
$request->getContent()
|
||||
);
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new folder on the server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function createFolder(CreateFolderRequest $request): Response
|
||||
{
|
||||
$this->fileRepository
|
||||
->setServer($request->getModel(Server::class))
|
||||
->createDirectory($request->input('name'), $request->input('directory', '/'));
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file on the remote machine.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function renameFile(RenameFileRequest $request): Response
|
||||
{
|
||||
$this->fileRepository
|
||||
->setServer($request->getModel(Server::class))
|
||||
->renameFile($request->input('rename_from'), $request->input('rename_to'));
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a file on the server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\CopyFileRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function copyFile(CopyFileRequest $request): Response
|
||||
{
|
||||
$this->fileRepository
|
||||
->setServer($request->getModel(Server::class))
|
||||
->copyFile($request->input('location'));
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file or folder from the server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\DeleteFileRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function delete(DeleteFileRequest $request): Response
|
||||
{
|
||||
$this->fileRepository
|
||||
->setServer($request->getModel(Server::class))
|
||||
->deleteFile($request->input('location'));
|
||||
|
||||
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a reference to a file to download in the cache so that when the
|
||||
* user hits the Daemon and it verifies with the Panel they'll actually be able
|
||||
* to download that file.
|
||||
*
|
||||
* Returns the token that needs to be used when downloading the file.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function download(DownloadFileRequest $request): JsonResponse
|
||||
{
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $request->getModel(Server::class);
|
||||
$token = Uuid::uuid4()->toString();
|
||||
|
||||
$this->cache->put(
|
||||
'Server:Downloads:' . $token, ['server' => $server->uuid, 'path' => $request->route()->parameter('file')], Carbon::now()->addMinutes(5)
|
||||
);
|
||||
|
||||
return JsonResponse::create(['token' => $token]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Transformers\Api\Client\AllocationTransformer;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest;
|
||||
|
||||
class NetworkController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* NetworkController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(AllocationRepositoryInterface $repository)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists all of the allocations available to a server and wether or
|
||||
* not they are currently assigned as the primary for this server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Network\GetNetworkRequest $request
|
||||
* @return array
|
||||
*/
|
||||
public function index(GetNetworkRequest $request): array
|
||||
{
|
||||
$server = $request->getModel(Server::class);
|
||||
|
||||
$allocations = $this->repository->findWhere([
|
||||
['server_id', '=', $server->id],
|
||||
]);
|
||||
|
||||
return $this->fractal->collection($allocations)
|
||||
->transformWith($this->getTransformer(AllocationTransformer::class))
|
||||
->toArray();
|
||||
}
|
||||
}
|
132
app/Http/Controllers/Auth/AbstractLoginController.php
Normal file
132
app/Http/Controllers/Auth/AbstractLoginController.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Auth\Events\Failed;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
abstract class AbstractLoginController extends Controller
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* Lockout time for failed login requests.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lockoutTime;
|
||||
|
||||
/**
|
||||
* After how many attempts should logins be throttled and locked.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxLoginAttempts;
|
||||
|
||||
/**
|
||||
* Where to redirect users after login / registration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Auth\AuthManager
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* LoginController constructor.
|
||||
*
|
||||
* @param \Illuminate\Auth\AuthManager $auth
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*/
|
||||
public function __construct(AuthManager $auth, Repository $config)
|
||||
{
|
||||
$this->lockoutTime = $config->get('auth.lockout.time');
|
||||
$this->maxLoginAttempts = $config->get('auth.lockout.attempts');
|
||||
|
||||
$this->auth = $auth;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null)
|
||||
{
|
||||
$this->incrementLoginAttempts($request);
|
||||
$this->fireFailedLoginEvent($user, [
|
||||
$this->getField($request->input('user')) => $request->input('user'),
|
||||
]);
|
||||
|
||||
if ($request->route()->named('auth.login-checkpoint')) {
|
||||
throw new DisplayException(trans('auth.two_factor.checkpoint_failed'));
|
||||
}
|
||||
|
||||
throw new DisplayException(trans('auth.failed'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the response after the user was authenticated.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendLoginResponse(User $user, Request $request): JsonResponse
|
||||
{
|
||||
$request->session()->regenerate();
|
||||
$this->clearLoginAttempts($request);
|
||||
|
||||
$this->auth->guard()->login($user, true);
|
||||
|
||||
return JsonResponse::create([
|
||||
'data' => [
|
||||
'complete' => true,
|
||||
'intended' => $this->redirectPath(),
|
||||
'user' => $user->toVueObject(),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is logging in using an email or username,.
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
protected function getField(string $input = null): string
|
||||
{
|
||||
return str_contains($input, '@') ? 'email' : 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a failed login event.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
* @param array $credentials
|
||||
*/
|
||||
protected function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = [])
|
||||
{
|
||||
event(new Failed('auth', $user, $credentials));
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@
|
|||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Events\Auth\FailedPasswordReset;
|
||||
|
@ -18,9 +18,9 @@ class ForgotPasswordController extends Controller
|
|||
*
|
||||
* @param \Illuminate\Http\Request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkFailedResponse(Request $request, $response): RedirectResponse
|
||||
protected function sendResetLinkFailedResponse(Request $request, $response): JsonResponse
|
||||
{
|
||||
// As noted in #358 we will return success even if it failed
|
||||
// to avoid pointing out that an account does or does not
|
||||
|
@ -29,4 +29,18 @@ class ForgotPasswordController extends Controller
|
|||
|
||||
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset link.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkResponse(Request $request, $response): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'status' => trans($response),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
95
app/Http/Controllers/Auth/LoginCheckpointController.php
Normal file
95
app/Http/Controllers/Auth/LoginCheckpointController.php
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
|
||||
class LoginCheckpointController extends AbstractLoginController
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \PragmaRX\Google2FA\Google2FA
|
||||
*/
|
||||
private $google2FA;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* LoginCheckpointController constructor.
|
||||
*
|
||||
* @param \Illuminate\Auth\AuthManager $auth
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \PragmaRX\Google2FA\Google2FA $google2FA
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AuthManager $auth,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
Repository $config,
|
||||
CacheRepository $cache,
|
||||
UserRepositoryInterface $repository
|
||||
) {
|
||||
parent::__construct($auth, $config);
|
||||
|
||||
$this->google2FA = $google2FA;
|
||||
$this->cache = $cache;
|
||||
$this->repository = $repository;
|
||||
$this->encrypter = $encrypter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a login where the user is required to provide a TOTP authentication
|
||||
* token. Once a user has reached this stage it is assumed that they have already
|
||||
* provided a valid username and password.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\IncompatibleWithGoogleAuthenticatorException
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\InvalidCharactersException
|
||||
* @throws \PragmaRX\Google2FA\Exceptions\SecretKeyTooShortException
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
*/
|
||||
public function __invoke(LoginCheckpointRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$user = $this->repository->find(
|
||||
$this->cache->pull($request->input('confirmation_token'), 0)
|
||||
);
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
$decrypted = $this->encrypter->decrypt($user->totp_secret);
|
||||
$window = $this->config->get('pterodactyl.auth.2fa.window');
|
||||
|
||||
if ($this->google2FA->verifyKey($decrypted, $request->input('authentication_code'), $window)) {
|
||||
return $this->sendLoginResponse($user, $request);
|
||||
}
|
||||
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
}
|
|
@ -2,117 +2,81 @@
|
|||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Illuminate\Auth\Events\Failed;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Contracts\Config\Repository;
|
||||
use Illuminate\Contracts\View\Factory as ViewFactory;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class LoginController extends Controller
|
||||
class LoginController extends AbstractLoginController
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
const USER_INPUT_FIELD = 'user';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Auth\AuthManager
|
||||
* @var \Illuminate\Contracts\View\Factory
|
||||
*/
|
||||
private $auth;
|
||||
private $view;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* @var \PragmaRX\Google2FA\Google2FA
|
||||
*/
|
||||
private $google2FA;
|
||||
|
||||
/**
|
||||
* Where to redirect users after login / registration.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Lockout time for failed login requests.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $lockoutTime;
|
||||
|
||||
/**
|
||||
* After how many attempts should logins be throttled and locked.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $maxLoginAttempts;
|
||||
|
||||
/**
|
||||
* LoginController constructor.
|
||||
*
|
||||
* @param \Illuminate\Auth\AuthManager $auth
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \PragmaRX\Google2FA\Google2FA $google2FA
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
* @param \Illuminate\Contracts\View\Factory $view
|
||||
*/
|
||||
public function __construct(
|
||||
AuthManager $auth,
|
||||
Repository $config,
|
||||
CacheRepository $cache,
|
||||
ConfigRepository $config,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
UserRepositoryInterface $repository
|
||||
UserRepositoryInterface $repository,
|
||||
ViewFactory $view
|
||||
) {
|
||||
$this->auth = $auth;
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->google2FA = $google2FA;
|
||||
$this->repository = $repository;
|
||||
parent::__construct($auth, $config);
|
||||
|
||||
$this->lockoutTime = $this->config->get('auth.lockout.time');
|
||||
$this->maxLoginAttempts = $this->config->get('auth.lockout.attempts');
|
||||
$this->view = $view;
|
||||
$this->cache = $cache;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle all incoming requests for the authentication routes and render the
|
||||
* base authentication view component. Vuejs will take over at this point and
|
||||
* turn the login area into a SPA.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
return $this->view->make('templates/auth.core');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
*/
|
||||
public function login(Request $request)
|
||||
public function login(Request $request): JsonResponse
|
||||
{
|
||||
$username = $request->input(self::USER_INPUT_FIELD);
|
||||
$username = $request->input('user');
|
||||
$useColumn = $this->getField($username);
|
||||
|
||||
if ($this->hasTooManyLoginAttempts($request)) {
|
||||
|
@ -126,122 +90,28 @@ class LoginController extends Controller
|
|||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
$validCredentials = password_verify($request->input('password'), $user->password);
|
||||
// Ensure that the account is using a valid username and password before trying to
|
||||
// continue. Previously this was handled in the 2FA checkpoint, however that has
|
||||
// a flaw in which you can discover if an account exists simply by seeing if you
|
||||
// can proceede to the next step in the login process.
|
||||
if (! password_verify($request->input('password'), $user->password)) {
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
|
||||
if ($user->use_totp) {
|
||||
$token = str_random(64);
|
||||
$this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => $validCredentials], 5);
|
||||
$token = Str::random(64);
|
||||
$this->cache->put($token, $user->id, 5);
|
||||
|
||||
return redirect()->route('auth.totp')->with('authentication_token', $token);
|
||||
}
|
||||
|
||||
if ($validCredentials) {
|
||||
$this->auth->guard()->login($user, true);
|
||||
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a TOTP implementation page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
|
||||
*/
|
||||
public function totp(Request $request)
|
||||
{
|
||||
$token = $request->session()->get('authentication_token');
|
||||
if (is_null($token) || $this->auth->guard()->user()) {
|
||||
return redirect()->route('auth.login');
|
||||
}
|
||||
|
||||
return view('auth.totp', ['verify_key' => $token]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a login where the user is required to provide a TOTP authentication
|
||||
* token. In order to add additional layers of security, users are not
|
||||
* informed of an incorrect password until this stage, forcing them to
|
||||
* provide a token on each login attempt.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response
|
||||
*/
|
||||
public function loginUsingTotp(Request $request)
|
||||
{
|
||||
if (is_null($request->input('verify_token'))) {
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
try {
|
||||
$cache = $this->cache->pull($request->input('verify_token'), []);
|
||||
$user = $this->repository->find(array_get($cache, 'user_id', 0));
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
if (is_null($request->input('2fa_token')) || ! array_get($cache, 'valid_credentials')) {
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
|
||||
if (! $this->google2FA->verifyKey(
|
||||
$this->encrypter->decrypt($user->totp_secret),
|
||||
$request->input('2fa_token'),
|
||||
$this->config->get('pterodactyl.auth.2fa.window')
|
||||
)) {
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
return JsonResponse::create([
|
||||
'data' => [
|
||||
'complete' => false,
|
||||
'confirmation_token' => $token,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$this->auth->guard()->login($user, true);
|
||||
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request, Authenticatable $user = null): RedirectResponse
|
||||
{
|
||||
$this->incrementLoginAttempts($request);
|
||||
$this->fireFailedLoginEvent($user, [
|
||||
$this->getField($request->input(self::USER_INPUT_FIELD)) => $request->input(self::USER_INPUT_FIELD),
|
||||
]);
|
||||
|
||||
$errors = [self::USER_INPUT_FIELD => trans('auth.failed')];
|
||||
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json($errors, 422);
|
||||
}
|
||||
|
||||
return redirect()->route('auth.login')
|
||||
->withInput($request->only(self::USER_INPUT_FIELD))
|
||||
->withErrors($errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the user is logging in using an email or username,.
|
||||
*
|
||||
* @param string $input
|
||||
* @return string
|
||||
*/
|
||||
private function getField(string $input = null): string
|
||||
{
|
||||
return str_contains($input, '@') ? 'email' : 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire a failed login event.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Authenticatable|null $user
|
||||
* @param array $credentials
|
||||
*/
|
||||
private function fireFailedLoginEvent(Authenticatable $user = null, array $credentials = [])
|
||||
{
|
||||
event(new Failed(config('auth.defaults.guard'), $user, $credentials));
|
||||
return $this->sendLoginResponse($user, $request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,15 @@
|
|||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\Hashing\Hasher;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use Illuminate\Auth\Events\PasswordReset;
|
||||
use Illuminate\Contracts\Events\Dispatcher;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Pterodactyl\Http\Requests\Auth\ResetPasswordRequest;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
|
||||
class ResetPasswordController extends Controller
|
||||
|
@ -28,11 +30,6 @@ class ResetPasswordController extends Controller
|
|||
*/
|
||||
protected $hasTwoFactor = false;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
private $alerts;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Events\Dispatcher
|
||||
*/
|
||||
|
@ -51,31 +48,44 @@ class ResetPasswordController extends Controller
|
|||
/**
|
||||
* ResetPasswordController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alerts
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $dispatcher
|
||||
* @param \Illuminate\Contracts\Hashing\Hasher $hasher
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $userRepository
|
||||
*/
|
||||
public function __construct(AlertsMessageBag $alerts, Dispatcher $dispatcher, Hasher $hasher, UserRepositoryInterface $userRepository)
|
||||
public function __construct(Dispatcher $dispatcher, Hasher $hasher, UserRepositoryInterface $userRepository)
|
||||
{
|
||||
$this->alerts = $alerts;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->hasher = $hasher;
|
||||
$this->userRepository = $userRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the rules used when validating password reset.
|
||||
* Reset the given user's password.
|
||||
*
|
||||
* @return array
|
||||
* @param \Pterodactyl\Http\Requests\Auth\ResetPasswordRequest $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
*/
|
||||
protected function rules(): array
|
||||
public function __invoke(ResetPasswordRequest $request): JsonResponse
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'email' => 'required|email',
|
||||
'password' => 'required|confirmed|min:8',
|
||||
];
|
||||
// Here we will attempt to reset the user's password. If it is successful we
|
||||
// will update the password on an actual user model and persist it to the
|
||||
// database. Otherwise we will parse the error and return the response.
|
||||
$response = $this->broker()->reset(
|
||||
$this->credentials($request), function ($user, $password) {
|
||||
$this->resetPassword($user, $password);
|
||||
}
|
||||
);
|
||||
|
||||
// If the password was successfully reset, we will redirect the user back to
|
||||
// the application's home authenticated view. If there is an error we can
|
||||
// redirect them back to where they came from with their error message.
|
||||
if ($response === Password::PASSWORD_RESET) {
|
||||
return $this->sendResetResponse();
|
||||
}
|
||||
|
||||
throw new DisplayException(trans($response));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,19 +118,16 @@ class ResetPasswordController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset.
|
||||
* Send a successful password reset response back to the callee.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetResponse(Request $request, $response)
|
||||
protected function sendResetResponse(): JsonResponse
|
||||
{
|
||||
if ($this->hasTwoFactor) {
|
||||
$this->alerts->success('Your password was successfully updated. Please log in to continue.')->flash();
|
||||
}
|
||||
|
||||
return redirect($this->hasTwoFactor ? route('auth.login') : $this->redirectPath())
|
||||
->with('status', trans($response));
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'redirect_to' => $this->redirectTo,
|
||||
'send_to_login' => $this->hasTwoFactor,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Base;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Illuminate\Contracts\Session\Session;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Users\UserUpdateService;
|
||||
use Pterodactyl\Traits\Helpers\AvailableLanguages;
|
||||
use Pterodactyl\Http\Requests\Base\AccountDataFormRequest;
|
||||
|
||||
class AccountController extends Controller
|
||||
{
|
||||
use AvailableLanguages;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
protected $alert;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Auth\SessionGuard
|
||||
*/
|
||||
protected $sessionGuard;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Users\UserUpdateService
|
||||
*/
|
||||
protected $updateService;
|
||||
|
||||
/**
|
||||
* AccountController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||
* @param \Illuminate\Auth\AuthManager $authManager
|
||||
* @param \Pterodactyl\Services\Users\UserUpdateService $updateService
|
||||
*/
|
||||
public function __construct(AlertsMessageBag $alert, AuthManager $authManager, UserUpdateService $updateService)
|
||||
{
|
||||
$this->alert = $alert;
|
||||
$this->updateService = $updateService;
|
||||
$this->sessionGuard = $authManager->guard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display base account information page.
|
||||
*
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
return view('base.account', [
|
||||
'languages' => $this->getAvailableLanguages(true),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update details for a user's account.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Base\AccountDataFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(AccountDataFormRequest $request)
|
||||
{
|
||||
// Prevent logging this specific session out when the password is changed. This will
|
||||
// automatically update the user's password anyways, so no need to do anything else here.
|
||||
if ($request->input('do_action') === 'password') {
|
||||
$this->sessionGuard->logoutOtherDevices($request->input('new_password'));
|
||||
} else {
|
||||
if ($request->input('do_action') === 'email') {
|
||||
$data = ['email' => $request->input('new_email')];
|
||||
} elseif ($request->input('do_action') === 'identity') {
|
||||
$data = $request->only(['name_first', 'name_last', 'username', 'language']);
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$this->updateService->setUserLevel(User::USER_LEVEL_USER);
|
||||
$this->updateService->handle($request->user(), $data);
|
||||
}
|
||||
|
||||
$this->alert->success(trans('base.account.details_updated'))->flash();
|
||||
|
||||
return redirect()->route('account');
|
||||
}
|
||||
}
|
|
@ -53,13 +53,13 @@ class IndexController extends Controller
|
|||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function getIndex(Request $request)
|
||||
public function index(Request $request)
|
||||
{
|
||||
$servers = $this->repository->setSearchTerm($request->input('query'))->filterUserAccessServers(
|
||||
$request->user(), User::FILTER_LEVEL_ALL, config('pterodactyl.paginate.frontend.servers')
|
||||
);
|
||||
|
||||
return view('base.index', ['servers' => $servers]);
|
||||
return view('templates/base.core', ['servers' => $servers]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Pterodactyl\Http\Controllers\Base;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Users\TwoFactorSetupService;
|
||||
|
@ -62,36 +63,28 @@ class SecurityController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns Security Management Page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
if ($this->config->get('session.driver') === 'database') {
|
||||
$activeSessions = $this->repository->getUserSessions($request->user()->id);
|
||||
}
|
||||
|
||||
return view('base.security', [
|
||||
'sessions' => $activeSessions ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates TOTP Secret and returns popup data for user to verify
|
||||
* that they can generate a valid response.
|
||||
* Return information about the user's two-factor authentication status. If not enabled setup their
|
||||
* secret and return information to allow the user to proceede with setup.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function generateTotp(Request $request)
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'qrImage' => $this->twoFactorSetupService->handle($request->user()),
|
||||
if ($request->user()->use_totp) {
|
||||
return JsonResponse::create([
|
||||
'enabled' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
$response = $this->twoFactorSetupService->handle($request->user());
|
||||
|
||||
return JsonResponse::create([
|
||||
'enabled' => false,
|
||||
'qr_image' => $response,
|
||||
'secret' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -99,53 +92,43 @@ class SecurityController extends Controller
|
|||
* Verifies that 2FA token received is valid and will work on the account.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function setTotp(Request $request)
|
||||
public function store(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->toggleTwoFactorService->handle($request->user(), $request->input('token') ?? '');
|
||||
|
||||
return response('true');
|
||||
} catch (TwoFactorAuthenticationTokenInvalid $exception) {
|
||||
return response('false');
|
||||
$error = true;
|
||||
}
|
||||
|
||||
return JsonResponse::create([
|
||||
'success' => ! isset($error),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables TOTP on an account.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function disableTotp(Request $request)
|
||||
public function delete(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$this->toggleTwoFactorService->handle($request->user(), $request->input('token') ?? '', false);
|
||||
} catch (TwoFactorAuthenticationTokenInvalid $exception) {
|
||||
$this->alert->danger(trans('base.security.2fa_disable_error'))->flash();
|
||||
$error = true;
|
||||
}
|
||||
|
||||
return redirect()->route('account.security');
|
||||
}
|
||||
|
||||
/**
|
||||
* Revokes a user session.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $id
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function revoke(Request $request, string $id)
|
||||
{
|
||||
$this->repository->deleteUserSession($request->user()->id, $id);
|
||||
|
||||
return redirect()->route('account.security');
|
||||
return JsonResponse::create([
|
||||
'success' => ! isset($error),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,14 @@ namespace Pterodactyl\Http\Controllers\Daemon;
|
|||
use Cache;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Repositories\Eloquent\ServerRepository;
|
||||
use Pterodactyl\Events\Server\Installed as ServerInstalled;
|
||||
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
|
||||
class ActionController extends Controller
|
||||
{
|
||||
|
@ -16,15 +20,21 @@ class ActionController extends Controller
|
|||
* @var \Illuminate\Contracts\Events\Dispatcher
|
||||
*/
|
||||
private $eventDispatcher;
|
||||
/**
|
||||
* @var \Pterodactyl\Repositories\Eloquent\ServerRepository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ActionController constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $eventDispatcher
|
||||
* @param \Pterodactyl\Repositories\Eloquent\ServerRepository $repository
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $eventDispatcher
|
||||
*/
|
||||
public function __construct(EventDispatcher $eventDispatcher)
|
||||
public function __construct(ServerRepository $repository, EventDispatcher $eventDispatcher)
|
||||
{
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -32,34 +42,47 @@ class ActionController extends Controller
|
|||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function markInstall(Request $request)
|
||||
public function markInstall(Request $request): JsonResponse
|
||||
{
|
||||
$server = Server::where('uuid', $request->input('server'))->with('node')->first();
|
||||
if (! $server) {
|
||||
return response()->json([
|
||||
try {
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $this->repository->findFirstWhere([
|
||||
'uuid' => $request->input('server'),
|
||||
]);
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
return JsonResponse::create([
|
||||
'error' => 'No server by that ID was found on the system.',
|
||||
], 422);
|
||||
], Response::HTTP_UNPROCESSABLE_ENTITY);
|
||||
}
|
||||
|
||||
if (! $server->relationLoaded('node')) {
|
||||
$server->load('node');
|
||||
}
|
||||
|
||||
$hmac = $request->input('signed');
|
||||
$status = $request->input('installed');
|
||||
|
||||
if (! hash_equals(base64_decode($hmac), hash_hmac('sha256', $server->uuid, $server->node->daemonSecret, true))) {
|
||||
return response()->json([
|
||||
if (! hash_equals(base64_decode($hmac), hash_hmac('sha256', $server->uuid, $server->getRelation('node')->daemonSecret, true))) {
|
||||
return JsonResponse::create([
|
||||
'error' => 'Signed HMAC was invalid.',
|
||||
], 403);
|
||||
], Response::HTTP_FORBIDDEN);
|
||||
}
|
||||
|
||||
$server->installed = ($status === 'installed') ? 1 : 2;
|
||||
$server->save();
|
||||
$this->repository->update($server->id, [
|
||||
'installed' => ($status === 'installed') ? 1 : 2,
|
||||
], true, true);
|
||||
|
||||
// Only fire event if server installed successfully.
|
||||
if ($server->installed === 1) {
|
||||
if ($status === 'installed') {
|
||||
$this->eventDispatcher->dispatch(new ServerInstalled($server));
|
||||
}
|
||||
|
||||
return response()->json([]);
|
||||
// Don't use a 204 here, the daemon is hard-checking for a 200 code.
|
||||
return JsonResponse::create([]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class ConsoleController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* ConsoleController constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*/
|
||||
public function __construct(ConfigRepository $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render server index page with the console and power options.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
|
||||
$this->setRequest($request)->injectJavascript([
|
||||
'server' => [
|
||||
'cpu' => $server->cpu,
|
||||
],
|
||||
'meta' => [
|
||||
'saveFile' => route('server.files.save', $server->uuidShort),
|
||||
'csrfToken' => csrf_token(),
|
||||
],
|
||||
'config' => [
|
||||
'console_count' => $this->config->get('pterodactyl.console.count'),
|
||||
'console_freq' => $this->config->get('pterodactyl.console.frequency'),
|
||||
],
|
||||
]);
|
||||
|
||||
return view('server.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a stand-alone console in the browser.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function console(Request $request): View
|
||||
{
|
||||
$this->setRequest($request)->injectJavascript(['config' => [
|
||||
'console_count' => $this->config->get('pterodactyl.console.count'),
|
||||
'console_freq' => $this->config->get('pterodactyl.console.frequency'),
|
||||
]]);
|
||||
|
||||
return view('server.console');
|
||||
}
|
||||
}
|
48
app/Http/Controllers/Server/CredentialsController.php
Normal file
48
app/Http/Controllers/Server/CredentialsController.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
||||
|
||||
class CredentialsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
|
||||
*/
|
||||
private $keyProviderService;
|
||||
|
||||
/**
|
||||
* CredentialsController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
||||
*/
|
||||
public function __construct(DaemonKeyProviderService $keyProviderService)
|
||||
{
|
||||
$this->keyProviderService = $keyProviderService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a set of credentials that the currently authenticated user can use to access
|
||||
* a given server with.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $request->attributes->get('server');
|
||||
$server->loadMissing('node');
|
||||
|
||||
return JsonResponse::create([
|
||||
'node' => $server->getRelation('node')->getConnectionAddress(),
|
||||
'key' => $this->keyProviderService->handle($server, $request->user()),
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,165 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Services\Databases\DatabasePasswordService;
|
||||
use Pterodactyl\Services\Databases\DatabaseManagementService;
|
||||
use Pterodactyl\Services\Databases\DeployServerDatabaseService;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Server\Database\StoreServerDatabaseRequest;
|
||||
use Pterodactyl\Http\Requests\Server\Database\DeleteServerDatabaseRequest;
|
||||
|
||||
class DatabaseController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
private $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DeployServerDatabaseService
|
||||
*/
|
||||
private $deployServerDatabaseService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface
|
||||
*/
|
||||
private $databaseHostRepository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DatabaseManagementService
|
||||
*/
|
||||
private $managementService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Databases\DatabasePasswordService
|
||||
*/
|
||||
private $passwordService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* DatabaseController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||
* @param \Pterodactyl\Services\Databases\DeployServerDatabaseService $deployServerDatabaseService
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseHostRepositoryInterface $databaseHostRepository
|
||||
* @param \Pterodactyl\Services\Databases\DatabaseManagementService $managementService
|
||||
* @param \Pterodactyl\Services\Databases\DatabasePasswordService $passwordService
|
||||
* @param \Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
DeployServerDatabaseService $deployServerDatabaseService,
|
||||
DatabaseHostRepositoryInterface $databaseHostRepository,
|
||||
DatabaseManagementService $managementService,
|
||||
DatabasePasswordService $passwordService,
|
||||
DatabaseRepositoryInterface $repository
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->databaseHostRepository = $databaseHostRepository;
|
||||
$this->deployServerDatabaseService = $deployServerDatabaseService;
|
||||
$this->managementService = $managementService;
|
||||
$this->passwordService = $passwordService;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the database listing for a server.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('view-databases', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
$canCreateDatabase = config('pterodactyl.client_features.databases.enabled');
|
||||
$allowRandom = config('pterodactyl.client_features.databases.allow_random');
|
||||
|
||||
if ($this->databaseHostRepository->findCountWhere([['node_id', '=', $server->node_id]]) === 0) {
|
||||
if ($canCreateDatabase && ! $allowRandom) {
|
||||
$canCreateDatabase = false;
|
||||
}
|
||||
}
|
||||
|
||||
$databases = $this->repository->getDatabasesForServer($server->id);
|
||||
|
||||
return view('server.databases.index', [
|
||||
'allowCreation' => $canCreateDatabase,
|
||||
'overLimit' => ! is_null($server->database_limit) && count($databases) >= $server->database_limit,
|
||||
'databases' => $databases,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request from a user to create a new database for the server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Database\StoreServerDatabaseRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Pterodactyl\Exceptions\Service\Database\DatabaseClientFeatureNotEnabledException
|
||||
*/
|
||||
public function store(StoreServerDatabaseRequest $request): RedirectResponse
|
||||
{
|
||||
$this->deployServerDatabaseService->handle($request->getServer(), $request->validated());
|
||||
|
||||
$this->alert->success('Successfully created a new database.')->flash();
|
||||
|
||||
return redirect()->route('server.databases.index', $request->getServer()->uuidShort);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request to update the password for a specific database.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(Request $request): JsonResponse
|
||||
{
|
||||
$this->authorize('reset-db-password', $request->attributes->get('server'));
|
||||
|
||||
$password = str_random(20);
|
||||
$this->passwordService->handle($request->attributes->get('database'), $password);
|
||||
|
||||
return response()->json(['password' => $password]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a database for this server from the SQL server and Panel database.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Database\DeleteServerDatabaseRequest $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function delete(DeleteServerDatabaseRequest $request): Response
|
||||
{
|
||||
$this->managementService->delete($request->attributes->get('database')->id);
|
||||
|
||||
return response('', Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
57
app/Http/Controllers/Server/FileController.php
Normal file
57
app/Http/Controllers/Server/FileController.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use GuzzleHttp\Exception\TransferException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
||||
class FileController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
|
||||
*/
|
||||
private $fileRepository;
|
||||
|
||||
/**
|
||||
* FileController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository
|
||||
*/
|
||||
public function __construct(FileRepositoryInterface $fileRepository)
|
||||
{
|
||||
$this->fileRepository = $fileRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('list-files', $server);
|
||||
|
||||
$requestDirectory = '/' . trim(urldecode($request->route()->parameter('directory', '/')), '/');
|
||||
|
||||
try {
|
||||
$contents = $this->fileRepository->setServer($server)->setToken(
|
||||
$request->attributes->get('server_token')
|
||||
)->getDirectory($requestDirectory);
|
||||
} catch (TransferException $exception) {
|
||||
throw new DaemonConnectionException($exception, true);
|
||||
}
|
||||
|
||||
return JsonResponse::create([
|
||||
'contents' => $contents,
|
||||
'editable' => config('pterodactyl.files.editable'),
|
||||
'current_directory' => $requestDirectory,
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Files;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Cache\Repository;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
|
||||
class DownloadController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Cache\Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* DownloadController constructor.
|
||||
*
|
||||
* @param \Illuminate\Cache\Repository $cache
|
||||
*/
|
||||
public function __construct(Repository $cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup a unique download link for a user to download a file from.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string $uuid
|
||||
* @param string $file
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request, string $uuid, string $file): RedirectResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('download-files', $server);
|
||||
|
||||
$token = Uuid::uuid4()->toString();
|
||||
$node = $server->getRelation('node');
|
||||
|
||||
$this->cache->put('Server:Downloads:' . $token, ['server' => $server->uuid, 'path' => $file], 5);
|
||||
|
||||
return redirect(sprintf('%s://%s:%s/v1/server/file/download/%s', $node->scheme, $node->fqdn, $node->daemonListen, $token));
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Files;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
||||
class FileActionsController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* FileActionsController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(FileRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display server file index list.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('list-files', $server);
|
||||
|
||||
$this->setRequest($request)->injectJavascript([
|
||||
'meta' => [
|
||||
'directoryList' => route('server.files.directory-list', $server->uuidShort),
|
||||
'csrftoken' => csrf_token(),
|
||||
],
|
||||
'permissions' => [
|
||||
'moveFiles' => $request->user()->can('move-files', $server),
|
||||
'copyFiles' => $request->user()->can('copy-files', $server),
|
||||
'compressFiles' => $request->user()->can('compress-files', $server),
|
||||
'decompressFiles' => $request->user()->can('decompress-files', $server),
|
||||
'createFiles' => $request->user()->can('create-files', $server),
|
||||
'downloadFiles' => $request->user()->can('download-files', $server),
|
||||
'deleteFiles' => $request->user()->can('delete-files', $server),
|
||||
],
|
||||
]);
|
||||
|
||||
return view('server.files.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render page to manually create a file in the panel.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$this->authorize('create-files', $request->attributes->get('server'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.files.add', [
|
||||
'directory' => (in_array($request->get('dir'), [null, '/', ''])) ? '' : trim($request->get('dir'), '/') . '/',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to allow for editing of a file.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\UpdateFileContentsFormRequest $request
|
||||
* @param string $uuid
|
||||
* @param string $file
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function view(UpdateFileContentsFormRequest $request, string $uuid, string $file): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
|
||||
$dirname = str_replace('\\', '/', pathinfo($file, PATHINFO_DIRNAME));
|
||||
try {
|
||||
$content = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getContent($file);
|
||||
} catch (RequestException $exception) {
|
||||
throw new DaemonConnectionException($exception);
|
||||
}
|
||||
|
||||
$this->setRequest($request)->injectJavascript(['stat' => $request->attributes->get('file_stats')]);
|
||||
|
||||
return view('server.files.edit', [
|
||||
'file' => $file,
|
||||
'stat' => $request->attributes->get('file_stats'),
|
||||
'contents' => $content,
|
||||
'directory' => (in_array($dirname, ['.', './', '/'])) ? '/' : trim($dirname, '/') . '/',
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Files;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
|
||||
class RemoteRequestController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* RemoteRequestController constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ConfigRepository $config, FileRepositoryInterface $repository)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a listing of a servers file directory.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function directory(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('list-files', $server);
|
||||
|
||||
$requestDirectory = '/' . trim(urldecode($request->input('directory', '/')), '/');
|
||||
$directory = [
|
||||
'header' => $requestDirectory !== '/' ? $requestDirectory : '',
|
||||
'first' => $requestDirectory !== '/',
|
||||
];
|
||||
|
||||
$goBack = explode('/', trim($requestDirectory, '/'));
|
||||
if (! empty(array_filter($goBack)) && count($goBack) >= 2) {
|
||||
array_pop($goBack);
|
||||
|
||||
$directory['show'] = true;
|
||||
$directory['link'] = '/' . implode('/', $goBack);
|
||||
$directory['link_show'] = implode('/', $goBack) . '/';
|
||||
}
|
||||
|
||||
try {
|
||||
$listing = $this->repository->setServer($server)->setToken($request->attributes->get('server_token'))->getDirectory($requestDirectory);
|
||||
} catch (RequestException $exception) {
|
||||
throw new DaemonConnectionException($exception, true);
|
||||
}
|
||||
|
||||
return view('server.files.list', [
|
||||
'files' => $listing['files'],
|
||||
'folders' => $listing['folders'],
|
||||
'editableMime' => $this->config->get('pterodactyl.files.editable'),
|
||||
'directory' => $directory,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the contents of a file onto the daemon.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function store(Request $request): Response
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('save-files', $server);
|
||||
|
||||
try {
|
||||
$this->repository->setServer($server)->setToken($request->attributes->get('server_token'))
|
||||
->putContent($request->input('file'), $request->input('contents') ?? '');
|
||||
|
||||
return response('', 204);
|
||||
} catch (RequestException $exception) {
|
||||
throw new DaemonConnectionException($exception);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Settings;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Contracts\Extensions\HashidsInterface;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Services\Allocations\SetDefaultAllocationService;
|
||||
use Pterodactyl\Contracts\Repository\AllocationRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Service\Allocation\AllocationDoesNotBelongToServerException;
|
||||
|
||||
class AllocationController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Allocations\SetDefaultAllocationService
|
||||
*/
|
||||
private $defaultAllocationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
|
||||
*/
|
||||
private $hashids;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* AllocationController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\AllocationRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
|
||||
* @param \Pterodactyl\Services\Allocations\SetDefaultAllocationService $defaultAllocationService
|
||||
*/
|
||||
public function __construct(
|
||||
AllocationRepositoryInterface $repository,
|
||||
HashidsInterface $hashids,
|
||||
SetDefaultAllocationService $defaultAllocationService
|
||||
) {
|
||||
$this->defaultAllocationService = $defaultAllocationService;
|
||||
$this->hashids = $hashids;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the allocation management overview page for a server.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('view-allocations', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.settings.allocation', [
|
||||
'allocations' => $this->repository->findWhere([['server_id', '=', $server->id]]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the default allocation for a server.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(Request $request): JsonResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('edit-allocation', $server);
|
||||
|
||||
$allocation = $this->hashids->decodeFirst($request->input('allocation'), 0);
|
||||
|
||||
try {
|
||||
$this->defaultAllocationService->handle($server->id, $allocation);
|
||||
} catch (AllocationDoesNotBelongToServerException $exception) {
|
||||
return response()->json(['error' => 'No matching allocation was located for this server.'], 404);
|
||||
}
|
||||
|
||||
return response()->json();
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Settings;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest;
|
||||
|
||||
class NameController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* NameController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->authorize('view-name', $request->attributes->get('server'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.settings.name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the stored name for a specific server.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Settings\ChangeServerNameRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(ChangeServerNameRequest $request): RedirectResponse
|
||||
{
|
||||
$this->repository->update($request->getServer()->id, $request->validated());
|
||||
|
||||
return redirect()->route('server.settings.name', $request->getServer()->uuidShort);
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Settings;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
|
||||
class SftpController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* Render the server SFTP settings page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$this->authorize('access-sftp', $request->attributes->get('server'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.settings.sftp');
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Settings;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Services\Servers\StartupCommandViewService;
|
||||
use Pterodactyl\Services\Servers\StartupModificationService;
|
||||
use Pterodactyl\Http\Requests\Server\UpdateStartupParametersFormRequest;
|
||||
|
||||
class StartupController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
private $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\StartupCommandViewService
|
||||
*/
|
||||
private $commandViewService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Servers\StartupModificationService
|
||||
*/
|
||||
private $modificationService;
|
||||
|
||||
/**
|
||||
* StartupController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||
* @param \Pterodactyl\Services\Servers\StartupCommandViewService $commandViewService
|
||||
* @param \Pterodactyl\Services\Servers\StartupModificationService $modificationService
|
||||
*/
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
StartupCommandViewService $commandViewService,
|
||||
StartupModificationService $modificationService
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->commandViewService = $commandViewService;
|
||||
$this->modificationService = $modificationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the server startup page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('view-startup', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
$data = $this->commandViewService->handle($server->id);
|
||||
|
||||
return view('server.settings.startup', [
|
||||
'variables' => $data->get('variables'),
|
||||
'server_values' => $data->get('server_values'),
|
||||
'startup' => $data->get('startup'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request to update the startup variables for a server. Authorization
|
||||
* is handled in the form request.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\UpdateStartupParametersFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Illuminate\Validation\ValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(UpdateStartupParametersFormRequest $request): RedirectResponse
|
||||
{
|
||||
$this->modificationService->setUserLevel(User::USER_LEVEL_USER);
|
||||
$this->modificationService->handle($request->attributes->get('server'), $request->normalize());
|
||||
$this->alert->success(trans('server.config.startup.edited'))->flash();
|
||||
|
||||
return redirect()->route('server.settings.startup', ['server' => $request->attributes->get('server')->uuidShort]);
|
||||
}
|
||||
}
|
|
@ -1,197 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Subusers\SubuserUpdateService;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Services\Subusers\SubuserCreationService;
|
||||
use Pterodactyl\Services\Subusers\SubuserDeletionService;
|
||||
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest;
|
||||
use Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest;
|
||||
|
||||
class SubuserController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
protected $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Subusers\SubuserCreationService
|
||||
*/
|
||||
protected $subuserCreationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Subusers\SubuserDeletionService
|
||||
*/
|
||||
protected $subuserDeletionService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Subusers\SubuserUpdateService
|
||||
*/
|
||||
protected $subuserUpdateService;
|
||||
|
||||
/**
|
||||
* SubuserController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||
* @param \Pterodactyl\Services\Subusers\SubuserCreationService $subuserCreationService
|
||||
* @param \Pterodactyl\Services\Subusers\SubuserDeletionService $subuserDeletionService
|
||||
* @param \Pterodactyl\Contracts\Repository\SubuserRepositoryInterface $repository
|
||||
* @param \Pterodactyl\Services\Subusers\SubuserUpdateService $subuserUpdateService
|
||||
*/
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
SubuserCreationService $subuserCreationService,
|
||||
SubuserDeletionService $subuserDeletionService,
|
||||
SubuserRepositoryInterface $repository,
|
||||
SubuserUpdateService $subuserUpdateService
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->repository = $repository;
|
||||
$this->subuserCreationService = $subuserCreationService;
|
||||
$this->subuserDeletionService = $subuserDeletionService;
|
||||
$this->subuserUpdateService = $subuserUpdateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the subuser overview index.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('list-subusers', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.users.index', [
|
||||
'subusers' => $this->repository->findWhere([['server_id', '=', $server->id]]),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a single subuser overview.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function view(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('view-subuser', $server);
|
||||
|
||||
$subuser = $this->repository->getWithPermissions($request->attributes->get('subuser'));
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.users.view', [
|
||||
'subuser' => $subuser,
|
||||
'permlist' => Permission::getPermissions(),
|
||||
'permissions' => $subuser->getRelation('permissions')->mapWithKeys(function ($item) {
|
||||
return [$item->permission => true];
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles editing a subuser.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserUpdateFormRequest $request
|
||||
* @param string $uuid
|
||||
* @param string $hash
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function update(SubuserUpdateFormRequest $request, string $uuid, string $hash): RedirectResponse
|
||||
{
|
||||
$this->subuserUpdateService->handle($request->attributes->get('subuser'), $request->input('permissions', []));
|
||||
$this->alert->success(trans('server.users.user_updated'))->flash();
|
||||
|
||||
return redirect()->route('server.subusers.view', ['uuid' => $uuid, 'subuser' => $hash]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display new subuser creation page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('create-subuser', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.users.new', ['permissions' => Permission::getPermissions()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles creating a new subuser.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\Subuser\SubuserStoreFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Subuser\ServerSubuserExistsException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Subuser\UserIsServerOwnerException
|
||||
*/
|
||||
public function store(SubuserStoreFormRequest $request): RedirectResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
|
||||
$subuser = $this->subuserCreationService->handle($server, $request->input('email'), $request->input('permissions', []));
|
||||
$this->alert->success(trans('server.users.user_assigned'))->flash();
|
||||
|
||||
return redirect()->route('server.subusers.view', [
|
||||
'uuid' => $server->uuidShort,
|
||||
'id' => $subuser->hashid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles deleting a subuser.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\DisplayException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('delete-subuser', $server);
|
||||
|
||||
$this->subuserDeletionService->handle($request->attributes->get('subuser'));
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Tasks;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Services\Schedules\ProcessScheduleService;
|
||||
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
||||
|
||||
class ActionController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Schedules\ProcessScheduleService
|
||||
*/
|
||||
private $processScheduleService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* ActionController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Schedules\ProcessScheduleService $processScheduleService
|
||||
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ProcessScheduleService $processScheduleService, ScheduleRepositoryInterface $repository)
|
||||
{
|
||||
$this->processScheduleService = $processScheduleService;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle a task to be active or inactive for a given server.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function toggle(Request $request): Response
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$schedule = $request->attributes->get('schedule');
|
||||
$this->authorize('toggle-schedule', $server);
|
||||
|
||||
$this->repository->update($schedule->id, [
|
||||
'is_active' => ! $schedule->is_active,
|
||||
]);
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a schedule to run now.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function trigger(Request $request): Response
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('toggle-schedule', $server);
|
||||
|
||||
$this->processScheduleService->handle(
|
||||
$request->attributes->get('schedule')
|
||||
);
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Server\Tasks;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Pterodactyl\Contracts\Extensions\HashidsInterface;
|
||||
use Pterodactyl\Traits\Controllers\JavascriptInjection;
|
||||
use Pterodactyl\Services\Schedules\ScheduleUpdateService;
|
||||
use Pterodactyl\Services\Schedules\ScheduleCreationService;
|
||||
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest;
|
||||
|
||||
class TaskManagementController extends Controller
|
||||
{
|
||||
use JavascriptInjection;
|
||||
|
||||
/**
|
||||
* @var \Prologue\Alerts\AlertsMessageBag
|
||||
*/
|
||||
protected $alert;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Schedules\ScheduleCreationService
|
||||
*/
|
||||
protected $creationService;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
|
||||
*/
|
||||
protected $hashids;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Schedules\ScheduleUpdateService
|
||||
*/
|
||||
private $updateService;
|
||||
|
||||
/**
|
||||
* TaskManagementController constructor.
|
||||
*
|
||||
* @param \Prologue\Alerts\AlertsMessageBag $alert
|
||||
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
|
||||
* @param \Pterodactyl\Services\Schedules\ScheduleCreationService $creationService
|
||||
* @param \Pterodactyl\Services\Schedules\ScheduleUpdateService $updateService
|
||||
* @param \Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AlertsMessageBag $alert,
|
||||
HashidsInterface $hashids,
|
||||
ScheduleCreationService $creationService,
|
||||
ScheduleUpdateService $updateService,
|
||||
ScheduleRepositoryInterface $repository
|
||||
) {
|
||||
$this->alert = $alert;
|
||||
$this->creationService = $creationService;
|
||||
$this->hashids = $hashids;
|
||||
$this->repository = $repository;
|
||||
$this->updateService = $updateService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the task page listing.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('list-schedules', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.schedules.index', [
|
||||
'schedules' => $this->repository->findServerSchedules($server->id),
|
||||
'actions' => [
|
||||
'command' => trans('server.schedule.actions.command'),
|
||||
'power' => trans('server.schedule.actions.power'),
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the task creation page.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function create(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$this->authorize('create-schedule', $server);
|
||||
$this->setRequest($request)->injectJavascript();
|
||||
|
||||
return view('server.schedules.new');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle request to store a new schedule and tasks in the database.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException
|
||||
*/
|
||||
public function store(ScheduleCreationFormRequest $request): RedirectResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
|
||||
$schedule = $this->creationService->handle($server, $request->normalize(), $request->getTasks());
|
||||
$this->alert->success(trans('server.schedule.schedule_created'))->flash();
|
||||
|
||||
return redirect()->route('server.schedules.view', [
|
||||
'server' => $server->uuidShort,
|
||||
'schedule' => $schedule->hashid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a view to modify a schedule.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function view(Request $request): View
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$schedule = $request->attributes->get('schedule');
|
||||
$this->authorize('view-schedule', $server);
|
||||
|
||||
$this->setRequest($request)->injectJavascript([
|
||||
'tasks' => $schedule->getRelation('tasks')->map(function ($task) {
|
||||
/* @var \Pterodactyl\Models\Task $task */
|
||||
return collect($task->toArray())->only('action', 'time_offset', 'payload')->all();
|
||||
}),
|
||||
]);
|
||||
|
||||
return view('server.schedules.view', ['schedule' => $schedule]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a specific parent task on the system.
|
||||
*
|
||||
* @param \Pterodactyl\Http\Requests\Server\ScheduleCreationFormRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Pterodactyl\Exceptions\Service\Schedule\Task\TaskIntervalTooLongException
|
||||
*/
|
||||
public function update(ScheduleCreationFormRequest $request): RedirectResponse
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$schedule = $request->attributes->get('schedule');
|
||||
|
||||
$this->updateService->handle($schedule, $request->normalize(), $request->getTasks());
|
||||
$this->alert->success(trans('server.schedule.schedule_updated'))->flash();
|
||||
|
||||
return redirect()->route('server.schedules.view', [
|
||||
'server' => $server->uuidShort,
|
||||
'schedule' => $schedule->hashid,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a parent task from the Panel.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
* @throws \Illuminate\Auth\Access\AuthorizationException
|
||||
*/
|
||||
public function delete(Request $request): Response
|
||||
{
|
||||
$server = $request->attributes->get('server');
|
||||
$schedule = $request->attributes->get('schedule');
|
||||
$this->authorize('delete-schedule', $server);
|
||||
|
||||
$this->repository->delete($schedule->id);
|
||||
|
||||
return response('', 204);
|
||||
}
|
||||
}
|
|
@ -49,6 +49,7 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middleware = [
|
||||
CheckForMaintenanceMode::class,
|
||||
EncryptCookies::class,
|
||||
ValidatePostSize::class,
|
||||
TrimStrings::class,
|
||||
ConvertEmptyStringsToNull::class,
|
||||
|
@ -62,7 +63,6 @@ class Kernel extends HttpKernel
|
|||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
EncryptCookies::class,
|
||||
AddQueuedCookiesToResponse::class,
|
||||
StartSession::class,
|
||||
AuthenticateSession::class,
|
||||
|
@ -73,7 +73,7 @@ class Kernel extends HttpKernel
|
|||
RequireTwoFactorAuthentication::class,
|
||||
],
|
||||
'api' => [
|
||||
'throttle:120,1',
|
||||
'throttle:240,1',
|
||||
ApiSubstituteBindings::class,
|
||||
SetSessionDriver::class,
|
||||
'api..key:' . ApiKey::TYPE_APPLICATION,
|
||||
|
@ -81,9 +81,11 @@ class Kernel extends HttpKernel
|
|||
AuthenticateIPAccess::class,
|
||||
],
|
||||
'client-api' => [
|
||||
'throttle:60,1',
|
||||
SubstituteClientApiBindings::class,
|
||||
'throttle:240,1',
|
||||
StartSession::class,
|
||||
SetSessionDriver::class,
|
||||
AuthenticateSession::class,
|
||||
SubstituteClientApiBindings::class,
|
||||
'api..key:' . ApiKey::TYPE_ACCOUNT,
|
||||
AuthenticateIPAccess::class,
|
||||
],
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Pterodactyl\Http\Middleware\Api;
|
|||
use Closure;
|
||||
use Cake\Chronos\Chronos;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\ApiKey;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
|
@ -58,13 +59,43 @@ class AuthenticateKey
|
|||
*/
|
||||
public function handle(Request $request, Closure $next, int $keyType)
|
||||
{
|
||||
if (is_null($request->bearerToken())) {
|
||||
if (is_null($request->bearerToken()) && is_null($request->user())) {
|
||||
throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']);
|
||||
}
|
||||
|
||||
$raw = $request->bearerToken();
|
||||
$identifier = substr($raw, 0, ApiKey::IDENTIFIER_LENGTH);
|
||||
$token = substr($raw, ApiKey::IDENTIFIER_LENGTH);
|
||||
|
||||
// This is a request coming through using cookies, we have an authenticated user not using
|
||||
// an API key. Make some fake API key models and continue on through the process.
|
||||
if (empty($raw) && $request->user() instanceof User) {
|
||||
$model = (new ApiKey())->forceFill([
|
||||
'user_id' => $request->user()->id,
|
||||
'key_type' => ApiKey::TYPE_ACCOUNT,
|
||||
]);
|
||||
} else {
|
||||
$model = $this->authenticateApiKey($raw, $keyType);
|
||||
$this->auth->guard()->loginUsingId($model->user_id);
|
||||
}
|
||||
|
||||
$request->attributes->set('api_key', $model);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate an API key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param int $keyType
|
||||
* @return \Pterodactyl\Models\ApiKey
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
protected function authenticateApiKey(string $key, int $keyType): ApiKey
|
||||
{
|
||||
$identifier = substr($key, 0, ApiKey::IDENTIFIER_LENGTH);
|
||||
$token = substr($key, ApiKey::IDENTIFIER_LENGTH);
|
||||
|
||||
try {
|
||||
$model = $this->repository->findFirstWhere([
|
||||
|
@ -79,10 +110,8 @@ class AuthenticateKey
|
|||
throw new AccessDeniedHttpException;
|
||||
}
|
||||
|
||||
$this->auth->guard()->loginUsingId($model->user_id);
|
||||
$request->attributes->set('api_key', $model);
|
||||
$this->repository->withoutFreshModel()->update($model->id, ['last_used_at' => Chronos::now()]);
|
||||
|
||||
return $next($request);
|
||||
return $model;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Middleware\Api\Client;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
class AuthenticateClientAccess
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
|
||||
*/
|
||||
private $keyProviderService;
|
||||
|
||||
/**
|
||||
* AuthenticateClientAccess constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
|
||||
*/
|
||||
public function __construct(DaemonKeyProviderService $keyProviderService)
|
||||
{
|
||||
$this->keyProviderService = $keyProviderService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate that the currently authenticated user has permission
|
||||
* to access the specified server. This only checks that the user is an
|
||||
* admin, owner, or a subuser. You'll need to do more specific checks in
|
||||
* the API calls to determine if they can perform different actions.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if (is_null($request->user())) {
|
||||
throw new AccessDeniedHttpException('A request must be made using an authenticated client.');
|
||||
}
|
||||
|
||||
/** @var \Pterodactyl\Models\Server $server */
|
||||
$server = $request->route()->parameter('server');
|
||||
|
||||
try {
|
||||
$token = $this->keyProviderService->handle($server, $request->user());
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
throw new NotFoundHttpException('The requested server could not be located.');
|
||||
}
|
||||
|
||||
$request->attributes->set('server_token', $token);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Middleware\Api\Client\Server;
|
||||
|
||||
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 Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
class AuthenticateServerAccess
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* AuthenticateServerAccess constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(ServerRepositoryInterface $repository)
|
||||
{
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate that this server exists and is not suspended or marked as installing.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$server = $request->route()->parameter('server');
|
||||
|
||||
if (! $server instanceof Server) {
|
||||
throw new NotFoundHttpException(trans('exceptions.api.resource_not_found'));
|
||||
}
|
||||
|
||||
if ($server->suspended) {
|
||||
throw new AccessDeniedHttpException('Cannot access a server that is marked as being suspended.');
|
||||
}
|
||||
|
||||
if (! $server->isInstalled()) {
|
||||
throw new ConflictHttpException('Server has not completed the installation process.');
|
||||
}
|
||||
|
||||
$request->attributes->set('server', $server);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
|
@ -4,9 +4,11 @@ namespace Pterodactyl\Http\Middleware\Api\Client;
|
|||
|
||||
use Closure;
|
||||
use Illuminate\Container\Container;
|
||||
use Pterodactyl\Contracts\Extensions\HashidsInterface;
|
||||
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
use Pterodactyl\Contracts\Repository\DatabaseRepositoryInterface;
|
||||
|
||||
class SubstituteClientApiBindings extends ApiSubstituteBindings
|
||||
{
|
||||
|
@ -24,8 +26,13 @@ class SubstituteClientApiBindings extends ApiSubstituteBindings
|
|||
// column rather than the default 'id'.
|
||||
$this->router->bind('server', function ($value) use ($request) {
|
||||
try {
|
||||
$column = 'uuidShort';
|
||||
if (preg_match('/^[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i', $value)) {
|
||||
$column = 'uuid';
|
||||
}
|
||||
|
||||
return Container::getInstance()->make(ServerRepositoryInterface::class)->findFirstWhere([
|
||||
['uuidShort', '=', $value],
|
||||
[$column, '=', $value],
|
||||
]);
|
||||
} catch (RecordNotFoundException $ex) {
|
||||
$request->attributes->set('is_missing_model', true);
|
||||
|
@ -34,6 +41,20 @@ class SubstituteClientApiBindings extends ApiSubstituteBindings
|
|||
}
|
||||
});
|
||||
|
||||
$this->router->bind('database', function ($value) use ($request) {
|
||||
try {
|
||||
$id = Container::getInstance()->make(HashidsInterface::class)->decodeFirst($value);
|
||||
|
||||
return Container::getInstance()->make(DatabaseRepositoryInterface::class)->findFirstWhere([
|
||||
['id', '=', $id],
|
||||
]);
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
$request->attributes->set('is_missing_model', true);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
return parent::handle($request, $next);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,17 +4,10 @@ namespace Pterodactyl\Http\Middleware\Api;
|
|||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Barryvdh\Debugbar\LaravelDebugbar;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class SetSessionDriver
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
private $app;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
|
@ -23,12 +16,10 @@ class SetSessionDriver
|
|||
/**
|
||||
* SetSessionDriver constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $app
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
*/
|
||||
public function __construct(Application $app, ConfigRepository $config)
|
||||
public function __construct(ConfigRepository $config)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
|
@ -41,10 +32,6 @@ class SetSessionDriver
|
|||
*/
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
if ($this->config->get('app.debug')) {
|
||||
$this->app->make(LaravelDebugbar::class)->disable();
|
||||
}
|
||||
|
||||
$this->config->set('session.driver', 'array');
|
||||
|
||||
return $next($request);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
namespace Pterodactyl\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
use Prologue\Alerts\AlertsMessageBag;
|
||||
|
||||
|
@ -24,27 +25,12 @@ class RequireTwoFactorAuthentication
|
|||
*/
|
||||
private $alert;
|
||||
|
||||
/**
|
||||
* The names of routes that should be accessible without 2FA enabled.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'account.security',
|
||||
'account.security.revoke',
|
||||
'account.security.totp',
|
||||
'account.security.totp.set',
|
||||
'account.security.totp.disable',
|
||||
'auth.totp',
|
||||
'auth.logout',
|
||||
];
|
||||
|
||||
/**
|
||||
* The route to redirect a user to to enable 2FA.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectRoute = 'account.security';
|
||||
protected $redirectRoute = 'account';
|
||||
|
||||
/**
|
||||
* RequireTwoFactorAuthentication constructor.
|
||||
|
@ -69,7 +55,8 @@ class RequireTwoFactorAuthentication
|
|||
return $next($request);
|
||||
}
|
||||
|
||||
if (in_array($request->route()->getName(), $this->except)) {
|
||||
$current = $request->route()->getName();
|
||||
if (in_array($current, ['auth', 'account']) || Str::startsWith($current, ['auth.', 'account.'])) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace Pterodactyl\Http\Middleware\Server;
|
|||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Contracts\Session\Session;
|
||||
use Illuminate\Contracts\Routing\ResponseFactory;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
|
||||
|
@ -29,29 +28,21 @@ class AccessingValidServer
|
|||
*/
|
||||
private $response;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Session\Session
|
||||
*/
|
||||
private $session;
|
||||
|
||||
/**
|
||||
* AccessingValidServer constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Routing\ResponseFactory $response
|
||||
* @param \Pterodactyl\Contracts\Repository\ServerRepositoryInterface $repository
|
||||
* @param \Illuminate\Contracts\Session\Session $session
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigRepository $config,
|
||||
ResponseFactory $response,
|
||||
ServerRepositoryInterface $repository,
|
||||
Session $session
|
||||
ServerRepositoryInterface $repository
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
$this->response = $response;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,7 +52,6 @@ class AccessingValidServer
|
|||
* @param \Closure $next
|
||||
* @return \Illuminate\Http\Response|mixed
|
||||
*
|
||||
* @throws \Illuminate\Auth\AuthenticationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
|
@ -90,10 +80,6 @@ class AccessingValidServer
|
|||
return $this->response->view('errors.installing', [], 409);
|
||||
}
|
||||
|
||||
// Store the server in the session.
|
||||
// @todo remove from session. use request attributes.
|
||||
$this->session->now('server_data.model', $server);
|
||||
|
||||
// Add server to the request attributes. This will replace sessions
|
||||
// as files are updated.
|
||||
$request->attributes->set('server', $server);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Admin\Servers\Databases;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Pterodactyl\Http\Requests\Admin\AdminFormRequest;
|
||||
|
||||
class StoreServerDatabaseRequest extends AdminFormRequest
|
||||
|
@ -14,7 +16,15 @@ class StoreServerDatabaseRequest extends AdminFormRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'database' => 'required|string|min:1|max:24',
|
||||
'database' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:24',
|
||||
Rule::unique('databases')->where(function (Builder $query) {
|
||||
$query->where('database_host_id', $this->input('database_host_id') ?? 0);
|
||||
}),
|
||||
],
|
||||
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
|
||||
'database_host_id' => 'required|integer|exists:database_hosts,id',
|
||||
];
|
||||
|
|
|
@ -126,10 +126,6 @@ abstract class ApplicationApiRequest extends FormRequest
|
|||
*
|
||||
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
|
||||
*/
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
protected function passesAuthorization()
|
||||
{
|
||||
// If we have already validated we do not need to call this function
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
namespace Pterodactyl\Http\Requests\Api\Application\Locations;
|
||||
|
||||
use Pterodactyl\Models\Location;
|
||||
use Pterodactyl\Http\Controllers\Api\Application\Locations\StoreLocationRequest;
|
||||
|
||||
class UpdateLocationRequest extends StoreLocationRequest
|
||||
{
|
||||
|
@ -31,6 +30,6 @@ class UpdateLocationRequest extends StoreLocationRequest
|
|||
return collect(Location::getUpdateRulesForId($locationId))->only([
|
||||
'short',
|
||||
'long',
|
||||
]);
|
||||
])->toArray();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ class StoreNodeRequest extends ApplicationApiRequest
|
|||
'memory',
|
||||
'memory_overallocate',
|
||||
'disk',
|
||||
'disk_overallocation',
|
||||
'disk_overallocate',
|
||||
'upload_size',
|
||||
'daemonListen',
|
||||
'daemonSFTP',
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Application\Servers\Databases;
|
||||
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Pterodactyl\Services\Acl\Api\AdminAcl;
|
||||
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||
|
||||
|
@ -25,7 +27,15 @@ class StoreServerDatabaseRequest extends ApplicationApiRequest
|
|||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'database' => 'required|string|min:1|max:24',
|
||||
'database' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:1',
|
||||
'max:24',
|
||||
Rule::unique('databases')->where(function (Builder $query) {
|
||||
$query->where('database_host_id', $this->input('host') ?? 0);
|
||||
}),
|
||||
],
|
||||
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
|
||||
'host' => 'required|integer|exists:database_hosts,id',
|
||||
];
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Pterodactyl\Http\Requests\Api\Application\Servers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
|
||||
{
|
||||
|
@ -17,15 +18,29 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
|
|||
|
||||
return [
|
||||
'allocation' => $rules['allocation_id'],
|
||||
'memory' => $rules['memory'],
|
||||
'swap' => $rules['swap'],
|
||||
'io' => $rules['io'],
|
||||
'cpu' => $rules['cpu'],
|
||||
'disk' => $rules['disk'],
|
||||
|
||||
'limits' => 'sometimes|array',
|
||||
'limits.memory' => $this->requiredToOptional('memory', $rules['memory'], true),
|
||||
'limits.swap' => $this->requiredToOptional('swap', $rules['swap'], true),
|
||||
'limits.io' => $this->requiredToOptional('io', $rules['io'], true),
|
||||
'limits.cpu' => $this->requiredToOptional('cpu', $rules['cpu'], true),
|
||||
'limits.disk' => $this->requiredToOptional('disk', $rules['disk'], true),
|
||||
|
||||
// Legacy rules to maintain backwards compatable API support without requiring
|
||||
// a major version bump.
|
||||
//
|
||||
// @see https://github.com/pterodactyl/panel/issues/1500
|
||||
'memory' => $this->requiredToOptional('memory', $rules['memory']),
|
||||
'swap' => $this->requiredToOptional('swap', $rules['swap']),
|
||||
'io' => $this->requiredToOptional('io', $rules['io']),
|
||||
'cpu' => $this->requiredToOptional('cpu', $rules['cpu']),
|
||||
'disk' => $this->requiredToOptional('disk', $rules['disk']),
|
||||
|
||||
'add_allocations' => 'bail|array',
|
||||
'add_allocations.*' => 'integer',
|
||||
'remove_allocations' => 'bail|array',
|
||||
'remove_allocations.*' => 'integer',
|
||||
|
||||
'feature_limits' => 'required|array',
|
||||
'feature_limits.databases' => $rules['database_limit'],
|
||||
'feature_limits.allocations' => $rules['allocation_limit'],
|
||||
|
@ -46,6 +61,15 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
|
|||
$data['allocation_limit'] = $data['feature_limits']['allocations'];
|
||||
unset($data['allocation'], $data['feature_limits']);
|
||||
|
||||
// Adjust the limits field to match what is expected by the model.
|
||||
if (! empty($data['limits'])) {
|
||||
foreach ($data['limits'] as $key => $value) {
|
||||
$data[$key] = $value;
|
||||
}
|
||||
|
||||
unset($data['limits']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -65,4 +89,30 @@ class UpdateServerBuildConfigurationRequest extends ServerWriteRequest
|
|||
'feature_limits.allocations' => 'Allocation Limit',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts existing rules for certain limits into a format that maintains backwards
|
||||
* compatability with the old API endpoint while also supporting a more correct API
|
||||
* call.
|
||||
*
|
||||
* @param string $field
|
||||
* @param array $rules
|
||||
* @param bool $limits
|
||||
* @return array
|
||||
*
|
||||
* @see https://github.com/pterodactyl/panel/issues/1500
|
||||
*/
|
||||
protected function requiredToOptional(string $field, array $rules, bool $limits = false)
|
||||
{
|
||||
if (! in_array('required', $rules)) {
|
||||
return $rules;
|
||||
}
|
||||
|
||||
return (new Collection($rules))
|
||||
->filter(function ($value) {
|
||||
return $value !== 'required';
|
||||
})
|
||||
->prepend($limits ? 'required_with:limits' : 'required_without:limits')
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
|
|
39
app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php
Normal file
39
app/Http/Requests/Api/Client/Account/UpdateEmailRequest.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Account;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException;
|
||||
|
||||
class UpdateEmailRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! parent::authorize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify password matches when changing password or email.
|
||||
if (! password_verify($this->input('password'), $this->user()->password)) {
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = User::getUpdateRulesForId($this->user()->id);
|
||||
|
||||
return ['email' => $rules['email']];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Account;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
use Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException;
|
||||
|
||||
class UpdatePasswordRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Base\InvalidPasswordProvidedException
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
if (! parent::authorize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify password matches when changing password or email.
|
||||
if (! password_verify($this->input('current_password'), $this->user()->password)) {
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = User::getUpdateRulesForId($this->user()->id);
|
||||
|
||||
return ['password' => array_merge($rules['password'], ['confirmed'])];
|
||||
}
|
||||
}
|
|
@ -2,18 +2,23 @@
|
|||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Application\ApplicationApiRequest;
|
||||
|
||||
abstract class ClientApiRequest extends ApplicationApiRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the current user is authorized to perform
|
||||
* the requested action against the API.
|
||||
* Determine if the current user is authorized to perform the requested action against the API.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
if ($this instanceof ClientPermissionsRequest || method_exists($this, 'permission')) {
|
||||
return $this->user()->can($this->permission(), $this->getModel(Server::class));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Models\Database;
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class DeleteDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'delete-database';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function resourceExists(): bool
|
||||
{
|
||||
return $this->getModel(Server::class)->id === $this->getModel(Database::class)->server_id;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class GetDatabasesRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'view-databases';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Databases;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class StoreDatabaseRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'create-database';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'database' => 'required|alpha_dash|min:1|max:100',
|
||||
'remote' => 'required|string|regex:/^[0-9%.]{1,15}$/',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class CopyFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'copy-files';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'location' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class CreateFolderRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Checks that the authenticated user is allowed to create files on the server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('create-files', $this->getModel(Server::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'root' => 'sometimes|nullable|string',
|
||||
'name' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class DeleteFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'delete-files';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'location' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class DownloadFileRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Ensure that the user making this request has permission to download files
|
||||
* from this server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('download-files', $this->getModel(Server::class));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class GetFileContentsRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* Returns the permissions string indicating which permission should be used to
|
||||
* validate that the authenticated user has permission to perform this action aganist
|
||||
* the given resource (server).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'edit-files';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'file' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class ListFilesRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Check that the user making this request to the API is authorized to list all
|
||||
* of the files that exist for a given server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('list-files', $this->getModel(Server::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'directory' => 'sometimes|nullable|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class RenameFileRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* The permission the user is required to have in order to perform this
|
||||
* request action.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'move-files';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'rename_from' => 'string|required',
|
||||
'rename_to' => 'string|required',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||
|
||||
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class WriteFileContentRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||
{
|
||||
/**
|
||||
* Returns the permissions string indicating which permission should be used to
|
||||
* validate that the authenticated user has permission to perform this action aganist
|
||||
* the given resource (server).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function permission(): string
|
||||
{
|
||||
return 'save-files';
|
||||
}
|
||||
|
||||
/**
|
||||
* There is no rule here for the file contents since we just use the body content
|
||||
* on the request to set the file contents. If nothing is passed that is fine since
|
||||
* it just means we want to set the file to be empty.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'file' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Network;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||
|
||||
class GetNetworkRequest extends ClientApiRequest
|
||||
{
|
||||
/**
|
||||
* Check that the user has permission to view the allocations for
|
||||
* this server.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return $this->user()->can('view-allocations', $this->getModel(Server::class));
|
||||
}
|
||||
}
|
31
app/Http/Requests/Auth/LoginCheckpointRequest.php
Normal file
31
app/Http/Requests/Auth/LoginCheckpointRequest.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoginCheckpointRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the request is authorized.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rules to apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'confirmation_token' => 'required|string',
|
||||
'authentication_code' => 'required|numeric',
|
||||
];
|
||||
}
|
||||
}
|
27
app/Http/Requests/Auth/LoginRequest.php
Normal file
27
app/Http/Requests/Auth/LoginRequest.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorized(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'user' => 'required|string|min:1',
|
||||
'password' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
28
app/Http/Requests/Auth/ResetPasswordRequest.php
Normal file
28
app/Http/Requests/Auth/ResetPasswordRequest.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class ResetPasswordRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'token' => 'required|string',
|
||||
'email' => 'required|email',
|
||||
'password' => 'required|string|confirmed|min:8',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -28,7 +28,7 @@ class AccountDataFormRequest extends FrontendUserFormRequest
|
|||
// Verify password matches when changing password or email.
|
||||
if (in_array($this->input('do_action'), ['password', 'email'])) {
|
||||
if (! password_verify($this->input('current_password'), $this->user()->password)) {
|
||||
throw new InvalidPasswordProvidedException(trans('base.account.invalid_password'));
|
||||
throw new InvalidPasswordProvidedException(trans('validation.internal.invalid_password'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ class AccountDataFormRequest extends FrontendUserFormRequest
|
|||
'name_first' => array_get($modelRules, 'name_first'),
|
||||
'name_last' => array_get($modelRules, 'name_last'),
|
||||
'username' => array_get($modelRules, 'username'),
|
||||
'language' => array_get($modelRules, 'language'),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
|
|
34
app/Http/ViewComposers/AssetComposer.php
Normal file
34
app/Http/ViewComposers/AssetComposer.php
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\ViewComposers;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Pterodactyl\Services\Helpers\AssetHashService;
|
||||
|
||||
class AssetComposer
|
||||
{
|
||||
/**
|
||||
* @var \Pterodactyl\Services\Helpers\AssetHashService
|
||||
*/
|
||||
private $assetHashService;
|
||||
|
||||
/**
|
||||
* AssetComposer constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Services\Helpers\AssetHashService $assetHashService
|
||||
*/
|
||||
public function __construct(AssetHashService $assetHashService)
|
||||
{
|
||||
$this->assetHashService = $assetHashService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide access to the asset service in the views.
|
||||
*
|
||||
* @param \Illuminate\View\View $view
|
||||
*/
|
||||
public function compose(View $view)
|
||||
{
|
||||
$view->with('asset', $this->assetHashService);
|
||||
}
|
||||
}
|
|
@ -110,8 +110,8 @@ class Node extends Model implements CleansAttributes, ValidableContract
|
|||
'disk' => 'numeric|min:1',
|
||||
'disk_overallocate' => 'numeric|min:-1',
|
||||
'daemonBase' => 'regex:/^([\/][\d\w.\-\/]+)$/',
|
||||
'daemonSFTP' => 'numeric|between:1024,65535',
|
||||
'daemonListen' => 'numeric|between:1024,65535',
|
||||
'daemonSFTP' => 'numeric|between:1,65535',
|
||||
'daemonListen' => 'numeric|between:1,65535',
|
||||
'maintenance_mode' => 'boolean',
|
||||
'upload_size' => 'int|between:1,1024',
|
||||
];
|
||||
|
@ -132,6 +132,16 @@ class Node extends Model implements CleansAttributes, ValidableContract
|
|||
'maintenance_mode' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the connection address to use when making calls to this node.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnectionAddress(): string
|
||||
{
|
||||
return sprintf('%s://%s:%s', $this->scheme, $this->fqdn, $this->daemonListen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configuration in JSON format.
|
||||
*
|
||||
|
|
|
@ -63,7 +63,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
|
|||
'image' => 'required',
|
||||
'startup' => 'required',
|
||||
'database_limit' => 'present',
|
||||
'allocation_limit' => 'present',
|
||||
'allocation_limit' => 'sometimes',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -87,7 +87,7 @@ class Server extends Model implements CleansAttributes, ValidableContract
|
|||
'startup' => 'string',
|
||||
'skip_scripts' => 'boolean',
|
||||
'image' => 'string|max:255',
|
||||
'installed' => 'boolean',
|
||||
'installed' => 'in:0,1,2',
|
||||
'database_limit' => 'nullable|integer|min:0',
|
||||
'allocation_limit' => 'nullable|integer|min:0',
|
||||
];
|
||||
|
@ -143,6 +143,14 @@ class Server extends Model implements CleansAttributes, ValidableContract
|
|||
return Schema::getColumnListing($this->getTable());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isInstalled(): bool
|
||||
{
|
||||
return $this->installed === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user who owns the server.
|
||||
*
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace Pterodactyl\Models;
|
|||
use Sofa\Eloquence\Eloquence;
|
||||
use Sofa\Eloquence\Validable;
|
||||
use Pterodactyl\Rules\Username;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Validation\Rules\In;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
@ -177,6 +178,16 @@ class User extends Model implements
|
|||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the user model in a format that can be passed over to Vue templates.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function toVueObject(): array
|
||||
{
|
||||
return (new Collection($this->toArray()))->except(['id', 'external_id'])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the password reset notification.
|
||||
*
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Pterodactyl\Repositories\Daemon\FileRepository;
|
||||
use Pterodactyl\Repositories\Wings\FileRepository;
|
||||
use Pterodactyl\Repositories\Daemon\PowerRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\EggRepository;
|
||||
use Pterodactyl\Repositories\Eloquent\NestRepository;
|
||||
|
|
|
@ -22,20 +22,21 @@ class RouteServiceProvider extends ServiceProvider
|
|||
public function map()
|
||||
{
|
||||
Route::middleware(['web', 'auth', 'csrf'])
|
||||
->namespace($this->namespace . '\Base')
|
||||
->group(base_path('routes/base.php'));
|
||||
->namespace($this->namespace . '\Base')
|
||||
->group(base_path('routes/base.php'));
|
||||
|
||||
Route::middleware(['web', 'auth', 'admin', 'csrf'])->prefix('/admin')
|
||||
->namespace($this->namespace . '\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
->namespace($this->namespace . '\Admin')
|
||||
->group(base_path('routes/admin.php'));
|
||||
|
||||
Route::middleware(['web', 'csrf'])->prefix('/auth')
|
||||
->namespace($this->namespace . '\Auth')
|
||||
->group(base_path('routes/auth.php'));
|
||||
->namespace($this->namespace . '\Auth')
|
||||
->group(base_path('routes/auth.php'));
|
||||
|
||||
Route::middleware(['web', 'csrf', 'auth', 'server', 'subuser.auth', 'node.maintenance'])->prefix('/server/{server}')
|
||||
->namespace($this->namespace . '\Server')
|
||||
->group(base_path('routes/server.php'));
|
||||
Route::middleware(['web', 'csrf', 'auth', 'server', 'subuser.auth', 'node.maintenance'])
|
||||
->prefix('/api/server/{server}')
|
||||
->namespace($this->namespace . '\Server')
|
||||
->group(base_path('routes/server.php'));
|
||||
|
||||
Route::middleware(['api'])->prefix('/api/application')
|
||||
->namespace($this->namespace . '\Api\Application')
|
||||
|
@ -50,7 +51,7 @@ class RouteServiceProvider extends ServiceProvider
|
|||
->group(base_path('routes/api-remote.php'));
|
||||
|
||||
Route::middleware(['web', 'daemon-old'])->prefix('/daemon')
|
||||
->namespace($this->namespace . '\Daemon')
|
||||
->group(base_path('routes/daemon.php'));
|
||||
->namespace($this->namespace . '\Daemon')
|
||||
->group(base_path('routes/daemon.php'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Pterodactyl\Http\ViewComposers\AssetComposer;
|
||||
use Pterodactyl\Http\ViewComposers\ServerListComposer;
|
||||
use Pterodactyl\Http\ViewComposers\Server\ServerDataComposer;
|
||||
|
||||
|
@ -13,6 +14,8 @@ class ViewComposerServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->app->make('view')->composer('*', AssetComposer::class);
|
||||
|
||||
$this->app->make('view')->composer('server.*', ServerDataComposer::class);
|
||||
|
||||
// Add data to make the sidebar work when viewing a server.
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace Pterodactyl\Repositories\Daemon;
|
||||
|
||||
use stdClass;
|
||||
use RuntimeException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
|
||||
|
@ -84,33 +85,20 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface
|
|||
{
|
||||
$response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path)));
|
||||
|
||||
$contents = json_decode($response->getBody());
|
||||
$files = $folders = [];
|
||||
return json_decode($response->getBody());
|
||||
}
|
||||
|
||||
foreach ($contents as $value) {
|
||||
if ($value->directory) {
|
||||
array_push($folders, [
|
||||
'entry' => $value->name,
|
||||
'directory' => trim($path, '/'),
|
||||
'size' => null,
|
||||
'date' => strtotime($value->modified),
|
||||
'mime' => $value->mime,
|
||||
]);
|
||||
} elseif ($value->file) {
|
||||
array_push($files, [
|
||||
'entry' => $value->name,
|
||||
'directory' => trim($path, '/'),
|
||||
'extension' => str_replace('\\', '/', pathinfo($value->name, PATHINFO_EXTENSION)),
|
||||
'size' => human_readable($value->size),
|
||||
'date' => strtotime($value->modified),
|
||||
'mime' => $value->mime,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'files' => $files,
|
||||
'folders' => $folders,
|
||||
];
|
||||
/**
|
||||
* Creates a new directory for the server in the given $path.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function createDirectory(string $name, string $path): ResponseInterface
|
||||
{
|
||||
throw new RuntimeException('Not implemented.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ class DatabaseRepository extends EloquentRepository implements DatabaseRepositor
|
|||
*/
|
||||
public function getDatabasesForServer(int $server): Collection
|
||||
{
|
||||
return $this->getBuilder()->where('server_id', $server)->get($this->getColumns());
|
||||
return $this->getBuilder()->with('host')->where('server_id', $server)->get($this->getColumns());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -6,10 +6,8 @@ use Generator;
|
|||
use Pterodactyl\Models\Node;
|
||||
use Illuminate\Support\Collection;
|
||||
use Pterodactyl\Repositories\Concerns\Searchable;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
|
||||
class NodeRepository extends EloquentRepository implements NodeRepositoryInterface
|
||||
{
|
||||
|
@ -140,25 +138,6 @@ class NodeRepository extends EloquentRepository implements NodeRepositoryInterfa
|
|||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a node with all of the servers attached to that node.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Pterodactyl\Models\Node
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function getNodeServers(int $id): Node
|
||||
{
|
||||
try {
|
||||
return $this->getBuilder()->with([
|
||||
'servers.user', 'servers.nest', 'servers.egg',
|
||||
])->findOrFail($id, $this->getColumns());
|
||||
} catch (ModelNotFoundException $exception) {
|
||||
throw new RecordNotFoundException;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a collection of nodes for all locations to use in server creation UI.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Pterodactyl\Repositories\Eloquent;
|
||||
|
||||
use Pterodactyl\Models\Node;
|
||||
use Pterodactyl\Models\User;
|
||||
use Webmozart\Assert\Assert;
|
||||
use Pterodactyl\Models\Server;
|
||||
|
@ -358,4 +359,20 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt
|
|||
{
|
||||
return $this->getBuilder()->where('suspended', true)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all of the servers that exist for a given node in a paginated response.
|
||||
*
|
||||
* @param int $node
|
||||
* @param int $limit
|
||||
*
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
|
||||
*/
|
||||
public function loadAllServersForNode(int $node, int $limit): LengthAwarePaginator
|
||||
{
|
||||
return $this->getBuilder()
|
||||
->with(['user', 'nest', 'egg'])
|
||||
->where('node_id', '=', $node)
|
||||
->paginate($limit);
|
||||
}
|
||||
}
|
||||
|
|
33
app/Repositories/Wings/BaseWingsRepository.php
Normal file
33
app/Repositories/Wings/BaseWingsRepository.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Repositories\Wings;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Pterodactyl\Repositories\Daemon\BaseRepository;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\BaseRepositoryInterface;
|
||||
|
||||
abstract class BaseWingsRepository extends BaseRepository implements BaseRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Return an instance of the Guzzle HTTP Client to be used for requests.
|
||||
*
|
||||
* @param array $headers
|
||||
* @return \GuzzleHttp\Client
|
||||
*/
|
||||
public function getHttpClient(array $headers = []): Client
|
||||
{
|
||||
// We're just going to extend the parent client here since that logic is already quite
|
||||
// sound and does everything we need it to aside from provide the correct base URL
|
||||
// and authentication headers.
|
||||
$client = parent::getHttpClient($headers);
|
||||
|
||||
return new Client(array_merge($client->getConfig(), [
|
||||
'base_uri' => $this->getNode()->getConnectionAddress(),
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer ' . ($this->getToken() ?? $this->getNode()->daemonSecret),
|
||||
'Accept' => 'application/json',
|
||||
'Content-Type' => 'application/json',
|
||||
],
|
||||
]));
|
||||
}
|
||||
}
|
173
app/Repositories/Wings/FileRepository.php
Normal file
173
app/Repositories/Wings/FileRepository.php
Normal file
|
@ -0,0 +1,173 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Repositories\Wings;
|
||||
|
||||
use stdClass;
|
||||
use Exception;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
|
||||
class FileRepository extends BaseWingsRepository implements FileRepositoryInterface
|
||||
{
|
||||
/**
|
||||
* Return stat information for a given file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return \stdClass
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getFileStat(string $path): stdClass
|
||||
{
|
||||
throw new Exception('Function not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of a given file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param int|null $notLargerThan the maximum content length in bytes
|
||||
* @return string
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
* @throws \Pterodactyl\Exceptions\Http\Server\FileSizeTooLargeException
|
||||
*/
|
||||
public function getContent(string $path, int $notLargerThan = null): string
|
||||
{
|
||||
$response = $this->getHttpClient()->get(
|
||||
sprintf('/api/servers/%s/files/contents', $this->getServer()->uuid),
|
||||
[
|
||||
'query' => ['file' => $path],
|
||||
]
|
||||
);
|
||||
|
||||
$length = (int) $response->getHeader('Content-Length')[0] ?? 0;
|
||||
|
||||
if ($notLargerThan && $length > $notLargerThan) {
|
||||
throw new FileSizeTooLargeException(
|
||||
trans('server.files.exceptions.max_size')
|
||||
);
|
||||
}
|
||||
|
||||
return $response->getBody()->__toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save new contents to a given file. This works for both creating and updating
|
||||
* a file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $content
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function putContent(string $path, string $content): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->post(
|
||||
sprintf('/api/servers/%s/files/write', $this->getServer()->uuid),
|
||||
[
|
||||
'query' => ['file' => $path],
|
||||
'body' => $content,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a directory listing for a given path.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getDirectory(string $path): array
|
||||
{
|
||||
$response = $this->getHttpClient()->get(
|
||||
sprintf('/api/servers/%s/files/list-directory', $this->getServer()->uuid),
|
||||
[
|
||||
'query' => ['directory' => $path],
|
||||
]
|
||||
);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new directory for the server in the given $path.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $path
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function createDirectory(string $name, string $path): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->post(
|
||||
sprintf('/api/servers/%s/files/create-directory', $this->getServer()->uuid),
|
||||
[
|
||||
'json' => [
|
||||
'name' => $name,
|
||||
'path' => $path,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames or moves a file on the remote machine.
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function renameFile(string $from, string $to): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->put(
|
||||
sprintf('/api/servers/%s/files/rename', $this->getServer()->uuid),
|
||||
[
|
||||
'json' => [
|
||||
'rename_from' => $from,
|
||||
'rename_to' => $to,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a given file and give it a unique name.
|
||||
*
|
||||
* @param string $location
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function copyFile(string $location): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->post(
|
||||
sprintf('/api/servers/%s/files/copy', $this->getServer()->uuid),
|
||||
[
|
||||
'json' => [
|
||||
'location' => $location,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a file or folder for the server.
|
||||
*
|
||||
* @param string $location
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*/
|
||||
public function deleteFile(string $location): ResponseInterface
|
||||
{
|
||||
return $this->getHttpClient()->post(
|
||||
sprintf('/api/servers/%s/files/delete', $this->getServer()->uuid),
|
||||
[
|
||||
'json' => [
|
||||
'location' => $location,
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@ class DatabaseManagementService
|
|||
$data['server_id'] = $server;
|
||||
$data['database'] = sprintf('s%d_%s', $server, $data['database']);
|
||||
$data['username'] = sprintf('u%d_%s', $server, str_random(10));
|
||||
$data['password'] = $this->encrypter->encrypt(str_random(16));
|
||||
$data['password'] = $this->encrypter->encrypt(str_random(24));
|
||||
|
||||
$this->database->beginTransaction();
|
||||
try {
|
||||
|
|
141
app/Services/Helpers/AssetHashService.php
Normal file
141
app/Services/Helpers/AssetHashService.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Services\Helpers;
|
||||
|
||||
use Illuminate\Filesystem\FilesystemManager;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
|
||||
class AssetHashService
|
||||
{
|
||||
/**
|
||||
* Location of the manifest file generated by gulp.
|
||||
*/
|
||||
public const MANIFEST_PATH = './assets/manifest.json';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Filesystem\Filesystem
|
||||
*/
|
||||
private $filesystem;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Foundation\Application
|
||||
*/
|
||||
private $application;
|
||||
|
||||
/**
|
||||
* @var null|array
|
||||
*/
|
||||
protected static $manifest;
|
||||
|
||||
/**
|
||||
* AssetHashService constructor.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Foundation\Application $application
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Illuminate\Filesystem\FilesystemManager $filesystem
|
||||
*/
|
||||
public function __construct(Application $application, CacheRepository $cache, FilesystemManager $filesystem)
|
||||
{
|
||||
$this->application = $application;
|
||||
$this->cache = $cache;
|
||||
$this->filesystem = $filesystem->createLocalDriver(['root' => public_path()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a URL to append the asset hash.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function url(string $resource): string
|
||||
{
|
||||
$file = last(explode('/', $resource));
|
||||
$data = array_get($this->manifest(), $file, $file);
|
||||
|
||||
return str_replace($file, array_get($data, 'src', $file), $resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data integrity hash for a resource.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function integrity(string $resource): string
|
||||
{
|
||||
$file = last(explode('/', $resource));
|
||||
$data = array_get($this->manifest(), $file, $file);
|
||||
|
||||
return array_get($data, 'integrity', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a built CSS import using the provided URL.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function css(string $resource): string
|
||||
{
|
||||
return '<link href="' . $this->url($resource) . '"
|
||||
rel="stylesheet preload"
|
||||
as="style"
|
||||
crossorigin="anonymous"
|
||||
integrity="' . $this->integrity($resource) . '"
|
||||
referrerpolicy="no-referrer">';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a built JS import using the provided URL.
|
||||
*
|
||||
* @param string $resource
|
||||
* @return string
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
public function js(string $resource): string
|
||||
{
|
||||
return '<script src="' . $this->url($resource) . '"
|
||||
integrity="' . $this->integrity($resource) . '"
|
||||
crossorigin="anonymous"></script>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the asset manifest and store it in the cache for quicker lookups.
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
|
||||
*/
|
||||
protected function manifest(): array
|
||||
{
|
||||
if (! is_null(self::$manifest)) {
|
||||
return self::$manifest;
|
||||
}
|
||||
|
||||
// Skip checking the cache if we are not in production.
|
||||
if ($this->application->environment() === 'production') {
|
||||
$stored = $this->cache->get('Core:AssetManifest');
|
||||
if (! is_null($stored)) {
|
||||
return self::$manifest = $stored;
|
||||
}
|
||||
}
|
||||
|
||||
$contents = json_decode($this->filesystem->get(self::MANIFEST_PATH), true);
|
||||
$this->cache->put('Core:AssetManifest', $contents, 1440);
|
||||
|
||||
return self::$manifest = $contents;
|
||||
}
|
||||
}
|
|
@ -1,22 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* Pterodactyl - Panel
|
||||
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
|
||||
*
|
||||
* This software is licensed under the terms of the MIT license.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
namespace Pterodactyl\Services\Users;
|
||||
|
||||
use Exception;
|
||||
use RuntimeException;
|
||||
use Pterodactyl\Models\User;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
||||
|
||||
class TwoFactorSetupService
|
||||
{
|
||||
const VALID_BASE32_CHARACTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Config\Repository
|
||||
*/
|
||||
|
@ -27,11 +23,6 @@ class TwoFactorSetupService
|
|||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* @var \PragmaRX\Google2FA\Google2FA
|
||||
*/
|
||||
private $google2FA;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||
*/
|
||||
|
@ -42,24 +33,22 @@ class TwoFactorSetupService
|
|||
*
|
||||
* @param \Illuminate\Contracts\Config\Repository $config
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \PragmaRX\Google2FA\Google2FA $google2FA
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
ConfigRepository $config,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
UserRepositoryInterface $repository
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->google2FA = $google2FA;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a 2FA token and store it in the database before returning the
|
||||
* QR code image.
|
||||
* QR code URL. This URL will need to be attached to a QR generating service in
|
||||
* order to function.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @return string
|
||||
|
@ -69,13 +58,26 @@ class TwoFactorSetupService
|
|||
*/
|
||||
public function handle(User $user): string
|
||||
{
|
||||
$secret = $this->google2FA->generateSecretKey($this->config->get('pterodactyl.auth.2fa.bytes'));
|
||||
$image = $this->google2FA->getQRCodeGoogleUrl($this->config->get('app.name'), $user->email, $secret);
|
||||
$secret = '';
|
||||
try {
|
||||
for ($i = 0; $i < $this->config->get('pterodactyl.auth.2fa.bytes', 16); $i++) {
|
||||
$secret .= substr(self::VALID_BASE32_CHARACTERS, random_int(0, 31), 1);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
throw new RuntimeException($exception->getMessage(), 0, $exception);
|
||||
}
|
||||
|
||||
$this->repository->withoutFreshModel()->update($user->id, [
|
||||
'totp_secret' => $this->encrypter->encrypt($secret),
|
||||
]);
|
||||
|
||||
return $image;
|
||||
$company = $this->config->get('app.name');
|
||||
|
||||
return sprintf(
|
||||
'otpauth://totp/%1$s:%2$s?secret=%3$s&issuer=%1$s',
|
||||
rawurlencode($company),
|
||||
rawurlencode($user->email),
|
||||
rawurlencode($secret)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
37
app/Transformers/Api/Client/AccountTransformer.php
Normal file
37
app/Transformers/Api/Client/AccountTransformer.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
|
||||
class AccountTransformer extends BaseClientTransformer
|
||||
{
|
||||
/**
|
||||
* Return the resource name for the JSONAPI output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return 'user';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return basic information about the currently logged in user.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $model
|
||||
* @return array
|
||||
*/
|
||||
public function transform(User $model)
|
||||
{
|
||||
return [
|
||||
'id' => $model->id,
|
||||
'admin' => $model->root_admin,
|
||||
'username' => $model->username,
|
||||
'email' => $model->email,
|
||||
'first_name' => $model->name_first,
|
||||
'last_name' => $model->name_last,
|
||||
'language' => $model->language,
|
||||
];
|
||||
}
|
||||
}
|
36
app/Transformers/Api/Client/AllocationTransformer.php
Normal file
36
app/Transformers/Api/Client/AllocationTransformer.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Allocation;
|
||||
|
||||
class AllocationTransformer extends BaseClientTransformer
|
||||
{
|
||||
/**
|
||||
* Return the resource name for the JSONAPI output.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return 'allocation';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return basic information about the currently logged in user.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Allocation $model
|
||||
* @return array
|
||||
*/
|
||||
public function transform(Allocation $model)
|
||||
{
|
||||
$model->loadMissing('server');
|
||||
|
||||
return [
|
||||
'ip' => $model->ip,
|
||||
'alias' => $model->ip_alias,
|
||||
'port' => $model->port,
|
||||
'default' => $model->getRelation('server')->allocation_id === $model->id,
|
||||
];
|
||||
}
|
||||
}
|
78
app/Transformers/Api/Client/DatabaseTransformer.php
Normal file
78
app/Transformers/Api/Client/DatabaseTransformer.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Database;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Contracts\Extensions\HashidsInterface;
|
||||
|
||||
class DatabaseTransformer extends BaseClientTransformer
|
||||
{
|
||||
protected $availableIncludes = ['password'];
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
private $encrypter;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Extensions\HashidsInterface
|
||||
*/
|
||||
private $hashids;
|
||||
|
||||
/**
|
||||
* Handle dependency injection.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \Pterodactyl\Contracts\Extensions\HashidsInterface $hashids
|
||||
*/
|
||||
public function handle(Encrypter $encrypter, HashidsInterface $hashids)
|
||||
{
|
||||
$this->encrypter = $encrypter;
|
||||
$this->hashids = $hashids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getResourceName(): string
|
||||
{
|
||||
return Database::RESOURCE_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Pterodactyl\Models\Database $model
|
||||
* @return array
|
||||
*/
|
||||
public function transform(Database $model): array
|
||||
{
|
||||
$model->loadMissing('host');
|
||||
|
||||
return [
|
||||
'id' => $this->hashids->encode($model->id),
|
||||
'host' => [
|
||||
'address' => $model->getRelation('host')->host,
|
||||
'port' => $model->getRelation('host')->port,
|
||||
],
|
||||
'name' => $model->database,
|
||||
'username' => $model->username,
|
||||
'connections_from' => $model->remote,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Include the database password in the request.
|
||||
*
|
||||
* @param \Pterodactyl\Models\Database $model
|
||||
* @return \League\Fractal\Resource\Item
|
||||
*/
|
||||
public function includePassword(Database $model): Item
|
||||
{
|
||||
return $this->item($model, function (Database $model) {
|
||||
return [
|
||||
'password' => $this->encrypter->decrypt($model->password),
|
||||
];
|
||||
}, 'database_password');
|
||||
}
|
||||
}
|
|
@ -28,7 +28,12 @@ class ServerTransformer extends BaseClientTransformer
|
|||
'identifier' => $server->uuidShort,
|
||||
'uuid' => $server->uuid,
|
||||
'name' => $server->name,
|
||||
'node' => $server->node->name,
|
||||
'description' => $server->description,
|
||||
'allocation' => [
|
||||
'ip' => $server->allocation->alias,
|
||||
'port' => $server->allocation->port,
|
||||
],
|
||||
'limits' => [
|
||||
'memory' => $server->memory,
|
||||
'swap' => $server->swap,
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Pterodactyl\Transformers\Api\Client;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\ServerRepositoryInterface;
|
||||
|
||||
class StatsTransformer extends BaseClientTransformer
|
||||
|
@ -36,6 +38,8 @@ class StatsTransformer extends BaseClientTransformer
|
|||
*
|
||||
* @param \Pterodactyl\Models\Server $model
|
||||
* @return array
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Http\Connection\DaemonConnectionException
|
||||
*/
|
||||
public function transform(Server $model)
|
||||
{
|
||||
|
@ -61,7 +65,10 @@ class StatsTransformer extends BaseClientTransformer
|
|||
'disk' => [
|
||||
'current' => round(object_get($object, 'proc.disk.used', 0)),
|
||||
'limit' => floatval($model->disk),
|
||||
'io' => $model->io,
|
||||
],
|
||||
'installed' => $model->installed === 1,
|
||||
'suspended' => (bool) $model->suspended,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue