Merge branch 'develop' into feature/option-scripts

# Conflicts:
#	app/Http/Routes/AdminRoutes.php
#	app/Http/Routes/DaemonRoutes.php
#	app/Models/ServiceOption.php
This commit is contained in:
Dane Everitt 2017-04-20 17:08:08 -04:00
commit 8d24e5f168
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
683 changed files with 8854 additions and 12362 deletions

17
.gitignore vendored
View file

@ -6,14 +6,17 @@
storage/framework/*
composer.lock
Homestead.yaml
Vagrantfile
Vagrantfile
node_modules
yarn.lock
node_modules
_ide_helper_models.php
_ide_helper.php
sami.phar
/.sami
# For local development with docker
# Remove if we ever put the Dockerfile in the repo
.dockerignore
Dockerfile
# for image related files
misc

16
.sami.php Normal file
View file

@ -0,0 +1,16 @@
<?php
use Sami\Sami;
use Symfony\Component\Finder\Finder;
$iterator = Finder::create()
->files()
->name('*.php')
->in($dir = __DIR__ . '/app');
return new Sami($iterator, array(
'title' => 'Pterodactyl',
'build_dir' => __DIR__ . '/.sami/build',
'cache_dir' => __DIR__ . '/.sami/cache',
'default_opened_level' => 2,
));

View file

@ -3,6 +3,80 @@ This file is a running track of new features and fixes to each version of the pa
This project follows [Semantic Versioning](http://semver.org) guidelines.
## v0.6.0-beta.2.1 (Courageous Carniadactylus)
### Fixed
* `[beta.2]` — Suspended servers now show as suspended.
* `[beta.2]` — Corrected the information when a task has not run yet.
* `[beta.2]` — Fixes filemanager 404 when editing a file within a directory.
* `[beta.2]` — Fixes exception in tasks when deleting a server.
* `[beta.2]` — Fixes bug with Terarria and Voice servers reporting a `TypeError: Service is not a constructor` in the daemon due to a missing service configuration.
* `[beta.2]` — Fixes password reset form throwing a MethodNotAllowed error when accessed.
* `[beta.2]` — Fixes invalid password bug when attempting to change account email address.
* `[beta.2]` — New attempt at fixing the issues when rendering files in the browser file editor on certain browsers.
* `[beta.2]` — Fixes broken auto-deploy time checking causing no tokens to work.
* `[beta.2]` — Fixes display of subusers after creation.
* `[beta.2]` — Fixes bug throwing model not found exception when editing an existing subuser.
### Changed
* Deleting a server safely now continues even if the daemon reports a `HTTP/404` missing server error (requires `Daemon@0.4.0-beta.2.1`)
* Changed behavior when modifying server allocation information. You can now remove the default allocation assuming you assing a new allocation at the same time. Reduces the number of steps to change the default allocation for a server.
### Added
* Server listing and view in Admin CP now shows the SFTP username/Docker container name.
* Administrative server view includes link in navigation to go to server console/frontend management.
## v0.6.0-beta.2 (Courageous Carniadactylus)
### Fixed
* `[beta.1]` — Fixes task management ststem not running correctly.
* `[beta.1]` — Fixes API endpoint for command sending missing the required class definition.
* `[beta.1]` — Fixes panel looking for an old compiled classfile that is no longer used. This was causing errors relating to `missing class DingoAPI` when trying to upgrade the panel.
* `[beta.1]` — Should fix render issues when trying to edit some files via the panel file editor.
### Added
* Ability to launch the console in a new window as an individual unit. https://s3.kelp.in/IrTyE.png
## v0.6.0-beta.1 (Courageous Carniadactylus)
### Fixed
* `[pre.7]` — Fixes bug with subuser checkbox display.
* `[pre.7]` — Fixes bug with injected JS that was causing `<!DOCTYPE html>` to be ignored in templates.
* `[pre.7]` — Fixes exception thrown when trying to delete a node due to a misnamed model.
* `[pre.7]` — Fixes username vanishing on failed login attempts.
* `[pre.7]` — Terminal is now fixed to actually output all lines, rather than leaving one hanging in neverland until the browser is resized.
### Added
* Login attempts and pasword reset requests are now protected by invisible ReCaptcha. This feature can be disabled with a `.env` variable.
* Server listing for individual users is now searchable on the front-end.
* Servers that a user is assocaited with as a subuser are now displayed in addition to owned servers when listing users in the Admin CP.
### Changed
* Subuser permissions are now stored in `Permission::list()` to make views way cleaner and make adding to views significantly cleaner.
* `[pre.7]` — Sidebar for file manager now is a single link rather than a dropdown.
* Attempting to reset a password for an account that does not exist no longer returns an error, rather it displays a success message. Failed resets trigger a `Pterodactyl\Events\Auth\FailedPasswordReset` event that can be caught if needed to perform other actions.
* Servers are no longer queued for deletion due to the general hassle and extra logic required.
* Updated all panel components to run on Laravel v5.4 rather than 5.3 which is EOL.
* Routes are now handled in the `routes/` folder, and use a significantly cleaner syntax. Controller names and methods have been updated as well to be clearer as well as avoid conflicts with PHP reserved keywords.
* API has been completely overhauled to use new permissions system. **Any old API keys will immediately become invalid and fail to operate properly anymore. You will need to generate new keys.**
* Cleaned up dynamic database connection setting to use a single function call from the host model.
* `[pre.7]` — Corrected a config option for spigot servers to set a boolean value as boolean, and not as a string.
## v0.6.0-pre.7 (Courageous Carniadactylus)
### Fixed
* `[pre.6]` — Addresses misconfigured console queue that was still sending data way to quickly thus causing the console to explode on some devices when large amounts of data were sent.
* `[pre.6]` — Fixes bug in allocation parsing for a node that prevented adding new allocations.
* `[pre.6]` — Fixes typo in migrations that wouldn't save custom regex for non-required variables.
* `[pre.6]` — Fixes auto-deploy checkbox on server creation causing validation error.
## v0.6.0-pre.6 (Courageous Carniadactylus)
### Fixed
* `[pre.5]` — Console based server rebuild tool now actually rebuilds the servers with the correct information.
* `[pre.5]` — Fixes typo and wrong docker contaienr for certain applications.
### Changed
* Removed all old theme JS and CSS folders to cleanup and avoid confusion in the future.
### Added
* `[pre.5]` — Added foreign key to `pack_id` to ensure nothing eds up breaking there.
## v0.6.0-pre.5 (Courageous Carniadactylus)
### Changed
* New theme applied to Admin CP. Many graphical changes were made, some data was moved around and some display data changed. Too much was changed to feasibly log it all in here. Major breaking changes or notable new features will be logged.
@ -14,6 +88,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
* Many structural changes to the database and `Pterodactyl\Models` classes that would flood this changelog if they were all included. All required migrations included to handle database changes.
* `[pre.4]` — Service pack files are now stored in the database rather than on the host system to make updates easier.
* Clarified details for database hosts to prevent users entering invalid account details, as well as renamed tables and columns relating to it to keep things clearer.
* Updated all code to be Laravel compliant when using `env()` and moved to using `config()` throughout non `config/*.php` files.
### Fixed
* Fixes potential bug with invalid CIDR notation (ex: `192.168.1.1/z`) when adding allocations that could cause over 4 million records to be created at once.
@ -24,6 +99,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
* Fixes bug where daemon was unable to register that certain games had fully booted and were ready to play on.
* Fixes bug causing MySQL user accounts to be corrupted when resetting a password via the panel.
* `[pre.4]` — Multiple clients refreshing the console no longer clears the console for all parties involved... sorry about that.
* `[pre.4]` — Fixes bug in environment setting script that would not remeber defaults and try to re-assign values.
### Added
* Ability to assign multiple allocations at once when creating a new server.

View file

@ -83,7 +83,7 @@ class AddNode extends Command
$selectedLocation = $this->option('location');
}
$this->data['location'] = $locations->where('short', $selectedLocation)->first()->id;
$this->data['location_id'] = $locations->where('short', $selectedLocation)->first()->id;
if (is_null($this->option('fqdn'))) {
$this->line('Please enter domain name (e.g node.example.com) to be used for connecting to the daemon. An IP address may only be used if you are not using SSL for this node.');

View file

@ -64,7 +64,7 @@ class ClearTasks extends Command
*/
public function handle()
{
$entries = Models\TaskLog::where('run_time', '<=', Carbon::now()->subHours(env('APP_CLEAR_TASKLOG', 720))->toAtomString())->get();
$entries = Models\TaskLog::where('run_time', '<=', Carbon::now()->subHours(config('pterodactyl.tasks.clear_log'))->toAtomString())->get();
$this->info(sprintf('Preparing to delete %d old task log entries.', count($entries)));
$bar = $this->output->createProgressBar(count($entries));

View file

@ -0,0 +1,146 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Console\Commands;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Server;
use Illuminate\Console\Command;
class RebuildServer extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'pterodactyl:rebuild
{--all}
{--node= : Id of node to rebuild all servers on.}
{--server= : UUID of server to rebuild.}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Rebuild docker containers for a server or multiple servers.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
if ($this->option('all')) {
$servers = Server::all();
} elseif ($this->option('node')) {
$servers = Server::where('node_id', $this->option('node'))->get();
} elseif ($this->option('server')) {
$servers = Server::where('id', $this->option('server'))->get();
} else {
$this->error('You must pass a flag to determine which server(s) to rebuild.');
return;
}
$servers->load('node', 'service', 'option.variables', 'pack');
$this->line('Beginning processing, do not exit this script.');
$bar = $this->output->createProgressBar(count($servers));
$results = collect([]);
foreach ($servers as $server) {
try {
$environment = $server->option->variables->map(function ($item, $key) use ($server) {
$display = $server->variables->where('variable_id', $item->id)->pluck('variable_value')->first();
return [
'variable' => $item->env_variable,
'value' => (! is_null($display)) ? $display : $item->default_value,
];
});
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
'X-Access-Token' => $server->node->daemonSecret,
])->request('PATCH', '/server', [
'json' => [
'build' => [
'image' => $server->image,
'env|overwrite' => $environment->pluck('value', 'variable')->merge(['STARTUP' => $server->startup]),
],
'service' => [
'type' => $server->service->folder,
'option' => $server->option->tag,
'pack' => ! is_null($server->pack) ? $server->pack->uuid : null,
],
],
]);
$results = $results->merge([
$server->uuid => [
'status' => 'info',
'messages' => [
'[✓] Processed rebuild request for ' . $server->uuid,
],
],
]);
} catch (\Exception $ex) {
$results = $results->merge([
$server->uuid => [
'status' => 'error',
'messages' => [
'[✗] Failed to process rebuild request for ' . $server->uuid,
$ex->getMessage(),
],
],
]);
}
$bar->advance();
}
$bar->finish();
$console = $this;
$this->line("\n");
$results->each(function ($item, $key) use ($console) {
foreach ($item['messages'] as $line) {
$console->{$item['status']}($line);
}
});
$this->line("\nCompleted rebuild command processing.");
}
}

View file

