Update all the middlewares

This commit is contained in:
Dane Everitt 2017-10-29 12:37:25 -05:00
parent e0d03513e4
commit 79decafdc8
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
16 changed files with 161 additions and 100 deletions

View File

@ -28,6 +28,7 @@ This project follows [Semantic Versioning](http://semver.org) guidelines.
* Server creation page now only asks for a node to deploy to, rather than requiring a location and then a node.
* Database passwords are now hidden by default and will only show if clicked on. In addition, database view in ACP now indicates that passwords must be viewed on the front-end.
* Localhost cannot be used as a connection address in the environment configuration script. `127.0.0.1` is allowed.
* Application locale can now be quickly set using an environment variable `APP_LOCALE` rather than having to edit core files.
### Fixed
* Unable to change the daemon secret for a server via the Admin CP.

View File

@ -6,6 +6,7 @@ use Pterodactyl\Http\Middleware\DaemonAuthenticate;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Pterodactyl\Http\Middleware\AccessingValidServer;
use Pterodactyl\Http\Middleware\Server\AuthenticateAsSubuser;
use Pterodactyl\Http\Middleware\Server\SubuserBelongsToServer;
use Pterodactyl\Http\Middleware\Server\DatabaseBelongsToServer;
use Pterodactyl\Http\Middleware\Server\ScheduleBelongsToServer;
@ -66,7 +67,7 @@ class Kernel extends HttpKernel
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \Pterodactyl\Http\Middleware\RedirectIfAuthenticated::class,
'server' => AccessingValidServer::class,
'subuser.auth' => \Pterodactyl\Http\Middleware\SubuserAccessAuthenticate::class,
'subuser.auth' => AuthenticateAsSubuser::class,
'admin' => \Pterodactyl\Http\Middleware\AdminAuthenticate::class,
'daemon-old' => DaemonAuthenticate::class,
'csrf' => \Pterodactyl\Http\Middleware\VerifyCsrfToken::class,

View File

@ -10,6 +10,8 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
class AdminAuthenticate
{
@ -20,18 +22,10 @@ class AdminAuthenticate
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if (! $request->user()) {
if ($request->expectsJson() || $request->json()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('auth/login');
}
}
if (! $request->user()->root_admin) {
return abort(403);
if (! $request->user() || ! $request->user()->root_admin) {
throw new HttpException(403, 'Access Denied');
}
return $next($request);

View File

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
@ -31,7 +32,7 @@ class Authenticate
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {

View File

@ -33,9 +33,12 @@ use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
class DaemonAuthenticate
{
/**
* Daemon routes that this middleware should be skipped on.
* @var array
*/
protected $except = ['daemon.configuration'];
protected $except = [
'daemon.configuration',
];
/**
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
@ -63,6 +66,10 @@ class DaemonAuthenticate
*/
public function handle(Request $request, Closure $next)
{
if (in_array($request->route()->getName(), $this->except)) {
return $next($request);
}
$token = $request->bearerToken();
if (is_null($token)) {

View File

@ -10,35 +10,36 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Models\Node;
use Illuminate\Contracts\Auth\Guard;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Pterodactyl\Contracts\Repository\NodeRepositoryInterface;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
class DaemonAuthenticate
{
/**
* The Guard implementation.
*
* @var \Illuminate\Contracts\Auth\Guard
*/
protected $auth;
/**
* An array of route names to not apply this middleware to.
*
* @var array
*/
protected $except = [
private $except = [
'daemon.configuration',
];
/**
* @var \Pterodactyl\Contracts\Repository\NodeRepositoryInterface
*/
private $repository;
/**
* Create a new filter instance.
*
* @param \Illuminate\Contracts\Auth\Guard $auth
* @param \Pterodactyl\Contracts\Repository\NodeRepositoryInterface $repository
*/
public function __construct(Guard $auth)
public function __construct(NodeRepositoryInterface $repository)
{
$this->auth = $auth;
$this->repository = $repository;
}
/**
@ -48,21 +49,24 @@ class DaemonAuthenticate
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
if (in_array($request->route()->getName(), $this->except)) {
return $next($request);
}
if (! $request->header('X-Access-Node')) {
return abort(403);
throw new HttpException(403);
}
$node = Node::where('daemonSecret', $request->header('X-Access-Node'))->first();
if (! $node) {
return abort(401);
try {
$node = $this->repository->findWhere(['daemonSecret' => $request->header('X-Access-Node')]);
} catch (RecordNotFoundException $exception) {
throw new HttpException(401);
}
$request->attributes->set('node', $node);
return $next($request);
}
}

View File

@ -11,6 +11,5 @@ class EncryptCookies extends BaseEncrypter
*
* @var array
*/
protected $except = [
];
protected $except = [];
}

View File

@ -9,14 +9,28 @@
namespace Pterodactyl\Http\Middleware;
use Auth;
use Closure;
use Session;
use Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
use Illuminate\Contracts\Config\Repository;
class LanguageMiddleware
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
private $config;
/**
* LanguageMiddleware constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
*/
public function __construct(Repository $config)
{
$this->config = $config;
}
/**
* Handle an incoming request.
*
@ -24,17 +38,9 @@ class LanguageMiddleware
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
// if (Session::has('applocale')) {
// App::setLocale(Session::get('applocale'));
// } elseif (Auth::check() && isset(Auth::user()->language)) {
// Session::put('applocale', Auth::user()->language);
// App::setLocale(Auth::user()->language);
// } else {
// App::setLocale(Settings::get('default_language', 'en'));
// }
App::setLocale('en');
App::setLocale($this->config->get('app.locale', 'en'));
return $next($request);
}

View File

@ -3,10 +3,26 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Auth\AuthManager;
class RedirectIfAuthenticated
{
/**
* @var \Illuminate\Contracts\Auth\Guard
*/
private $authManager;
/**
* RedirectIfAuthenticated constructor.
*
* @param \Illuminate\Auth\AuthManager $authManager
*/
public function __construct(AuthManager $authManager)
{
$this->authManager = $authManager;
}
/**
* Handle an incoming request.
*
@ -15,9 +31,9 @@ class RedirectIfAuthenticated
* @param string|null $guard
* @return mixed
*/
public function handle($request, Closure $next, $guard = null)
public function handle(Request $request, Closure $next, string $guard = null)
{
if (Auth::guard($guard)->check()) {
if ($this->authManager->guard($guard)->check()) {
return redirect(route('index'));
}

View File

@ -10,6 +10,7 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Krucas\Settings\Settings;
use Prologue\Alerts\AlertsMessageBag;
@ -22,28 +23,35 @@ class RequireTwoFactorAuthentication
/**
* @var \Prologue\Alerts\AlertsMessageBag
*/
protected $alert;
private $alert;
/**
* @var \Krucas\Settings\Settings
*/
protected $settings;
private $settings;
/**
* All TOTP related routes.
* The names of routes that should be accessable without 2FA enabled.
*
* @var array
*/
protected $ignoreRoutes = [
'account.security',
'account.security.revoke',
'account.security.totp',
'account.security.totp.set',
'account.security.totp.disable',
'auth.totp',
'auth.logout',
protected $except = [
'account.security',
'account.security.revoke',
'account.security.totp',
'account.security.totp.set',
'account.security.totp.disable',
'auth.totp',
'auth.logout',
];
/**
* The route to redirect a user to to enable 2FA.
*
* @var string
*/
protected $redirectRoute = 'account.security';
/**
* RequireTwoFactorAuthentication constructor.
*
@ -63,7 +71,7 @@ class RequireTwoFactorAuthentication
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
// Ignore non-users
if (! $request->user()) {
@ -71,7 +79,7 @@ class RequireTwoFactorAuthentication
}
// Skip the 2FA pages
if (in_array($request->route()->getName(), $this->ignoreRoutes)) {
if (in_array($request->route()->getName(), $this->except)) {
return $next($request);
}
@ -93,8 +101,8 @@ class RequireTwoFactorAuthentication
break;
}
$this->alert->danger('The administrator has required 2FA to be enabled. You must enable it before you can do any other action.')->flash();
$this->alert->danger(trans('auth.2fa_must_be_enabled'))->flash();
return redirect()->route('account.security');
return redirect()->route($this->redirectRoute);
}
}

View File

@ -7,7 +7,7 @@
* https://opensource.org/licenses/MIT
*/
namespace Pterodactyl\Http\Middleware;
namespace Pterodactyl\Http\Middleware\Server;
use Closure;
use Illuminate\Http\Request;
@ -16,17 +16,17 @@ use Illuminate\Auth\AuthenticationException;
use Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService;
use Pterodactyl\Exceptions\Repository\RecordNotFoundException;
class SubuserAccessAuthenticate
class AuthenticateAsSubuser
{
/**
* @var \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService
*/
protected $keyProviderService;
private $keyProviderService;
/**
* @var \Illuminate\Contracts\Session\Session
*/
protected $session;
private $session;
/**
* SubuserAccessAuthenticate constructor.
@ -34,10 +34,8 @@ class SubuserAccessAuthenticate
* @param \Pterodactyl\Services\DaemonKeys\DaemonKeyProviderService $keyProviderService
* @param \Illuminate\Contracts\Session\Session $session
*/
public function __construct(
DaemonKeyProviderService $keyProviderService,
Session $session
) {
public function __construct(DaemonKeyProviderService $keyProviderService, Session $session)
{
$this->keyProviderService = $keyProviderService;
$this->session = $session;
}
@ -55,16 +53,17 @@ class SubuserAccessAuthenticate
*/
public function handle(Request $request, Closure $next)
{
$server = $this->session->get('server_data.model');
$server = $request->attributes->get('server');
try {
$token = $this->keyProviderService->handle($server->id, $request->user()->id);
$this->session->now('server_data.token', $token);
$request->attributes->set('server_token', $token);
} catch (RecordNotFoundException $exception) {
throw new AuthenticationException('This account does not have permission to access this server.');
}
$this->session->now('server_data.token', $token);
$request->attributes->set('server_token', $token);
return $next($request);
}
}

View File

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Middleware\Server;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
use Pterodactyl\Contracts\Repository\ScheduleRepositoryInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -41,7 +42,7 @@ class ScheduleBelongsToServer
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
$server = $request->attributes->get('server');

View File

@ -3,6 +3,7 @@
namespace Pterodactyl\Http\Middleware\Server;
use Closure;
use Illuminate\Http\Request;
use Pterodactyl\Exceptions\DisplayException;
use Pterodactyl\Contracts\Extensions\HashidsInterface;
use Pterodactyl\Contracts\Repository\SubuserRepositoryInterface;
@ -43,7 +44,7 @@ class SubuserBelongsToServer
* @throws \Pterodactyl\Exceptions\Repository\RecordNotFoundException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function handle($request, Closure $next)
public function handle(Request $request, Closure $next)
{
$server = $request->attributes->get('server');

View File

@ -3,28 +3,46 @@
namespace Pterodactyl\Http\Middleware;
use Closure;
use GuzzleHttp\Client;
use Illuminate\Http\Request;
use Pterodactyl\Events\Auth\FailedCaptcha;
use Illuminate\Contracts\Config\Repository;
class VerifyReCaptcha
{
/**
* @var \Illuminate\Contracts\Config\Repository
*/
private $config;
/**
* VerifyReCaptcha constructor.
*
* @param \Illuminate\Contracts\Config\Repository $config
*/
public function __construct(Repository $config)
{
$this->config = $config;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return \Illuminate\Http\RediectResponse
* @return \Illuminate\Http\RedirectResponse|mixed
*/
public function handle($request, Closure $next)
{
if (! config('recaptcha.enabled')) {
if (! $this->config->get('recaptcha.enabled')) {
return $next($request);
}
if ($request->has('g-recaptcha-response')) {
$client = new \GuzzleHttp\Client();
$res = $client->post(config('recaptcha.domain'), [
$client = new Client();
$res = $client->post($this->config->get('recaptcha.domain'), [
'form_params' => [
'secret' => config('recaptcha.secret_key'),
'secret' => $this->config->get('recaptcha.secret_key'),
'response' => $request->input('g-recaptcha-response'),
],
]);
@ -32,29 +50,33 @@ class VerifyReCaptcha
if ($res->getStatusCode() === 200) {
$result = json_decode($res->getBody());
$verified = function ($result, $request) {
if (! config('recaptcha.verify_domain')) {
return false;
}
$url = parse_url($request->url());
if (! array_key_exists('host', $url)) {
return false;
}
return $result->hostname === $url['host'];
};
if ($result->success && (! config('recaptcha.verify_domain') || $verified($result, $request))) {
if ($result->success && (! $this->config->get('recaptcha.verify_domain') || $this->isResponseVerified($result, $request))) {
return $next($request);
}
}
}
// Emit an event and return to the previous view with an error (only the captcha error will be shown!)
event(new FailedCaptcha($request->ip(), (! isset($result->hostname) ?: $result->hostname)));
event(new FailedCaptcha($request->ip(), (! isset($result) ?: object_get($result, 'hostname'))));
return back()->withErrors(['g-recaptcha-response' => trans('strings.captcha_invalid')])->withInput();
return redirect()->back()->withErrors(['g-recaptcha-response' => trans('strings.captcha_invalid')])->withInput();
}
/**
* Determine if the response from the recaptcha servers was valid.
*
* @param object $result
* @param \Illuminate\Http\Request $request
* @return bool
*/
private function isResponseVerified(object $result, Request $request): bool
{
if (! $this->config->get('recaptcha.verify_domain')) {
return false;
}
$url = parse_url($request->url());
return $result->hostname === array_get($url, 'host');
}
}

View File

@ -66,7 +66,7 @@ return [
|
*/
'locale' => 'en',
'locale' => env('APP_LOCALE', 'en'),
/*
|--------------------------------------------------------------------------

View File

@ -18,4 +18,5 @@ return [
'2fa_required' => '2-Factor Authentication',
'2fa_failed' => 'The 2FA token provided was invalid.',
'totp_failed' => 'There was an error while attempting to validate TOTP.',
'2fa_must_be_enabled' => 'The administrator has required that 2-Factor Authentication be enabled for your account in order to use the Panel.',
];