Ensure tokens are found in the database using the expected logic

This commit is contained in:
DaneEveritt 2022-05-22 16:05:58 -04:00
parent e9c633fd03
commit f7fc67344e
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
5 changed files with 39 additions and 25 deletions

View file

@ -47,6 +47,18 @@ class Handler extends ExceptionHandler
ValidationException::class, ValidationException::class,
]; ];
/**
* Maps exceptions to a specific response code. This handles special exception
* types that don't have a defined response code.
*
* @var array<string, int>
*/
protected static array $exceptionResponseCodes = [
AuthenticationException::class => 401,
AuthorizationException::class => 403,
ValidationException::class => 422,
];
/** /**
* A list of the inputs that are never flashed for validation exceptions. * A list of the inputs that are never flashed for validation exceptions.
* *
@ -187,12 +199,14 @@ class Handler extends ExceptionHandler
*/ */
public static function convertToArray(Throwable $exception, array $override = []): array public static function convertToArray(Throwable $exception, array $override = []): array
{ {
$match = self::$exceptionResponseCodes[get_class($exception)] ?? null;
$error = [ $error = [
'code' => class_basename($exception), 'code' => class_basename($exception),
'status' => method_exists($exception, 'getStatusCode') 'status' => method_exists($exception, 'getStatusCode')
? strval($exception->getStatusCode()) ? strval($exception->getStatusCode())
: ($exception instanceof ValidationException ? '422' : '500'), : strval($match ?? '500'),
'detail' => $exception instanceof HttpExceptionInterface 'detail' => $exception instanceof HttpExceptionInterface || !is_null($match)
? $exception->getMessage() ? $exception->getMessage()
: 'An unexpected error was encountered while processing this request, please try again.', : 'An unexpected error was encountered while processing this request, please try again.',
]; ];

View file

@ -19,19 +19,19 @@ class AccountController extends ClientApiController
private $updateService; private $updateService;
/** /**
* @var \Illuminate\Auth\SessionGuard * @var \Illuminate\Auth\AuthManager
*/ */
private $sessionGuard; private $manager;
/** /**
* AccountController constructor. * AccountController constructor.
*/ */
public function __construct(AuthManager $sessionGuard, UserUpdateService $updateService) public function __construct(AuthManager $manager, UserUpdateService $updateService)
{ {
parent::__construct(); parent::__construct();
$this->updateService = $updateService; $this->updateService = $updateService;
$this->sessionGuard = $sessionGuard; $this->manager = $manager;
} }
public function index(Request $request): array public function index(Request $request): array
@ -64,13 +64,17 @@ class AccountController extends ClientApiController
{ {
$user = $this->updateService->handle($request->user(), $request->validated()); $user = $this->updateService->handle($request->user(), $request->validated());
$guard = $this->manager->guard();
// If you do not update the user in the session you'll end up working with a // If you do not update the user in the session you'll end up working with a
// cached copy of the user that does not include the updated password. Do this // cached copy of the user that does not include the updated password. Do this
// to correctly store the new user details in the guard and allow the logout // to correctly store the new user details in the guard and allow the logout
// other devices functionality to work. // other devices functionality to work.
$this->sessionGuard->setUser($user); $guard->setUser($user);
$this->sessionGuard->logoutOtherDevices($request->input('password')); // This method doesn't exist in the stateless Sanctum world.
if (method_exists($guard, 'logoutOtherDevices')) {
$guard->logoutOtherDevices($request->input('password'));
}
return new JsonResponse([], Response::HTTP_NO_CONTENT); return new JsonResponse([], Response::HTTP_NO_CONTENT);
} }

View file

@ -195,9 +195,13 @@ class ApiKey extends Model
public static function findToken($token) public static function findToken($token)
{ {
$id = Str::substr($token, 0, self::IDENTIFIER_LENGTH); $id = Str::substr($token, 0, self::IDENTIFIER_LENGTH);
$token = Str::substr($token, strlen($id));
return static::where('identifier', $id)->where('token', encrypt($token))->first(); $model = static::where('identifier', $id)->first();
if (!is_null($model) && decrypt($model->token) === Str::substr($token, strlen($id))) {
return $model;
}
return null;
} }
/** /**

View file

@ -25,7 +25,7 @@ class ApiKeyFactory extends Factory
return [ return [
'key_type' => ApiKey::TYPE_APPLICATION, 'key_type' => ApiKey::TYPE_APPLICATION,
'identifier' => Str::random(ApiKey::IDENTIFIER_LENGTH), 'identifier' => ApiKey::generateTokenIdentifier(),
'token' => $token ?: $token = encrypt(Str::random(ApiKey::KEY_LENGTH)), 'token' => $token ?: $token = encrypt(Str::random(ApiKey::KEY_LENGTH)),
'allowed_ips' => null, 'allowed_ips' => null,
'memo' => 'Test Function Key', 'memo' => 'Test Function Key',

View file

@ -2,8 +2,8 @@
namespace Pterodactyl\Tests\Integration\Api\Application; namespace Pterodactyl\Tests\Integration\Api\Application;
use Pterodactyl\Models\User;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Pterodactyl\Models\User;
use PHPUnit\Framework\Assert; use PHPUnit\Framework\Assert;
use Pterodactyl\Models\ApiKey; use Pterodactyl\Models\ApiKey;
use Pterodactyl\Services\Acl\Api\AdminAcl; use Pterodactyl\Services\Acl\Api\AdminAcl;
@ -41,10 +41,9 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase
$this->user = $this->createApiUser(); $this->user = $this->createApiUser();
$this->key = $this->createApiKey($this->user); $this->key = $this->createApiKey($this->user);
$this->withHeader('Accept', 'application/vnd.pterodactyl.v1+json'); $this
$this->withHeader('Authorization', 'Bearer ' . $this->getApiKey()->identifier . decrypt($this->getApiKey()->token)); ->withHeader('Accept', 'application/vnd.pterodactyl.v1+json')
->withHeader('Authorization', 'Bearer ' . $this->key->identifier . decrypt($this->key->token));
$this->withMiddleware('api..key:' . ApiKey::TYPE_APPLICATION);
} }
public function getApiUser(): User public function getApiUser(): User
@ -63,19 +62,12 @@ abstract class ApplicationApiIntegrationTestCase extends IntegrationTestCase
protected function createNewDefaultApiKey(User $user, array $permissions = []): ApiKey protected function createNewDefaultApiKey(User $user, array $permissions = []): ApiKey
{ {
$this->key = $this->createApiKey($user, $permissions); $this->key = $this->createApiKey($user, $permissions);
$this->refreshHeaders($this->key);
$this->withHeader('Authorization', 'Bearer ' . $this->key->identifier . decrypt($this->key->token));
return $this->key; return $this->key;
} }
/**
* Refresh the authorization header for a request to use a different API key.
*/
protected function refreshHeaders(ApiKey $key)
{
$this->withHeader('Authorization', 'Bearer ' . $key->identifier . decrypt($key->token));
}
/** /**
* Create an administrative user. * Create an administrative user.
*/ */