@ -25,7 +25,7 @@
namespace Pterodactyl\Console\Commands;
use Carbon;
use Pterodactyl\Models;
use Pterodactyl\Models\Task;
use Illuminate\Console\Command;
use Pterodactyl\Jobs\SendScheduledTask;
use Illuminate\Foundation\Bus\DispatchesJobs;
@ -65,14 +65,14 @@ class RunTasks extends Command
*/
public function handle()
{
$tasks = Models\Task::where('queued', 0)->where('active', 1)->where('next_run', '<=', Carbon::now()->toAtomString())->get();
$tasks = Task::where('queued', false)->where('active', true)->where('next_run', '<=', Carbon::now()->toAtomString())->get();
$this->info(sprintf('Preparing to queue %d tasks.', count($tasks)));
$bar = $this->output->createProgressBar(count($tasks));
foreach ($tasks as &$task) {
$bar->advance();
$this->dispatch((new SendScheduledTask(Models\Server::findOrFail($task->server), $task))->onQueue(env('QUEUE_LOW', 'low')));
$this->dispatch((new SendScheduledTask($task))->onQueue(config('pterodactyl.queues.low')));
}
$bar->finish();

View file

@ -100,6 +100,7 @@ class UpdateEmailSettings extends Command
'Postmark Transactional Email Service',
],
]);
$variables['MAIL_DRIVER'] = is_null($this->option('driver')) ? $this->choice('Which email driver would you like to use?', [
'smtp',
'mail',
@ -110,9 +111,9 @@ class UpdateEmailSettings extends Command
switch ($variables['MAIL_DRIVER']) {
case 'smtp':
$variables['MAIL_HOST'] = is_null($this->option('host')) ? $this->ask('SMTP Host (e.g smtp.google.com)') : $this->option('host');
$variables['MAIL_PORT'] = is_null($this->option('port')) ? $this->anticipate('SMTP Host Port (e.g 587)', ['587']) : $this->option('port');
$variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('SMTP Username') : $this->option('password');
$variables['MAIL_HOST'] = is_null($this->option('host')) ? $this->ask('SMTP Host (e.g smtp.google.com)', config('mail.host')) : $this->option('host');
$variables['MAIL_PORT'] = is_null($this->option('port')) ? $this->anticipate('SMTP Host Port (e.g 587)', ['587', config('mail.port')], config('mail.port')) : $this->option('port');
$variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('SMTP Username', config('mail.username')) : $this->option('password');
$variables['MAIL_PASSWORD'] = is_null($this->option('password')) ? $this->secret('SMTP Password') : $this->option('password');
break;
case 'mail':
@ -128,7 +129,7 @@ class UpdateEmailSettings extends Command
$variables['MAIL_DRIVER'] = 'smtp';
$variables['MAIL_HOST'] = 'smtp.postmarkapp.com';
$variables['MAIL_PORT'] = 587;
$variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('Postmark API Token') : $this->option('username');
$variables['MAIL_USERNAME'] = is_null($this->option('username')) ? $this->ask('Postmark API Token', config('mail.username')) : $this->option('username');
$variables['MAIL_PASSWORD'] = $variables['MAIL_USERNAME'];
break;
default:
@ -137,8 +138,9 @@ class UpdateEmailSettings extends Command
break;
}
$variables['MAIL_FROM'] = is_null($this->option('email')) ? $this->ask('Email address emails should originate from') : $this->option('email');
$variables['MAIL_FROM_NAME'] = is_null($this->option('from-name')) ? $this->ask('Name emails should appear to be from') : $this->option('from-name');
$variables['MAIL_FROM'] = is_null($this->option('email')) ? $this->ask('Email address emails should originate from', config('mail.from.address')) : $this->option('email');
$variables['MAIL_FROM_NAME'] = is_null($this->option('from-name')) ? $this->ask('Name emails should appear to be from', config('mail.from.name')) : $this->option('from-name');
$variables['MAIL_FROM_NAME'] = '"' . $variables['MAIL_FROM_NAME'] . '"';
$variables['MAIL_ENCRYPTION'] = 'tls';
$bar = $this->output->createProgressBar(count($variables));

View file

@ -41,6 +41,9 @@ class UpdateEnvironment extends Command
{--dbuser=}
{--dbpass=}
{--url=}
{--driver=}
{--session-driver=}
{--queue-driver=}
{--timezone=}';
/**
@ -77,39 +80,37 @@ class UpdateEnvironment extends Command
$envContents = file_get_contents($file);
$this->info('Simply leave blank and press enter to fields that you do not wish to update.');
if (! env('SERVICE_AUTHOR', false)) {
if (is_null(config('pterodactyl.service.author', null))) {
$this->info('No service author set, setting one now.');
$variables['SERVICE_AUTHOR'] = env('SERVICE_AUTHOR', (string) Uuid::generate(4));
$variables['SERVICE_AUTHOR'] = (string) Uuid::generate(4);
}
if (! env('QUEUE_STANDARD', false) || ! env('QUEUE_DRIVER', false)) {
$this->info('Setting default queue settings.');
$variables['QUEUE_DRIVER'] = env('QUEUE_DRIVER', 'database');
$variables['QUEUE_HIGH'] = env('QUEUE_HIGH', 'high');
$variables['QUEUE_STANDARD'] = env('QUEUE_STANDARD', 'standard');
$variables['QUEUE_LOW'] = env('QUEUE_LOW', 'low');
if(isset($variables['APP_THEME'])) {
if ($variables['APP_THEME'] === 'default') {
$variables['APP_THEME'] = 'pterodactyl';
}
}
if (is_null($this->option('dbhost'))) {
$variables['DB_HOST'] = $this->anticipate('Database Host', ['localhost', '127.0.0.1', env('DB_HOST')], env('DB_HOST'));
$variables['DB_HOST'] = $this->anticipate('Database Host', ['localhost', '127.0.0.1', config('database.connections.mysql.host')], config('database.connections.mysql.host'));
} else {
$variables['DB_HOST'] = $this->option('dbhost');
}
if (is_null($this->option('dbport'))) {
$variables['DB_PORT'] = $this->anticipate('Database Port', [3306, env('DB_PORT')], env('DB_PORT'));
$variables['DB_PORT'] = $this->anticipate('Database Port', [3306, config('database.connections.mysql.port')], config('database.connections.mysql.port'));
} else {
$variables['DB_PORT'] = $this->option('dbport');
}
if (is_null($this->option('dbname'))) {
$variables['DB_DATABASE'] = $this->anticipate('Database Name', ['pterodactyl', 'homestead', ENV('DB_DATABASE')], env('DB_DATABASE'));
$variables['DB_DATABASE'] = $this->anticipate('Database Name', ['pterodactyl', 'homestead', config('database.connections.mysql.database')], config('database.connections.mysql.database'));
} else {
$variables['DB_DATABASE'] = $this->option('dbname');
}
if (is_null($this->option('dbuser'))) {
$variables['DB_USERNAME'] = $this->anticipate('Database Username', [ENV('DB_DATABASE')], env('DB_USERNAME'));
$variables['DB_USERNAME'] = $this->anticipate('Database Username', [config('database.connections.mysql.username')], config('database.connections.mysql.username'));
} else {
$variables['DB_USERNAME'] = $this->option('dbuser');
}
@ -122,25 +123,68 @@ class UpdateEnvironment extends Command
}
if (is_null($this->option('url'))) {
$variables['APP_URL'] = $this->ask('Panel URL (include http(s)://)', env('APP_URL'));
$variables['APP_URL'] = $this->ask('Panel URL (include http(s)://)', config('app.url'));
} else {
$variables['APP_URL'] = $this->option('url');
}
if (is_null($this->option('timezone'))) {
$this->line('The timezone should match one of the supported timezones according to http://php.net/manual/en/timezones.php');
$variables['APP_TIMEZONE'] = $this->anticipate('Panel Timezone', \DateTimeZone::listIdentifiers(\DateTimeZone::ALL), env('APP_TIMEZONE'));
$variables['APP_TIMEZONE'] = $this->anticipate('Panel Timezone', \DateTimeZone::listIdentifiers(\DateTimeZone::ALL), config('app.timezone'));
} else {
$variables['APP_TIMEZONE'] = $this->option('timezone');
}
$variables['APP_THEME'] = 'pterodactyl';
$variables['CACHE_DRIVER'] = 'memcached';
$variables['SESSION_DRIVER'] = 'database';
if (is_null($this->option('driver'))) {
$options = [
'memcached' => 'Memcache',
'redis' => 'Redis (recommended)',
'apc' => 'APC',
'array' => 'PHP Array',
];
$default = (in_array(config('cache.default', 'memcached'), $options)) ? config('cache.default', 'memcached') : 'memcached';
$this->line('If you chose redis as your cache driver backend, you *must* have a redis server configured already.');
$variables['CACHE_DRIVER'] = $this->choice('Which cache driver backend would you like to use?', $options, $default);
} else {
$variables['CACHE_DRIVER'] = $this->option('driver');
}
if (is_null($this->option('session-driver'))) {
$options = [
'database' => 'MySQL (recommended)',
'redis' => 'Redis',
'file' => 'File',
'cookie' => 'Cookie',
'apc' => 'APC',
'array' => 'PHP Array',
];
$default = (in_array(config('session.driver', 'database'), $options)) ? config('cache.default', 'database') : 'database';
$this->line('If you chose redis as your cache driver backend, you *must* have a redis server configured already.');
$variables['SESSION_DRIVER'] = $this->choice('Which session driver backend would you like to use?', $options, $default);
} else {
$variables['SESSION_DRIVER'] = $this->option('session-driver');
}
if (is_null($this->option('queue-driver'))) {
$options = [
'database' => 'Database (recommended)',
'redis' => 'Redis',
'sqs' => 'Amazon SQS',
'sync' => 'Sync',
'null' => 'None',
];
$default = (in_array(config('queue.driver', 'database'), $options)) ? config('queue.driver', 'database') : 'database';
$this->line('If you chose redis as your queue driver backend, you *must* have a redis server configured already.');
$variables['QUEUE_DRIVER'] = $this->choice('Which queue driver backend would you like to use?', $options, $default);
} else {
$variables['QUEUE_DRIVER'] = $this->option('queue-driver');
}
$bar = $this->output->createProgressBar(count($variables));
$this->line('Writing new environment configuration to file.');
foreach ($variables as $key => $value) {
$newValue = $key . '=' . $value;
@ -155,8 +199,7 @@ class UpdateEnvironment extends Command
file_put_contents($file, $envContents);
$bar->finish();
$this->line('Updating evironment configuration cache file.');
$this->call('config:cache');
echo "\n";
$this->line("\n");
}
}

View file

@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel
\Pterodactyl\Console\Commands\CleanServiceBackup::class,
\Pterodactyl\Console\Commands\AddNode::class,
\Pterodactyl\Console\Commands\AddLocation::class,
\Pterodactyl\Console\Commands\RebuildServer::class,
];
/**

View file

@ -0,0 +1,59 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Events\Auth;
use Illuminate\Queue\SerializesModels;
class FailedCaptcha
{
use SerializesModels;
/**
* The IP that the request originated from.
*
* @var string
*/
public $ip;
/**
* The domain that was used to try to verify the request with recaptcha api.
*
* @var string
*/
public $domain;
/**
* Create a new event instance.
*
* @param string $ip
* @param string $domain
* @return void
*/
public function __construct($ip, $domain)
{
$this->ip = $ip;
$this->domain = $domain;
}
}

View file

@ -22,42 +22,38 @@
* SOFTWARE.
*/
namespace Pterodactyl\Jobs;
namespace Pterodactyl\Events\Auth;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Pterodactyl\Repositories\ServerRepository;
class DeleteServer extends Job implements ShouldQueue
class FailedPasswordReset
{
use InteractsWithQueue, SerializesModels;
use SerializesModels;
/**
* Id of server to be deleted.
* @var object
*/
protected $id;
/**
* Create a new job instance.
* The IP that the request originated from.
*
* @param int $server
* @var string
*/
public $ip;
/**
* The email address that was used when the reset request failed.
*
* @var string
*/
public $email;
/**
* Create a new event instance.
*
* @param string $ip
* @param string $email
* @return void
*/
public function __construct($id)
public function __construct($ip, $email)
{
$this->id = $id;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$repo = new ServerRepository;
$repo->delete($this->id);
$this->ip = $ip;
$this->email = $email;
}
}

View file

@ -31,11 +31,17 @@ class Created
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Creating
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Deleted
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Deleting
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Saved
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Saving
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Updated
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Updating
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Server
*/
public $server;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function __construct(Server $server)

View file

@ -31,11 +31,17 @@ class Created
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Subuser
*/
public $subuser;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function __construct(Subuser $subuser)

View file

@ -31,11 +31,17 @@ class Creating
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Subuser
*/
public $subuser;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function __construct(Subuser $subuser)

View file

@ -31,11 +31,17 @@ class Deleted
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Subuser
*/
public $subuser;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function __construct(Subuser $subuser)

View file

@ -31,11 +31,17 @@ class Deleting
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\Subuser
*/
public $subuser;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function __construct(Subuser $subuser)

View file

@ -31,11 +31,17 @@ class Created
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\User $user
* @return void
*/
public function __construct(User $user)

View file

@ -31,11 +31,17 @@ class Creating
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\User $user
* @return void
*/
public function __construct(User $user)

View file

@ -31,11 +31,17 @@ class Deleted
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\User $user
* @return void
*/
public function __construct(User $user)

View file

@ -31,11 +31,17 @@ class Deleting
{
use SerializesModels;
/**
* The Eloquent model of the server.
*
* @var \Pterodactyl\Models\User
*/
public $user;
/**
* Create a new event instance.
*
* @param \Pterodactyl\Models\User $user
* @return void
*/
public function __construct(User $user)

View file

@ -26,4 +26,5 @@ namespace Pterodactyl\Exceptions;
class AccountNotFoundException extends \Exception
{
//
}

View file

@ -28,20 +28,19 @@ use Log;
class DisplayException extends \Exception
{
private $_logging = null;
/**
* Exception constructor.
*
* @param string $message
* @param mixed $log
* @return void
*/
public function __construct($message, $log = null)
{
$this->_logging = $log;
if ($this->_logging !== null) {
if (! is_null($log)) {
Log::error($log);
}
parent::__construct($message);
}
public function getLogging()
{
return $this->_logging;
}
}

View file

@ -4,7 +4,6 @@ namespace Pterodactyl\Exceptions;
use Log;
use Exception;
use DisplayException;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
@ -29,7 +28,7 @@ class Handler extends ExceptionHandler
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* @param \Exception $e
* @param \Exception $exception
* @return void
*/
public function report(Exception $exception)
@ -41,15 +40,25 @@ class Handler extends ExceptionHandler
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $e
* @param \Exception $exception
* @return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if ($request->expectsJson()) {
if ($request->expectsJson() || $request->isJson() || $request->is(...config('pterodactyl.json_routes'))) {
$exception = $this->prepareException($exception);
if (config('app.debug')) {
$report = [
'code' => (! $this->isHttpException($exception)) ?: $exception->getStatusCode(),
'message' => class_basename($exception) . ' in ' . $exception->getFile() . ' on line ' . $exception->getLine(),
];
}
$response = response()->json([
'error' => ($exception instanceof DisplayException) ? $exception->getMessage() : 'An unhandled error occured while attempting to process this request.',
], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500);
'error' => (config('app.debug')) ? $exception->getMessage() : 'An unhandled exception was encountered with this request.',
'exception' => ! isset($report) ?: $report,
], ($this->isHttpException($exception)) ? $exception->getStatusCode() : 500, [], JSON_UNESCAPED_SLASHES);
parent::report($exception);
}
@ -70,6 +79,6 @@ class Handler extends ExceptionHandler
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest('/auth/login');
return redirect()->guest(route('auth.login'));
}
}

View file

