forked from Alex/Pterodactyl-Panel
Fix session management on client API requests; closes #3727
Versions of Pterodactyl prior to 1.6.3 used a different throttle pathway for requests. That pathway found the current request user before continuing on to other in-app middleware, thus the user was available downstream. Changes introduced in 1.6.3 changed the throttler logic, therefore removing this step. As a result, the client API could not always get the currently authenticated user when cookies were used (aka, requests from the Panel UI, and not API directly). This change corrects the logic to get the session setup correctly before falling through to authenticating as a user using the API key. If a cookie is present and a user is found as a result that session will be used. If an API key is provided it is ignored when a cookie is also present. In order to keep the API stateless any session created for an API request stemming from an API key will have the associated session deleted at the end of the request, and the 'Set-Cookies' header will be stripped from the response.
This commit is contained in:
parent
d0663dcbd4
commit
60eff40a0c
@ -3,6 +3,10 @@ This file is a running track of new features and fixes to each version of the pa
|
|||||||
|
|
||||||
This project follows [Semantic Versioning](http://semver.org) guidelines.
|
This project follows [Semantic Versioning](http://semver.org) guidelines.
|
||||||
|
|
||||||
|
## v1.6.4
|
||||||
|
### Fixed
|
||||||
|
* Fixes a session management bug that would cause a user who signs out of one browser to be unintentionally logged out of other browser sessions when using the client API.
|
||||||
|
|
||||||
## v1.6.3
|
## v1.6.3
|
||||||
### Fixed
|
### Fixed
|
||||||
* **[Security]** Changes logout endpoint to be a POST request with CSRF-token validation to prevent a malicious actor from triggering a user logout.
|
* **[Security]** Changes logout endpoint to be a POST request with CSRF-token validation to prevent a malicious actor from triggering a user logout.
|
||||||
|
@ -18,7 +18,6 @@ use Pterodactyl\Http\Middleware\LanguageMiddleware;
|
|||||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||||
use Pterodactyl\Http\Middleware\Api\AuthenticateKey;
|
use Pterodactyl\Http\Middleware\Api\AuthenticateKey;
|
||||||
use Illuminate\Routing\Middleware\SubstituteBindings;
|
use Illuminate\Routing\Middleware\SubstituteBindings;
|
||||||
use Pterodactyl\Http\Middleware\Api\SetSessionDriver;
|
|
||||||
use Illuminate\Session\Middleware\AuthenticateSession;
|
use Illuminate\Session\Middleware\AuthenticateSession;
|
||||||
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
use Illuminate\View\Middleware\ShareErrorsFromSession;
|
||||||
use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
|
use Pterodactyl\Http\Middleware\MaintenanceMiddleware;
|
||||||
@ -27,6 +26,7 @@ use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth;
|
|||||||
use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess;
|
use Pterodactyl\Http\Middleware\Api\AuthenticateIPAccess;
|
||||||
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
|
use Pterodactyl\Http\Middleware\Api\ApiSubstituteBindings;
|
||||||
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
use Illuminate\Foundation\Http\Middleware\ValidatePostSize;
|
||||||
|
use Pterodactyl\Http\Middleware\Api\HandleStatelessRequest;
|
||||||
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
|
||||||
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
|
use Pterodactyl\Http\Middleware\Api\Daemon\DaemonAuthenticate;
|
||||||
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
use Pterodactyl\Http\Middleware\RequireTwoFactorAuthentication;
|
||||||
@ -68,18 +68,18 @@ class Kernel extends HttpKernel
|
|||||||
RequireTwoFactorAuthentication::class,
|
RequireTwoFactorAuthentication::class,
|
||||||
],
|
],
|
||||||
'api' => [
|
'api' => [
|
||||||
|
HandleStatelessRequest::class,
|
||||||
IsValidJson::class,
|
IsValidJson::class,
|
||||||
ApiSubstituteBindings::class,
|
ApiSubstituteBindings::class,
|
||||||
SetSessionDriver::class,
|
|
||||||
'api..key:' . ApiKey::TYPE_APPLICATION,
|
'api..key:' . ApiKey::TYPE_APPLICATION,
|
||||||
AuthenticateApplicationUser::class,
|
AuthenticateApplicationUser::class,
|
||||||
AuthenticateIPAccess::class,
|
AuthenticateIPAccess::class,
|
||||||
],
|
],
|
||||||
'client-api' => [
|
'client-api' => [
|
||||||
StartSession::class,
|
HandleStatelessRequest::class,
|
||||||
SetSessionDriver::class,
|
|
||||||
AuthenticateSession::class,
|
|
||||||
IsValidJson::class,
|
IsValidJson::class,
|
||||||
|
StartSession::class,
|
||||||
|
AuthenticateSession::class,
|
||||||
SubstituteClientApiBindings::class,
|
SubstituteClientApiBindings::class,
|
||||||
'api..key:' . ApiKey::TYPE_ACCOUNT,
|
'api..key:' . ApiKey::TYPE_ACCOUNT,
|
||||||
AuthenticateIPAccess::class,
|
AuthenticateIPAccess::class,
|
||||||
|
@ -42,8 +42,10 @@ class AuthenticateKey
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle an API request by verifying that the provided API key
|
* Handle an API request by verifying that the provided API key is in a valid
|
||||||
* is in a valid format and exists in the database.
|
* format and exists in the database. If there is currently a user in the session
|
||||||
|
* do not even bother to look at the token (they provided a cookie for this to
|
||||||
|
* be the case).
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
*
|
*
|
||||||
@ -56,17 +58,17 @@ class AuthenticateKey
|
|||||||
throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']);
|
throw new HttpException(401, null, null, ['WWW-Authenticate' => 'Bearer']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$raw = $request->bearerToken();
|
// This is a request coming through using cookies, we have an authenticated user
|
||||||
|
// not using an API key. Make some fake API key models and continue on through
|
||||||
// This is a request coming through using cookies, we have an authenticated user not using
|
// the process.
|
||||||
// an API key. Make some fake API key models and continue on through the process.
|
if ($request->user() instanceof User) {
|
||||||
if (empty($raw) && $request->user() instanceof User) {
|
|
||||||
$model = (new ApiKey())->forceFill([
|
$model = (new ApiKey())->forceFill([
|
||||||
'user_id' => $request->user()->id,
|
'user_id' => $request->user()->id,
|
||||||
'key_type' => ApiKey::TYPE_ACCOUNT,
|
'key_type' => ApiKey::TYPE_ACCOUNT,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$model = $this->authenticateApiKey($raw, $keyType);
|
$model = $this->authenticateApiKey($request->bearerToken(), $keyType);
|
||||||
|
|
||||||
$this->auth->guard()->loginUsingId($model->user_id);
|
$this->auth->guard()->loginUsingId($model->user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
35
app/Http/Middleware/Api/HandleStatelessRequest.php
Normal file
35
app/Http/Middleware/Api/HandleStatelessRequest.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Middleware\Api;
|
||||||
|
|
||||||
|
use Closure;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
|
class HandleStatelessRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Ensure that the 'Set-Cookie' header is removed from the response if
|
||||||
|
* a bearer token is present and there is an api_key in the request attributes.
|
||||||
|
*
|
||||||
|
* This will also delete the session from the database automatically so that
|
||||||
|
* it is effectively treated as a stateless request. Any additional requests
|
||||||
|
* attempting to use that session will find no data.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function handle(Request $request, Closure $next)
|
||||||
|
{
|
||||||
|
/** @var \Illuminate\Http\Response $response */
|
||||||
|
$response = $next($request);
|
||||||
|
|
||||||
|
if (!is_null($request->bearerToken()) && $request->isJson()) {
|
||||||
|
$request->session()->getHandler()->destroy(
|
||||||
|
$request->session()->getId()
|
||||||
|
);
|
||||||
|
|
||||||
|
$response->headers->remove('Set-Cookie');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Pterodactyl\Http\Middleware\Api;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Contracts\Config\Repository as ConfigRepository;
|
|
||||||
|
|
||||||
class SetSessionDriver
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @var \Illuminate\Contracts\Config\Repository
|
|
||||||
*/
|
|
||||||
private $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SetSessionDriver constructor.
|
|
||||||
*/
|
|
||||||
public function __construct(ConfigRepository $config)
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the session for API calls to only last for the one request.
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function handle(Request $request, Closure $next)
|
|
||||||
{
|
|
||||||
$this->config->set('session.driver', 'array');
|
|
||||||
|
|
||||||
return $next($request);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user