Merge branch 'feature/vuejs' into develop
8
.babelrc
@ -1,8 +0,0 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"compact": true,
|
||||
"minified": true,
|
||||
"only": "public/themes/pterodactyl/js/frontend/files/src/*.js",
|
||||
"sourceMaps": "inline",
|
||||
"comments": false
|
||||
}
|
@ -21,6 +21,12 @@ debconf-set-selections <<< 'mariadb-server-5.5 mysql-server/root_password_again
|
||||
# actually install
|
||||
apt-get install -y php7.2 php7.2-cli php7.2-gd php7.2-mysql php7.2-pdo php7.2-mbstring php7.2-tokenizer php7.2-bcmath php7.2-xml php7.2-fpm php7.2-memcached php7.2-curl php7.2-zip php-xdebug mariadb-server nginx curl tar unzip git memcached > /dev/null
|
||||
|
||||
echo "Install nodejs and yarn"
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
|
||||
apt-get -y install nodejs yarn > /dev/null
|
||||
|
||||
echo "Install composer"
|
||||
curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
|
||||
|
26
.env.dusk
Normal file
@ -0,0 +1,26 @@
|
||||
APP_ENV=local
|
||||
APP_DEBUG=false
|
||||
APP_KEY=NDWgIKKi9ovNK1PXZpzfNVSBdfCXGb5i
|
||||
APP_JWT_KEY=test1234
|
||||
APP_TIMEZONE=America/Los_Angeles
|
||||
APP_URL=http://pterodactyl.local
|
||||
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
|
||||
HASHIDS_SALT=IqRr0g82tCTeuyxGs8RV
|
||||
HASHIDS_LENGTH=8
|
||||
|
||||
MAIL_DRIVER=log
|
||||
MAIL_FROM=support@pterodactyl.io
|
||||
QUEUE_DRIVER=array
|
||||
|
||||
APP_SERVICE_AUTHOR=testing@pterodactyl.io
|
||||
MAIL_FROM_NAME="Pterodactyl Panel"
|
||||
RECAPTCHA_ENABLED=false
|
||||
|
||||
DB_CONNECTION=testing
|
||||
TESTING_DB_HOST=192.168.1.202
|
||||
TESTING_DB_DATABASE=panel_test
|
||||
TESTING_DB_USERNAME=panel_test
|
||||
TESTING_DB_PASSWORD=Test1234
|
@ -28,4 +28,4 @@ MAIL_FROM=no-reply@example.com
|
||||
|
||||
QUEUE_HIGH=high
|
||||
QUEUE_STANDARD=standard
|
||||
QUEUE_LOW=low
|
||||
QUEUE_LOW=low
|
||||
|
14
.gitignore
vendored
@ -7,15 +7,12 @@ storage/framework/*
|
||||
/.idea
|
||||
/nbproject
|
||||
|
||||
package-lock.json
|
||||
composer.lock
|
||||
node_modules
|
||||
|
||||
_ide_helper_models.php
|
||||
*.log
|
||||
_ide_helper.php
|
||||
|
||||
sami.phar
|
||||
/.sami
|
||||
.phpstorm.meta.php
|
||||
.php_cs.cache
|
||||
public/assets/*
|
||||
|
||||
# For local development with docker
|
||||
# Remove if we ever put the Dockerfile in the repo
|
||||
@ -32,3 +29,6 @@ coverage.xml
|
||||
|
||||
# Vagrant
|
||||
*.log
|
||||
resources/lang/locales.js
|
||||
resources/assets/pterodactyl/scripts/helpers/ziggy.js
|
||||
resources/assets/scripts/helpers/ziggy.js
|
||||
|
57
BUILDING.md
Normal file
@ -0,0 +1,57 @@
|
||||
# Local Development
|
||||
Pterodactyl is now powered by Vuejs and Tailwindcss and uses webpack at its core to generate compiled assets. Release
|
||||
versions of Pterodactyl will include pre-compiled, minified, and hashed assets ready-to-go.
|
||||
|
||||
However, if you are interested in running custom themes or making modifications to the Vue files you'll need a build
|
||||
system in place to generate these compiled assets. To get your environment setup, you'll first need to install at least Nodejs
|
||||
`8`, and it is _highly_ recommended that you also install [Yarn](https://yarnpkg.com) to manage your `node_modules`.
|
||||
|
||||
### Install Dependencies
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
The command above will download all of the dependencies necessary to get Pterodactyl assets building. After that, its as
|
||||
simple as running the command below to generate assets while you're developing.
|
||||
|
||||
```bash
|
||||
# build the compiled assets for development
|
||||
yarn run build
|
||||
|
||||
# build the assets automatically when files are modified
|
||||
yarn run watch
|
||||
```
|
||||
|
||||
|
||||
### Hot Module Reloading
|
||||
For more advanced users, we also support 'Hot Module Reloading', allowing you to quickly see changes you're making
|
||||
to the Vue template files without having to reload the page you're on. To Get started with this, you just need
|
||||
to run the command below.
|
||||
|
||||
```bash
|
||||
PUBLIC_PATH=http://192.168.1.1:8080 yarn run serve --host 192.168.1.1
|
||||
```
|
||||
|
||||
There are two _very important_ parts of this command to take note of and change for your specific environment. The first
|
||||
is the `--host` flag, which is required and should point to the machine where the `webpack-serve` server will be running.
|
||||
The second is the `PUBLIC_PATH` environment variable which is the URL pointing to the HMR server and is appended to all of
|
||||
the asset URLs used in Pterodactyl.
|
||||
|
||||
#### Vagrant
|
||||
If you want to use HMR with our Vagrant image, you can use `yarn run v:serve` as a shortcut for the correct parameters.
|
||||
In order to have proper file change detection you can use the [`vagrant-notify-forwarder`](https://github.com/mhallin/vagrant-notify-forwarder) to notify file events from the host to the VM.
|
||||
```sh
|
||||
vagrant plugin install vagrant-notify-forwarder
|
||||
vagrant reload
|
||||
```
|
||||
|
||||
### Building for Production
|
||||
Once you have your files squared away and ready for the live server, you'll be needing to generate compiled, minified, and
|
||||
hashed assets to push live. To do so, run the command below:
|
||||
|
||||
```bash
|
||||
yarn run build:production
|
||||
```
|
||||
|
||||
This will generate a production ready `bundle.js` and `bundle.css` as well as a `manifest.json` and store them in
|
||||
the `/public/assets` directory where they can then be access by clients, and read by the Panel.
|
@ -239,7 +239,7 @@ the response from the server `GET` endpoint.
|
||||
* Nest and Egg listings now show the associated ID in order to make API requests easier.
|
||||
* Added star indicators to user listing in Admin CP to indicate users who are set as a root admin.
|
||||
* Creating a new node will now requires a SSL connection if the Panel is configured to use SSL as well.
|
||||
* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
|
||||
* Connector error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
|
||||
* File manager now supports mass deletion option for files and folders.
|
||||
* Support for CS:GO as a default service option selection.
|
||||
* Support for GMOD as a default service option selection.
|
||||
@ -369,7 +369,7 @@ the response from the server `GET` endpoint.
|
||||
* Changed 2FA login process to be more secure. Previously authentication checking happened on the 2FA post page, now it happens prior and is passed along to the 2FA page to avoid storing any credentials.
|
||||
|
||||
### Added
|
||||
* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
|
||||
* Connector error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
|
||||
|
||||
## v0.7.0-beta.1 (Derelict Dermodactylus)
|
||||
### Added
|
||||
|
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,7 +13,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
||||
* @param string $path
|
||||
* @return \stdClass
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getFileStat(string $path): stdClass;
|
||||
|
||||
@ -23,7 +23,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
||||
* @param string $path
|
||||
* @return string
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getContent(string $path): string;
|
||||
|
||||
@ -34,7 +34,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 +44,7 @@ interface FileRepositoryInterface extends BaseRepositoryInterface
|
||||
* @param string $path
|
||||
* @return array
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\RequestException
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getDirectory(string $path): array;
|
||||
}
|
||||
|
@ -268,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'));
|
||||
|
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);
|
||||
}
|
||||
}
|
79
app/Http/Controllers/Api/Client/Servers/FileController.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Pterodactyl\Models\Server;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
|
||||
use Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\ListFilesRequest;
|
||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest;
|
||||
|
||||
class FileController extends ClientApiController
|
||||
{
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Factory
|
||||
*/
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface
|
||||
*/
|
||||
private $fileRepository;
|
||||
|
||||
/**
|
||||
* FileController constructor.
|
||||
*
|
||||
* @param \Pterodactyl\Contracts\Repository\Daemon\FileRepositoryInterface $fileRepository
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
*/
|
||||
public function __construct(FileRepositoryInterface $fileRepository, CacheRepository $cache)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->cache = $cache;
|
||||
$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') ?? '/'
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
159
app/Http/Controllers/Auth/AbstractLoginController.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Auth\AuthManager;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use PragmaRX\Google2FA\Google2FA;
|
||||
use Illuminate\Auth\Events\Failed;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Http\Controllers\Controller;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
|
||||
abstract class AbstractLoginController extends Controller
|
||||
{
|
||||
use AuthenticatesUsers;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Auth\AuthManager
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Cache\Repository
|
||||
*/
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* @var \Illuminate\Contracts\Encryption\Encrypter
|
||||
*/
|
||||
protected $encrypter;
|
||||
|
||||
/**
|
||||
* @var \PragmaRX\Google2FA\Google2FA
|
||||
*/
|
||||
protected $google2FA;
|
||||
|
||||
/**
|
||||
* @var \Pterodactyl\Contracts\Repository\UserRepositoryInterface
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* 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 = '/';
|
||||
|
||||
/**
|
||||
* LoginController constructor.
|
||||
*
|
||||
* @param \Illuminate\Auth\AuthManager $auth
|
||||
* @param \Illuminate\Contracts\Cache\Repository $cache
|
||||
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
|
||||
* @param \PragmaRX\Google2FA\Google2FA $google2FA
|
||||
* @param \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AuthManager $auth,
|
||||
CacheRepository $cache,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
UserRepositoryInterface $repository
|
||||
) {
|
||||
$this->auth = $auth;
|
||||
$this->cache = $cache;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->google2FA = $google2FA;
|
||||
$this->repository = $repository;
|
||||
|
||||
$this->lockoutTime = config('auth.lockout.time');
|
||||
$this->maxLoginAttempts = config('auth.lockout.attempts');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 response()->json([
|
||||
'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,17 @@ class ForgotPasswordController extends Controller
|
||||
|
||||
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response for a successful password reset link.
|
||||
*
|
||||
* @param string $response
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
protected function sendResetLinkResponse($response): JsonResponse
|
||||
{
|
||||
return response()->json([
|
||||
'status' => trans($response),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
44
app/Http/Controllers/Auth/LoginCheckpointController.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Pterodactyl\Http\Requests\Auth\LoginCheckpointRequest;
|
||||
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
|
||||
|
||||
class LoginCheckpointController extends AbstractLoginController
|
||||
{
|
||||
/**
|
||||
* 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 \Pterodactyl\Exceptions\DisplayException
|
||||
*/
|
||||
public function __invoke(LoginCheckpointRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$cache = $this->cache->pull($request->input('confirmation_token'), []);
|
||||
$user = $this->repository->find(array_get($cache, 'user_id', 0));
|
||||
} catch (RecordNotFoundException $exception) {
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
if (array_get($cache, 'request_ip') !== $request->ip()) {
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
|
||||
if (! $this->google2FA->verifyKey(
|
||||
$this->encrypter->decrypt($user->totp_secret),
|
||||
$request->input('authentication_code'),
|
||||
config('pterodactyl.auth.2fa.window')
|
||||
)) {
|
||||
return $this->sendFailedLoginResponse($request, $user);
|
||||
}
|
||||
|
||||
return $this->sendLoginResponse($user, $request);
|
||||
}
|
||||
}
|
@ -3,116 +3,36 @@
|
||||
namespace Pterodactyl\Http\Controllers\Auth;
|
||||
|
||||
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\Contracts\Cache\Repository as CacheRepository;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Contracts\View\View;
|
||||
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
|
||||
*/
|
||||
private $auth;
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* 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.
|
||||
*
|
||||
* @var string
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
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 \Pterodactyl\Contracts\Repository\UserRepositoryInterface $repository
|
||||
*/
|
||||
public function __construct(
|
||||
AuthManager $auth,
|
||||
CacheRepository $cache,
|
||||
ConfigRepository $config,
|
||||
Encrypter $encrypter,
|
||||
Google2FA $google2FA,
|
||||
UserRepositoryInterface $repository
|
||||
) {
|
||||
$this->auth = $auth;
|
||||
$this->cache = $cache;
|
||||
$this->config = $config;
|
||||
$this->encrypter = $encrypter;
|
||||
$this->google2FA = $google2FA;
|
||||
$this->repository = $repository;
|
||||
|
||||
$this->lockoutTime = $this->config->get('auth.lockout.time');
|
||||
$this->maxLoginAttempts = $this->config->get('auth.lockout.attempts');
|
||||
public function index(): View
|
||||
{
|
||||
return view('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 +46,29 @@ 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 the user is using 2FA we do not actually log them in at this step, we return
|
||||
// a one-time token to link the 2FA credentials to this account via the UI.
|
||||
if ($user->use_totp) {
|
||||
$token = str_random(64);
|
||||
$this->cache->put($token, ['user_id' => $user->id, 'valid_credentials' => $validCredentials], 5);
|
||||
$token = str_random(128);
|
||||
$this->cache->put($token, [
|
||||
'user_id' => $user->id,
|
||||
'request_ip' => $request->ip(),
|
||||
], 5);
|
||||
|
||||
return redirect()->route('auth.totp')->with('authentication_token', $token);
|
||||
return response()->json([
|
||||
'complete' => false,
|
||||
'login_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);
|
||||
}
|
||||
|
||||
$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->get('image'),
|
||||
'secret' => $response->get('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),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -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
@ -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(24);
|
||||
$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
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
@ -34,6 +36,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);
|
||||
|
@ -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
|
||||
|
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,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,20 @@
|
||||
<?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));
|
||||
}
|
||||
}
|
@ -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
@ -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|int',
|
||||
];
|
||||
}
|
||||
}
|
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
@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
|
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);
|
||||
}
|
||||
}
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
|
@ -84,33 +84,6 @@ class FileRepository extends BaseRepository implements FileRepositoryInterface
|
||||
{
|
||||
$response = $this->getHttpClient()->request('GET', sprintf('server/directory/%s', rawurlencode($path)));
|
||||
|
||||
$contents = json_decode($response->getBody());
|
||||
$files = $folders = [];
|
||||
|
||||
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,
|
||||
];
|
||||
return json_decode($response->getBody());
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
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',
|
||||
],
|
||||
]));
|
||||
}
|
||||
}
|
69
app/Repositories/Wings/FileRepository.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Pterodactyl\Repositories\Wings;
|
||||
|
||||
use stdClass;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
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 \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getFileStat(string $path): stdClass
|
||||
{
|
||||
// TODO: Implement getFileStat() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the contents of a given file if it can be edited in the Panel.
|
||||
*
|
||||
* @param string $path
|
||||
* @return string
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function getContent(string $path): string
|
||||
{
|
||||
// TODO: Implement getContent() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* Save new contents to a given file.
|
||||
*
|
||||
* @param string $path
|
||||
* @param string $content
|
||||
* @return \Psr\Http\Message\ResponseInterface
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\TransferException
|
||||
*/
|
||||
public function putContent(string $path, string $content): ResponseInterface
|
||||
{
|
||||
// TODO: Implement putContent() method.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(
|
||||
// Reason for the path check is because it is unnecessary on the Daemon but we need
|
||||
// to respect the interface.
|
||||
sprintf('/api/servers/%s/files/list/%s', $this->getServer()->uuid, $path === '/' ? '' : $path)
|
||||
);
|
||||
|
||||
return json_decode($response->getBody(), true);
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
namespace Pterodactyl\Services\Users;
|
||||
|
||||
use Pterodactyl\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use PragmaRX\Google2FAQRCode\Google2FA;
|
||||
use Illuminate\Contracts\Encryption\Encrypter;
|
||||
use Pterodactyl\Contracts\Repository\UserRepositoryInterface;
|
||||
@ -62,12 +63,12 @@ class TwoFactorSetupService
|
||||
* QR code image.
|
||||
*
|
||||
* @param \Pterodactyl\Models\User $user
|
||||
* @return string
|
||||
* @return \Illuminate\Support\Collection
|
||||
*
|
||||
* @throws \Pterodactyl\Exceptions\Model\DataValidationException
|
||||
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
|
||||
*/
|
||||
public function handle(User $user): string
|
||||
public function handle(User $user): Collection
|
||||
{
|
||||
$secret = $this->google2FA->generateSecretKey($this->config->get('pterodactyl.auth.2fa.bytes'));
|
||||
$image = $this->google2FA->getQRCodeInline($this->config->get('app.name'), $user->email, $secret);
|
||||
@ -76,6 +77,9 @@ class TwoFactorSetupService
|
||||
'totp_secret' => $this->encrypter->encrypt($secret),
|
||||
]);
|
||||
|
||||
return $image;
|
||||
return new Collection([
|
||||
'image' => $image,
|
||||
'secret' => $secret,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
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
@ -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
@ -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,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -48,10 +48,13 @@
|
||||
"filp/whoops": "^2.1",
|
||||
"friendsofphp/php-cs-fixer": "^2.11.1",
|
||||
"fzaninotto/faker": "^1.6",
|
||||
"laravel/dusk": "^3.0",
|
||||
"martinlindhe/laravel-vue-i18n-generator": "^0.1.28",
|
||||
"mockery/mockery": "^1.0",
|
||||
"nunomaduro/collision": "^2.0",
|
||||
"php-mock/php-mock-phpunit": "^2.1",
|
||||
"phpunit/phpunit": "~7.0"
|
||||
"phpunit/phpunit": "~7.0",
|
||||
"tightenco/ziggy": "^0.5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
@ -66,6 +69,7 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Pterodactyl\\Tests\\Browser\\": "tests/Browser",
|
||||
"Pterodactyl\\Tests\\Integration\\": "tests/Integration",
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
|
1119
composer.lock
generated
@ -28,7 +28,7 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'lifetime' => env('SESSION_LIFETIME', 10080),
|
||||
'lifetime' => env('SESSION_LIFETIME', 720),
|
||||
|
||||
'expire_on_close' => false,
|
||||
|
||||
|
50
config/vue-i18n-generator.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Laravel translations path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default path where the translations are stored by Laravel.
|
||||
| Note: the path will be prepended to point to the App directory.
|
||||
|
|
||||
*/
|
||||
|
||||
'langPath' => '/resources/lang',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Laravel translation files
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| You can choose which translation files to be generated.
|
||||
| Note: leave this empty for all the translation files to be generated.
|
||||
|
|
||||
*/
|
||||
|
||||
'langFiles' => [],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Output file
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The javascript path where I will place the generated file.
|
||||
| Note: the path will be prepended to point to the App directory.
|
||||
|
|
||||
*/
|
||||
'jsPath' => '/resources/lang/i18n',
|
||||
'jsFile' => '/resources/lang/locales.js',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| i18n library
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specify the library you use for localization.
|
||||
| Options are vue-i18n or vuex-i18n.
|
||||
|
|
||||
*/
|
||||
'i18nLib' => 'vuex-i18n',
|
||||
];
|
80
package.json
@ -1,11 +1,73 @@
|
||||
{
|
||||
"name": "pterodactyl-panel",
|
||||
"devDependencies": {
|
||||
"babel-cli": "6.18.0",
|
||||
"babel-plugin-transform-strict-mode": "^6.18.0",
|
||||
"babel-preset-es2015": "6.18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./node_modules/babel-cli/bin/babel.js public/themes/pterodactyl/js/frontend/files/src --source-maps --out-file public/themes/pterodactyl/js/frontend/files/filemanager.min.js"
|
||||
}
|
||||
"name": "pterodactyl-panel",
|
||||
"dependencies": {
|
||||
"axios": "^0.18.0",
|
||||
"date-fns": "^1.29.0",
|
||||
"feather-icons": "^4.10.0",
|
||||
"jquery": "^3.3.1",
|
||||
"lodash": "^4.17.11",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"vee-validate": "^2.1.7",
|
||||
"vue": "^2.6.4",
|
||||
"vue-axios": "^2.1.1",
|
||||
"vue-i18n": "^8.6.0",
|
||||
"vue-router": "^3.0.1",
|
||||
"vuex": "^3.0.1",
|
||||
"vuex-router-sync": "^5.0.0",
|
||||
"ws-wrapper": "^2.0.0",
|
||||
"xterm": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.2.2",
|
||||
"@babel/plugin-proposal-class-properties": "^7.3.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.3.1",
|
||||
"@babel/preset-env": "^7.3.1",
|
||||
"@types/feather-icons": "^4.7.0",
|
||||
"@types/lodash": "^4.14.119",
|
||||
"@types/node": "^10.12.15",
|
||||
"@types/socket.io-client": "^1.4.32",
|
||||
"@types/webpack-env": "^1.13.6",
|
||||
"babel-loader": "^8.0.5",
|
||||
"clean-webpack-plugin": "^0.1.19",
|
||||
"css-loader": "^2.1.0",
|
||||
"cssnano": "^4.0.3",
|
||||
"fork-ts-checker-webpack-plugin": "^0.5.2",
|
||||
"glob-all": "^3.1.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"mini-css-extract-plugin": "^0.5.0",
|
||||
"postcss": "^6.0.21",
|
||||
"postcss-import": "^11.1.0",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^3.4.0",
|
||||
"precss": "^3.1.2",
|
||||
"purgecss-webpack-plugin": "^1.1.0",
|
||||
"resolve-url-loader": "^3.0.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"tailwindcss": "^0.7.4",
|
||||
"ts-loader": "^5.3.3",
|
||||
"typescript": "^3.3.1",
|
||||
"uglifyjs-webpack-plugin": "^2.1.1",
|
||||
"vue-devtools": "^3.1.9",
|
||||
"vue-feather-icons": "^4.7.1",
|
||||
"vue-loader": "^15.6.2",
|
||||
"vue-mc": "^0.2.4",
|
||||
"vue-template-compiler": "^2.6.4",
|
||||
"vueify-insert-css": "^1.0.0",
|
||||
"webpack": "^4.29.0",
|
||||
"webpack-assets-manifest": "^3.1.1",
|
||||
"webpack-cli": "^3.0.2",
|
||||
"webpack-dev-server": "^3.1.14",
|
||||
"webpack-manifest-plugin": "^2.0.3",
|
||||
"webpack-shell-plugin": "^0.5.0",
|
||||
"webpack-stream": "^4.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "NODE_ENV=development ./node_modules/.bin/webpack --watch --progress",
|
||||
"build": "NODE_ENV=development ./node_modules/.bin/webpack --progress",
|
||||
"build:production": "NODE_ENV=production ./node_modules/.bin/webpack",
|
||||
"serve": "NODE_ENV=development webpack-dev-server --host 0.0.0.0 --hot",
|
||||
"v:serve": "PUBLIC_PATH=http://pterodactyl.test:8080 yarn run serve",
|
||||
"compile:assets": "php artisan vue-i18n:generate & php artisan ziggy:generate resources/assets/scripts/helpers/ziggy.js"
|
||||
}
|
||||
}
|
||||
|
21
phpunit.dusk.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Browser Test Suite">
|
||||
<directory suffix="Test.php">./tests/Browser</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./app</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
@ -10,6 +10,9 @@
|
||||
processIsolation="false"
|
||||
stopOnFailure="false">
|
||||
<testsuites>
|
||||
<testsuite name="Browser">
|
||||
<directory suffix="Test.php">./tests/Browser/Processes</directory>
|
||||
</testsuite>
|
||||
<testsuite name="Integration">
|
||||
<directory suffix="Test.php">./tests/Integration</directory>
|
||||
</testsuite>
|
||||
|
BIN
public/favicons/android-icon-144x144.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
public/favicons/android-icon-192x192.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
public/favicons/android-icon-36x36.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
public/favicons/android-icon-48x48.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
public/favicons/android-icon-72x72.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
public/favicons/android-icon-96x96.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
public/favicons/apple-icon-114x114.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
public/favicons/apple-icon-120x120.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
public/favicons/apple-icon-144x144.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
public/favicons/apple-icon-152x152.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
public/favicons/apple-icon-180x180.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
public/favicons/apple-icon-57x57.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
public/favicons/apple-icon-60x60.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
public/favicons/apple-icon-72x72.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
public/favicons/apple-icon-76x76.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
public/favicons/apple-icon-precomposed.png
Normal file
After Width: | Height: | Size: 6.6 KiB |