@ -35,7 +35,7 @@ class PhraseAppTranslator extends LaravelTranslator
* @param array $replace
* @param string|null $locale
* @param bool $fallback
* @return string|array|null
* @return string
*/
public function get($key, array $replace = [], $locale = null, $fallback = true)
{

View file

@ -28,6 +28,11 @@ use Illuminate\Support\Facades\Facade;
class Version extends Facade
{
/**
* Returns the facade accessor class.
*
* @return strig
*/
protected static function getFacadeAccessor()
{
return '\Pterodactyl\Services\VersionService';

View file

@ -22,38 +22,30 @@
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API;
namespace Pterodactyl\Http\Controllers\API\Admin;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Models\Location;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Transformers\Admin\LocationTransformer;
/**
* @Resource("Servers")
*/
class LocationController extends BaseController
class LocationController extends Controller
{
public function __construct()
{
//
}
/**
* List All Locations.
* Controller to handle returning all locations on the system.
*
* Lists all locations currently on the system.
*
* @Get("/locations")
* @Versions({"v1"})
* @Response(200)
* @param \Illuminate\Http\Request $request
* @return array
*/
public function lists(Request $request)
public function index(Request $request)
{
return Location::with('nodes')->get()->map(function ($item) {
$item->nodes->transform(function ($item) {
return collect($item)->only(['id', 'name', 'fqdn', 'scheme', 'daemonListen', 'daemonSFTP']);
});
$this->authorize('location-list', $request->apiKey());
return $item;
})->toArray();
return Fractal::create()
->collection(Location::all())
->transformWith(new LocationTransformer($request))
->withResourceName('location')
->toArray();
}
}

View file

@ -0,0 +1,173 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API\Admin;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\NodeRepository;
use Pterodactyl\Transformers\Admin\NodeTransformer;
use Pterodactyl\Exceptions\DisplayValidationException;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class NodeController extends Controller
{
/**
* Controller to handle returning all nodes on the system.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
$this->authorize('node-list', $request->apiKey());
$nodes = Node::paginate(config('pterodactyl.paginate.api.nodes'));
$fractal = Fractal::create()->collection($nodes)
->transformWith(new NodeTransformer($request))
->withResourceName('user')
->paginateWith(new IlluminatePaginatorAdapter($nodes));
if (config('pterodactyl.api.include_on_list') && $request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->toArray();
}
/**
* Display information about a single node on the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function view(Request $request, $id)
{
$this->authorize('node-view', $request->apiKey());
$fractal = Fractal::create()->item(Node::findOrFail($id));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->transformWith(new NodeTransformer($request))
->withResourceName('node')
->toArray();
}
/**
* Display information about a single node on the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function viewConfig(Request $request, $id)
{
$this->authorize('node-view-config', $request->apiKey());
$node = Node::findOrFail($id);
return response()->json(json_decode($node->getConfigurationAsJson()));
}
/**
* Create a new node on the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse|array
*/
public function store(Request $request)
{
$this->authorize('node-create', $request->apiKey());
$repo = new NodeRepository;
try {
$node = $repo->create(array_merge(
$request->only([
'public', 'disk_overallocate', 'memory_overallocate',
]),
$request->intersect([
'name', 'location_id', 'fqdn',
'scheme', 'memory', 'disk',
'daemonBase', 'daemonSFTP', 'daemonListen',
])
));
$fractal = Fractal::create()->item($node)->transformWith(new NodeTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('node')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to create this node. Please try again.',
], 500);
}
}
/**
* Delete a node from the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function delete(Request $request, $id)
{
$this->authorize('node-delete', $request->apiKey());
$repo = new NodeRepository;
try {
$repo->delete($id);
return response('', 204);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to delete this node. Please try again.',
], 500);
}
}
}

View file

@ -0,0 +1,428 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API\Admin;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use GuzzleHttp\Exception\TransferException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\ServerRepository;
use Pterodactyl\Transformers\Admin\ServerTransformer;
use Pterodactyl\Exceptions\DisplayValidationException;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class ServerController extends Controller
{
/**
* Controller to handle returning all servers on the system.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
$this->authorize('server-list', $request->apiKey());
$servers = Server::paginate(config('pterodactyl.paginate.api.servers'));
$fractal = Fractal::create()->collection($servers)
->transformWith(new ServerTransformer($request))
->withResourceName('user')
->paginateWith(new IlluminatePaginatorAdapter($servers));
if (config('pterodactyl.api.include_on_list') && $request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->toArray();
}
/**
* Controller to handle returning information on a single server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function view(Request $request, $id)
{
$this->authorize('server-view', $request->apiKey());
$server = Server::findOrFail($id);
$fractal = Fractal::create()->item($server);
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->transformWith(new ServerTransformer($request))
->withResourceName('server')
->toArray();
}
/**
* Create a new server on the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse|array
*/
public function store(Request $request)
{
$this->authorize('server-create', $request->apiKey());
$repo = new ServerRepository;
try {
$server = $repo->create($request->all());
$fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('server')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to add this server. Please try again.',
], 500);
}
}
/**
* Delete a server from the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function delete(Request $request, $id)
{
$this->authorize('server-delete', $request->apiKey());
$repo = new ServerRepository;
try {
$repo->delete($id, $request->has('force_delete'));
return response('', 204);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to add this server. Please try again.',
], 500);
}
}
/**
* Update the details for a server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse|array
*/
public function details(Request $request, $id)
{
$this->authorize('server-edit-details', $request->apiKey());
$repo = new ServerRepository;
try {
$server = $repo->updateDetails($id, $request->intersect([
'owner_id', 'name', 'description', 'reset_token',
]));
$fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('server')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to modify this server. Please try again.',
], 500);
}
}
/**
* Set the new docker container for a server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse|array
*/
public function container(Request $request, $id)
{
$this->authorize('server-edit-container', $request->apiKey());
$repo = new ServerRepository;
try {
$server = $repo->updateContainer($id, $request->intersect('docker_image'));
$fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('server')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to modify this server container. Please try again.',
], 500);
}
}
/**
* Toggles the install status for a server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function install(Request $request, $id)
{
$this->authorize('server-install', $request->apiKey());
$repo = new ServerRepository;
try {
$repo->toggleInstall($id);
return response('', 204);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to toggle the install status for this server. Please try again.',
], 500);
}
}
/**
* Setup a server to have a container rebuild.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function rebuild(Request $request, $id)
{
$this->authorize('server-rebuild', $request->apiKey());
$server = Server::with('node')->findOrFail($id);
try {
$server->node->guzzleClient([
'X-Access-Server' => $server->uuid,
'X-Access-Token' => $server->node->daemonSecret,
])->request('POST', '/server/rebuild');
return response('', 204);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
}
}
/**
* Manage the suspension status for a server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function suspend(Request $request, $id)
{
$this->authorize('server-suspend', $request->apiKey());
$repo = new ServerRepository;
$action = $request->input('action');
if (! in_array($action, ['suspend', 'unsuspend'])) {
return response()->json([
'error' => 'The action provided was invalid. Action should be one of: suspend, unsuspend.',
], 400);
}
try {
$repo->toggleAccess($id, ($action === 'unsuspend'));
return response('', 204);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to ' . $action . ' this server. Please try again.',
], 500);
}
}
/**
* Update the build configuration for a server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse|array
*/
public function build(Request $request, $id)
{
$this->authorize('server-edit-build', $request->apiKey());
$repo = new ServerRepository;
try {
$server = $repo->changeBuild($id, $request->intersect([
'allocation_id', 'add_allocations', 'remove_allocations',
'memory', 'swap', 'io', 'cpu',
]));
$fractal = Fractal::create()->item($server)->transformWith(new ServerTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('server')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to modify the build settings for this server. Please try again.',
], 500);
}
}
/**
* Update the startup command as well as variables.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function startup(Request $request, $id)
{
$this->authorize('server-edit-startup', $request->apiKey());
$repo = new ServerRepository;
try {
$repo->updateStartup($id, $request->all(), true);
return response('', 204);
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (TransferException $ex) {
Log::warning($ex);
return response()->json([
'error' => 'A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.',
], 504);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to modify the startup settings for this server. Please try again.',
], 500);
}
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API\Admin;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Models\Service;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Transformers\Admin\ServiceTransformer;
class ServiceController extends Controller
{
/**
* Controller to handle returning all locations on the system.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
$this->authorize('service-list', $request->apiKey());
return Fractal::create()
->collection(Service::all())
->transformWith(new ServiceTransformer($request))
->withResourceName('service')
->toArray();
}
/**
* Controller to handle returning information on a single server.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function view(Request $request, $id)
{
$this->authorize('service-view', $request->apiKey());
$service = Service::findOrFail($id);
$fractal = Fractal::create()->item($service);
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->transformWith(new ServiceTransformer($request))
->withResourceName('service')
->toArray();
}
}

View file

@ -0,0 +1,184 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API\Admin;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\UserRepository;
use Pterodactyl\Transformers\Admin\UserTransformer;
use Pterodactyl\Exceptions\DisplayValidationException;
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
class UserController extends Controller
{
/**
* Controller to handle returning all users on the system.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
$this->authorize('user-list', $request->apiKey());
$users = User::paginate(config('pterodactyl.paginate.api.users'));
$fractal = Fractal::create()->collection($users)
->transformWith(new UserTransformer($request))
->withResourceName('user')
->paginateWith(new IlluminatePaginatorAdapter($users));
if (config('pterodactyl.api.include_on_list') && $request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->toArray();
}
/**
* Display information about a single user on the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return array
*/
public function view(Request $request, $id)
{
$this->authorize('user-view', $request->apiKey());
$fractal = Fractal::create()->item(User::findOrFail($id));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->transformWith(new UserTransformer($request))
->withResourceName('user')
->toArray();
}
/**
* Create a new user on the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse|array
*/
public function store(Request $request)
{
$this->authorize('user-create', $request->apiKey());
$repo = new UserRepository;
try {
$user = $repo->create($request->only([
'custom_id', 'email', 'password', 'name_first',
'name_last', 'username', 'root_admin',
]));
$fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('user')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to create this user. Please try again.',
], 500);
}
}
/**
* Update a user.
*
* @param \Illuminate\Http\Request $request
* @param int $user
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $user)
{
$this->authorize('user-edit', $request->apiKey());
$repo = new UserRepository;
try {
$user = $repo->update($user, $request->intersect([
'email', 'password', 'name_first',
'name_last', 'username', 'root_admin',
]));
$fractal = Fractal::create()->item($user)->transformWith(new UserTransformer($request));
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return $fractal->withResourceName('user')->toArray();
} catch (DisplayValidationException $ex) {
return response()->json([
'error' => json_decode($ex->getMessage()),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to update this user. Please try again.',
], 500);
}
}
/**
* Delete a user from the system.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function delete(Request $request, $id)
{
$this->authorize('user-delete', $request->apiKey());
$repo = new UserRepository;
try {
$repo->delete($id);
return response('', 204);
} catch (DisplayException $ex) {
return response()->json([
'error' => $ex->getMessage(),
], 400);
} catch (\Exception $ex) {
Log::error($ex);
return response()->json([
'error' => 'An unhandled exception occured while attemping to delete this user. Please try again.',
], 500);
}
}
}

View file

@ -1,216 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API;
use Log;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Dingo\Api\Exception\ResourceException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\NodeRepository;
use Pterodactyl\Exceptions\DisplayValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
/**
* @Resource("Servers")
*/
class NodeController extends BaseController
{
public function __construct()
{
//
}
/**
* List All Nodes.
*
* Lists all nodes currently on the system.
*
* @Get("/nodes/{?page}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("page", type="integer", description="The page of results to view.", default=1)
* })
* @Response(200)
*/
public function lists(Request $request)
{
return Models\Node::all()->toArray();
}
/**
* Create a New Node.
*
* @Post("/nodes")
* @Versions({"v1"})
* @Transaction({
* @Request({
* 'name' => 'My API Node',
* 'location' => 1,
* 'public' => 1,
* 'fqdn' => 'daemon.wuzzle.woo',
* 'scheme' => 'https',
* 'memory' => 10240,
* 'memory_overallocate' => 100,
* 'disk' => 204800,
* 'disk_overallocate' => -1,
* 'daemonBase' => '/srv/daemon-data',
* 'daemonSFTP' => 2022,
* 'daemonListen' => 8080
* }, headers={"Authorization": "Bearer <jwt-token>"}),
* @Response(200),
* @Response(422, body={
* "message": "A validation error occured.",
* "errors": {},
* "status_code": 422
* }),
* @Response(503, body={
* "message": "There was an error while attempting to add this node to the system.",
* "status_code": 503
* })
* })
*/
public function create(Request $request)
{
try {
$repo = new NodeRepository;
$node = $repo->create($request->only([
'name', 'location_id', 'public', 'fqdn',
'scheme', 'memory', 'memory_overallocate',
'disk', 'disk_overallocate', 'daemonBase',
'daemonSFTP', 'daemonListen',
]));
return ['id' => $repo->id];
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
Log::error($ex);
throw new BadRequestHttpException('There was an error while attempting to add this node to the system.');
}
}
/**
* List Specific Node.
*
* Lists specific fields about a server or all fields pertaining to that node.
*
* @Get("/nodes/{id}/{?fields}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the node to get information on."),
* @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.")
* })
* @Response(200)
*/
public function view(Request $request, $id, $fields = null)
{
$node = Models\Node::with('allocations')->where('id', $id)->first();
if (! $node) {
throw new NotFoundHttpException('No node by that ID was found.');
}
$node->allocations->transform(function ($item) {
return collect($item)->only([
'id', 'ip', 'ip_alias', 'port', 'server_id',
]);
});
if (! is_null($request->input('fields'))) {
$fields = explode(',', $request->input('fields'));
if (! empty($fields) && is_array($fields)) {
return collect($node)->only($fields);
}
}
return $node;
}
public function config(Request $request, $id)
{
$node = Models\Node::where('id', $id)->first();
if (! $node) {
throw new NotFoundHttpException('No node by that ID was found.');
}
return $node->getConfigurationAsJson();
}
/**
* List all Node Allocations.
*
* Returns a listing of all allocations for every node.
*
* @Get("/nodes/allocations")
* @Versions({"v1"})
* @Response(200)
*/
public function allocations(Request $request)
{
return Models\Allocation::all()->toArray();
}
/**
* List Node Allocation based on assigned to ID.
*
* Returns a listing of the allocation for the specified server id.
*
* @Get("/nodes/allocations/{id}")
* @Versions({"v1"})
* @Response(200)
*/
public function allocationsView(Request $request, $id)
{
return Models\Allocation::where('server_id', $id)->get()->toArray();
}
/**
* Delete Node.
*
* @Delete("/nodes/{id}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the node."),
* })
* @Response(204)
*/
public function delete(Request $request, $id)
{
try {
$node = new NodeRepository;
$node->delete($id);
return $this->response->noContent();
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $e) {
throw new ServiceUnavailableHttpException('An error occured while attempting to delete this node.');
}
}
}

View file

@ -1,296 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API;
use Log;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Dingo\Api\Exception\ResourceException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\ServerRepository;
use Pterodactyl\Exceptions\DisplayValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
/**
* @Resource("Servers")
*/
class ServerController extends BaseController
{
public function __construct()
{
//
}
/**
* List All Servers.
*
* Lists all servers currently on the system.
*
* @Get("/servers/{?page}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("page", type="integer", description="The page of results to view.", default=1)
* })
* @Response(200)
*/
public function lists(Request $request)
{
return Models\Server::all()->toArray();
}
/**
* Create Server.
*
* @Post("/servers")
* @Versions({"v1"})
* @Response(201)
*/
public function create(Request $request)
{
try {
$repo = new ServerRepository;
$server = $repo->create($request->all());
return ['id' => $server->id];
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
Log::error($ex);
throw new BadRequestHttpException('There was an error while attempting to add this server to the system.');
}
}
/**
* List Specific Server.
*
* Lists specific fields about a server or all fields pertaining to that server.
*
* @Get("/servers/{id}{?fields}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server to get information on."),
* @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.")
* })
* @Response(200)
*/
public function view(Request $request, $id)
{
$server = Models\Server::with('node', 'allocations', 'pack')->where('id', $id)->first();
if (! $server) {
throw new NotFoundHttpException('No server by that ID was found.');
}
if (! is_null($request->input('fields'))) {
$fields = explode(',', $request->input('fields'));
if (! empty($fields) && is_array($fields)) {
return collect($server)->only($fields);
}
}
if ($request->input('daemon') === 'true') {
try {
$response = $server->node->guzzleClient([
'X-Access-Token' => $server->node->daemonSecret,
])->request('GET', '/servers');
$server->daemon = json_decode($response->getBody())->{$server->uuid};
} catch (\GuzzleHttp\Exception\TransferException $ex) {
// Couldn't hit the daemon, return what we have though.
$server->daemon = [
'error' => 'There was an error encountered while attempting to connect to the remote daemon.',
];
}
}
$server->allocations->transform(function ($item) {
return collect($item)->except(['created_at', 'updated_at']);
});
return $server->toArray();
}
/**
* Update Server configuration.
*
* Updates display information on panel.
*
* @Patch("/servers/{id}/config")
* @Versions({"v1"})
* @Transaction({
* @Request({
* "owner": "new@email.com",
* "name": "New Name",
* "reset_token": true
* }, headers={"Authorization": "Bearer <token>"}),
* @Response(200, body={"name": "New Name"}),
* @Response(422)
* })
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server to modify.")
* })
*/
public function config(Request $request, $id)
{
try {
$server = new ServerRepository;
$server->updateDetails($id, $request->only([
'owner', 'name', 'reset_token',
]));
return Models\Server::findOrFail($id);
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.');
}
}
/**
* Update Server Build Configuration.
*
* Updates server build information on panel and on node.
*
* @Patch("/servers/{id}/build")
* @Versions({"v1"})
* @Transaction({
* @Request({
* "default": "192.168.0.1:25565",
* "add_additional": [
* "192.168.0.1:25566",
* "192.168.0.1:25567",
* "192.168.0.1:25568"
* ],
* "remove_additional": [],
* "memory": 1024,
* "swap": 0,
* "io": 500,
* "cpu": 0,
* "disk": 1024
* }, headers={"Authorization": "Bearer <token>"}),
* @Response(200, body={"name": "New Name"}),
* @Response(422)
* })
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server to modify.")
* })
*/
public function build(Request $request, $id)
{
try {
$server = new ServerRepository;
$server->changeBuild($id, $request->only([
'default', 'add_additional', 'remove_additional',
'memory', 'swap', 'io', 'cpu', 'disk',
]));
return Models\Server::findOrFail($id);
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('Unable to update server on system due to an error.');
}
}
/**
* Suspend Server.
*
* @Post("/servers/{id}/suspend")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server."),
* })
* @Response(204)
*/
public function suspend(Request $request, $id)
{
try {
$server = new ServerRepository;
$server->suspend($id);
return $this->response->noContent();
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('An error occured while attempting to suspend this server instance.');
}
}
/**
* Unsuspend Server.
*
* @Post("/servers/{id}/unsuspend")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server."),
* })
* @Response(204)
*/
public function unsuspend(Request $request, $id)
{
try {
$server = new ServerRepository;
$server->unsuspend($id);
return $this->response->noContent();
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('An error occured while attempting to unsuspend this server instance.');
}
}
/**
* Delete Server.
*
* @Delete("/servers/{id}/{force}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the server."),
* @Parameter("force", type="string", required=false, description="Use 'force' if the server should be removed regardless of daemon response."),
* })
* @Response(204)
*/
public function delete(Request $request, $id, $force = null)
{
try {
$server = new ServerRepository;
$server->deleteServer($id, $force);
return $this->response->noContent();
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $e) {
throw new ServiceUnavailableHttpException('An error occured while attempting to delete this server.');
}
}
}

View file

