Initial Commit of Files
PufferPanel v0.9 (Laravel) is now Pterodactyl 1.0
This commit is contained in:
commit
1489f7a694
154 changed files with 10159 additions and 0 deletions
33
app/Console/Commands/Inspire.php
Normal file
33
app/Console/Commands/Inspire.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Foundation\Inspiring;
|
||||
|
||||
class Inspire extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'inspire';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Display an inspiring quote';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->comment(PHP_EOL.Inspiring::quote().PHP_EOL);
|
||||
}
|
||||
}
|
30
app/Console/Kernel.php
Normal file
30
app/Console/Kernel.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
\Pterodactyl\Console\Commands\Inspire::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
$schedule->command('inspire')
|
||||
->hourly();
|
||||
}
|
||||
}
|
8
app/Events/Event.php
Normal file
8
app/Events/Event.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Events;
|
||||
|
||||
abstract class Event
|
||||
{
|
||||
//
|
||||
}
|
8
app/Exceptions/AccountNotFoundException.php
Normal file
8
app/Exceptions/AccountNotFoundException.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions;
|
||||
|
||||
class AccountNotFoundException extends \Exception
|
||||
{
|
||||
|
||||
}
|
8
app/Exceptions/DisplayException.php
Normal file
8
app/Exceptions/DisplayException.php
Normal file
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions;
|
||||
|
||||
class DisplayException extends \Exception
|
||||
{
|
||||
|
||||
}
|
78
app/Exceptions/Handler.php
Normal file
78
app/Exceptions/Handler.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use DisplayException;
|
||||
use Debugbar;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Exception\HttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that should not be reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
HttpException::class,
|
||||
ModelNotFoundException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $e)
|
||||
{
|
||||
return parent::report($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $e
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $e)
|
||||
{
|
||||
if ($e instanceof ModelNotFoundException) {
|
||||
$e = new NotFoundHttpException($e->getMessage(), $e);
|
||||
}
|
||||
|
||||
if ($request->isXmlHttpRequest() || $request->ajax() || $request->is('/api/*')) {
|
||||
|
||||
$exception = 'An exception occured while attempting to perform this action, please try again.';
|
||||
|
||||
if ($e instanceof DisplayException) {
|
||||
$exception = $e->getMessage();
|
||||
}
|
||||
|
||||
// Live environment, just return a nice error.
|
||||
if(!env('APP_DEBUG', false)) {
|
||||
return response()->json([
|
||||
'error' => $exception
|
||||
], 500);
|
||||
}
|
||||
|
||||
// If we are debugging, return the exception in it's full manner.
|
||||
return response()->json([
|
||||
'error' => $e->getMessage(),
|
||||
'code' => $e->getCode(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine()
|
||||
], 500);
|
||||
|
||||
}
|
||||
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
83
app/Http/Controllers/API/UserController.php
Normal file
83
app/Http/Controllers/API/UserController.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
43
app/Http/Controllers/Admin/AccountsController.php
Normal file
43
app/Http/Controllers/Admin/AccountsController.php
Normal 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)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
}
|
30
app/Http/Controllers/Admin/BaseController.php
Normal file
30
app/Http/Controllers/Admin/BaseController.php
Normal 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');
|
||||
}
|
||||
|
||||
}
|
47
app/Http/Controllers/Admin/ServersController.php
Normal file
47
app/Http/Controllers/Admin/ServersController.php
Normal 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)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
}
|
97
app/Http/Controllers/Auth/AuthController.php
Normal file
97
app/Http/Controllers/Auth/AuthController.php
Normal 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']),
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
32
app/Http/Controllers/Auth/PasswordController.php
Normal file
32
app/Http/Controllers/Auth/PasswordController.php
Normal 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');
|
||||
}
|
||||
}
|
222
app/Http/Controllers/Base/IndexController.php
Normal file
222
app/Http/Controllers/Base/IndexController.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
13
app/Http/Controllers/Controller.php
Normal file
13
app/Http/Controllers/Controller.php
Normal 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;
|
||||
}
|
193
app/Http/Controllers/Scales/FileController.php
Normal file
193
app/Http/Controllers/Scales/FileController.php
Normal 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,
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
}
|
182
app/Http/Controllers/Server/AjaxController.php
Normal file
182
app/Http/Controllers/Server/AjaxController.php
Normal 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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
162
app/Http/Controllers/Server/ServerController.php
Normal file
162
app/Http/Controllers/Server/ServerController.php
Normal 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
58
app/Http/Helpers.php
Normal 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
36
app/Http/Kernel.php
Normal 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,
|
||||
];
|
||||
}
|
46
app/Http/Middleware/APIAuthenticate.php
Normal file
46
app/Http/Middleware/APIAuthenticate.php
Normal 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);
|
||||
|
||||
}
|
||||
}
|
51
app/Http/Middleware/AdminAuthenticate.php
Normal file
51
app/Http/Middleware/AdminAuthenticate.php
Normal 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);
|
||||
}
|
||||
}
|
47
app/Http/Middleware/Authenticate.php
Normal file
47
app/Http/Middleware/Authenticate.php
Normal 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);
|
||||
}
|
||||
}
|
33
app/Http/Middleware/CheckServer.php
Normal file
33
app/Http/Middleware/CheckServer.php
Normal 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);
|
||||
|
||||
}
|
||||
}
|
17
app/Http/Middleware/EncryptCookies.php
Normal file
17
app/Http/Middleware/EncryptCookies.php
Normal 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 = [
|
||||
//
|
||||
];
|
||||
}
|
43
app/Http/Middleware/RedirectIfAuthenticated.php
Normal file
43
app/Http/Middleware/RedirectIfAuthenticated.php
Normal 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);
|
||||
}
|
||||
}
|
17
app/Http/Middleware/VerifyCsrfToken.php
Normal file
17
app/Http/Middleware/VerifyCsrfToken.php
Normal 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 = [
|
||||
//
|
||||
];
|
||||
}
|
10
app/Http/Requests/Request.php
Normal file
10
app/Http/Requests/Request.php
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
abstract class Request extends FormRequest
|
||||
{
|
||||
//
|
||||
}
|
30
app/Http/Routes/AdminRoutes.php
Normal file
30
app/Http/Routes/AdminRoutes.php
Normal 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' ]);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
18
app/Http/Routes/AuthRoutes.php
Normal file
18
app/Http/Routes/AuthRoutes.php
Normal 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' ]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
30
app/Http/Routes/BaseRoutes.php
Normal file
30
app/Http/Routes/BaseRoutes.php
Normal 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' ]);
|
||||
|
||||
}
|
||||
|
||||
}
|
22
app/Http/Routes/RestRoutes.php
Normal file
22
app/Http/Routes/RestRoutes.php
Normal 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]+');
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
27
app/Http/Routes/ServerRoutes.php
Normal file
27
app/Http/Routes/ServerRoutes.php
Normal 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' ]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
21
app/Jobs/Job.php
Normal file
21
app/Jobs/Job.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
|
||||
abstract class Job
|
||||
{
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queueable Jobs
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This job base class provides a central location to place any logic that
|
||||
| is shared across all of your jobs. The trait included with the class
|
||||
| provides access to the "onQueue" and "delay" queue helper methods.
|
||||
|
|
||||
*/
|
||||
|
||||
use Queueable;
|
||||
}
|
0
app/Listeners/.gitkeep
Normal file
0
app/Listeners/.gitkeep
Normal file
63
app/Models/API.php
Normal file
63
app/Models/API.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Log;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Models\APIPermission;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class API extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'api';
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['daemonSecret'];
|
||||
|
||||
public function permissions()
|
||||
{
|
||||
return $this->hasMany(APIPermission::class);
|
||||
}
|
||||
|
||||
public static function findKey($key)
|
||||
{
|
||||
return self::where('key', $key)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an API key has permission to perform an action.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $permission
|
||||
* @return boolean
|
||||
*/
|
||||
public static function checkPermission($key, $permission)
|
||||
{
|
||||
$api = self::findKey($key);
|
||||
|
||||
if (!$api) {
|
||||
throw new DisplayException('The requested API key (' . $key . ') was not found in the system.');
|
||||
}
|
||||
|
||||
return APIPermission::check($api->id, $permission);
|
||||
|
||||
}
|
||||
|
||||
public static function noPermissionError($error = 'You do not have permission to perform this action with this API key.')
|
||||
{
|
||||
return response()->json([
|
||||
'error' => 'You do not have permission to perform this action with this API key.'
|
||||
], 403);
|
||||
}
|
||||
|
||||
}
|
30
app/Models/APIPermission.php
Normal file
30
app/Models/APIPermission.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Debugbar;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class APIPermission extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'api_permissions';
|
||||
|
||||
/**
|
||||
* Checks if an API key has a specific permission.
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $permission
|
||||
* @return boolean
|
||||
*/
|
||||
public static function check($id, $permission)
|
||||
{
|
||||
return self::where('key_id', $id)->where('permission', $permission)->exists();
|
||||
}
|
||||
|
||||
}
|
18
app/Models/Download.php
Normal file
18
app/Models/Download.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Debugbar;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Download extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'downloads';
|
||||
|
||||
}
|
82
app/Models/Node.php
Normal file
82
app/Models/Node.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Node extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'nodes';
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['daemonSecret'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $guzzle = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $nodes = [];
|
||||
|
||||
/**
|
||||
* Returns an instance of the database object for the requested node ID.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
*/
|
||||
public static function getByID($id)
|
||||
{
|
||||
|
||||
// The Node is already cached.
|
||||
if (array_key_exists($id, self::$nodes)) {
|
||||
return self::$nodes[$id];
|
||||
}
|
||||
|
||||
self::$nodes[$id] = Node::where('id', $id)->first();
|
||||
return self::$nodes[$id];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Guzzle Client for the node in question.
|
||||
*
|
||||
* @param int $node
|
||||
* @return \GuzzleHttp\Client
|
||||
*/
|
||||
public static function guzzleRequest($node)
|
||||
{
|
||||
|
||||
// The Guzzle Client is cached already.
|
||||
if (array_key_exists($node, self::$guzzle)) {
|
||||
return self::$guzzle[$node];
|
||||
}
|
||||
|
||||
$nodeData = self::getByID($node);
|
||||
|
||||
// @TODO: Better solution to disabling verification. Security risk.
|
||||
self::$guzzle[$node] = new Client([
|
||||
'base_uri' => sprintf('https://%s:%s/', $nodeData->fqdn, $nodeData->daemonListen),
|
||||
'timeout' => 10.0,
|
||||
'connect_timeout' => 5.0,
|
||||
'verify' => false,
|
||||
]);
|
||||
|
||||
return self::$guzzle[$node];
|
||||
|
||||
}
|
||||
|
||||
}
|
27
app/Models/Permission.php
Normal file
27
app/Models/Permission.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Permission extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'permissions';
|
||||
|
||||
public function scopePermission($query, $permission)
|
||||
{
|
||||
return $query->where('permission', $permission);
|
||||
}
|
||||
|
||||
public function scopeServer($query, $server)
|
||||
{
|
||||
return $query->where('server_id', $server->id);
|
||||
}
|
||||
|
||||
}
|
141
app/Models/Server.php
Normal file
141
app/Models/Server.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Auth;
|
||||
use Pterodactyl\Models\Permission;
|
||||
use Pterodactyl\Models\Subuser;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Server extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'servers';
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['daemonSecret'];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $serverUUIDInstance = [];
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected static $user;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::$user = Auth::user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we need to change the server's daemonSecret value to
|
||||
* match that of the user if they are a subuser.
|
||||
*
|
||||
* @param Illuminate\Database\Eloquent\Model\Server $server
|
||||
* @return string
|
||||
*/
|
||||
protected static function getUserDaemonSecret(Server $server)
|
||||
{
|
||||
|
||||
if (self::$user->id === $server->owner || self::$user->root_admin === 1) {
|
||||
return $server->daemonSecret;
|
||||
}
|
||||
|
||||
$subuser = Subuser::where('server_id', $server->id)->where('user_id', self::$user->id)->first();
|
||||
|
||||
if (is_null($subuser)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $subuser->daemonSecret;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of all servers owned by the logged in user.
|
||||
* Returns all active servers if user is a root admin.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getUserServers()
|
||||
{
|
||||
|
||||
$query = self::select('servers.*', 'nodes.name as nodeName', 'locations.long as location')
|
||||
->join('nodes', 'servers.node', '=', 'nodes.id')
|
||||
->join('locations', 'nodes.location', '=', 'locations.id')
|
||||
->where('active', 1);
|
||||
|
||||
if (self::$user->root_admin !== 1) {
|
||||
$query->whereIn('servers.id', Subuser::accessServers());
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a single server specified by UUID
|
||||
*
|
||||
* @param string $uuid The Short-UUID of the server to return an object about.
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getByUUID($uuid)
|
||||
{
|
||||
|
||||
if (array_key_exists($uuid, self::$serverUUIDInstance)) {
|
||||
return self::$serverUUIDInstance[$uuid];
|
||||
}
|
||||
|
||||
$query = self::where('uuidShort', $uuid)->where('active', 1);
|
||||
|
||||
if (self::$user->root_admin !== 1) {
|
||||
$query->whereIn('servers.id', Subuser::accessServers());
|
||||
}
|
||||
|
||||
$result = $query->first();
|
||||
|
||||
if(!is_null($result)) {
|
||||
$result->daemonSecret = self::getUserDaemonSecret($result);
|
||||
}
|
||||
|
||||
self::$serverUUIDInstance[$uuid] = $result;
|
||||
return self::$serverUUIDInstance[$uuid];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-administrative headers for accessing a server on Scales
|
||||
*
|
||||
* @param string $uuid
|
||||
* @return array
|
||||
*/
|
||||
public static function getGuzzleHeaders($uuid)
|
||||
{
|
||||
|
||||
if (array_key_exists($uuid, self::$serverUUIDInstance)) {
|
||||
return [
|
||||
'X-Access-Server' => self::$serverUUIDInstance[$uuid]->uuid,
|
||||
'X-Access-Token' => self::$serverUUIDInstance[$uuid]->daemonSecret
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
|
||||
}
|
||||
|
||||
}
|
52
app/Models/Subuser.php
Normal file
52
app/Models/Subuser.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Auth;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Subuser extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'subusers';
|
||||
|
||||
/**
|
||||
* @var mixed
|
||||
*/
|
||||
protected static $user;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
self::$user = Auth::user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of each server ID that the user has access to.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function accessServers()
|
||||
{
|
||||
|
||||
$access = [];
|
||||
|
||||
$union = self::select('server_id')->where('user_id', self::$user->id);
|
||||
$select = Server::select('id')->where('owner', self::$user->id)->union($union)->get();
|
||||
|
||||
foreach($select as &$select) {
|
||||
$access = array_merge($access, [ $select->id ]);
|
||||
}
|
||||
|
||||
return $access;
|
||||
|
||||
}
|
||||
|
||||
}
|
157
app/Models/User.php
Normal file
157
app/Models/User.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Models;
|
||||
|
||||
use Google2FA;
|
||||
use Pterodactyl\Exceptions\AccountNotFoundException;
|
||||
use Pterodactyl\Exceptions\DisplayException;
|
||||
use Pterodactyl\Models\Permission;
|
||||
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Foundation\Auth\Access\Authorizable;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
|
||||
class User extends Model implements AuthenticatableContract,
|
||||
AuthorizableContract,
|
||||
CanResetPasswordContract
|
||||
{
|
||||
use Authenticatable, Authorizable, CanResetPassword;
|
||||
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['name', 'email', 'password'];
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $hidden = ['password', 'remember_token', 'totp_secret'];
|
||||
|
||||
public function permissions()
|
||||
{
|
||||
return $this->hasMany(Permission::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the TOTP secret for an account.
|
||||
*
|
||||
* @param int $id Account ID for which we want to generate a TOTP secret
|
||||
* @return string
|
||||
*/
|
||||
public static function setTotpSecret($id)
|
||||
{
|
||||
|
||||
$totpSecretKey = Google2FA::generateSecretKey();
|
||||
|
||||
$user = User::find($id);
|
||||
|
||||
if (!$user) {
|
||||
throw new AccountNotFoundException('An account with that ID (' . $id . ') does not exist in the system.');
|
||||
}
|
||||
|
||||
$user->totp_secret = $totpSecretKey;
|
||||
$user->save();
|
||||
|
||||
return $totpSecretKey;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables TOTP on an account if the token is valid.
|
||||
*
|
||||
* @param int $id Account ID for which we want to generate a TOTP secret
|
||||
* @return boolean
|
||||
*/
|
||||
public static function toggleTotp($id, $token)
|
||||
{
|
||||
|
||||
$user = User::find($id);
|
||||
|
||||
if (!$user) {
|
||||
throw new AccountNotFoundException('An account with that ID (' . $id . ') does not exist in the system.');
|
||||
}
|
||||
|
||||
if (!Google2FA::verifyKey($user->totp_secret, $token)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$user->use_totp = ($user->use_totp === 1) ? 0 : 1;
|
||||
$user->save();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user password to a new value assuming it meets the following requirements:
|
||||
* - 8 or more characters in length
|
||||
* - at least one uppercase character
|
||||
* - at least one lowercase character
|
||||
* - at least one number
|
||||
*
|
||||
* @param int $id The ID of the account to update the password on.
|
||||
* @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,})'.
|
||||
* @return void
|
||||
*/
|
||||
public static function setPassword($id, $password, $regex = '((?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,})')
|
||||
{
|
||||
|
||||
$user = User::find($id);
|
||||
if (!$user) {
|
||||
throw new AccountNotFoundException('An account with that ID (' . $id . ') does not exist in the system.');
|
||||
}
|
||||
|
||||
if (!preg_match($regex, $password)) {
|
||||
throw new DisplayException('The password passed did not meet the minimum password requirements.');
|
||||
}
|
||||
|
||||
$user->password = password_hash($password, PASSWORD_BCRYPT);
|
||||
$user->save();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the email address for an account.
|
||||
*
|
||||
* @param int $id
|
||||
* @param string $email
|
||||
* @return void
|
||||
*/
|
||||
public static function setEmail($id, $email)
|
||||
{
|
||||
|
||||
$user = User::find($id);
|
||||
if (!$user) {
|
||||
throw new AccountNotFoundException('An account with that ID (' . $id . ') does not exist in the system.');
|
||||
}
|
||||
|
||||
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
throw new DisplayException('The email provided (' . $email . ') was not valid.');
|
||||
}
|
||||
|
||||
$user->email = $email;
|
||||
$user->save();
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
}
|
0
app/Policies/.gitkeep
Normal file
0
app/Policies/.gitkeep
Normal file
178
app/Policies/ServerPolicy.php
Normal file
178
app/Policies/ServerPolicy.php
Normal file
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Policies;
|
||||
|
||||
use Log;
|
||||
use Debugbar;
|
||||
use Pterodactyl\Models\User;
|
||||
use Pterodactyl\Models\Server;
|
||||
|
||||
class ServerPolicy
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a new policy instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if current user is the owner of a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
protected function isOwner(User $user, Server $server)
|
||||
{
|
||||
return $server->owner === $user->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs before any of the functions are called. Used to determine if user is root admin, if so, ignore permissions.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param string $ability
|
||||
* @return boolean
|
||||
*/
|
||||
public function before(User $user, $ability)
|
||||
{
|
||||
if ($user->root_admin === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to control power for a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function power(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('power')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to run a command on a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function command(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('command')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to list files on a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function listFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('list-files')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to edit files on a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function editFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('edit-files')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to save files on a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function saveFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('save-files')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to add files to a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function addFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('add-files')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to upload files to a server.
|
||||
* This permission relies on the user having the 'add-files' permission as well due to page authorization.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function uploadFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('upload-files')->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has permission to download files from a server.
|
||||
*
|
||||
* @param Pterodactyl\Models\User $user
|
||||
* @param Pterodactyl\Models\Server $server
|
||||
* @return boolean
|
||||
*/
|
||||
public function downloadFiles(User $user, Server $server)
|
||||
{
|
||||
if ($this->isOwner($user, $server)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $user->permissions()->server($server)->permission('download-files')->exists();
|
||||
}
|
||||
|
||||
}
|
28
app/Providers/AppServiceProvider.php
Normal file
28
app/Providers/AppServiceProvider.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
35
app/Providers/AuthServiceProvider.php
Normal file
35
app/Providers/AuthServiceProvider.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Pterodactyl\Models\Server;
|
||||
use Pterodactyl\Policies\ServerPolicy;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'Pterodactyl\Model' => 'Pterodactyl\Policies\ModelPolicy',
|
||||
Server::class => ServerPolicy::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any application authentication / authorization services.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Access\Gate $gate
|
||||
* @return void
|
||||
*/
|
||||
public function boot(GateContract $gate)
|
||||
{
|
||||
parent::registerPolicies($gate);
|
||||
|
||||
//
|
||||
}
|
||||
}
|
33
app/Providers/EventServiceProvider.php
Normal file
33
app/Providers/EventServiceProvider.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Contracts\Events\Dispatcher as DispatcherContract;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'Pterodactyl\Events\SomeEvent' => [
|
||||
'Pterodactyl\Listeners\EventListener',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any other events for your application.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Events\Dispatcher $events
|
||||
* @return void
|
||||
*/
|
||||
public function boot(DispatcherContract $events)
|
||||
{
|
||||
parent::boot($events);
|
||||
|
||||
//
|
||||
}
|
||||
}
|
46
app/Providers/RouteServiceProvider.php
Normal file
46
app/Providers/RouteServiceProvider.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Pterodactyl\Providers;
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* This namespace is applied to the controller routes in your routes file.
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace = 'Pterodactyl\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function boot(Router $router)
|
||||
{
|
||||
//
|
||||
|
||||
parent::boot($router);
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @param \Illuminate\Routing\Router $router
|
||||
* @return void
|
||||
*/
|
||||
public function map(Router $router)
|
||||
{
|
||||
$router->group(['namespace' => $this->namespace], function ($router) {
|
||||
foreach (glob(app_path('Http//Routes') . '/*.php') as $file) {
|
||||
$this->app->make('Pterodactyl\\Http\\Routes\\' . basename($file, '.php'))->map($router);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue