Update JWT library to support PHP 8; bumps minimum PHP version for Pterodactyl to 7.4

This commit is contained in:
Dane Everitt
2021-01-20 21:21:28 -08:00
parent 988b711ea2
commit 8dbf60de2c
4 changed files with 134 additions and 66 deletions

View File

@@ -3,12 +3,12 @@
namespace Pterodactyl\Services\Nodes; namespace Pterodactyl\Services\Nodes;
use DateTimeImmutable; use DateTimeImmutable;
use Lcobucci\JWT\Builder;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Lcobucci\JWT\Signer\Key;
use Pterodactyl\Models\Node; use Pterodactyl\Models\Node;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
class NodeJWTService class NodeJWTService
{ {
@@ -68,15 +68,15 @@ class NodeJWTService
* @param \Pterodactyl\Models\Node $node * @param \Pterodactyl\Models\Node $node
* @param string|null $identifiedBy * @param string|null $identifiedBy
* @param string $algo * @param string $algo
* @return \Lcobucci\JWT\Token * @return \Lcobucci\JWT\Token\Plain
*/ */
public function handle(Node $node, string $identifiedBy, string $algo = 'md5') public function handle(Node $node, string $identifiedBy, string $algo = 'md5')
{ {
$signer = new Sha256;
$identifier = hash($algo, $identifiedBy); $identifier = hash($algo, $identifiedBy);
$config = Configuration::forSymmetricSigner(new Sha256, InMemory::plainText($node->getDecryptedKey()));
$builder = (new Builder)->issuedBy(config('app.url')) $builder = $config->builder()
->issuedBy(config('app.url'))
->permittedFor($node->getConnectionAddress()) ->permittedFor($node->getConnectionAddress())
->identifiedBy($identifier) ->identifiedBy($identifier)
->withHeader('jti', $identifier) ->withHeader('jti', $identifier)
@@ -97,6 +97,6 @@ class NodeJWTService
return $builder return $builder
->withClaim('unique_id', Str::random(16)) ->withClaim('unique_id', Str::random(16))
->getToken($signer, new Key($node->getDecryptedKey())); ->getToken($config->signer(), $config->signingKey());
} }
} }

View File

@@ -11,7 +11,7 @@
} }
], ],
"require": { "require": {
"php": "^7.3|^8.0", "php": "^7.4 | ^8.0",
"ext-json": "*", "ext-json": "*",
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-pdo_mysql": "*", "ext-pdo_mysql": "*",
@@ -28,7 +28,7 @@
"laravel/helpers": "^1.4", "laravel/helpers": "^1.4",
"laravel/tinker": "^2.5", "laravel/tinker": "^2.5",
"laravel/ui": "^3.0", "laravel/ui": "^3.0",
"lcobucci/jwt": "^3.4", "lcobucci/jwt": "^4.0",
"league/flysystem-aws-s3-v3": "^1.0", "league/flysystem-aws-s3-v3": "^1.0",
"league/flysystem-memory": "^1.0", "league/flysystem-memory": "^1.0",
"matriphe/iso-639": "^1.2", "matriphe/iso-639": "^1.2",

144
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "f12494defb79b412566ee0f5104e3986", "content-hash": "47ce788551352ef0f38290c53bce29dd",
"packages": [ "packages": [
{ {
"name": "appstract/laravel-blade-directives", "name": "appstract/laravel-blade-directives",
@@ -1754,69 +1754,53 @@
"time": "2021-01-06T19:20:22+00:00" "time": "2021-01-06T19:20:22+00:00"
}, },
{ {
"name": "lcobucci/jwt", "name": "lcobucci/clock",
"version": "3.4.2", "version": "2.0.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/lcobucci/jwt.git", "url": "https://github.com/lcobucci/clock.git",
"reference": "17cb82dd625ccb17c74bf8f38563d3b260306483" "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/17cb82dd625ccb17c74bf8f38563d3b260306483", "url": "https://api.github.com/repos/lcobucci/clock/zipball/353d83fe2e6ae95745b16b3d911813df6a05bfb3",
"reference": "17cb82dd625ccb17c74bf8f38563d3b260306483", "reference": "353d83fe2e6ae95745b16b3d911813df6a05bfb3",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"ext-mbstring": "*", "php": "^7.4 || ^8.0"
"ext-openssl": "*",
"php": "^5.6 || ^7.0"
}, },
"require-dev": { "require-dev": {
"mikey179/vfsstream": "~1.5", "infection/infection": "^0.17",
"phpmd/phpmd": "~2.2", "lcobucci/coding-standard": "^6.0",
"phpunit/php-invoker": "~1.1", "phpstan/extension-installer": "^1.0",
"phpunit/phpunit": "^5.7 || ^7.3", "phpstan/phpstan": "^0.12",
"squizlabs/php_codesniffer": "~2.3" "phpstan/phpstan-deprecation-rules": "^0.12",
}, "phpstan/phpstan-phpunit": "^0.12",
"suggest": { "phpstan/phpstan-strict-rules": "^0.12",
"lcobucci/clock": "*" "phpunit/php-code-coverage": "9.1.4",
"phpunit/phpunit": "9.3.7"
}, },
"type": "library", "type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1-dev"
}
},
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"Lcobucci\\JWT\\": "src" "Lcobucci\\Clock\\": "src"
}, }
"files": [
"compat/class-aliases.php",
"compat/json-exception-polyfill.php",
"compat/lcobucci-clock-polyfill.php"
]
}, },
"notification-url": "https://packagist.org/downloads/", "notification-url": "https://packagist.org/downloads/",
"license": [ "license": [
"BSD-3-Clause" "MIT"
], ],
"authors": [ "authors": [
{ {
"name": "Luís Otávio Cobucci Oblonczyk", "name": "Luís Cobucci",
"email": "lcobucci@gmail.com", "email": "lcobucci@gmail.com"
"role": "Developer"
} }
], ],
"description": "A simple library to work with JSON Web Token and JSON Web Signature", "description": "Yet another clock abstraction",
"keywords": [
"JWS",
"jwt"
],
"support": { "support": {
"issues": "https://github.com/lcobucci/jwt/issues", "issues": "https://github.com/lcobucci/clock/issues",
"source": "https://github.com/lcobucci/jwt/tree/3.4.2" "source": "https://github.com/lcobucci/clock/tree/2.0.x"
}, },
"funding": [ "funding": [
{ {
@@ -1828,7 +1812,83 @@
"type": "patreon" "type": "patreon"
} }
], ],
"time": "2020-12-03T13:43:45+00:00" "time": "2020-08-27T18:56:02+00:00"
},
{
"name": "lcobucci/jwt",
"version": "4.0.0",
"source": {
"type": "git",
"url": "https://github.com/lcobucci/jwt.git",
"reference": "6d8665ccd924dc076a9b65d1ea8abe21d68f6958"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/lcobucci/jwt/zipball/6d8665ccd924dc076a9b65d1ea8abe21d68f6958",
"reference": "6d8665ccd924dc076a9b65d1ea8abe21d68f6958",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-openssl": "*",
"lcobucci/clock": "^2.0",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"infection/infection": "^0.20",
"lcobucci/coding-standard": "^6.0",
"mikey179/vfsstream": "^1.6",
"phpbench/phpbench": "^0.17",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-deprecation-rules": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpstan/phpstan-strict-rules": "^0.12",
"phpunit/php-invoker": "^3.1",
"phpunit/phpunit": "^9.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.0-dev"
}
},
"autoload": {
"psr-4": {
"Lcobucci\\JWT\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Luís Cobucci",
"email": "lcobucci@gmail.com",
"role": "Developer"
}
],
"description": "A simple library to work with JSON Web Token and JSON Web Signature",
"keywords": [
"JWS",
"jwt"
],
"support": {
"issues": "https://github.com/lcobucci/jwt/issues",
"source": "https://github.com/lcobucci/jwt/tree/4.0.0"
},
"funding": [
{
"url": "https://github.com/lcobucci",
"type": "github"
},
{
"url": "https://www.patreon.com/lcobucci",
"type": "patreon"
}
],
"time": "2020-11-25T02:06:12+00:00"
}, },
{ {
"name": "league/commonmark", "name": "league/commonmark",

View File

@@ -3,12 +3,13 @@
namespace Pterodactyl\Tests\Integration\Api\Client\Server; namespace Pterodactyl\Tests\Integration\Api\Client\Server;
use Carbon\Carbon; use Carbon\Carbon;
use Lcobucci\JWT\Parser;
use Carbon\CarbonImmutable; use Carbon\CarbonImmutable;
use Lcobucci\JWT\Signer\Key;
use Illuminate\Http\Response; use Illuminate\Http\Response;
use Lcobucci\JWT\Configuration;
use Pterodactyl\Models\Permission; use Pterodactyl\Models\Permission;
use Lcobucci\JWT\Signer\Hmac\Sha256; use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase; use Pterodactyl\Tests\Integration\Api\Client\ClientApiIntegrationTestCase;
class WebsocketControllerTest extends ClientApiIntegrationTestCase class WebsocketControllerTest extends ClientApiIntegrationTestCase
@@ -52,22 +53,25 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase
$this->assertStringStartsWith('wss://', $connection, 'Failed asserting that websocket connection address has expected "wss://" prefix.'); $this->assertStringStartsWith('wss://', $connection, 'Failed asserting that websocket connection address has expected "wss://" prefix.');
$this->assertStringEndsWith("/api/servers/{$server->uuid}/ws", $connection, 'Failed asserting that websocket connection address uses expected Wings endpoint.'); $this->assertStringEndsWith("/api/servers/{$server->uuid}/ws", $connection, 'Failed asserting that websocket connection address uses expected Wings endpoint.');
$token = (new Parser)->parse($response->json('data.token')); $config = Configuration::forSymmetricSigner(new Sha256, $key = InMemory::plainText($server->node->getDecryptedKey()));
$config->setValidationConstraints(new SignedWith(new Sha256, $key));
/** @var \Lcobucci\JWT\Token\Plain $token */
$token = $config->parser()->parse($response->json('data.token'));
$this->assertTrue( $this->assertTrue(
$token->verify(new Sha256, new Key($server->node->getDecryptedKey())), $config->validator()->validate($token, ...$config->validationConstraints()),
'Failed to validate that the JWT data returned was signed using the Node\'s secret key.' 'Failed to validate that the JWT data returned was signed using the Node\'s secret key.'
); );
// Check that the claims are generated correctly. // Check that the claims are generated correctly.
$this->assertSame(config('app.url'), $token->getClaim('iss')); $this->assertTrue($token->hasBeenIssuedBy(config('app.url')));
$this->assertSame($server->node->getConnectionAddress(), $token->getClaim('aud')); $this->assertTrue($token->isPermittedFor($server->node->getConnectionAddress()));
$this->assertSame(CarbonImmutable::now()->getTimestamp(), $token->getClaim('iat')); $this->assertEquals(CarbonImmutable::now()->toDateTimeImmutable(), $token->claims()->get('iat'));
$this->assertSame(CarbonImmutable::now()->subMinutes(5)->getTimestamp(), $token->getClaim('nbf')); $this->assertEquals(CarbonImmutable::now()->subMinutes(5)->toDateTimeImmutable(), $token->claims()->get('nbf'));
$this->assertSame(CarbonImmutable::now()->addMinutes(10)->getTimestamp(), $token->getClaim('exp')); $this->assertEquals(CarbonImmutable::now()->addMinutes(10)->toDateTimeImmutable(), $token->claims()->get('exp'));
$this->assertSame($user->id, $token->getClaim('user_id')); $this->assertSame($user->id, $token->claims()->get('user_id'));
$this->assertSame($server->uuid, $token->getClaim('server_uuid')); $this->assertSame($server->uuid, $token->claims()->get('server_uuid'));
$this->assertSame(['*'], $token->getClaim('permissions')); $this->assertSame(['*'], $token->claims()->get('permissions'));
} }
/** /**
@@ -86,14 +90,18 @@ class WebsocketControllerTest extends ClientApiIntegrationTestCase
$response->assertOk(); $response->assertOk();
$response->assertJsonStructure(['data' => ['token', 'socket']]); $response->assertJsonStructure(['data' => ['token', 'socket']]);
$token = (new Parser)->parse($response->json('data.token')); $config = Configuration::forSymmetricSigner(new Sha256, $key = InMemory::plainText($server->node->getDecryptedKey()));
$config->setValidationConstraints(new SignedWith(new Sha256, $key));
/** @var \Lcobucci\JWT\Token\Plain $token */
$token = $config->parser()->parse($response->json('data.token'));
$this->assertTrue( $this->assertTrue(
$token->verify(new Sha256, new Key($server->node->getDecryptedKey())), $config->validator()->validate($token, ...$config->validationConstraints()),
'Failed to validate that the JWT data returned was signed using the Node\'s secret key.' 'Failed to validate that the JWT data returned was signed using the Node\'s secret key.'
); );
// Check that the claims are generated correctly. // Check that the claims are generated correctly.
$this->assertSame($permissions, $token->getClaim('permissions')); $this->assertSame($permissions, $token->claims()->get('permissions'));
} }
} }