More tests and more fixes in accordance with them.
This commit is contained in:
parent
3fbbdd5b9a
commit
46ec5b9d88
15 changed files with 250 additions and 57 deletions
25
Makefile
25
Makefile
|
@ -14,9 +14,25 @@ DEV_DIRS := $(wildcard ${DEV_DIR}/*)
|
|||
VENDOR := $(realpath vendor)
|
||||
VENDOR_BIN := ${VENDOR}/bin
|
||||
|
||||
# We'll use the lowest common denominator to maintain the root packages
|
||||
PHP_702 := $(shell which php7.2)
|
||||
export PHP_702
|
||||
PHP_703 := $(shell which php7.3)
|
||||
export PHP_703
|
||||
PHP_704 := $(shell which php7.4)
|
||||
export PHP_704
|
||||
PHP_800 := $(shell which php8.0)
|
||||
export PHP_800
|
||||
PHP_801 := $(shell which php8.1)
|
||||
export PHP_801
|
||||
PHP_802 := $(shell which php8.2)
|
||||
export PHP_802
|
||||
PHP_803 := $(shell which php8.3)
|
||||
export PHP_803
|
||||
PHP_804 := $(shell which php8.4)
|
||||
export PHP_804
|
||||
|
||||
COMPOSER := $(shell which composer)
|
||||
export COMPOSER
|
||||
|
||||
SUDO = $(shell which sudo)
|
||||
|
||||
|
@ -33,6 +49,13 @@ update:
|
|||
|
||||
tests:
|
||||
${PHP_702} ${VENDOR_BIN}/phpunit
|
||||
${PHP_703} ${VENDOR_BIN}/phpunit
|
||||
${PHP_704} ${VENDOR_BIN}/phpunit
|
||||
${PHP_800} ${VENDOR_BIN}/phpunit
|
||||
${PHP_801} ${VENDOR_BIN}/phpunit
|
||||
${PHP_802} ${VENDOR_BIN}/phpunit
|
||||
${PHP_803} ${VENDOR_BIN}/phpunit
|
||||
${PHP_804} ${VENDOR_BIN}/phpunit
|
||||
|
||||
fix-perms: # WSL skill issues
|
||||
${SUDO} chown -R ${USER_NAME}:${GROUP_NAME} .
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
v0.3.2
|
||||
v0.3.3
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_702 := $(shell which php7.2)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_702} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_703 := $(shell which php7.3)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_703} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_704 := $(shell which php7.4)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_704} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_800 := $(shell which php8.0)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_800} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_801 := $(shell which php8.1)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_801} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_802 := $(shell which php8.2)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_802} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_803 := $(shell which php8.3)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_803} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
PHP_804 := $(shell which php8.4)
|
||||
COMPOSER := $(shell which composer)
|
||||
VENDOR_BIN := $(realpath vendor/bin)
|
||||
|
||||
PHPSTAN = "${VENDOR_BIN}/phpstan"
|
||||
PHPUNIT = "${VENDOR_BIN}/phpunit"
|
||||
|
||||
ifeq ($(origin PHPSTAN_CONFIG),undefined)
|
||||
$(error PHPSTAN_CONFIG is not defined)
|
||||
endif
|
||||
|
||||
install:
|
||||
${PHP_804} ${COMPOSER} install
|
||||
|
||||
|
|
|
@ -32,6 +32,9 @@ class HmacJwk implements Jwk {
|
|||
?string $algo = null,
|
||||
?string $id = null
|
||||
) {
|
||||
if($algo !== null && !array_key_exists($algo, self::$algos))
|
||||
throw new InvalidArgumentException('$algo is not a supported algorithm');
|
||||
|
||||
$this->key = $key;
|
||||
$this->algo = $algo;
|
||||
$this->id = $id;
|
||||
|
@ -73,12 +76,16 @@ class HmacJwk implements Jwk {
|
|||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize() {
|
||||
return [
|
||||
'kty' => 'oct',
|
||||
'alg' => $this->algo,
|
||||
'use' => 'sig',
|
||||
'k' => UriBase64::encode($this->key),
|
||||
];
|
||||
$json = [];
|
||||
$json['kty'] = 'oct';
|
||||
if($this->algo !== null)
|
||||
$json['alg'] = $this->algo;
|
||||
if($this->id !== null)
|
||||
$json['kid'] = $this->id;
|
||||
$json['use'] = 'sig';
|
||||
$json['k'] = UriBase64::encode($this->key);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
117
tests/HmacJwkTest.php
Normal file
117
tests/HmacJwkTest.php
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\HmacJwk;
|
||||
use Railgun\Jwt\Utility\Json;
|
||||
|
||||
final class HmacJwkTest extends TestCase {
|
||||
/**
|
||||
* @return array{string, ?string, ?string, string, string, array<string, string>}[]
|
||||
*/
|
||||
public function hmacJwkGeneralDataProvider(): array {
|
||||
return [
|
||||
[
|
||||
'14pDJ4ScThxtDkubL7X7',
|
||||
null,
|
||||
null,
|
||||
'{"kty":"oct","use":"sig","k":"MTRwREo0U2NUaHh0RGt1Ykw3WDc"}',
|
||||
'FqS2nJj4gOf78JuKdtv8wtyx78QirkXuwcil3gvUXaDTEBSNlFHZKjnfiAe1',
|
||||
[
|
||||
'HS256' => 'a37a05bed788ae05cd756c376596aa5bd8bfe352494cddc7a95a857fbd1b6d7c',
|
||||
'HS384' => '51e6e7aac281e0c491dcac3ed3aa46295c7b168a31c347e3a23961cbbf6b92268c82b510bec011f08ee8ef5e6630e96f',
|
||||
'HS512' => 'fd1f1709b695c7e0adef34a5565a0b96d4d90441215b5d11ad14af66390d37c0d3bc9cf9e4c081c7ee213f27ceea337619117c96660f8794ad44394bfd73a5ac',
|
||||
],
|
||||
],
|
||||
[
|
||||
'chXEGnMXVPhIwZIzCnjbUOnuFHMIlSpblAdMBCA9',
|
||||
null,
|
||||
'soup',
|
||||
'{"kty":"oct","kid":"soup","use":"sig","k":"Y2hYRUduTVhWUGhJd1pJekNuamJVT251RkhNSWxTcGJsQWRNQkNBOQ"}',
|
||||
'nw7fS7arkmcKjYsXnrHpwt0cG2jBqvb2xIqBNgcbsKCaLWnP4vbDWTPF4p0sjxD38jHgDzsgi1fOQvdK',
|
||||
[
|
||||
'HS256' => '08ce793f6f542b0e0d54dfd6d13c28ee5ab664be1de1327a52f49881763fe6f6',
|
||||
'HS384' => '8381a30356c797acf2e52e41a5391a96c4909606d7780bb964a1725dc680f22cc005973d3fb6de522ec64e0c0ab6cb65',
|
||||
'HS512' => '989b78db27a04f723fcd8245a2122ec78d2b66262d472c5277cf255c30c17ab4f7c5f398a0b95abc27a9ab310da3ed3cccfa021d3284123da59f10f222fe5a7d',
|
||||
],
|
||||
],
|
||||
[
|
||||
'9MgUWsfYaYhngVMHZebpAsePHW2CKy',
|
||||
'HS384',
|
||||
null,
|
||||
'{"kty":"oct","alg":"HS384","use":"sig","k":"OU1nVVdzZllhWWhuZ1ZNSFplYnBBc2VQSFcyQ0t5"}',
|
||||
'pp8tNF9ZDZk0CGOzqNkeuaONYlO5tV10Tm64cjVjwC5',
|
||||
[
|
||||
'' => 'f660d719307a42b74e2e344454746b742234ac7580b40aa3abbd7e0ed82c1a61561c9540c9e8c61ce6966cf7abcffc04',
|
||||
'HS256' => '3035cf9e5f683357e07fce493e847800ea5e3584973f00ec814a40deee2cbf88',
|
||||
'HS512' => 'a3d3cdb61fd5003ebcc4b5b6ccd5d254d8e3e255b3b1749d7629a49ea78b88f8b4dac13547d21fcc68756a2d6fca756424d1e9de26cec7389573912af81eeb0c',
|
||||
],
|
||||
],
|
||||
[
|
||||
'tIyoGOvEpxRXD8bubowJgsm9kMtdyLpRSWibcYl1K9MZLvJ0XZ',
|
||||
'HS256',
|
||||
'beans-key',
|
||||
'{"kty":"oct","alg":"HS256","kid":"beans-key","use":"sig","k":"dEl5b0dPdkVweFJYRDhidWJvd0pnc205a010ZHlMcFJTV2liY1lsMUs5TVpMdkowWFo"}',
|
||||
'6ZlMT201j8bkTBhGdnhVauj',
|
||||
[
|
||||
'' => 'f33e4829d49e4045788590324f731fb65ddcfb961b92034b4466813336bd9ad8',
|
||||
'HS384' => '309509fcb736370dec267cc758113a88365a0c01f13bd2af4e1419d7bc8865f924a5c8f05c811f33bcd25fe9a2c84e95',
|
||||
'HS512' => 'bb1da7fec614b1ee03422d439475f0c25370ea453d770e2c198d22ed15ee6c880915a5120d3c7ad5fd998d15fefaa2827515d68ea8deec2c2c4587c4c817b29a',
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $results
|
||||
* @dataProvider hmacJwkGeneralDataProvider
|
||||
*/
|
||||
public function testHmacJwkGeneral(string $key, ?string $algo, ?string $id, string $json, string $subject, array $results): void {
|
||||
$jwk = new HmacJwk($key, $algo, $id);
|
||||
|
||||
$this->assertSame($id, $jwk->getId());
|
||||
$this->assertSame($algo, $jwk->getAlgorithm());
|
||||
$this->assertNull($jwk->getPublicKey()); // HMAC is not asymmetric so this will always be null
|
||||
$this->assertSame($json, Json::encode($jwk));
|
||||
|
||||
foreach($results as $algo => $expect) {
|
||||
if(is_int($algo))
|
||||
continue;
|
||||
if($algo === '')
|
||||
$algo = null;
|
||||
|
||||
$signature = $jwk->sign($subject, $algo);
|
||||
$this->assertSame($expect, bin2hex($signature));
|
||||
$this->assertTrue($jwk->verify($subject, $signature, $algo));
|
||||
}
|
||||
}
|
||||
|
||||
public function testEnsureConstructorThrowsWhenUnsupportedAlgo(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$algo is not a supported algorithm');
|
||||
new HmacJwk('a', 'WXP2600');
|
||||
}
|
||||
|
||||
public function testEnsureSignAlgoNullWhenNoDefaultThrows(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('this key does not specify a default algorithm, you must specify $algo');
|
||||
(new HmacJwk('b'))->sign('meow');
|
||||
}
|
||||
|
||||
public function testEnsureVerifyAlgoNullWhenNoDefaultThrows(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('this key does not specify a default algorithm, you must specify $algo');
|
||||
(new HmacJwk('c'))->verify('meow', 'mewow');
|
||||
}
|
||||
|
||||
public function testEnsureSignUnsupportedAlgoThrowsEvenWhenSupportedDefault(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$algo is not a supported algorithm');
|
||||
(new HmacJwk('d', 'HS256'))->sign('hello', 'SPC700');
|
||||
}
|
||||
|
||||
public function testEnsureVerifyUnsupportedAlgoThrowsEvenWhenSupportedDefault(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$algo is not a supported algorithm');
|
||||
(new HmacJwk('e', 'HS384'))->verify('hello', 'world', 'VR4300');
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ final class ClaimsUtilsTest extends TestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return array{object, }[]
|
||||
* @return array{object, string[], callable(mixed, string): mixed, mixed}[]
|
||||
*/
|
||||
public function ensureIdenticalAndRetrieveDataProvider(): array {
|
||||
return [
|
||||
|
|
48
tests/Validators/NotValidBeforeValidatorTest.php
Normal file
48
tests/Validators/NotValidBeforeValidatorTest.php
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\UnexpectedValueExceptionWithPayload;
|
||||
use Railgun\Jwt\Validators\{
|
||||
NotValidBeforeValidator,
|
||||
NotValidYetException
|
||||
};
|
||||
|
||||
final class NotValidBeforeValidatorTest extends TestCase {
|
||||
/**
|
||||
* @return array{int, int, string[]|null, object, ?class-string<Throwable>}[]
|
||||
*/
|
||||
public function notValidBeforeDataProvider(): array {
|
||||
return [
|
||||
[0, 1000, null, (object)[], null],
|
||||
[0, 1000, null, (object)['nbf' => 999], null],
|
||||
[0, 1000, null, (object)['nbf' => 1001], NotValidYetException::class],
|
||||
[30, 1000, null, (object)['nbf' => 1030], null],
|
||||
[30, 1000, null, (object)['nbf' => 1031], NotValidYetException::class],
|
||||
[0, 1000, null, (object)['nbf' => 1000, 'iat' => 1000], null],
|
||||
[0, 900, null, (object)['nbf' => 1000, 'iat' => 1000], NotValidYetException::class],
|
||||
[0, 900, null, (object)['nbf' => 1000, 'iat' => 1200], UnexpectedValueExceptionWithPayload::class],
|
||||
[0, 900, ['nbf', 'iat', 'beans'], (object)['nbf' => 800, 'iat' => 800, 'beans' => 800], null],
|
||||
[0, 900, ['beans'], (object)['nbf' => 9001, 'beans' => 800], null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider notValidBeforeDataProvider
|
||||
*/
|
||||
public function testNotValidBefore(int $leeway, int $time, ?array $claims, object $payload, ?string $throwable): void {
|
||||
if($throwable !== null)
|
||||
$this->expectException($throwable);
|
||||
|
||||
$time = (function(int $time) {
|
||||
return function() use ($time) {
|
||||
return $time;
|
||||
};
|
||||
})($time);
|
||||
|
||||
$validator = $claims === null
|
||||
? new NotValidBeforeValidator($leeway, $time)
|
||||
: new NotValidBeforeValidator($leeway, $time, $claims);
|
||||
|
||||
$this->assertNull($validator->validate((object)[], $payload));
|
||||
}
|
||||
}
|
46
tests/Validators/TokenExpirationValidatorTest.php
Normal file
46
tests/Validators/TokenExpirationValidatorTest.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php declare(strict_types=1);
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
use Railgun\Jwt\UnexpectedValueExceptionWithPayload;
|
||||
use Railgun\Jwt\Validators\{
|
||||
TokenExpirationValidator,
|
||||
TokenExpiredException
|
||||
};
|
||||
|
||||
final class TokenExpirationValidatorTest extends TestCase {
|
||||
/**
|
||||
* @return array{int, int, string[]|null, object, ?class-string<Throwable>}[]
|
||||
*/
|
||||
public function tokenExpirationProvider(): array {
|
||||
return [
|
||||
[0, 1000, null, (object)[], null],
|
||||
[0, 1000, null, (object)['exp' => 999], TokenExpiredException::class],
|
||||
[0, 1000, null, (object)['exp' => 1001], null],
|
||||
[30, 1000, null, (object)['exp' => 970], null],
|
||||
[30, 1000, null, (object)['exp' => 969], TokenExpiredException::class],
|
||||
[0, 700, ['exp', 'beans'], (object)['exp' => 800, 'beans' => 800], null],
|
||||
[0, 900, ['exp', 'beans'], (object)['exp' => 800, 'beans' => 900], UnexpectedValueExceptionWithPayload::class],
|
||||
[0, 900, ['beans'], (object)['exp' => 334, 'beans' => 80004], null],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider tokenExpirationProvider
|
||||
*/
|
||||
public function testTokenExpiration(int $leeway, int $time, ?array $claims, object $payload, ?string $throwable): void {
|
||||
if($throwable !== null)
|
||||
$this->expectException($throwable);
|
||||
|
||||
$time = (function(int $time) {
|
||||
return function() use ($time) {
|
||||
return $time;
|
||||
};
|
||||
})($time);
|
||||
|
||||
$validator = $claims === null
|
||||
? new TokenExpirationValidator($leeway, $time)
|
||||
: new TokenExpirationValidator($leeway, $time, $claims);
|
||||
|
||||
$this->assertNull($validator->validate((object)[], $payload));
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue