Initial Commit of Files

PufferPanel v0.9 (Laravel) is now Pterodactyl 1.0
This commit is contained in:
Dane Everitt 2015-12-06 13:58:49 -05:00
commit 1489f7a694
154 changed files with 10159 additions and 0 deletions

View file

@ -0,0 +1,83 @@
<?php
namespace Pterodactyl\Http\Controllers\API;
use Gate;
use Log;
use Debugbar;
use Pterodactyl\Models\API;
use Pterodactyl\Models\User;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class UserController extends Controller
{
/**
* Constructor
*/
public function __construct()
{
$this->middleware('api');
}
public function getAllUsers(Request $request)
{
// Policies don't work if the user isn't logged in for whatever reason in Laravel...
if(!API::checkPermission($request->header('X-Authorization'), 'get-users')) {
return API::noPermissionError();
}
return response()->json([
'users' => User::all()
]);
}
/**
* Returns JSON response about a user given their ID.
* If fields are provided only those fields are returned.
*
* Does not return protected fields (i.e. password & totp_secret)
*
* @param Request $request
* @param int $id
* @param string $fields
* @return Response
*/
public function getUser(Request $request, $id, $fields = null)
{
// Policies don't work if the user isn't logged in for whatever reason in Laravel...
if(!API::checkPermission($request->header('X-Authorization'), 'get-users')) {
return API::noPermissionError();
}
if (is_null($fields)) {
return response()->json(User::find($id));
}
$query = User::where('id', $id);
$explode = explode(',', $fields);
foreach($explode as &$exploded) {
if(!empty($exploded)) {
$query->addSelect($exploded);
}
}
try {
return response()->json($query->get());
} catch (\Exception $e) {
if ($e instanceof \Illuminate\Database\QueryException) {
return response()->json([
'error' => 'One of the fields provided in your argument list is invalid.'
], 500);
}
throw $e;
}
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Debugbar;
use Pterodactyl\Models\User;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class AccountsController extends Controller
{
/**
* Controller Constructor
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
$this->middleware('admin');
}
public function getIndex(Request $request)
{
return view('admin.accounts.index', [
'users' => User::paginate(20)
]);
}
public function getNew(Request $request)
{
//
}
public function getView(Request $request, $id)
{
//
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Debugbar;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class BaseController extends Controller
{
/**
* Controller Constructor
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
$this->middleware('admin');
}
public function getIndex(Request $request)
{
return view('admin.index');
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Pterodactyl\Http\Controllers\Admin;
use Debugbar;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Node;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ServersController extends Controller
{
/**
* Controller Constructor
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
$this->middleware('admin');
}
public function getIndex(Request $request)
{
return view('admin.servers.index', [
'servers' => Server::select('servers.*', 'nodes.name as a_nodeName', 'users.email as a_ownerEmail')
->join('nodes', 'servers.node', '=', 'nodes.id')
->join('users', 'servers.owner', '=', 'users.id')
->paginate(20),
]);
}
public function getNew(Request $request)
{
//
}
public function getView(Request $request, $id)
{
//
}
}

View file

@ -0,0 +1,97 @@
<?php
namespace Pterodactyl\Http\Controllers\Auth;
use Pterodactyl\Models\User;
use Validator;
use Auth;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Foundation\Auth\AuthenticatesAndRegistersUsers;
class AuthController extends Controller
{
/*
|--------------------------------------------------------------------------
| Registration & Login Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
/**
* Post-Authentication redirect location.
*
* @var string
*/
protected $redirectPath = '/';
/**
* Failed post-authentication redirect location.
*
* @var string
*/
protected $loginPath = '/auth/login';
/**
* Lockout time for failed login requests.
*
* @var integer
*/
protected $lockoutTime = 120;
/**
* After how many attempts should logins be throttled and locked.
*
* @var integer
*/
protected $maxLoginAttempts = 5;
/**
* Create a new authentication controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest', ['except' => 'getLogout']);
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:8',
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => password_hash($data['password']),
]);
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace Pterodactyl\Http\Controllers\Auth;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class PasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Create a new password controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
}

View file

@ -0,0 +1,222 @@
<?php
namespace Pterodactyl\Http\Controllers\Base;
use Auth;
use Debugbar;
use Google2FA;
use Log;
use Alert;
use Pterodactyl\Exceptions\AccountNotFoundException;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Models\User;
use Pterodactyl\Models\Server;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class IndexController extends Controller
{
/**
* Controller Constructor
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
}
/**
* Returns listing of user's servers.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getIndex(Request $request)
{
return view('base.index', [
'servers' => Server::getUserServers(),
]);
}
/**
* Returns TOTP Management Page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getAccountTotp(Request $request)
{
return view('base.totp');
}
/**
* Generates TOTP Secret and returns popup data for user to verify
* that they can generate a valid response.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function putAccountTotp(Request $request)
{
try {
$totpSecret = User::setTotpSecret(Auth::user()->id);
} catch (\Exception $e) {
if ($e instanceof AccountNotFoundException) {
return response($e->getMessage(), 500);
}
throw $e;
}
return response()->json([
'qrImage' => Google2FA::getQRCodeGoogleUrl(
'Pterodactyl',
Auth::user()->email,
$totpSecret
),
'secret' => $totpSecret
]);
}
/**
* Verifies that 2FA token recieved is valid and will work on the account.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function postAccountTotp(Request $request)
{
if (!$request->has('token')) {
return response('No input \'token\' defined.', 500);
}
try {
if(User::toggleTotp(Auth::user()->id, $request->input('token'))) {
return response('true');
}
return response('false');
} catch (\Exception $e) {
if ($e instanceof AccountNotFoundException) {
return response($e->getMessage(), 500);
}
throw $e;
}
}
/**
* Disables TOTP on an account.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function deleteAccountTotp(Request $request)
{
if (!$request->has('token')) {
Alert::danger('Missing required `token` field in request.')->flash();
return redirect()->route('account.totp');
}
try {
if(User::toggleTotp(Auth::user()->id, $request->input('token'))) {
return redirect()->route('account.totp');
}
Alert::danger('Unable to disable TOTP on this account, was the token correct?')->flash();
return redirect()->route('account.totp');
} catch (\Exception $e) {
if ($e instanceof AccountNotFoundException) {
Alert::danger('An error occured while attempting to perform this action.')->flash();
return redirect()->route('account.totp');
}
throw $e;
}
}
/**
* Display base account information page.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getAccount(Request $request)
{
return view('base.account');
}
/**
* Update an account email.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function postAccountEmail(Request $request)
{
$this->validate($request, [
'new_email' => 'required|email',
'password' => 'required'
]);
if (!password_verify($request->input('password'), Auth::user()->password)) {
Alert::danger('The password provided was not valid for this account.')->flash();
return redirect()->route('account');
}
// Met Validation, lets roll out.
try {
User::setEmail(Auth::user()->id, $request->input('new_email'));
Alert::success('Your email address has successfully been updated.')->flash();
return redirect()->route('account');
} catch (\Exception $e) {
if ($e instanceof AccountNotFoundException || $e instanceof DisplayException) {
Alert::danger($e->getMessage())->flash();
return redirect()->route('account');
}
throw $e;
}
}
/**
* Update an account password.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function postAccountPassword(Request $request)
{
$this->validate($request, [
'current_password' => 'required',
'new_password' => 'required|confirmed|different:current_password|regex:((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})',
'new_password_confirmation' => 'required'
]);
if (!password_verify($request->input('current_password'), Auth::user()->password)) {
Alert::danger('The password provided was not valid for this account.')->flash();
return redirect()->route('account');
}
// Met Validation, lets roll out.
try {
User::setPassword(Auth::user()->id, $request->input('new_password'));
Alert::success('Your password has successfully been updated.')->flash();
return redirect()->route('account');
} catch (\Exception $e) {
if ($e instanceof AccountNotFoundException || $e instanceof DisplayException) {
Alert::danger($e->getMessage())->flash();
return redirect()->route('account');
}
throw $e;
}
}
}

View file

@ -0,0 +1,13 @@
<?php
namespace Pterodactyl\Http\Controllers;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
abstract class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

View file

@ -0,0 +1,193 @@
<?php
namespace Pterodactyl\Http\Controllers\Scales;
use \Exception;
use Log;
use Debugbar;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Node;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Helpers;
use Pterodactyl\Http\Controllers\Controller;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class FileController extends Controller
{
/**
* The Eloquent Model associated with the requested server.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $server;
/**
* The Eloquent Model for the node corresponding with the requested server.
*
* @var \Illuminate\Database\Eloquent\Model
*/
protected $node;
/**
* The Guzzle Client associated with the requested server and node.
*
* @var \GuzzleHttp\Client
*/
protected $client;
/**
* The Guzzle Client headers associated with the requested server and node.
* (non-administrative headers)
*
* @var array
*/
protected $headers;
/**
* Constructor
*
* @param string $server The server Short UUID
*/
public function __construct($uuid)
{
$this->server = Server::getByUUID($uuid);
$this->node = Node::getByID($this->server->node);
$this->client = Node::guzzleRequest($this->server->node);
$this->headers = Server::getGuzzleHeaders($uuid);
}
/**
* Get the contents of a requested file for the server.
*
* @param string $file
* @return string
*/
public function returnFileContents($file)
{
if (empty($file)) {
throw new Exception('Not all parameters were properly passed to the function.');
}
$file = (object) pathinfo($file);
if (!in_array($file->extension, Helpers::editableFiles())) {
throw new DisplayException('You do not have permission to edit this type of file.');
}
$file->dirname = (in_array($file->dirname, ['.', './', '/'])) ? null : trim($file->dirname, '/') . '/';
$res = $this->client->request('GET', '/server/file/' . rawurlencode($file->dirname.$file->basename), [
'headers' => $this->headers
]);
$json = json_decode($res->getBody());
if($res->getStatusCode() !== 200 || !isset($json->contents)) {
throw new DisplayException('Scales provided a non-200 error code: HTTP\\' . $res->getStatusCode());
}
return $json;
}
/**
* Save the contents of a requested file on the Scales instance.
*
* @param string $file
* @param string $content
* @return boolean
*/
public function saveFileContents($file, $content)
{
if (empty($file)) {
throw new Exception('A valid file and path must be specified to save a file.');
}
$file = (object) pathinfo($file);
if(!in_array($file->extension, Helpers::editableFiles())) {
throw new DisplayException('You do not have permission to edit this type of file.');
}
$file->dirname = (in_array($file->dirname, ['.', './', '/'])) ? null : trim($file->dirname, '/') . '/';
$res = $this->client->request('PUT', '/server/file/' . rawurlencode($file->dirname.$file->basename), [
'headers' => $this->headers,
'form_params' => [
'contents' => $content
]
]);
if ($res->getStatusCode() !== 204) {
throw new DisplayException('An error occured while attempting to save this file. Scales said: ' . $res->getBody());
}
return true;
}
/**
* Returns a listing of all files and folders within a specified Scales directory.
*
* @param string $directory
* @return object
*/
public function returnDirectoryListing($directory)
{
if (empty($directory)) {
throw new Exception('A valid directory must be specified in order to list its contents.');
}
$res = $this->client->request('GET', '/server/directory/' . $directory, [
'headers' => $this->headers
]);
$json = json_decode($res->getBody());
if($res->getStatusCode() !== 200) {
throw new DisplayException('An error occured while attempting to save this file. Scales said: ' . $res->getBody());
}
// Iterate through results
$files = [];
$folders = [];
foreach($json as &$value) {
if ($value->file !== true) {
// @TODO Handle Symlinks
$folders = array_merge($folders, [[
'entry' => $value->name,
'directory' => trim($directory, '/'),
'size' => null,
'date' => strtotime($value->modified)
]]);
} else {
$files = array_merge($files, [[
'entry' => $value->name,
'directory' => trim($directory, '/'),
'extension' => pathinfo($value->name, PATHINFO_EXTENSION),
'size' => Helpers::bytesToHuman($value->size),
'date' => strtotime($value->modified)
]]);
}
}
return (object) [
'files' => $files,
'folders' => $folders,
];
}
}

View file

@ -0,0 +1,182 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Log;
use Debugbar;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Node;
use Pterodactyl\Http\Helpers;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Scales\FileController;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
class AjaxController extends Controller
{
/**
* @var array
*/
protected $files = [];
/**
* @var array
*/
protected $folders = [];
/**
* @var string
*/
protected $directory;
/**
* Controller Constructor
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
// Routes in this file are also checked aganist the server middleware. If the user
// does not have permission to view the server it will not load.
$this->middleware('server');
}
/**
* 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
*/
public function getStatus(Request $request, $uuid)
{
$server = Server::getByUUID($uuid);
$client = Node::guzzleRequest($server->node);
try {
$res = $client->request('GET', '/server', [
'headers' => Server::getGuzzleHeaders($uuid)
]);
if($res->getStatusCode() === 200) {
$json = json_decode($res->getBody());
if (isset($json->status) && $json->status === 1) {
return 'true';
}
}
} catch (RequestException $e) {
Debugbar::error($e->getMessage());
Log::notice('An exception was raised while attempting to contact a Scales instance to get server status information.', [
'exception' => $e->getMessage(),
'path' => $request->path()
]);
}
return 'false';
}
/**
* 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
*/
public function postDirectoryList(Request $request, $uuid)
{
$server = Server::getByUUID($uuid);
$this->directory = '/' . trim(urldecode($request->input('directory', '/')), '/');
$this->authorize('list-files', $server);
$prevDir = [
'header' => ($this->directory !== '/') ? $this->directory : ''
];
if ($this->directory !== '/') {
$prevDir['first'] = true;
}
// Determine if we should show back links in the file browser.
// This code is strange, and could probably be rewritten much better.
$goBack = explode('/', rtrim($this->directory, '/'));
if (isset($goBack[2]) && !empty($goBack[2])) {
$prevDir['show'] = true;
$prevDir['link'] = '/' . trim(str_replace(end($goBack), '', $this->directory), '/');
$prevDir['link_show'] = trim($prevDir['link'], '/');
}
$controller = new FileController($uuid);
try {
$directoryContents = $controller->returnDirectoryListing($this->directory);
} catch (\Exception $e) {
Debugbar::addException($e);
$exception = 'An error occured while attempting to load the requested directory, please try again.';
if ($e instanceof DisplayException) {
$exception = $e->getMessage();
}
return response($exception, 500);
}
return view('server.files.list', [
'server' => $server,
'files' => $directoryContents->files,
'folders' => $directoryContents->folders,
'extensions' => Helpers::editableFiles(),
'directory' => $prevDir
]);
}
/**
* Handles a POST request to save a file.
*
* @param Request $request
* @param string $uuid
* @return \Illuminate\Http\Response
*/
public function postSaveFile(Request $request, $uuid)
{
$server = Server::getByUUID($uuid);
$this->authorize('save-files', $server);
$controller = new FileController($uuid);
try {
$controller->saveFileContents($request->input('file'), $request->input('contents'));
return response(null, 204);
} catch (\Exception $e) {
Debugbar::addException($e);
$exception = 'An error occured while attempting to save that file, please try again.';
if ($e instanceof DisplayException) {
$exception = $e->getMessage();
}
return response($exception, 500);
}
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace Pterodactyl\Http\Controllers\Server;
use Auth;
use Pterodactyl\Models\Server;
use Pterodactyl\Models\Node;
use Pterodactyl\Models\Download;
use Debugbar;
use Uuid;
use Alert;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Http\Controllers\Scales\FileController;
use Pterodactyl\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ServerController extends Controller
{
/**
* Controller Constructor
*
* @return void
*/
public function __construct()
{
// All routes in this controller are protected by the authentication middleware.
$this->middleware('auth');
// Routes in this file are also checked aganist the server middleware. If the user
// does not have permission to view the server it will not load.
$this->middleware('server');
}
/**
* Renders server index page for specified server.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getIndex(Request $request)
{
$server = Server::getByUUID($request->route()->server);
return view('server.index', [
'server' => $server,
'node' => Node::find($server->node)
]);
}
/**
* Renders file overview page.
*
* @param Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getFiles(Request $request)
{
$server = Server::getByUUID($request->route()->server);
$this->authorize('list-files', $server);
return view('server.files.index', [
'server' => $server,
'node' => Node::find($server->node)
]);
}
/**
* Renders add file page.
*
* @param Request $request
* @return \Illuminate\Contracts\View\View
*/
public function getAddFile(Request $request)
{
$server = Server::getByUUID($request->route()->server);
$this->authorize('add-files', $server);
return view('server.files.add', [
'server' => $server,
'node' => Node::find($server->node),
'directory' => (in_array($request->get('dir'), [null, '/', ''])) ? '' : trim($request->get('dir'), '/') . '/'
]);
}
/**
* Renders edit file page for a given file.
*
* @param Request $request
* @param string $uuid
* @param string $file
* @return \Illuminate\Contracts\View\View
*/
public function getEditFile(Request $request, $uuid, $file)
{
$server = Server::getByUUID($uuid);
$this->authorize('edit-files', $server);
$fileInfo = (object) pathinfo($file);
$controller = new FileController($uuid);
try {
$fileContent = $controller->returnFileContents($file);
} catch (\Exception $e) {
Debugbar::addException($e);
$exception = 'An error occured while attempting to load the requested file for editing, please try again.';
if ($e instanceof DisplayException) {
$exception = $e->getMessage();
}
Alert::danger($exception)->flash();
return redirect()->route('files.index', $uuid);
}
return view('server.files.edit', [
'server' => $server,
'node' => Node::find($server->node),
'file' => $file,
'contents' => $fileContent->contents,
'directory' => (in_array($fileInfo->dirname, ['.', './', '/'])) ? '/' : trim($fileInfo->dirname, '/') . '/',
'extension' => $fileInfo->extension
]);
}
/**
* Handles downloading a file for the user.
*
* @param Request $request
* @param string $uuid
* @param string $file
* @return \Illuminate\Contracts\View\View
*/
public function getDownloadFile(Request $request, $uuid, $file)
{
$server = Server::getByUUID($uuid);
$node = Node::find($server->node);
$this->authorize('download-files', $server);
$download = new Download;
$download->token = Uuid::generate(4);
$download->server = $server->id;
$download->path = str_replace('../', '', $file);
$download->save();
return redirect('https://' . $node->fqdn . ':' . $node->daemonListen . '/server/download/' . $download->token);
}
}

58
app/Http/Helpers.php Normal file
View file

@ -0,0 +1,58 @@
<?php
namespace Pterodactyl\Http;
class Helpers {
/**
* Listing of editable files in the control panel.
* @var array
*/
protected static $editable = [
'txt',
'yml',
'yaml',
'log',
'conf',
'config',
'html',
'json',
'properties',
'props',
'cfg',
'lang',
'ini',
'cmd',
'sh',
'lua',
'0' // Supports BungeeCord Files
];
public function __construct()
{
//
}
/**
* Converts from bytes to the largest possible size that is still readable.
*
* @param int $bytes
* @param int $decimals
* @return string
*/
public static function bytesToHuman($bytes, $decimals = 2)
{
$sz = explode(',', 'B,KB,MB,GB');
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)).' '.$sz[$factor];
}
public static function editableFiles()
{
return self::$editable;
}
}

36
app/Http/Kernel.php Normal file
View file

@ -0,0 +1,36 @@
<?php
namespace Pterodactyl\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* @var array
*/
protected $middleware = [
\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\VerifyCsrfToken::class,
];
/**
* The application's route middleware.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \Pterodactyl\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \Pterodactyl\Http\Middleware\RedirectIfAuthenticated::class,
'server' => \Pterodactyl\Http\MiddleWare\CheckServer::class,
'api' => \Pterodactyl\Http\MiddleWare\APIAuthenticate::class,
'admin' => \Pterodactyl\Http\Middleware\AdminAuthenticate::class,
];
}

View file

@ -0,0 +1,46 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Debugbar;
use Pterodactyl\Models\API;
class APIAuthenticate
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if(!$request->header('X-Authorization')) {
return response()->json([
'error' => 'Authorization header was missing with this request. Please pass the \'X-Authorization\' header with your request.'
], 403);
}
$api = API::where('key', $request->header('X-Authorization'))->first();
if (!$api) {
return response()->json([
'error' => 'Invalid API key was provided in the request.'
], 403);
}
if (!is_null($api->allowed_ips)) {
if (!in_array($request->ip(), json_decode($api->allowed_ips, true))) {
return response()->json([
'error' => 'This IP (' . $request->ip() . ') is not permitted to access the API with that token.'
], 403);
}
}
return $next($request);
}
}

View file

@ -0,0 +1,51 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class AdminAuthenticate
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
if($this->auth->user()->root_admin !== 1) {
return abort(403);
}
return $next($request);
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
return $next($request);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Auth;
use Pterodactyl\Models\Server;
use Debugbar;
class CheckServer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if (!Auth::user()) {
return redirect()->guest('auth/login');
}
if (!Server::getByUUID($request->route()->server)) {
return redirect('/');
}
return $next($request);
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;
class EncryptCookies extends BaseEncrypter
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
//
];
}

View file

@ -0,0 +1,43 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class RedirectIfAuthenticated
{
/**
* The Guard implementation.
*
* @var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* @param Guard $auth
* @return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->check()) {
return redirect('/home');
}
return $next($request);
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace Pterodactyl\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
//
];
}

View file

@ -0,0 +1,10 @@
<?php
namespace Pterodactyl\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest
{
//
}

View file

@ -0,0 +1,30 @@
<?php
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class AdminRoutes {
public function map(Router $router) {
$router->group(['prefix' => 'admin'], function ($server) use ($router) {
$router->get('/', [ 'as' => 'admin.index', 'uses' => 'Admin\BaseController@getIndex' ]);
// Account Routes
$router->group(['prefix' => 'accounts'], function ($server) use ($router) {
$router->get('/', [ 'as' => 'admin.accounts', 'uses' => 'Admin\AccountsController@getIndex' ]);
$router->get('/new', [ 'as' => 'admin.accounts.new', 'uses' => 'Admin\AccountsController@getNew' ]);
$router->get('/view/{id}', [ 'as' => 'admin.accounts.view', 'uses' => 'Admin\AccountsController@getView' ]);
});
// Server Routes
$router->group(['prefix' => 'servers'], function ($server) use ($router) {
$router->get('/', [ 'as' => 'admin.servers', 'uses' => 'Admin\ServersController@getIndex' ]);
$router->get('/new', [ 'as' => 'admin.servers.new', 'uses' => 'Admin\ServersController@getNew' ]);
$router->get('/view/{id}', [ 'as' => 'admin.servers.view', 'uses' => 'Admin\ServersController@getView' ]);
});
});
}
}

View file

@ -0,0 +1,18 @@
<?php
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class AuthRoutes {
public function map(Router $router) {
$router->group(['prefix' => 'auth'], function () use ($router) {
$router->get('login', [ 'as' => 'auth.login', 'uses' => 'Auth\AuthController@getLogin' ]);
$router->post('login', [ 'as' => 'auth.login.submit', 'uses' => 'Auth\AuthController@postLogin' ]);
$router->get('logout', [ 'as' => 'auth.logout', 'uses' => 'Auth\AuthController@getLogout' ]);
});
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class BaseRoutes {
public function map(Router $router) {
// Handle Index. Redirect /index to /
$router->get('/', [ 'as' => 'index', 'uses' => 'Base\IndexController@getIndex' ]);
$router->get('/index', function () {
return redirect()->route('index');
});
// Account Routes
$router->get('/account', [ 'as' => 'account', 'uses' => 'Base\IndexController@getAccount' ]);
$router->post('/account/password', [ 'uses' => 'Base\IndexController@postAccountPassword' ]);
$router->post('/account/email', [ 'uses' => 'Base\IndexController@postAccountEmail' ]);
// TOTP Routes
$router->get('/account/totp', [ 'as' => 'account.totp', 'uses' => 'Base\IndexController@getAccountTotp' ]);
$router->put('/account/totp', [ 'uses' => 'Base\IndexController@putAccountTotp' ]);
$router->post('/account/totp', [ 'uses' => 'Base\IndexController@postAccountTotp' ]);
$router->delete('/account/totp', [ 'uses' => 'Base\IndexController@deleteAccountTotp' ]);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class RestRoutes {
public function map(Router $router) {
$router->group(['prefix' => 'api/v1'], function ($server) use ($router) {
$router->group(['prefix' => 'users'], function ($server) use ($router) {
$router->get('/', [ 'uses' => 'API\UserController@getAllUsers' ]);
$router->get('/{id}/{fields?}', [ 'uses' => 'API\UserController@getUser' ])->where('id', '[0-9]+');
});
});
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace Pterodactyl\Http\Routes;
use Illuminate\Routing\Router;
class ServerRoutes {
public function map(Router $router) {
$router->group(['prefix' => 'server/{server}'], function ($server) use ($router) {
$router->get('/', [ 'as' => 'server.index', 'uses' => 'Server\ServerController@getIndex' ]);
$router->get('/files', [ 'as' => 'files.index', 'uses' => 'Server\ServerController@getFiles' ]);
$router->get('/files/edit/{file}', [ 'as' => 'files.edit', 'uses' => 'Server\ServerController@getEditFile' ])->where('file', '.*');
$router->get('/files/download/{file}', [ 'as' => 'files.download', 'uses' => 'Server\ServerController@getDownloadFile' ])->where('file', '.*');
$router->get('/files/add', [ 'as' => 'files.add', 'uses' => 'Server\ServerController@getAddFile' ]);
// Ajax Routes
$router->group(['prefix' => 'ajax'], function ($server) use ($router) {
$router->get('status', [ 'uses' => 'Server\AjaxController@getStatus' ]);
$router->post('files/directory-list', [ 'uses' => 'Server\AjaxController@postDirectoryList' ]);
$router->post('files/save', [ 'uses' => 'Server\AjaxController@postSaveFile' ]);
});
});
}
}