@ -24,24 +24,28 @@
namespace Pterodactyl\Http\Controllers\API\User;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Http\Controllers\API\BaseController;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Transformers\User\OverviewTransformer;
class InfoController extends BaseController
class CoreController extends Controller
{
public function me(Request $request)
/**
* Controller to handle base user request for all of their servers.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function index(Request $request)
{
return $request->user()->serverAccessCollection()->load('allocation', 'option')->map(function ($server) {
return [
'id' => $server->uuidShort,
'uuid' => $server->uuid,
'name' => $server->name,
'node' => $server->node->name,
'ip' => $server->allocation->alias,
'port' => $server->allocation->port,
'service' => $server->service->name,
'option' => $server->option->name,
];
})->all();
$this->authorize('user.server-list', $request->apiKey());
$servers = $request->user()->access('service', 'node', 'allocation', 'option')->get();
return Fractal::collection($servers)
->transformWith(new OverviewTransformer)
->withResourceName('server')
->toArray();
}
}

View file

@ -24,78 +24,76 @@
namespace Pterodactyl\Http\Controllers\API\User;
use Log;
use Pterodactyl\Models;
use Fractal;
use Illuminate\Http\Request;
use Pterodactyl\Http\Controllers\API\BaseController;
use Pterodactyl\Models\Server;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Repositories\Daemon\PowerRepository;
use Pterodactyl\Transformers\User\ServerTransformer;
use Pterodactyl\Repositories\Daemon\CommandRepository;
class ServerController extends BaseController
class ServerController extends Controller
{
public function info(Request $request, $uuid)
/**
* Controller to handle base request for individual server information.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return array
*/
public function index(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid)->load('allocations');
$this->authorize('user.server-view', $request->apiKey());
try {
$response = $server->guzzleClient()->request('GET', '/server');
$server = Server::byUuid($uuid);
$fractal = Fractal::create()->item($server);
$json = json_decode($response->getBody());
$daemon = [
'status' => $json->status,
'stats' => $json->proc,
];
} catch (\Exception $ex) {
$daemon = [
'error' => 'An error was encountered while trying to connect to the daemon to collect information. It might be offline.',
];
Log::error($ex);
if ($request->input('include')) {
$fractal->parseIncludes(explode(',', $request->input('include')));
}
return [
'uuidShort' => $server->uuidShort,
'uuid' => $server->uuid,
'name' => $server->name,
'node' => $server->node->name,
'limits' => [
'memory' => $server->memory,
'swap' => $server->swap,
'disk' => $server->disk,
'io' => $server->io,
'cpu' => $server->cpu,
'oom_disabled' => (bool) $server->oom_disabled,
],
'allocations' => $server->allocations->map(function ($item) use ($server) {
return [
'ip' => $item->alias,
'port' => $item->port,
'default' => ($item->id === $server->allocation_id),
];
}),
'sftp' => [
'username' => ($request->user()->can('view-sftp', $server)) ? $server->username : null,
],
'daemon' => [
'token' => $server->daemonSecret,
'response' => $daemon,
],
];
return $fractal->transformWith(new ServerTransformer)
->withResourceName('server')
->toArray();
}
/**
* Controller to handle request for server power toggle.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\Response
*/
public function power(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
Auth::user()->can('power-' . $request->input('action'), $server);
$this->authorize('user.server-power', $request->apiKey());
$res = $server->guzzleClient()->request('PUT', '/server/power', [
'exceptions' => false,
'json' => [
'action' => $request->input('action'),
],
]);
$server = Server::byUuid($uuid);
$request->user()->can('power-' . $request->input('action'), $server);
if ($res->getStatusCode() !== 204) {
return $this->response->error(json_decode($res->getBody())->error, $res->getStatusCode());
$repo = new PowerRepository($server, $request->user());
$repo->do($request->input('action'));
return response('', 204)->header('Content-Type', 'application/json');
}
return $this->response->noContent();
/**
* Controller to handle base request for individual server information.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\Response
*/
public function command(Request $request, $uuid)
{
$this->authorize('user.server-command', $request->apiKey());
$server = Server::byUuid($uuid);
$request->user()->can('send-command', $server);
$repo = new CommandRepository($server, $request->user());
$repo->send($request->input('command'));
return response('', 204)->header('Content-Type', 'application/json');
}
}

View file

@ -1,202 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\API;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Dingo\Api\Exception\ResourceException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Repositories\UserRepository;
use Pterodactyl\Exceptions\DisplayValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
/**
* @Resource("Users")
*/
class UserController extends BaseController
{
public function __construct()
{
}
/**
* List All Users.
*
* Lists all users currently on the system.
*
* @Get("/users/{?page}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("page", type="integer", description="The page of results to view.", default=1)
* })
* @Response(200)
*/
public function lists(Request $request)
{
return Models\User::all()->toArray();
}
/**
* List Specific User.
*
* Lists specific fields about a user or all fields pertaining to that user.
*
* @Get("/users/{id}/{fields}")
* @Versions({"v1"})
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the user to get information on."),
* @Parameter("fields", type="string", required=false, description="A comma delimidated list of fields to include.")
* })
* @Response(200)
*/
public function view(Request $request, $id)
{
$user = Models\User::with('servers')->where((is_numeric($id) ? 'id' : 'email'), $id)->first();
if (! $user->first()) {
throw new NotFoundHttpException('No user by that ID was found.');
}
$user->servers->transform(function ($item) {
return collect($item)->only([
'id', 'node_id', 'uuidShort',
'uuid', 'name', 'suspended',
'owner_id',
]);
});
if (! is_null($request->input('fields'))) {
$fields = explode(',', $request->input('fields'));
if (! empty($fields) && is_array($fields)) {
return collect($user)->only($fields);
}
}
return $user->toArray();
}
/**
* Create a New User.
*
* @Post("/users")
* @Versions({"v1"})
* @Transaction({
* @Request({
* "email": "foo@example.com",
* "password": "foopassword",
* "admin": false,
* "custom_id": 123
* }, headers={"Authorization": "Bearer <token>"}),
* @Response(201),
* @Response(422)
* })
*/
public function create(Request $request)
{
try {
$user = new UserRepository;
$create = $user->create($request->only([
'email', 'username', 'name_first',
'name_last', 'password',
'root_admin', 'custom_id',
]));
$create = $user->create($request->input('email'), $request->input('password'), $request->input('admin'), $request->input('custom_id'));
return ['id' => $create];
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('Unable to create a user on the system due to an error.');
}
}
/**
* Update an Existing User.
*
* The data sent in the request will be used to update the existing user on the system.
*
* @Patch("/users/{id}")
* @Versions({"v1"})
* @Transaction({
* @Request({
* "email": "new@email.com"
* }, headers={"Authorization": "Bearer <token>"}),
* @Response(200, body={"email": "new@email.com"}),
* @Response(422)
* })
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the user to modify.")
* })
*/
public function update(Request $request, $id)
{
try {
$user = new UserRepository;
$user->update($id, $request->only([
'username', 'email', 'name_first',
'name_last', 'password',
'root_admin', 'language',
]));
return Models\User::findOrFail($id);
} catch (DisplayValidationException $ex) {
throw new ResourceException('A validation error occured.', json_decode($ex->getMessage(), true));
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('Unable to update a user on the system due to an error.');
}
}
/**
* Delete a User.
*
* @Delete("/users/{id}")
* @Versions({"v1"})
* @Transaction({
* @Request(headers={"Authorization": "Bearer <token>"}),
* @Response(204),
* @Response(422)
* })
* @Parameters({
* @Parameter("id", type="integer", required=true, description="The ID of the user to delete.")
* })
*/
public function delete(Request $request, $id)
{
try {
$user = new UserRepository;
$user->delete($id);
return $this->response->noContent();
} catch (DisplayException $ex) {
throw new ResourceException($ex->getMessage());
} catch (\Exception $ex) {
throw new ServiceUnavailableHttpException('Unable to delete this user due to an error.');
}
}
}

View file

@ -33,30 +33,38 @@ use Pterodactyl\Http\Controllers\Controller;
class BaseController extends Controller
{
/**
* Controller Constructor.
* Return the admin index view.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function __construct()
{
//
}
public function getIndex(Request $request)
{
return view('admin.index');
}
/**
* Return the admin settings view.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function getSettings(Request $request)
{
return view('admin.settings');
}
/**
* Handle settings post request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function postSettings(Request $request)
{
$validator = Validator::make($request->all(), [
'company' => 'required|between:1,256',
'default_language' => 'required|alpha_dash|min:2|max:5',
'email_from' => 'required|email',
'email_sender_name' => 'required|between:1,256',
]);
if ($validator->fails()) {
@ -65,8 +73,6 @@ class BaseController extends Controller
Settings::set('company', $request->input('company'));
Settings::set('default_language', $request->input('default_language'));
Settings::set('email_from', $request->input('email_from'));
Settings::set('email_sender_name', $request->input('email_sender_name'));
Alert::success('Settings have been successfully updated.')->flash();

View file

@ -40,7 +40,7 @@ class DatabaseController extends Controller
/**
* Display database host index.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
@ -54,7 +54,7 @@ class DatabaseController extends Controller
/**
* Display database host to user.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -69,8 +69,8 @@ class DatabaseController extends Controller
/**
* Handle post request to create database host.
*
* @param Request $request
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function create(Request $request)
{
@ -99,9 +99,9 @@ class DatabaseController extends Controller
/**
* Handle post request to update a database host.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $id)
{

View file

@ -38,7 +38,7 @@ class LocationController extends Controller
/**
* Return the location overview page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
@ -51,7 +51,7 @@ class LocationController extends Controller
/**
* Return the location view page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -63,8 +63,8 @@ class LocationController extends Controller
/**
* Handle request to create new location.
*
* @param Request $request
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function create(Request $request)
{
@ -88,9 +88,9 @@ class LocationController extends Controller
/**
* Handle request to update or delete location.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $id)
{

View file

@ -40,7 +40,7 @@ class NodesController extends Controller
/**
* Displays the index page listing all nodes on the panel.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
@ -57,10 +57,10 @@ class NodesController extends Controller
/**
* Displays create new node page.
*
* @param Request $request
* @return \Illuminate\View\View|\Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/
public function new(Request $request)
public function create(Request $request)
{
$locations = Models\Location::all();
if (! $locations->count()) {
@ -75,10 +75,10 @@ class NodesController extends Controller
/**
* Post controller to create a new node on the system.
*
* @param Request $request
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function create(Request $request)
public function store(Request $request)
{
try {
$repo = new NodeRepository;
@ -110,9 +110,8 @@ class NodesController extends Controller
/**
* Shows the index overview page for a specific node.
*
* @param Request $request
* @param int $id The ID of the node to display information for.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewIndex(Request $request, $id)
@ -138,9 +137,8 @@ class NodesController extends Controller
/**
* Shows the settings page for a specific node.
*
* @param Request $request
* @param int $id The ID of the node to display information for.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewSettings(Request $request, $id)
@ -154,9 +152,8 @@ class NodesController extends Controller
/**
* Shows the configuration page for a specific node.
*
* @param Request $request
* @param int $id The ID of the node to display information for.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewConfiguration(Request $request, $id)
@ -169,9 +166,8 @@ class NodesController extends Controller
/**
* Shows the allocation page for a specific node.
*
* @param Request $request
* @param int $id The ID of the node to display information for.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewAllocation(Request $request, $id)
@ -189,9 +185,8 @@ class NodesController extends Controller
/**
* Shows the server listing page for a specific node.
*
* @param Request $request
* @param int $id The ID of the node to display information for.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewServers(Request $request, $id)
@ -209,8 +204,8 @@ class NodesController extends Controller
/**
* Updates settings for a node.
*
* @param Request $request
* @param int $node
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function updateSettings(Request $request, $id)
@ -244,9 +239,9 @@ class NodesController extends Controller
/**
* Removes a single allocation from a node.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $node
* @param int $allocation [description]
* @param int $allocation
* @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
*/
public function allocationRemoveSingle(Request $request, $node, $allocation)
@ -264,7 +259,7 @@ class NodesController extends Controller
/**
* Remove all allocations for a specific IP at once on a node.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $node
* @return \Illuminate\Http\RedirectResponse
*/
@ -283,10 +278,9 @@ class NodesController extends Controller
/**
* Sets an alias for a specific allocation on a node.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $node
* @return \Illuminate\Http\Response
* @throws \Exception
*/
public function allocationSetAlias(Request $request, $node)
{
@ -308,7 +302,7 @@ class NodesController extends Controller
/**
* Creates new allocations on a node.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $node
* @return \Illuminate\Http\RedirectResponse
*/
@ -334,7 +328,7 @@ class NodesController extends Controller
/**
* Deletes a node from the system.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
@ -360,7 +354,7 @@ class NodesController extends Controller
/**
* Returns the configuration token to auto-deploy a node.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/

View file

@ -41,10 +41,10 @@ class OptionController extends Controller
/**
* Handles request to view page for adding new option.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function new(Request $request)
public function create(Request $request)
{
$services = Service::with('options')->get();
Javascript::put(['services' => $services->keyBy('id')]);
@ -55,10 +55,10 @@ class OptionController extends Controller
/**
* Handles POST request to create a new option.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Response\RedirectResponse
*/
public function create(Request $request)
public function store(Request $request)
{
$repo = new OptionRepository;
@ -86,9 +86,9 @@ class OptionController extends Controller
/**
* Handles POST request to create a new option variable.
*
* @param Request $request
* @param int $id The ID of the service option to assign this variable to.
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function createVariable(Request $request, $id)
{
@ -116,7 +116,7 @@ class OptionController extends Controller
/**
* Display option overview page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -128,7 +128,7 @@ class OptionController extends Controller
/**
* Display variable overview page for a service option.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -152,8 +152,9 @@ class OptionController extends Controller
/**
* Handles POST when editing a configration for a service option.
*
* @param Request $request
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function editConfiguration(Request $request, $id)
{
@ -188,10 +189,10 @@ class OptionController extends Controller
/**
* Handles POST when editing a configration for a service option.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $option
* @param int $variable
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function editVariable(Request $request, $option, $variable)
{

View file

@ -40,7 +40,7 @@ class PackController extends Controller
/**
* Display listing of all packs on the system.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
@ -57,10 +57,10 @@ class PackController extends Controller
/**
* Display new pack creation form.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function new(Request $request)
public function create(Request $request)
{
return view('admin.packs.new', [
'services' => Service::with('options')->get(),
@ -70,7 +70,7 @@ class PackController extends Controller
/**
* Display new pack creation modal for use with template upload.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function newTemplate(Request $request)
@ -83,10 +83,10 @@ class PackController extends Controller
/**
* Handle create pack request and route user to location.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
public function store(Request $request)
{
$repo = new PackRepository;
@ -117,7 +117,7 @@ class PackController extends Controller
/**
* Display pack view template to user.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -132,9 +132,9 @@ class PackController extends Controller
/**
* Handle updating or deleting pack information.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $id)
{
@ -168,10 +168,10 @@ class PackController extends Controller
/**
* Creates an archive of the pack and downloads it to the browser.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @param bool $files
* @return \Illuminate\Response\BinaryFileResponse
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function export(Request $request, $id, $files = false)
{

View file

@ -41,14 +41,12 @@ class ServersController extends Controller
/**
* Display the index page with all servers currently on the system.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
$servers = Models\Server::withTrashed()->with(
'node', 'user', 'allocation'
);
$servers = Models\Server::with('node', 'user', 'allocation');
if (! is_null($request->input('query'))) {
$servers->search($request->input('query'));
@ -62,10 +60,10 @@ class ServersController extends Controller
/**
* Display create new server page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function new(Request $request)
public function create(Request $request)
{
$services = Models\Service::with('options.packs', 'options.variables')->get();
Javascript::put([
@ -85,10 +83,10 @@ class ServersController extends Controller
/**
* Create server controller method.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Response\RedirectResponse
*/
public function create(Request $request)
public function store(Request $request)
{
try {
$repo = new ServerRepository;
@ -99,6 +97,9 @@ class ServersController extends Controller
return redirect()->route('admin.servers.new')->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (TransferException $ex) {
Log::warning($ex);
Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An unhandled exception occured while attemping to add this server. Please try again.')->flash();
@ -110,10 +111,10 @@ class ServersController extends Controller
/**
* Returns a tree of all avaliable nodes in a given location.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return array
*/
public function newServerNodes(Request $request)
public function nodes(Request $request)
{
$nodes = Models\Node::with('allocations')->where('location_id', $request->input('location'))->get();
@ -140,19 +141,19 @@ class ServersController extends Controller
/**
* Display the index when viewing a specific server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewIndex(Request $request, $id)
{
return view('admin.servers.view.index', ['server' => Models\Server::withTrashed()->findOrFail($id)]);
return view('admin.servers.view.index', ['server' => Models\Server::findOrFail($id)]);
}
/**
* Display the details page when viewing a specific server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -166,7 +167,7 @@ class ServersController extends Controller
/**
* Display the build details page when viewing a specific server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -184,7 +185,7 @@ class ServersController extends Controller
/**
* Display startup configuration page for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -203,7 +204,7 @@ class ServersController extends Controller
/**
* Display the database management page for a specific server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -220,7 +221,7 @@ class ServersController extends Controller
/**
* Display the management page when viewing a specific server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -232,28 +233,28 @@ class ServersController extends Controller
/**
* Display the deletion page for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function viewDelete(Request $request, $id)
{
return view('admin.servers.view.delete', ['server' => Models\Server::withTrashed()->findOrFail($id)]);
return view('admin.servers.view.delete', ['server' => Models\Server::findOrFail($id)]);
}
/**
* Update the details for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function setDetails(Request $request, $id)
{
$repo = new ServerRepository;
try {
$repo->updateDetails($id, $request->intersect([
'owner_id', 'name', 'reset_token',
'owner_id', 'name', 'description', 'reset_token',
]));
Alert::success('Server details were successfully updated.')->flash();
@ -272,9 +273,9 @@ class ServersController extends Controller
/**
* Set the new docker container for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function setContainer(Request $request, $id)
{
@ -286,8 +287,9 @@ class ServersController extends Controller
Alert::success('Successfully updated this server\'s docker image.')->flash();
} catch (DisplayValidationException $ex) {
return redirect()->route('admin.servers.view.details', $id)->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (TransferException $ex) {
Log::warning($ex);
Alert::danger('A TransferException occured while attempting to update the container image. Is the daemon online? This error has been logged.');
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An unhandled exception occured while attemping to update this server\'s docker image. This error has been logged.')->flash();
@ -299,9 +301,9 @@ class ServersController extends Controller
/**
* Toggles the install status for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function toggleInstall(Request $request, $id)
{
@ -323,9 +325,9 @@ class ServersController extends Controller
/**
* Setup a server to have a container rebuild.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function rebuildContainer(Request $request, $id)
{
@ -349,9 +351,9 @@ class ServersController extends Controller
/**
* Manage the suspension status for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function manageSuspension(Request $request, $id)
{
@ -365,11 +367,12 @@ class ServersController extends Controller
}
try {
$repo->$action($id);
$repo->toggleAccess($id, ($action === 'unsuspend'));
Alert::success('Server has been ' . $action . 'ed.');
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (TransferException $ex) {
Log::warning($ex);
Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An unhandled exception occured while attemping to ' . $action . ' this server. This error has been logged.')->flash();
@ -381,9 +384,9 @@ class ServersController extends Controller
/**
* Update the build configuration for a server.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function updateBuild(Request $request, $id)
{
@ -400,6 +403,9 @@ class ServersController extends Controller
return redirect()->route('admin.servers.view.build', $id)->withErrors(json_decode($ex->getMessage()))->withInput();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (TransferException $ex) {
Log::warning($ex);
Alert::danger('A TransferException was encountered while trying to contact the daemon, please ensure it is online and accessible. This error has been logged.')->flash();
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An unhandled exception occured while attemping to add this server. This error has been logged.')->flash();
@ -411,58 +417,16 @@ class ServersController extends Controller
/**
* Start the server deletion process.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function delete(Request $request, $id)
{
$repo = new ServerRepository;
try {
$repo->queueDeletion($id, ($request->input('is_force') > 0));
Alert::success('Server has been marked for deletion on the system.')->flash();
} catch (DisplayException $ex) {
Alert::danger($ex->getMessage())->flash();
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An unhandled exception occured while attemping to delete this server. This error has been logged.')->flash();
}
return redirect()->route('admin.servers.view.delete', $id);
}
/**
* Cancels a pending server deletion request.
*
* @param Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
*/
public function cancelDeletion(Request $request, $id)
{
$repo = new ServerRepository;
$repo->cancelDeletion($id);
Alert::success('Server deletion has been cancelled. This server will remain suspended until you unsuspend it.')->flash();
return redirect()->route('admin.servers.view.delete', $id);
}
/**
* Skips the queue and continues the server deletion process.
*
* @param Request $request
* @param int $id
* @param string $method
* @return \Illuminate\Response\RedirectResponse
*/
public function continueDeletion(Request $request, $id, $method = 'safe')
{
$repo = new ServerRepository;
try {
$repo->delete($id, (isset($method) && $method === 'force'));
$repo->delete($id, $request->has('force_delete'));
Alert::success('Server was successfully deleted from the system.')->flash();
return redirect()->route('admin.servers');
@ -482,9 +446,9 @@ class ServersController extends Controller
/**
* Update the startup command as well as variables.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function saveStartup(Request $request, $id)
{
@ -511,9 +475,10 @@ class ServersController extends Controller
/**
* Creates a new database assigned to a specific server.
* @param Request $request
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function newDatabase(Request $request, $id)
{
@ -537,9 +502,10 @@ class ServersController extends Controller
/**
* Resets the database password for a specific database on this server.
* @param Request $request
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function resetDatabasePassword(Request $request, $id)
{
@ -559,9 +525,11 @@ class ServersController extends Controller
/**
* Deletes a database from a server.
* @param Request $request
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @param int $database
* @return \Illuminate\Http\RedirectResponse
*/
public function deleteDatabase(Request $request, $id, $database)
{

View file

@ -38,7 +38,7 @@ class ServiceController extends Controller
/**
* Display service overview page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
@ -51,10 +51,10 @@ class ServiceController extends Controller
/**
* Display create service page.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function new(Request $request)
public function create(Request $request)
{
return view('admin.services.new');
}
@ -62,7 +62,7 @@ class ServiceController extends Controller
/**
* Return base view for a service.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -76,7 +76,7 @@ class ServiceController extends Controller
/**
* Return function editing view for a service.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
@ -88,10 +88,10 @@ class ServiceController extends Controller
/**
* Handle post action for new service.
*
* @param Request $request
* @return \Illuminate\Response\RedirectResponse
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function create(Request $request)
public function store(Request $request)
{
$repo = new ServiceRepository;
@ -117,9 +117,9 @@ class ServiceController extends Controller
/**
* Edits configuration for a specific service.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Response\RedirectResponse
* @return \Illuminate\Http\RedirectResponse
*/
public function edit(Request $request, $id)
{

View file

@ -37,17 +37,14 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class UserController extends Controller
{
/**
* Controller Constructor.
* Display user index page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function __construct()
public function index(Request $request)
{
//
}
// @TODO: implement nicolaslopezj/searchable to clean up this disaster.
public function getIndex(Request $request)
{
$users = User::withCount('servers');
$users = User::withCount('servers', 'subuserOf');
if (! is_null($request->input('query'))) {
$users->search($request->input('query'));
@ -58,19 +55,39 @@ class UserController extends Controller
]);
}
public function getNew(Request $request)
/**
* Display new user page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
return view('admin.users.new');
}
public function getView(Request $request, $id)
/**
* Display user view page.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\View\View
*/
public function view(Request $request, $id)
{
return view('admin.users.view', [
'user' => User::with('servers.node')->findOrFail($id),
]);
}
public function deleteUser(Request $request, $id)
/**
* Delete a user.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function delete(Request $request, $id)
{
try {
$repo = new UserRepository;
@ -88,7 +105,13 @@ class UserController extends Controller
return redirect()->route('admin.users.view', $id);
}
public function postNew(Request $request)
/**
* Create a user.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
try {
$user = new UserRepository;
@ -109,26 +132,39 @@ class UserController extends Controller
}
}
public function updateUser(Request $request, $user)
/**
* Update a user.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $id)
{
try {
$repo = new UserRepository;
$repo->update($user, $request->only([
$user = $repo->update($id, $request->intersect([
'email', 'password', 'name_first',
'name_last', 'username', 'root_admin',
]));
Alert::success('User account was successfully updated.')->flash();
} catch (DisplayValidationException $ex) {
return redirect()->route('admin.users.view', $user)->withErrors(json_decode($ex->getMessage()));
} catch (\Exception $e) {
Log::error($e);
return redirect()->route('admin.users.view', $id)->withErrors(json_decode($ex->getMessage()));
} catch (\Exception $ex) {
Log::error($ex);
Alert::danger('An error occured while attempting to update this user.')->flash();
}
return redirect()->route('admin.users.view', $user);
return redirect()->route('admin.users.view', $id);
}
public function getJson(Request $request)
/**
* Get a JSON response of users on the system.
*
* @param \Illuminate\Http\Request $request
* @return \Pterodactyl\Models\User
*/
public function json(Request $request)
{
return User::select('id', 'email', 'username', 'name_first', 'name_last')
->search($request->input('q'))

View file

@ -1,8 +1,33 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Controllers\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Events\Auth\FailedPasswordReset;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
@ -29,4 +54,21 @@ class ForgotPasswordController extends Controller
{
$this->middleware('guest');
}
/**
* Get the response for a failed password reset link.
*
* @param \Illuminate\Http\Request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
// As noted in #358 we will return success even if it failed
// to avoid pointing out that an account does or does not
// exist on the system.
event(new FailedPasswordReset($request->ip(), $request->only('email')));
return $this->sendResetLinkResponse(Password::RESET_LINK_SENT);
}
}

View file

@ -28,9 +28,11 @@ namespace Pterodactyl\Http\Controllers\Auth;
use Auth;
use Alert;
use Cache;
use Crypt;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use PragmaRX\Google2FA\Google2FA;
use Pterodactyl\Events\Auth\FailedLogin;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
@ -79,95 +81,152 @@ class LoginController extends Controller
$this->middleware('guest', ['except' => 'logout']);
}
/**
* Get the failed login response instance.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendFailedLoginResponse(Request $request)
{
$this->incrementLoginAttempts($request);
$errors = [$this->username() => trans('auth.failed')];
if ($request->expectsJson()) {
return response()->json($errors, 422);
}
return redirect()->route('auth.login')
->withInput($request->only($this->username(), 'remember'))
->withErrors($errors);
}
/**
* Handle a login request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\Response|\Illuminate\Response\RedirectResponse
*/
public function login(Request $request)
{
// Check wether the user identifier is an email address or a username
$isEmail = str_contains($request->input('user'), '@');
$checkField = str_contains($request->input('user'), '@') ? 'email' : 'username';
$this->validate($request, [
'user' => $isEmail ? 'required|email' : 'required|string',
'password' => 'required',
]);
if ($lockedOut = $this->hasTooManyLoginAttempts($request)) {
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
// Is the user (email or username) & password valid?
if (! Auth::once([
$isEmail ? 'email' : 'username' => $request->input('user'),
'password' => $request->input('password'),
], $request->has('remember'))) {
if (! $lockedOut) {
$this->incrementLoginAttempts($request);
}
// Determine if the user even exists.
$user = User::where($checkField, $request->input($this->username()))->first();
if (! $user) {
return $this->sendFailedLoginResponse($request);
}
// Verify TOTP Token was Valid
if (Auth::user()->use_totp) {
$verifyKey = str_random(64);
Cache::put($verifyKey, Auth::user()->id, 5);
// If user uses 2FA, redirect to that page.
if ($user->use_totp) {
$token = str_random(64);
Cache::put($token, [
'user_id' => $user->id,
'credentials' => Crypt::encrypt(serialize([
$checkField => $request->input($this->username()),
'password' => $request->input('password'),
])),
], 5);
return redirect()->route('auth.totp')->with('authentication_token', $verifyKey);
} else {
Auth::login(Auth::user(), $request->has('remember'));
return redirect()->route('auth.totp')->with('authentication_token', $token);
}
$attempt = Auth::attempt([
$checkField => $request->input($this->username()),
'password' => $request->input('password'),
'use_totp' => 0,
], $request->has('remember'));
if ($attempt) {
return $this->sendLoginResponse($request);
}
// Login failed, send response.
return $this->sendFailedLoginResponse($request);
}
/**
* Handle a TOTP implementation page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\View\View
*/
public function totp(Request $request)
{
$verifyKey = $request->session()->get('authentication_token');
$token = $request->session()->get('authentication_token');
if (is_null($verifyKey) || Auth::user()) {
if (is_null($token) || Auth::user()) {
return redirect()->route('auth.login');
}
return view('auth.totp', [
'verify_key' => $verifyKey,
'verify_key' => $token,
'remember' => $request->has('remember'),
]);
}
/**
* Handle a TOTP input.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function totpCheckpoint(Request $request)
{
$G2FA = new Google2FA();
if (is_null($request->input('verify_token'))) {
$this->incrementLoginAttempts($request);
Alert::danger(trans('auth.totp_failed'))->flash();
return redirect()->route('auth.login');
return $this->sendFailedLoginResponse($request);
}
$user = User::where('id', Cache::pull($request->input('verify_token')))->first();
if (! $user) {
$this->incrementLoginAttempts($request);
Alert::danger(trans('auth.totp_failed'))->flash();
$cache = Cache::pull($request->input('verify_token'));
$user = User::where('id', $cache['user_id'])->first();
return redirect()->route('auth.login');
if (! $user || ! $cache) {
$this->sendFailedLoginResponse($request);
}
if (! is_null($request->input('2fa_token')) && $G2FA->verifyKey($user->totp_secret, $request->input('2fa_token'), 1)) {
Auth::login($user, $request->has('remember'));
return redirect()->intended($this->redirectPath());
} else {
$this->incrementLoginAttempts($request);
Alert::danger(trans('auth.2fa_failed'))->flash();
return redirect()->route('auth.login');
if (is_null($request->input('2fa_token'))) {
return $this->sendFailedLoginResponse($request);
}
try {
$credentials = unserialize(Crypt::decrypt($cache['credentials']));
} catch (\Illuminate\Contracts\Encryption\DecryptException $ex) {
return $this->sendFailedLoginResponse($request);
}
if (! $G2FA->verifyKey($user->totp_secret, $request->input('2fa_token'), 2)) {
event(new \Illuminate\Auth\Events\Failed($user, $credentials));
return $this->sendFailedLoginResponse($request);
}
$attempt = Auth::attempt($credentials, $request->has('remember'));
if ($attempt) {
return $this->sendLoginResponse($request);
}
// Login failed, send response.
return $this->sendFailedLoginResponse($request);
}
/**
* Get the login username to be used by the controller.
*
* @return string
*/
public function username()
{
return 'user';
}
}

View file

@ -21,6 +21,11 @@ class ResetPasswordController extends Controller
use ResetsPasswords;
/**
* The URL to redirect users to after password reset.
*
* @var string
*/
public $redirectTo = '/';
/**
@ -33,6 +38,11 @@ class ResetPasswordController extends Controller
$this->middleware('guest');
}
/**
* Return the rules used when validating password reset.
*
* @return array
*/
protected function rules()
{
return [

View file

@ -27,8 +27,9 @@ namespace Pterodactyl\Http\Controllers\Base;
use Log;
use Alert;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Pterodactyl\Models\APIKey;
use Pterodactyl\Models\APIPermission;
use Pterodactyl\Repositories\APIRepository;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
@ -36,25 +37,48 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class APIController extends Controller
{
/**
* Display base API index page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
return view('base.api.index', [
'keys' => Models\APIKey::where('user_id', $request->user()->id)->get(),
'keys' => APIKey::where('user_id', $request->user()->id)->get(),
]);
}
/**
* Display API key creation page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\View
*/
public function create(Request $request)
{
return view('base.api.new');
return view('base.api.new', [
'permissions' => [
'user' => collect(APIPermission::permissions())->pull('_user'),
'admin' => collect(APIPermission::permissions())->except('_user')->toArray(),
],
]);
}
public function save(Request $request)
/**
* Handle saving new API key.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request)
{
try {
$repo = new APIRepository($request->user());
$secret = $repo->create($request->intersect([
'memo', 'allowed_ips',
'adminPermissions', 'permissions',
'admin_permissions', 'permissions',
]));
Alert::success('An API Key-Pair has successfully been generated. The API secret for this public key is shown below and will not be shown again.<br /><br /><code>' . $secret . '</code>')->flash();
@ -71,6 +95,13 @@ class APIController extends Controller
return redirect()->route('account.api.new')->withInput();
}
/**
* Handle revoking API key.
*
* @param \Illuminate\Http\Request $request
* @param string $key
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
*/
public function revoke(Request $request, $key)
{
try {

View file

@ -39,7 +39,7 @@ class AccountController extends Controller
* Display base account information page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
@ -48,8 +48,10 @@ class AccountController extends Controller
/**
* Update details for a users account.
*
* @param \Illuminate\Http\Request $request
* @return void
* @return \Illuminate\Http\RedirectResponse
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function update(Request $request)
{
@ -80,7 +82,7 @@ class AccountController extends Controller
if (
in_array($request->input('do_action'), ['email', 'password'])
&& ! password_verify($request->input('password'), $request->user()->password)
&& ! password_verify($request->input('current_password'), $request->user()->password)
) {
Alert::danger(trans('base.account.invalid_pass'))->flash();

View file

@ -30,24 +30,22 @@ use Pterodactyl\Http\Controllers\Controller;
class IndexController extends Controller
{
/**
* Controller Constructor.
*/
public function __construct()
{
//
}
/**
* Returns listing of user's servers.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\View\View
*/
public function getIndex(Request $request)
{
$servers = $request->user()->access()->with('user');
if (! is_null($request->input('query'))) {
$servers->search($request->input('query'));
}
return view('base.index', [
'servers' => $request->user()->serverAccessCollection(config('pterodactyl.paginate.frontend.servers')),
'servers' => $servers->paginate(config('pterodactyl.paginate.frontend.servers')),
]);
}
@ -55,7 +53,9 @@ class IndexController extends Controller
* Generate a random string.
*
* @param \Illuminate\Http\Request $request
* @param int $length
* @return string
* @deprecated
*/
public function getPassword(Request $request, $length = 16)
{

View file

@ -32,6 +32,11 @@ use Pterodactyl\Http\Controllers\Controller;
class LanguageController extends Controller
{
/**
* A list of supported languages on the panel.
*
* @var array
*/
protected $languages = [
'de' => 'German',
'en' => 'English',
@ -44,13 +49,12 @@ class LanguageController extends Controller
];
/**
* Controller Constructor.
* Sets the language for a user.
*
* @param \Illuminate\Http\Request $request
* @param string $language
* @return \Illuminate\Http\RedirectResponse
*/
public function __construct()
{
//
}
public function setLanguage(Request $request, $language)
{
if (array_key_exists($language, $this->languages)) {
@ -59,7 +63,7 @@ class LanguageController extends Controller
$user->language = $language;
$user->save();
}
Session::set('applocale', $language);
Session::put('applocale', $language);
}
return redirect()->back();

View file

@ -37,7 +37,7 @@ class SecurityController extends Controller
* Returns Security Management Page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\View\View
*/
public function index(Request $request)
{
@ -51,7 +51,7 @@ class SecurityController extends Controller
* that they can generate a valid response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\JsonResponse
*/
public function generateTotp(Request $request)
{
@ -96,7 +96,7 @@ class SecurityController extends Controller
* Disables TOTP on an account.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function disableTotp(Request $request)
{
@ -116,6 +116,13 @@ class SecurityController extends Controller
return redirect()->route('account.security');
}
/**
* Revokes a user session.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function revoke(Request $request, $id)
{
Session::where('user_id', $request->user()->id)->findOrFail($id)->delete();

View file

@ -31,19 +31,12 @@ use Pterodactyl\Http\Controllers\Controller;
class PackController extends Controller
{
/**
* Controller Constructor.
*/
public function __construct()
{
//
}
/**
* Pulls an install pack archive from the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @param string $uuid
* @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function pull(Request $request, $uuid)
{
@ -64,7 +57,8 @@ class PackController extends Controller
* Returns the hash information for a pack.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @param string $uuid
* @return \Illuminate\Http\JsonResponse
*/
public function hash(Request $request, $uuid)
{
@ -87,9 +81,10 @@ class PackController extends Controller
* Pulls an update pack archive from the system.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
* @return void
*/
public function pullUpdate(Request $request)
{
//
}
}

View file

@ -32,13 +32,11 @@ use Pterodactyl\Http\Controllers\Controller;
class RemoteController extends Controller
{
/**
* Controller Constructor.
* Handles download request from daemon.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function __construct()
{
// No middleware for this route.
}
public function postDownload(Request $request)
{
$download = Models\Download::where('token', $request->input('token'))->first();
@ -56,6 +54,12 @@ class RemoteController extends Controller
]);
}
/**
* Handles install toggle request from daemon.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse
*/
public function postInstall(Request $request)
{
$server = Models\Server::where('uuid', $request->input('server'))->with('node')->first();
@ -82,6 +86,13 @@ class RemoteController extends Controller
], 200);
}
/**
* Handles event from daemon.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
* @deprecated
*/
public function event(Request $request)
{
$server = Models\Server::where('uuid', $request->input('server'))->with('node')->first();
@ -101,6 +112,13 @@ class RemoteController extends Controller
return response('', 201);
}
/**
* Handles configuration data request from daemon.
*
* @param \Illuminate\Http\Request $request
* @param string $token
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
*/
public function getConfiguration(Request $request, $token)
{
// Try to query the token and the node from the database
@ -111,7 +129,7 @@ class RemoteController extends Controller
}
// Check if token is expired
if ($model->created_at->lt(Carbon::now())) {
if ($model->created_at->addMinutes(5)->lt(Carbon::now())) {
$model->delete();
return response()->json(['error' => 'token_expired'], 403);

View file

@ -50,20 +50,12 @@ class AjaxController extends Controller
*/
protected $directory;
/**
* Controller Constructor.
*/
public function __construct()
{
//
}
/**
* Returns true or false depending on the power status of the requested server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\JsonResponse
*/
public function getStatus(Request $request, $uuid)
{
@ -97,8 +89,8 @@ class AjaxController extends Controller
* Returns a listing of files in a given directory for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid`
* @return \Illuminate\Contracts\View\View
* @param string $uuid
* @return \Illuminate\View\View|\Illuminate\Http\Response
*/
public function postDirectoryList(Request $request, $uuid)
{
@ -147,7 +139,7 @@ class AjaxController extends Controller
/**
* Handles a POST request to save a file.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\Response
*/
@ -172,10 +164,12 @@ class AjaxController extends Controller
}
/**
* [postSetPrimary description].
* @param Request $request
* Sets the primary allocation for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\JsonResponse
* @deprecated
*/
public function postSetPrimary(Request $request, $uuid)
{
@ -219,6 +213,14 @@ class AjaxController extends Controller
}
}
/**
* Resets a database password for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\JsonResponse
* @deprecated
*/
public function postResetDatabasePassword(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);

View file

@ -37,21 +37,12 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class ServerController extends Controller
{
/**
* Controller Constructor.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Renders server index page for specified server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getIndex(Request $request, $uuid)
{
@ -62,6 +53,10 @@ class ServerController extends Controller
'saveFile' => route('server.files.save', $server->uuidShort),
'csrfToken' => csrf_token(),
],
'config' => [
'console_count' => config('pterodactyl.console.count'),
'console_freq' => config('pterodactyl.console.frequency'),
],
]);
return view('server.index', [
@ -70,11 +65,37 @@ class ServerController extends Controller
]);
}
/**
* Renders server console as an individual item.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getConsole(Request $request, $uuid)
{
\Debugbar::disable();
$server = Models\Server::byUuid($uuid);
$server->js([
'config' => [
'console_count' => config('pterodactyl.console.count'),
'console_freq' => config('pterodactyl.console.frequency'),
],
]);
return view('server.console', [
'server' => $server,
'node' => $server->node,
]);
}
/**
* Renders file overview page.
*
* @param Request $request
* @return \Illuminate\Contracts\View\View
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getFiles(Request $request, $uuid)
{
@ -106,8 +127,9 @@ class ServerController extends Controller
/**
* Renders add file page.
*
* @param Request $request
* @return \Illuminate\Contracts\View\View
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getAddFile(Request $request, $uuid)
{
@ -126,10 +148,10 @@ class ServerController extends Controller
/**
* Renders edit file page for a given file.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param string $file
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\View\View
*/
public function getEditFile(Request $request, $uuid, $file)
{
@ -169,10 +191,10 @@ class ServerController extends Controller
/**
* Handles downloading a file for the user.
*
* @param Request $request
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param string $file
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\View\View
*/
public function getDownloadFile(Request $request, $uuid, $file)
{
@ -190,6 +212,13 @@ class ServerController extends Controller
return redirect($server->node->scheme . '://' . $server->node->fqdn . ':' . $server->node->daemonListen . '/server/file/download/' . $download->token);
}
/**
* Returns the allocation overview for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getAllocation(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
@ -205,6 +234,13 @@ class ServerController extends Controller
]);
}
/**
* Returns the startup overview for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getStartup(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
@ -235,6 +271,13 @@ class ServerController extends Controller
]);
}
/**
* Returns the database overview for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getDatabases(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
@ -250,6 +293,13 @@ class ServerController extends Controller
]);
}
/**
* Returns the SFTP overview for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function getSFTP(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
@ -262,6 +312,13 @@ class ServerController extends Controller
]);
}
/**
* Handles changing the SFTP password for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\RedirectResponse
*/
public function postSettingsSFTP(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
@ -283,6 +340,13 @@ class ServerController extends Controller
return redirect()->route('server.settings.sftp', $uuid);
}
/**
* Handles changing the startup settings for a server.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\RedirectResponse
*/
public function postSettingsStartup(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);

View file

@ -37,16 +37,13 @@ use Pterodactyl\Exceptions\DisplayValidationException;
class SubuserController extends Controller
{
/**
* Controller Constructor.
* Displays the subuser overview index.
*
* @return void
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function __construct()
{
//
}
public function getIndex(Request $request, $uuid)
public function index(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid)->load('subusers.user');
$this->authorize('list-subusers', $server);
@ -60,7 +57,15 @@ class SubuserController extends Controller
]);
}
public function getView(Request $request, $uuid, $id)
/**
* Displays the a single subuser overview.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param int $id
* @return \Illuminate\View\View
*/
public function view(Request $request, $uuid, $id)
{
$server = Models\Server::byUuid($uuid)->load('node');
$this->authorize('view-subuser', $server);
@ -74,13 +79,22 @@ class SubuserController extends Controller
'server' => $server,
'node' => $server->node,
'subuser' => $subuser,
'permlist' => Models\Permission::list(),
'permissions' => $subuser->permissions->mapWithKeys(function ($item, $key) {
return [$item->permission => true];
}),
]);
}
public function postView(Request $request, $uuid, $id)
/**
* Handles editing a subuser.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param int $id
* @return \Illuminate\Http\RedirectResponse
*/
public function update(Request $request, $uuid, $id)
{
$server = Models\Server::byUuid($uuid);
$this->authorize('edit-subuser', $server);
@ -118,7 +132,14 @@ class SubuserController extends Controller
]);
}
public function getNew(Request $request, $uuid)
/**
* Display new subuser creation page.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function create(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
$this->authorize('create-subuser', $server);
@ -126,11 +147,19 @@ class SubuserController extends Controller
return view('server.users.new', [
'server' => $server,
'permissions' => Models\Permission::list(),
'node' => $server->node,
]);
}
public function postNew(Request $request, $uuid)
/**
* Handles creating a new subuser.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
$this->authorize('create-subuser', $server);
@ -158,7 +187,15 @@ class SubuserController extends Controller
return redirect()->route('server.subusers.new', $uuid)->withInput();
}
public function deleteSubuser(Request $request, $uuid, $id)
/**
* Handles deleting a subuser.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param int $id
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response
*/
public function delete(Request $request, $uuid, $id)
{
$server = Models\Server::byUuid($uuid);
$this->authorize('delete-subuser', $server);

View file

@ -26,23 +26,25 @@ namespace Pterodactyl\Http\Controllers\Server;
use Log;
use Alert;
use Pterodactyl\Models;
use Illuminate\Http\Request;
use Pterodactyl\Repositories;
use Pterodactyl\Models\Server;
use Pterodactyl\Repositories\TaskRepository;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Controller;
use Pterodactyl\Exceptions\DisplayValidationException;
class TaskController extends Controller
{
public function __constructor()
/**
* Display task index page.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function index(Request $request, $uuid)
{
//
}
public function getIndex(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid)->load('tasks');
$server = Server::byUuid($uuid)->load('tasks');
$this->authorize('list-tasks', $server);
$server->js();
@ -57,9 +59,16 @@ class TaskController extends Controller
]);
}
public function getNew(Request $request, $uuid)
/**
* Display new task page.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\View\View
*/
public function create(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
$server = Server::byUuid($uuid);
$this->authorize('create-task', $server);
$server->js();
@ -69,14 +78,21 @@ class TaskController extends Controller
]);
}
public function postNew(Request $request, $uuid)
/**
* Handle creation of new task.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @return \Illuminate\Http\RedirectResponse
*/
public function store(Request $request, $uuid)
{
$server = Models\Server::byUuid($uuid);
$server = Server::byUuid($uuid);
$this->authorize('create-task', $server);
$repo = new TaskRepository;
try {
$repo = new Repositories\TaskRepository;
$repo->create($server->id, $request->except([
$repo->create($server->id, $request->user()->id, $request->except([
'_token',
]));
@ -93,9 +109,17 @@ class TaskController extends Controller
return redirect()->route('server.tasks.new', $uuid);
}
public function deleteTask(Request $request, $uuid, $id)
/**
* Handle deletion of a task.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function delete(Request $request, $uuid, $id)
{
$server = Models\Server::byUuid($uuid)->load('tasks');
$server = Server::byUuid($uuid)->load('tasks');
$this->authorize('delete-task', $server);
$task = $server->tasks->where('id', $id)->first();
@ -105,8 +129,8 @@ class TaskController extends Controller
], 404);
}
$repo = new TaskRepository;
try {
$repo = new Repositories\TaskRepository;
$repo->delete($id);
return response()->json([], 204);
@ -119,9 +143,17 @@ class TaskController extends Controller
}
}
public function toggleTask(Request $request, $uuid, $id)
/**
* Toggle the status of a task.
*
* @param \Illuminate\Http\Request $request
* @param string $uuid
* @param int $id
* @return \Illuminate\Http\JsonResponse
*/
public function toggle(Request $request, $uuid, $id)
{
$server = Models\Server::byUuid($uuid)->load('tasks');
$server = Server::byUuid($uuid)->load('tasks');
$this->authorize('toggle-task', $server);
$task = $server->tasks->where('id', $id)->first();
@ -131,8 +163,8 @@ class TaskController extends Controller
], 404);
}
$repo = new TaskRepository;
try {
$repo = new Repositories\TaskRepository;
$resp = $repo->toggle($id);
return response()->json([

View file

@ -15,10 +15,11 @@ class Kernel extends HttpKernel
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\Pterodactyl\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Pterodactyl\Http\Middleware\TrimStrings::class,
\Pterodactyl\Http\Middleware\LanguageMiddleware::class,
/*
* Custom middleware applied to all routes.
*/
\Fideloper\Proxy\TrustProxies::class,
];
@ -35,8 +36,10 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Pterodactyl\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Pterodactyl\Http\Middleware\LanguageMiddleware::class,
],
'api' => [
\Pterodactyl\Http\Middleware\HMACAuthorization::class,
'throttle:60,1',
'bindings',
],
@ -58,5 +61,6 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'recaptcha' => \Pterodactyl\Http\Middleware\VerifyReCaptcha::class,
];
}

View file

@ -1,133 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Middleware;
use Auth;
use Crypt;
use Config;
use IPTools\IP;
use IPTools\Range;
use Dingo\Api\Routing\Route;
use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use Pterodactyl\Models\APIKey;
use Pterodactyl\Models\APIPermission;
use Pterodactyl\Services\APILogService;
use Dingo\Api\Auth\Provider\Authorization;
use Symfony\Component\HttpKernel\Exception\HttpException; // 400
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 401
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException; //500
class APISecretToken extends Authorization
{
protected $algo = 'sha256';
protected $permissionAllowed = false;
protected $url = '';
public function __construct()
{
Config::set('session.driver', 'array');
}
public function getAuthorizationMethod()
{
return 'Authorization';
}
public function authenticate(Request $request, Route $route)
{
if (! $request->bearerToken() || empty($request->bearerToken())) {
APILogService::log($request, 'The authentication header was missing or malformed.');
throw new UnauthorizedHttpException('The authentication header was missing or malformed.');
}
list($public, $hashed) = explode('.', $request->bearerToken());
$key = APIKey::where('public', $public)->first();
if (! $key) {
APILogService::log($request, 'Invalid API Key.');
throw new AccessDeniedHttpException('Invalid API Key.');
}
// Check for Resource Permissions
if (! empty($request->route()->getName())) {
if (! is_null($key->allowed_ips)) {
$inRange = false;
foreach (json_decode($key->allowed_ips) as $ip) {
if (Range::parse($ip)->contains(new IP($request->ip()))) {
$inRange = true;
break;
}
}
if (! $inRange) {
APILogService::log($request, 'This IP address <' . $request->ip() . '> does not have permission to use this API key.');
throw new AccessDeniedHttpException('This IP address <' . $request->ip() . '> does not have permission to use this API key.');
}
}
$permission = APIPermission::where('key_id', $key->id)->where('permission', $request->route()->getName());
// Suport Wildcards
if (starts_with($request->route()->getName(), 'api.user')) {
$permission->orWhere('permission', 'api.user.*');
} elseif (starts_with($request->route()->getName(), 'api.admin')) {
$permission->orWhere('permission', 'api.admin.*');
}
if (! $permission->first()) {
APILogService::log($request, 'You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.');
throw new AccessDeniedHttpException('You do not have permission to access this resource. This API Key requires the ' . $request->route()->getName() . ' permission node.');
}
}
try {
$decrypted = Crypt::decrypt($key->secret);
} catch (\Illuminate\Contracts\Encryption\DecryptException $ex) {
APILogService::log($request, 'There was an error while attempting to check your secret key.');
throw new HttpException('There was an error while attempting to check your secret key.');
}
$this->url = urldecode($request->fullUrl());
if ($this->_generateHMAC($request->getContent(), $decrypted) !== base64_decode($hashed)) {
APILogService::log($request, 'The hashed body was not valid. Potential modification of contents in route.');
throw new BadRequestHttpException('The hashed body was not valid. Potential modification of contents in route.');
}
// Log the Route Access
APILogService::log($request, null, true);
return Auth::loginUsingId($key->user_id);
}
protected function _generateHMAC($body, $key)
{
$data = $this->url . $body;
return hash_hmac($this->algo, $data, $key, true);
}
}

View file

@ -32,14 +32,14 @@ class AdminAuthenticate
/**
* The Guard implementation.
*
* @var Guard
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @param \Illuminate\Contracts\Auth\Guard $auth
* @return void
*/
public function __construct(Guard $auth)

View file

@ -10,14 +10,14 @@ class Authenticate
/**
* The Guard implementation.
*
* @var Guard
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @param \Illuminate\Contracts\Auth\Guard $auth
* @return void
*/
public function __construct(Guard $auth)

View file

@ -28,9 +28,26 @@ use Auth;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Models\Server;
use Illuminate\Auth\AuthenticationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class CheckServer
{
/**
* The elquent model for the server.
*
* @var \Pterodactyl\Models\Server
*/
protected $server;
/**
* The request object.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Handle an incoming request.
*
@ -41,22 +58,72 @@ class CheckServer
public function handle(Request $request, Closure $next)
{
if (! Auth::user()) {
return redirect()->guest('auth/login');
throw new AuthenticationException();
}
$server = Server::byUuid($request->route()->server);
if (! $server) {
$this->request = $request;
$this->server = Server::byUuid($request->route()->server);
if (! $this->exists()) {
return response()->view('errors.404', [], 404);
}
if ($server->suspended) {
if ($this->suspended()) {
return response()->view('errors.suspended', [], 403);
}
if (! $server->installed) {
if (! $this->installed()) {
return response()->view('errors.installing', [], 403);
}
return $next($request);
}
/**
* Determine if the server was found on the system.
*
* @return bool
*/
protected function exists()
{
if (! $this->server) {
if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) {
throw new NotFoundHttpException('The requested server was not found on the system.');
}
}
return (! $this->server) ? false : true;
}
/**
* Determine if the server is suspended.
*
* @return bool
*/
protected function suspended()
{
if ($this->server->suspended) {
if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) {
throw new AccessDeniedHttpException('Server is suspended.');
}
}
return $this->server->suspended;
}
/**
* Determine if the server is installed.
*
* @return bool
*/
protected function installed()
{
if ($this->server->installed !== 1) {
if ($this->request->expectsJson() || $this->request->is(...config('pterodactyl.json_routes'))) {
throw new AccessDeniedHttpException('Server is completing install process.');
}
}
return $this->server->installed === 1;
}
}

View file

@ -33,14 +33,14 @@ class DaemonAuthenticate
/**
* The Guard implementation.
*
* @var Guard
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @param \Illuminate\Contracts\Auth\Guard $auth
* @return void
*/
public function __construct(Guard $auth)

View file

@ -0,0 +1,229 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Middleware;
use Auth;
use Crypt;
use Config;
use Closure;
use Debugbar;
use IPTools\IP;
use IPTools\Range;
use Illuminate\Http\Request;
use Pterodactyl\Models\APIKey;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; // 400
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; // 403
class HMACAuthorization
{
/**
* The algorithm to use for handling HMAC encryption.
*
* @var string
*/
const HMAC_ALGORITHM = 'sha256';
/**
* Stored values from the Authorization header.
*
* @var array
*/
protected $token = [];
/**
* The eloquent model for the API key.
*
* @var \Pterodactyl\Models\APIKey
*/
protected $key;
/**
* The illuminate request object.
*
* @var \Illuminate\Http\Request
*/
private $request;
/**
* Construct class instance.
*
* @return void
*/
public function __construct()
{
Debugbar::disable();
Config::set('session.driver', 'array');
}
/**
* Handle an incoming request for the API.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
$this->request = $request;
$this->checkBearer();
$this->validateRequest();
$this->validateIPAccess();
$this->validateContents();
Auth::loginUsingId($this->key()->user_id);
return $next($request);
}
/**
* Checks that the Bearer token is provided and in a valid format.
*
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function checkBearer()
{
if (empty($this->request()->bearerToken())) {
throw new BadRequestHttpException('Request was missing required Authorization header.');
}
$token = explode('.', $this->request()->bearerToken());
if (count($token) !== 2) {
throw new BadRequestHttpException('The Authorization header passed was not in a validate public/private key format.');
}
$this->token = [
'public' => $token[0],
'hash' => $token[1],
];
}
/**
* Determine if the request contains a valid public API key
* as well as permissions for the resource.
*
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function validateRequest()
{
$this->key = APIKey::where('public', $this->public())->first();
if (! $this->key) {
throw new AccessDeniedHttpException('Unable to identify requester. Authorization token is invalid.');
}
}
/**
* Determine if the requesting IP address is allowed to use this API key.
*
* @return bool
*
* @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
*/
protected function validateIPAccess()
{
if (! is_null($this->key()->allowed_ips)) {
foreach ($this->key()->allowed_ips as $ip) {
if (Range::parse($ip)->contains(new IP($this->request()->ip()))) {
return true;
}
}
throw new AccessDeniedHttpException('This IP address does not have permission to access the API using these credentials.');
}
return true;
}
/**
* Determine if the HMAC sent in the request matches the one generated
* on the panel side.
*
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*/
protected function validateContents()
{
if (base64_decode($this->hash()) !== $this->generateSignature()) {
throw new BadRequestHttpException('The HMAC for the request was invalid.');
}
}
/**
* Generate a HMAC from the request and known API secret key.
*
* @return string
*/
protected function generateSignature()
{
$content = urldecode($this->request()->fullUrl()) . $this->request()->getContent();
return hash_hmac(self::HMAC_ALGORITHM, $content, Crypt::decrypt($this->key()->secret), true);
}
/**
* Return the public key passed in the Authorization header.
*
* @return string
*/
protected function public()
{
return $this->token['public'];
}
/**
* Return the base64'd HMAC sent in the Authorization header.
*
* @return string
*/
protected function hash()
{
return $this->token['hash'];
}
/**
* Return the API Key model.
*
* @return \Pterodactyl\Models\APIKey
*/
protected function key()
{
return $this->key;
}
/**
* Return the Illuminate Request object.
*
* @return \Illuminate\Http\Request
*/
private function request()
{
return $this->request;
}
}

View file

@ -32,11 +32,6 @@ use Illuminate\Support\Facades\App;
class LanguageMiddleware
{
public function __construct()
{
//
}
/**
* Handle an incoming request.
*
@ -49,7 +44,7 @@ class LanguageMiddleware
if (Session::has('applocale')) {
App::setLocale(Session::get('applocale'));
} elseif (Auth::check() && isset(Auth::user()->language)) {
Session::set('applocale', Auth::user()->language);
Session::put('applocale', Auth::user()->language);
App::setLocale(Auth::user()->language);
} else {
App::setLocale(Settings::get('default_language', 'en'));

View file

@ -18,7 +18,7 @@ class RedirectIfAuthenticated
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
return redirect('/');
return redirect(route('index'));
}
return $next($request);

View file

@ -0,0 +1,18 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as BaseTrimmer;
class TrimStrings extends BaseTrimmer
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'password',
'password_confirmation',
];
}

View file

@ -0,0 +1,60 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Pterodactyl\Events\Auth\FailedCaptcha;
class VerifyReCaptcha
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\RediectResponse
*/
public function handle($request, Closure $next)
{
if (! config('recaptcha.enabled')) {
return $next($request);
}
if ($request->has('g-recaptcha-response')) {
$client = new \GuzzleHttp\Client();
$res = $client->post(config('recaptcha.domain'), [
'form_params' => [
'secret' => config('recaptcha.secret_key'),
'response' => $request->input('g-recaptcha-response'),
],
]);
if ($res->getStatusCode() === 200) {
$result = json_decode($res->getBody());
$verified = function ($result, $request) {
if (! config('recaptcha.verify_domain')) {
return false;
}
$url = parse_url($request->url());
if (! array_key_exists('host', $url)) {
return false;
}
return $result->hostname === $url['host'];
};
if ($result->success && (! config('recaptcha.verify_domain') || $verified($result, $request))) {
return $next($request);
}
}
}
// Emit an event and return to the previous view with an error (only the captcha error will be shown!)
event(new FailedCaptcha($request->ip(), (! isset($result->hostname) ?: $result->hostname)));
return back()->withErrors(['g-recaptcha-response' => trans('strings.captcha_invalid')])->withInput();
}
}

View file

@ -1,184 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class APIRoutes
{
public function map(Router $router)
{
$api = app('Dingo\Api\Routing\Router');
$api->version('v1', ['prefix' => 'api/me', 'middleware' => 'api.auth'], function ($api) {
$api->get('/', [
'as' => 'api.user.me',
'uses' => 'Pterodactyl\Http\Controllers\API\User\InfoController@me',
]);
$api->get('/server/{uuid}', [
'as' => 'api.user.server',
'uses' => 'Pterodactyl\Http\Controllers\API\User\ServerController@info',
]);
$api->put('/server/{uuid}', [
'as' => 'api.user.server.power',
'uses' => 'Pterodactyl\Http\Controllers\API\User\ServerController@power',
]);
});
$api->version('v1', ['prefix' => 'api', 'middleware' => 'api.auth'], function ($api) {
/*
* User Routes
*/
$api->get('users', [
'as' => 'api.admin.users.list',
'uses' => 'Pterodactyl\Http\Controllers\API\UserController@lists',
]);
$api->post('users', [
'as' => 'api.admin.users.create',
'uses' => 'Pterodactyl\Http\Controllers\API\UserController@create',
]);
$api->get('users/{id}', [
'as' => 'api.admin.users.view',
'uses' => 'Pterodactyl\Http\Controllers\API\UserController@view',
]);
$api->patch('users/{id}', [
'as' => 'api.admin.users.update',
'uses' => 'Pterodactyl\Http\Controllers\API\UserController@update',
]);
$api->delete('users/{id}', [
'as' => 'api.admin.users.delete',
'uses' => 'Pterodactyl\Http\Controllers\API\UserController@delete',
]);
/*
* Server Routes
*/
$api->get('servers', [
'as' => 'api.admin.servers.list',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@lists',
]);
$api->post('servers', [
'as' => 'api.admin.servers.create',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@create',
]);
$api->get('servers/{id}', [
'as' => 'api.admin.servers.view',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@view',
]);
$api->patch('servers/{id}/config', [
'as' => 'api.admin.servers.config',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@config',
]);
$api->patch('servers/{id}/build', [
'as' => 'api.admin.servers.build',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@build',
]);
$api->post('servers/{id}/suspend', [
'as' => 'api.admin.servers.suspend',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@suspend',
]);
$api->post('servers/{id}/unsuspend', [
'as' => 'api.admin.servers.unsuspend',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@unsuspend',
]);
$api->delete('servers/{id}/{force?}', [
'as' => 'api.admin.servers.delete',
'uses' => 'Pterodactyl\Http\Controllers\API\ServerController@delete',
]);
/*
* Node Routes
*/
$api->get('nodes', [
'as' => 'api.admin.nodes.list',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@lists',
]);
$api->post('nodes', [
'as' => 'api.admin.nodes.create',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@create',
]);
$api->get('nodes/allocations', [
'as' => 'api.admin.nodes.allocations',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@allocations',
]);
$api->get('nodes/allocations/{id}', [
'as' => 'api.admin.nodes.allocations',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@allocationsView',
]);
$api->get('nodes/{id}', [
'as' => 'api.admin.nodes.view',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@view',
]);
$api->get('nodes/{id}/config', [
'as' => 'api.admin.nodes.view',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@config',
]);
$api->delete('nodes/{id}', [
'as' => 'api.admin.nodes.delete',
'uses' => 'Pterodactyl\Http\Controllers\API\NodeController@delete',
]);
/*
* Location Routes
*/
$api->get('locations', [
'as' => 'api.admin.locations.list',
'uses' => 'Pterodactyl\Http\Controllers\API\LocationController@lists',
]);
/*
* Service Routes
*/
$api->get('services', [
'as' => 'api.admin.services.list',
'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@lists',
]);
$api->get('services/{id}', [
'as' => 'api.admin.services.view',
'uses' => 'Pterodactyl\Http\Controllers\API\ServiceController@view',
]);
});
}
}

View file

@ -1,94 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>
* Some Modifications (c) 2015 Dylan Seidt <dylan.seidt@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Routes;
use Auth;
use Illuminate\Routing\Router;
class AuthRoutes
{
public function map(Router $router)
{
$router->group([
'prefix' => 'auth',
'middleware' => [
'guest',
'csrf',
],
], function () use ($router) {
// Display Login Page
$router->get('login', [
'as' => 'auth.login',
'uses' => 'Auth\LoginController@showLoginForm',
]);
// Handle Login
$router->post('login', [
'uses' => 'Auth\LoginController@login',
]);
$router->get('login/totp', [
'as' => 'auth.totp',
'uses' => 'Auth\LoginController@totp',
]);
$router->post('login/totp', [
'uses' => 'Auth\LoginController@totpCheckpoint',
]);
// Show Password Reset Form
$router->get('password', [
'as' => 'auth.password',
'uses' => 'Auth\ForgotPasswordController@showLinkRequestForm',
]);
// Handle Password Reset
$router->post('password', [
'uses' => 'Auth\ForgotPasswordController@sendResetLinkEmail',
]);
// Show Verification Checkpoint
$router->get('password/reset/{token}', [
'as' => 'auth.reset',
'uses' => 'Auth\ResetPasswordController@showResetForm',
]);
// Handle Verification
$router->post('password/reset', [
'as' => 'auth.reset.post',
'uses' => 'Auth\ResetPasswordController@reset',
]);
});
// Not included above because we don't want the guest middleware
$router->get('auth/logout', [
'as' => 'auth.logout',
'middleware' => 'auth',
'uses' => 'Auth\LoginController@logout',
]);
}
}

View file

@ -1,125 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>
* Some Modifications (c) 2015 Dylan Seidt <dylan.seidt@gmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class BaseRoutes
{
public function map(Router $router)
{
// Index of Panel
$router->get('/', [
'as' => 'index',
'middleware' => 'auth',
'uses' => 'Base\IndexController@getIndex',
]);
// Handle Index. Redirect /index to /
$router->get('/index', function () {
return redirect()->route('index');
});
// Password Generation
$router->get('/password-gen/{length}', [
'as' => 'password-gen',
'middleware' => 'auth',
'uses' => 'Base\IndexController@getPassword',
]);
// Account Routes
$router->group([
'prefix' => 'account',
'middleware' => [
'auth',
'csrf',
],
], function () use ($router) {
$router->get('/', [
'as' => 'account',
'uses' => 'Base\AccountController@index',
]);
$router->post('/', [
'uses' => 'Base\AccountController@update',
]);
});
// API Management Routes
$router->group([
'prefix' => 'account/api',
'middleware' => [
'auth',
'csrf',
],
], function () use ($router) {
$router->get('/', [
'as' => 'account.api',
'uses' => 'Base\APIController@index',
]);
$router->get('/new', [
'as' => 'account.api.new',
'uses' => 'Base\APIController@create',
]);
$router->post('/new', [
'uses' => 'Base\APIController@save',
]);
$router->delete('/revoke/{key}', [
'as' => 'account.api.revoke',
'uses' => 'Base\APIController@revoke',
]);
});
// TOTP Routes
$router->group([
'prefix' => 'account/security',
'middleware' => [
'auth',
'csrf',
],
], function () use ($router) {
$router->get('/', [
'as' => 'account.security',
'uses' => 'Base\SecurityController@index',
]);
$router->get('/revoke/{id}', [
'as' => 'account.security.revoke',
'uses' => 'Base\SecurityController@revoke',
]);
$router->put('/totp', [
'as' => 'account.security.totp',
'uses' => 'Base\SecurityController@generateTotp',
]);
$router->post('/totp', [
'uses' => 'Base\SecurityController@setTotp',
]);
$router->delete('/totp', [
'uses' => 'Base\SecurityController@disableTotp',
]);
});
}
}

View file

@ -1,51 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class RemoteRoutes
{
public function map(Router $router)
{
$router->group(['prefix' => 'remote'], function () use ($router) {
// Handles Remote Download Authentication Requests
$router->post('download', [
'as' => 'remote.download',
'uses' => 'Remote\RemoteController@postDownload',
]);
$router->post('install', [
'as' => 'remote.install',
'uses' => 'Remote\RemoteController@postInstall',
]);
$router->get('configuration/{token}', [
'as' => 'remote.configuration',
'uses' => 'Remote\RemoteController@getConfiguration',
]);
});
}
}

View file

@ -1,186 +0,0 @@
<?php
/**
* Pterodactyl - Panel
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class ServerRoutes
{
public function map(Router $router)
{
// Returns Server Status
$router->get('/server/{server}/ajax/status', [
'middleware' => ['auth', 'csrf'],
'as' => 'server.ajax.status',
'uses' => 'Server\AjaxController@getStatus',
]);
$router->group([
'prefix' => 'server/{server}',
'middleware' => [
'auth',
'server',
'csrf',
],
], function ($server) use ($router) {
// Index View for Server
$router->get('/', [
'as' => 'server.index',
'uses' => 'Server\ServerController@getIndex',
]);
$router->get('/settings/databases', [
'as' => 'server.settings.databases',
'uses' => 'Server\ServerController@getDatabases',
]);
$router->get('/settings/sftp', [
'as' => 'server.settings.sftp',
'uses' => 'Server\ServerController@getSFTP',
]);
$router->post('/settings/sftp', [
'uses' => 'Server\ServerController@postSettingsSFTP',
]);
$router->get('/settings/startup', [
'as' => 'server.settings.startup',
'uses' => 'Server\ServerController@getStartup',
]);
$router->post('/settings/startup', [
'uses' => 'Server\ServerController@postSettingsStartup',
]);
$router->get('/settings/allocation', [
'as' => 'server.settings.allocation',
'uses' => 'Server\ServerController@getAllocation',
]);
// File Manager Routes
$router->get('/files', [
'as' => 'server.files.index',
'uses' => 'Server\ServerController@getFiles',
]);
$router->get('/files/edit/{file}', [
'as' => 'server.files.edit',
'uses' => 'Server\ServerController@getEditFile',
])->where('file', '.*');
$router->get('/files/download/{file}', [
'as' => 'server.files.download',
'uses' => 'Server\ServerController@getDownloadFile',
])->where('file', '.*');
$router->get('/files/add', [
'as' => 'server.files.add',
'uses' => 'Server\ServerController@getAddFile',
]);
$router->post('files/directory-list', [
'as' => 'server.files.directory-list',
'uses' => 'Server\AjaxController@postDirectoryList',
]);
$router->post('files/save', [
'as' => 'server.files.save',
'uses' => 'Server\AjaxController@postSaveFile',
]);
// Sub-User Routes
$router->get('users', [
'as' => 'server.subusers',
'uses' => 'Server\SubuserController@getIndex',
]);
$router->get('users/new', [
'as' => 'server.subusers.new',
'uses' => 'Server\SubuserController@getNew',
]);
$router->post('users/new', [
'uses' => 'Server\SubuserController@postNew',
]);
$router->get('users/view/{id}', [
'as' => 'server.subusers.view',
'uses' => 'Server\SubuserController@getView',
]);
$router->post('users/view/{id}', [
'uses' => 'Server\SubuserController@postView',
]);
$router->delete('users/delete/{id}', [
'as' => 'server.subusers.delete',
'uses' => 'Server\SubuserController@deleteSubuser',
]);
$router->get('tasks/', [
'as' => 'server.tasks',
'uses' => 'Server\TaskController@getIndex',
]);
$router->get('tasks/view/{id}', [
'as' => 'server.tasks.view',
'uses' => 'Server\TaskController@getView',
]);
$router->get('tasks/new', [
'as' => 'server.tasks.new',
'uses' => 'Server\TaskController@getNew',
]);
$router->post('tasks/new', [
'uses' => 'Server\TaskController@postNew',
]);
$router->delete('tasks/delete/{id}', [
'as' => 'server.tasks.delete',
'uses' => 'Server\TaskController@deleteTask',
]);
$router->post('tasks/toggle/{id}', [
'as' => 'server.tasks.toggle',
'uses' => 'Server\TaskController@toggleTask',
]);
// Assorted AJAX Routes
$router->group(['prefix' => 'ajax'], function ($server) use ($router) {
// Sets the Default Connection for the Server
$router->post('set-primary', [
'uses' => 'Server\AjaxController@postSetPrimary',
]);
$router->post('settings/reset-database-password', [
'as' => 'server.ajax.reset-database-password',
'uses' => 'Server\AjaxController@postResetDatabasePassword',
]);
});
});
}
}

View file

@ -26,7 +26,10 @@ namespace Pterodactyl\Jobs;
use Cron;
use Carbon;
use Pterodactyl\Models;
use Pterodactyl\Models\Task;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\TaskLog;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
@ -37,8 +40,9 @@ class SendScheduledTask extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $server;
/**
* @var \Pterodactyl\Models\Task
*/
protected $task;
/**
@ -46,13 +50,12 @@ class SendScheduledTask extends Job implements ShouldQueue
*
* @return void
*/
public function __construct(Models\Server $server, Models\Task $task)
public function __construct(Task $task)
{
$this->server = $server;
$this->task = $task;
$task->queued = 1;
$task->save();
$this->task->queued = true;
$this->task->save();
}
/**
@ -63,7 +66,7 @@ class SendScheduledTask extends Job implements ShouldQueue
public function handle()
{
$time = Carbon::now();
$log = new Models\TaskLog;
$log = new TaskLog;
if ($this->attempts() >= 1) {
// Just delete the job, we will attempt it again later anyways.
@ -72,12 +75,15 @@ class SendScheduledTask extends Job implements ShouldQueue
try {
if ($this->task->action === 'command') {
$repo = new CommandRepository($this->server);
$repo = new CommandRepository($this->task->server, $this->task->user);
$response = $repo->send($this->task->data);
} elseif ($this->task->action === 'power') {
$repo = new PowerRepository($this->server);
} else if ($this->task->action === 'power') {
$repo = new PowerRepository($this->task->server, $this->task->user);
$response = $repo->do($this->task->data);
} else {
throw new \Exception('Task type provided was not valid.');
}
$log->fill([
'task_id' => $this->task->id,
'run_time' => $time,
@ -103,7 +109,7 @@ class SendScheduledTask extends Job implements ShouldQueue
$this->task->fill([
'last_run' => $time,
'next_run' => $cron->getNextRunDate(),
'queued' => 0,
'queued' => false,
]);
$this->task->save();
$log->save();

View file

@ -28,6 +28,13 @@ use Illuminate\Database\Eloquent\Model;
class APIKey extends Model
{
/**
* Public key defined length used in verification methods.
*
* @var int
*/
const PUBLIC_KEY_LEN = 16;
/**
* The table associated with the model.
*
@ -42,6 +49,15 @@ class APIKey extends Model
*/
protected $hidden = ['secret'];
/**
* Cast values to correct type.
*
* @var array
*/
protected $casts = [
'allowed_ips' => 'json',
];
/**
* Fields that are not mass assignable.
*

View file

@ -57,4 +57,75 @@ class APIPermission extends Model
* @var bool
*/
public $timestamps = false;
/**
* List of permissions available for the API.
*
* @var array
*/
protected static $permissions = [
// Items within this block are available to non-adminitrative users.
'_user' => [
'server' => [
'list',
'view',
'power',
'command',
],
],
// All other pemissions below are administrative actions.
'server' => [
'list',
'create',
'view',
'edit-details',
'edit-container',
'edit-build',
'edit-startup',
'suspend',
'install',
'rebuild',
'delete',
],
'location' => [
'list',
],
'node' => [
'list',
'view',
'view-config',
'create',
'delete',
],
'user' => [
'list',
'view',
'create',
'edit',
'delete',
],
'service' => [
'list',
'view',
],
'option' => [
'list',
'view',
],
'pack' => [
'list',
'view',
],
];
/**
* Return permissions for API.
*
* @return array
*/
public static function permissions()
{
return self::$permissions;
}
}

View file

@ -24,6 +24,8 @@
namespace Pterodactyl\Models;
use Crypt;
use Config;
use Illuminate\Database\Eloquent\Model;
class DatabaseHost extends Model
@ -62,6 +64,26 @@ class DatabaseHost extends Model
'node_id' => 'integer',
];
/**
* Sets the database connection name with the details of the host.
*
* @param string $connection
* @return void
*/
public function setDynamicConnection($connection = 'dynamic')
{
Config::set('database.connections.' . $connection, [
'driver' => 'mysql',
'host' => $this->host,
'port' => $this->port,
'database' => 'mysql',
'username' => $this->username,
'password' => Crypt::decrypt($this->password),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
]);
}
/**
* Gets the node associated with a database host.
*

View file

@ -75,6 +75,11 @@ class Node extends Model
'daemonSFTP', 'daemonListen',
];
/**
* Fields that are searchable.
*
* @var array
*/
protected $searchable = [
'columns' => [
'nodes.name' => 10,
@ -106,8 +111,8 @@ class Node extends Model
/**
* Returns the configuration in JSON format.
*
* @param bool $pretty Wether to pretty print the JSON or not
* @return string The configration in JSON format
* @param bool $pretty
* @return string
*/
public function getConfigurationAsJson($pretty = false)
{

View file

@ -58,6 +58,80 @@ class Permission extends Model
'subuser_id' => 'integer',
];
/**
* A list of all permissions available for a user.
*
* @var array
*/
protected static $permissions = [
'power' => [
'power-start' => 's:power:start',
'power-stop' => 's:power:stop',
'power-restart' => 's:power:restart',
'power-kill' => 's:power:kill',
'send-command' => 's:command',
],
'subuser' => [
'list-subusers' => null,
'view-subuser' => null,
'edit-subuser' => null,
'create-subuser' => null,
'delete-subuser' => null,
],
'server' => [
'set-connection' => null,
'view-startup' => null,
'edit-startup' => null,
],
'sftp' => [
'view-sftp' => null,
'view-sftp-password' => null,
'reset-sftp' => 's:set-password',
],
'file' => [
'list-files' => 's:files:get',
'edit-files' => 's:files:read',
'save-files' => 's:files:post',
'move-files' => 's:files:move',
'copy-files' => 's:files:copy',
'compress-files' => 's:files:compress',
'decompress-files' => 's:files:decompress',
'create-files' => 's:files:create',
'upload-files' => 's:files:upload',
'delete-files' => 's:files:delete',
'download-files' => null,
],
'task' => [
'list-tasks' => null,
'view-task' => null,
'toggle-task' => null,
'queue-task' => null,
'create-task' => null,
'delete-task' => null,
],
'database' => [
'view-databases' => null,
'reset-db-password' => null,
],
];
/**
* Return a collection of permissions available.
*
* @param array $single
* @return \Illuminate\Support\Collection|array
*/
public static function list($single = false)
{
if ($single) {
return collect(self::$permissions)->mapWithKeys(function ($item) {
return $item;
})->all();
}
return collect(self::$permissions);
}
/**
* Find permission by permission node.
*

View file

@ -27,15 +27,15 @@ namespace Pterodactyl\Models;
use Auth;
use Cache;
use Carbon;
use Schema;
use Javascript;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Database\Eloquent\SoftDeletes;
use Nicolaslopezj\Searchable\SearchableTrait;
class Server extends Model
{
use Notifiable, SearchableTrait, SoftDeletes;
use Notifiable, SearchableTrait;
/**
* The table associated with the model.
@ -115,8 +115,11 @@ class Server extends Model
* DO NOT USE THIS TO MODIFY SERVER DETAILS OR SAVE THOSE DETAILS.
* YOU WILL OVERWRITE THE SECRET KEY AND BREAK THINGS.
*
* @param string $uuid The Short-UUID of the server to return an object about.
* @return \Illuminate\Database\Eloquent\Collection
* @param string $uuid
* @param array $with
* @param array $withCount
* @return \Pterodactyl\Models\Server
* @todo Remove $with and $withCount due to cache issues, they aren't used anyways.
*/
public static function byUuid($uuid, array $with = [], array $withCount = [])
{
@ -147,30 +150,39 @@ class Server extends Model
/**
* Returns non-administrative headers for accessing a server on the daemon.
*
* @param string $uuid
* @param Pterodactyl\Models\User|null $user
* @return array
*/
public function guzzleHeaders()
public function guzzleHeaders(User $user = null)
{
// If no specific user is passed, see if we can find an active
// auth session to pull data from.
if (is_null($user) && Auth::check()) {
$user = Auth::user();
}
return [
'X-Access-Server' => $this->uuid,
'X-Access-Token' => Auth::user()->daemonToken($this),
'X-Access-Token' => ($user) ? $user->daemonToken($this) : $this->daemonSecret,
];
}
/**
* Return an instance of the Guzzle client for this specific server using defined access token.
*
* @param Pterodactyl\Models\User|null $user
* @return \GuzzleHttp\Client
*/
public function guzzleClient()
public function guzzleClient(User $user = null)
{
return $this->node->guzzleClient($this->guzzleHeaders());
return $this->node->guzzleClient($this->guzzleHeaders($user));
}
/**
* Returns javascript object to be embedded on server view pages with relevant information.
*
* @param array|null $additional
* @param array|null $overwrite
* @return \Laracasts\Utilities\JavaScript\JavaScriptFacade
*/
public function js($additional = null, $overwrite = null)
@ -200,6 +212,16 @@ class Server extends Model
return Javascript::put($response);
}
/**
* Return the columns available for this table.
*
* @return array
*/
public function getTableColumns()
{
return Schema::getColumnListing($this->getTable());
}
/**
* Gets the user who owns the server.
*
@ -298,7 +320,7 @@ class Server extends Model
*/
public function tasks()
{
return $this->hasMany(Task::class, 'server', 'id');
return $this->hasMany(Task::class);
}
/**
@ -310,4 +332,24 @@ class Server extends Model
{
return $this->hasMany(Database::class);
}
/**
* Gets all downloads associated with a server.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function downloads()
{
return $this->hasMany(Download::class, 'server', 'id');
}
/**
* Gets the location of the server.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function location()
{
return $this->node->location();
}
}

View file

@ -42,6 +42,7 @@ class ServiceOption extends Model
*/
protected $guarded = ['id', 'created_at', 'updated_at'];
<<<<<<< HEAD
/**
* Cast values to correct type.
*
@ -51,6 +52,16 @@ class ServiceOption extends Model
'service_id' => 'integer',
'script_is_privileged' => 'boolean',
];
=======
/**
* Cast values to correct type.
*
* @var array
*/
protected $casts = [
'service_id' => 'integer',
];
>>>>>>> develop
/**
* Returns the display startup string for the option and will use the parent

View file

@ -57,7 +57,7 @@ class ServiceVariable extends Model
* Returns the display executable for the option and will use the parent
* service one if the option does not have one defined.
*
* @return string
* @return bool
*/
public function getRequiredAttribute($value)
{

View file

@ -49,8 +49,10 @@ class Task extends Model
*/
protected $casts = [
'id' => 'integer',
'server' => 'integer',
'queued' => 'integer',
'user_id' => 'integer',
'server_id' => 'integer',
'queued' => 'boolean',
'active' => 'boolean',
];
/**
@ -59,4 +61,24 @@ class Task extends Model
* @var array
*/
protected $dates = ['last_run', 'next_run', 'created_at', 'updated_at'];
/**
* Gets the server associated with a task.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function server()
{
return $this->belongsTo(Server::class);
}
/**
* Gets the user associated with a task.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
}

View file

@ -103,10 +103,12 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
],
];
protected $query;
/**
* Enables or disables TOTP on an account if the token is valid.
*
* @param int $token The token that we want to verify.
* @param int $token
* @return bool
*/
public function toggleTotp($token)
@ -116,9 +118,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
}
$this->use_totp = ! $this->use_totp;
$this->save();
return true;
return $this->save();
}
/**
@ -128,8 +129,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
* - at least one lowercase character
* - at least one number.
*
* @param string $password The raw password to set the account password to.
* @param string $regex The regex to use when validating the password. Defaults to '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})'.
* @param string $password
* @param string $regex
* @return void
*/
public function setPassword($password, $regex = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})')
@ -156,7 +157,7 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
/**
* Return true or false depending on wether the user is root admin or not.
*
* @return bool the user is root admin
* @return bool
*/
public function isRootAdmin()
{
@ -165,7 +166,8 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
/**
* Returns the user's daemon secret for a given server.
* @param Server $server \Pterodactyl\Models\Server
*
* @param \Pterodactyl\Models\Server $server
* @return null|string
*/
public function daemonToken(Server $server)
@ -174,13 +176,9 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
return $server->daemonSecret;
}
$subuser = Subuser::where('server_id', $server->id)->where('user_id', $this->id)->first();
$subuser = $this->subuserOf->where('server_id', $server->id)->first();
if (is_null($subuser)) {
return null;
}
return $subuser->daemonSecret;
return ($subuser) ? $subuser->daemonSecret : null;
}
/**
@ -191,25 +189,31 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
*/
public function serverAccessArray()
{
$union = Subuser::select('server_id')->where('user_id', $this->id);
return Server::select('id')->where('owner_id', $this->id)->union($union)->pluck('id')->all();
return Server::select('id')->where('owner_id', $this->id)->union(
Subuser::select('server_id')->where('user_id', $this->id)
)->pluck('id')->all();
}
/**
* Returns an array of all servers a user is able to access.
* Note: does not account for user admin status.
*
* @return Collection
* @param array $load
* @return \Illuiminate\Database\Eloquent\Builder
*/
public function serverAccessCollection($paginate = null, $load = ['service', 'node', 'allocation'])
public function access(...$load)
{
$query = Server::with($load);
if (count($load) > 0 && is_null($load[0])) {
$query = Server::query();
} else {
$query = Server::with(! empty($load) ? $load : ['service', 'node', 'allocation']);
}
if (! $this->isRootAdmin()) {
$query->whereIn('id', $this->serverAccessArray());
}
return (is_numeric($paginate)) ? $query->paginate($paginate) : $query->get();
return $query;
}
/**
@ -231,4 +235,14 @@ class User extends Model implements AuthenticatableContract, AuthorizableContrac
{
return $this->hasMany(Server::class, 'owner_id');
}
/**
* Return all servers that user is listed as a subuser of directly.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function subuserOf()
{
return $this->hasMany(Subuser::class);
}
}

View file

@ -36,13 +36,14 @@ class AccountCreated extends Notification implements ShouldQueue
/**
* The password reset token to send.
*
* @var string
* @var object
*/
public $user;
/**
* Create a new notification instance.
*
* @param aray $user
* @return void
*/
public function __construct(array $user)

View file

@ -33,6 +33,9 @@ class AddedToServer extends Notification implements ShouldQueue
{
use Queueable;
/**
* @var object
*/
public $server;
/**

View file

@ -33,6 +33,9 @@ class RemovedFromServer extends Notification implements ShouldQueue
{
use Queueable;
/**
* @var object
*/
public $server;
/**

View file

@ -43,6 +43,7 @@ class SendPasswordReset extends Notification implements ShouldQueue
/**
* Create a new notification instance.
*
* @param string $token
* @return void
*/
public function __construct($token)

View file

@ -33,6 +33,9 @@ class ServerCreated extends Notification implements ShouldQueue
{
use Queueable;
/**
* @var object
*/
public $server;
/**

View file

@ -25,11 +25,8 @@
namespace Pterodactyl\Observers;
use Cache;
use Carbon;
use Pterodactyl\Events;
use Pterodactyl\Models\Server;
use Pterodactyl\Jobs\DeleteServer;
use Pterodactyl\Jobs\SuspendServer;
use Pterodactyl\Notifications\ServerCreated;
use Illuminate\Foundation\Bus\DispatchesJobs;
@ -40,7 +37,7 @@ class ServerObserver
/**
* Listen to the Server creating event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function creating(Server $server)
@ -51,7 +48,7 @@ class ServerObserver
/**
* Listen to the Server created event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function created(Server $server)
@ -72,37 +69,29 @@ class ServerObserver
/**
* Listen to the Server deleting event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function deleting(Server $server)
{
event(new Events\Server\Deleting($server));
$this->dispatch((new SuspendServer($server->id))->onQueue(env('QUEUE_HIGH', 'high')));
}
/**
* Listen to the Server deleted event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function deleted(Server $server)
{
event(new Events\Server\Deleted($server));
$this->dispatch(
(new DeleteServer($server->id))
->delay(Carbon::now()->addMinutes(env('APP_DELETE_MINUTES', 10)))
->onQueue(env('QUEUE_STANDARD', 'standard'))
);
}
/**
* Listen to the Server saving event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function saving(Server $server)
@ -113,7 +102,7 @@ class ServerObserver
/**
* Listen to the Server saved event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function saved(Server $server)
@ -124,7 +113,7 @@ class ServerObserver
/**
* Listen to the Server updating event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function updating(Server $server)
@ -135,7 +124,7 @@ class ServerObserver
/**
* Listen to the Server saved event.
*
* @param Server $server The server model.
* @param \Pterodactyl\Models\Server $server
* @return void
*/
public function updated(Server $server)

View file

@ -34,7 +34,7 @@ class SubuserObserver
/**
* Listen to the Subuser creating event.
*
* @param Subuser $subuser The eloquent Subuser model.
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function creating(Subuser $subuser)
@ -45,7 +45,7 @@ class SubuserObserver
/**
* Listen to the Subuser created event.
*
* @param Subuser $subuser The eloquent Subuser model.
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function created(Subuser $subuser)
@ -62,7 +62,7 @@ class SubuserObserver
/**
* Listen to the Subuser deleting event.
*
* @param Subuser $subuser The eloquent Subuser model.
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function deleting(Subuser $subuser)
@ -73,7 +73,7 @@ class SubuserObserver
/**
* Listen to the Subuser deleted event.
*
* @param Subuser $subuser The eloquent Subuser model.
* @param \Pterodactyl\Models\Subuser $subuser
* @return void
*/
public function deleted(Subuser $subuser)

Some files were not shown because too many files have changed in this diff Show more