diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 687f04e..99c0ac0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: run: composer i --prefer-dist --no-interaction --no-progress --optimize-autoloader - name: PHPStan tests - run: vendor/bin/phpstan analyze -l 9 -a vendor/yiisoft/yii2/Yii.php --no-progress src + run: sh phpstan.sh Infection: name: PHP ${{ matrix.php }} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index b0078e1..e53873e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at pawel@positive.codes. All +reported by contacting the project team at pawel.bizley@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. diff --git a/README.md b/README.md index 564ac78..3c39331 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ Configuration array can be as the following: ] ``` -- key (`bizley\jwt\Jwt::KEY`) - _string_, default `''`, +- key (`bizley\jwt\Jwt::KEY`) - _string_, default `''`, start it with `@` if it's Yii alias, - passphrase (`bizley\jwt\Jwt::PASSPHRASE`) - _string_, default `''`, - method (`bizley\jwt\Jwt::METHOD`) - _string_, default `bizley\jwt\Jwt::METHOD_PLAIN`, available: `bizley\jwt\Jwt::METHOD_PLAIN`, `bizley\jwt\Jwt::METHOD_BASE64`, `bizley\jwt\Jwt::METHOD_FILE` diff --git a/composer.json b/composer.json index 689f33c..eb13ee5 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,7 @@ "infection/infection": "*", "lcobucci/clock": "^3.0", "phpstan/phpstan": "*", - "phpunit/phpunit": "^10.0", + "phpunit/phpunit": "^10.5", "roave/security-advisories": "dev-latest" }, "autoload": { diff --git a/infection.json.dist b/infection.json.dist index 1c4b65f..b075216 100644 --- a/infection.json.dist +++ b/infection.json.dist @@ -14,8 +14,8 @@ "@default": true, "MethodCallRemoval": { "ignore": [ - "bizley\\jwt\\Jwt::init::124", - "bizley\\jwt\\JwtHttpBearerAuth::init::72" + "bizley\\jwt\\Jwt::init::117", + "bizley\\jwt\\JwtHttpBearerAuth::init::68" ] } }, diff --git a/src/Jwt.php b/src/Jwt.php index a21a01a..fef40e3 100644 --- a/src/Jwt.php +++ b/src/Jwt.php @@ -4,14 +4,7 @@ namespace bizley\jwt; -use Lcobucci\JWT\Builder; -use Lcobucci\JWT\ClaimsFormatter; -use Lcobucci\JWT\Configuration; -use Lcobucci\JWT\Parser; -use Lcobucci\JWT\Signer; -use Lcobucci\JWT\Token; -use Lcobucci\JWT\Validation; -use Lcobucci\JWT\Validator; +use Lcobucci\JWT as BaseJwt; use yii\base\InvalidConfigException; /** @@ -21,7 +14,7 @@ * This implementation is based on the \Lcobucci\JWT\Configuration setup which requires both signing and verifying keys * to be defined (the standard way). If you need only some JWT tools, please use \bizley\jwt\JwtTools directly. * - * @author Paweł Bizley Brzozowski since 2.0 (fork) + * @author Paweł Bizley Brzozowski since 2.0 (fork) * @author Dmitriy Demin original package */ class Jwt extends JwtTools @@ -50,7 +43,7 @@ class Jwt extends JwtTools public const PASSPHRASE = 'passphrase'; /** - * @var string|array|Signer\Key Signing key definition. + * @var string|array|BaseJwt\Signer\Key Signing key definition. * This can be a simple string, an instance of Key, or a configuration array. * The configuration takes the following array keys: * - 'key' => Key's value or path to the key file. @@ -77,7 +70,7 @@ class Jwt extends JwtTools public $signingKey = ''; /** - * @var string|array|Signer\Key Verifying key definition. + * @var string|array|BaseJwt\Signer\Key Verifying key definition. * $signingKey documentation you can find above applies here as well. * Symmetric algorithms (like HMAC) use a single key to sign and verify tokens so this property is ignored in that * case. Asymmetric algorithms (like RSA and ECDSA) use a private key to sign and a public key to verify. @@ -86,7 +79,7 @@ class Jwt extends JwtTools public $verifyingKey = ''; /** - * @var string|Signer Signer ID or Signer instance to be used for signing/verifying. + * @var string|BaseJwt\Signer Signer ID or Signer instance to be used for signing/verifying. * See $signers for available values. Since 4.0.0 it cannot be empty anymore. * @since 3.0.0 */ @@ -114,7 +107,7 @@ class Jwt extends JwtTools ], ]; - private ?Configuration $configuration = null; + private ?BaseJwt\Configuration $configuration = null; /** * @throws InvalidConfigException @@ -124,18 +117,18 @@ public function init(): void parent::init(); $signerId = $this->signer; - if ($this->signer instanceof Signer) { + if ($this->signer instanceof BaseJwt\Signer) { $signerId = $this->signer->algorithmId(); } if (\in_array($signerId, $this->algorithmTypes[self::SYMMETRIC], true)) { - $this->configuration = Configuration::forSymmetricSigner( + $this->configuration = BaseJwt\Configuration::forSymmetricSigner( $this->buildSigner($this->signer), $this->buildKey($this->signingKey), $this->prepareEncoder(), $this->prepareDecoder() ); } elseif (\in_array($signerId, $this->algorithmTypes[self::ASYMMETRIC], true)) { - $this->configuration = Configuration::forAsymmetricSigner( + $this->configuration = BaseJwt\Configuration::forAsymmetricSigner( $this->buildSigner($this->signer), $this->buildKey($this->signingKey), $this->buildKey($this->verifyingKey), @@ -151,7 +144,7 @@ public function init(): void * @throws InvalidConfigException * @since 3.0.0 */ - public function getConfiguration(): Configuration + public function getConfiguration(): BaseJwt\Configuration { if ($this->configuration === null) { throw new InvalidConfigException('Configuration has not been set up. Did you call init()?'); @@ -165,7 +158,7 @@ public function getConfiguration(): Configuration * @see https://lcobucci-jwt.readthedocs.io/en/latest/issuing-tokens/ for details of using the builder. * @throws InvalidConfigException */ - public function getBuilder(?ClaimsFormatter $claimFormatter = null): Builder + public function getBuilder(?BaseJwt\ClaimsFormatter $claimFormatter = null): BaseJwt\Builder { return $this->getConfiguration()->builder($claimFormatter); } @@ -174,7 +167,7 @@ public function getBuilder(?ClaimsFormatter $claimFormatter = null): Builder * @see https://lcobucci-jwt.readthedocs.io/en/latest/parsing-tokens/ for details of using the parser. * @throws InvalidConfigException */ - public function getParser(): Parser + public function getParser(): BaseJwt\Parser { return $this->getConfiguration()->parser(); } @@ -183,7 +176,7 @@ public function getParser(): Parser * @see https://lcobucci-jwt.readthedocs.io/en/stable/validating-tokens/ for details of using the validator. * @throws InvalidConfigException */ - public function getValidator(): Validator + public function getValidator(): BaseJwt\Validator { return $this->getConfiguration()->validator(); } @@ -191,43 +184,43 @@ public function getValidator(): Validator /** * This method goes through every single constraint in the set, groups all the violations, and throws an exception * with the grouped violations. - * @param non-empty-string|Token $jwt JWT string or instance of Token - * @throws Validation\RequiredConstraintsViolated When constraint is violated - * @throws Validation\NoConstraintsGiven When no constraints are provided + * @param non-empty-string|BaseJwt\Token $jwt JWT string or instance of Token + * @throws BaseJwt\Validation\RequiredConstraintsViolated When constraint is violated + * @throws BaseJwt\Validation\NoConstraintsGiven When no constraints are provided * @throws InvalidConfigException * @since 3.0.0 */ public function assert($jwt): void { $configuration = $this->getConfiguration(); - $token = $jwt instanceof Token ? $jwt : $this->parse($jwt); + $token = $jwt instanceof BaseJwt\Token ? $jwt : $this->parse($jwt); $constraints = $this->prepareValidationConstraints(); $configuration->validator()->assert($token, ...$constraints); } /** * This method return false on first constraint violation - * @param non-empty-string|Token $jwt JWT string or instance of Token + * @param non-empty-string|BaseJwt\Token $jwt JWT string or instance of Token * @throws InvalidConfigException * @since 3.0.0 */ public function validate($jwt): bool { $configuration = $this->getConfiguration(); - $token = $jwt instanceof Token ? $jwt : $this->parse($jwt); + $token = $jwt instanceof BaseJwt\Token ? $jwt : $this->parse($jwt); $constraints = $this->prepareValidationConstraints(); return $configuration->validator()->validate($token, ...$constraints); } /** - * @return Validation\Constraint[] + * @return BaseJwt\Validation\Constraint[] * @throws InvalidConfigException */ protected function prepareValidationConstraints(): array { $configuredConstraints = $this->getConfiguration()->validationConstraints(); - if (\count($configuredConstraints)) { + if (!empty($configuredConstraints)) { return $configuredConstraints; } diff --git a/src/JwtHttpBearerAuth.php b/src/JwtHttpBearerAuth.php index 0d9523f..477bd13 100644 --- a/src/JwtHttpBearerAuth.php +++ b/src/JwtHttpBearerAuth.php @@ -10,11 +10,7 @@ use yii\base\InvalidConfigException; use yii\di\Instance; use yii\filters\auth\HttpBearerAuth; -use yii\web\IdentityInterface; -use yii\web\Request; -use yii\web\Response; -use yii\web\UnauthorizedHttpException; -use yii\web\User; +use yii\web; /** * JwtHttpBearerAuth is an action filter that supports the authentication method based on HTTP Bearer JSON Web Token. @@ -32,7 +28,7 @@ * } * ``` * - * @author Paweł Bizley Brzozowski since 2.0 (fork) + * @author Paweł Bizley Brzozowski since 2.0 (fork) * @author Dmitriy Demin original package */ class JwtHttpBearerAuth extends HttpBearerAuth @@ -91,10 +87,10 @@ public function getJwtComponent(): Jwt|JwtTools /** * Authenticates the current user. - * @param User $user - * @param Request $request - * @param Response $response - * @return IdentityInterface|null the authenticated user identity. If authentication information is not provided, null will be returned. + * @param web\User $user + * @param web\Request $request + * @param web\Response $response + * @return web\IdentityInterface|null the authenticated user identity. If authentication information is not provided, null will be returned. * @throws InvalidConfigException When JWT configuration has not been properly initialized. * @throws CannotDecodeContent When something goes wrong while decoding token. * @throws Token\InvalidTokenStructure When token string structure is invalid. @@ -102,9 +98,9 @@ public function getJwtComponent(): Jwt|JwtTools * @throws Validation\RequiredConstraintsViolated When constraint is not present in token. * @throws Validation\NoConstraintsGiven When no constraints are provided. * @throws Validation\ConstraintViolation When constraint is violated. - * @throws UnauthorizedHttpException if authentication information is provided but is invalid. + * @throws web\UnauthorizedHttpException if authentication information is provided but is invalid. */ - public function authenticate($user, $request, $response): ?IdentityInterface // BC signature + public function authenticate($user, $request, $response): ?web\IdentityInterface // BC signature { /** @var string|null $authHeader */ $authHeader = $request->getHeaders()->get($this->header); @@ -137,7 +133,7 @@ public function authenticate($user, $request, $response): ?IdentityInterface // } } - if (!$identity instanceof IdentityInterface) { + if (!$identity instanceof web\IdentityInterface) { return null; } @@ -157,9 +153,9 @@ public function processToken(string $data): ?Token } /** - * @throws UnauthorizedHttpException + * @throws web\UnauthorizedHttpException */ - public function fail(Response $response): void + public function fail(web\Response $response): void { $this->challenge($response); $this->handleFailure($response); @@ -167,11 +163,11 @@ public function fail(Response $response): void /** * Handles authentication failure. - * @param Response $response - * @throws UnauthorizedHttpException + * @param web\Response $response + * @throws web\UnauthorizedHttpException */ public function handleFailure($response): void // BC signature { - throw new UnauthorizedHttpException('Your request was made with invalid or expired JSON Web Token.'); + throw new web\UnauthorizedHttpException('Your request was made with invalid or expired JSON Web Token.'); } } diff --git a/src/JwtTools.php b/src/JwtTools.php index 7af2194..2061ef0 100644 --- a/src/JwtTools.php +++ b/src/JwtTools.php @@ -4,18 +4,8 @@ namespace bizley\jwt; -use Lcobucci\JWT\Builder; -use Lcobucci\JWT\ClaimsFormatter; -use Lcobucci\JWT\Decoder; -use Lcobucci\JWT\Encoder; -use Lcobucci\JWT\Encoding\CannotDecodeContent; -use Lcobucci\JWT\Encoding\ChainedFormatter; -use Lcobucci\JWT\Encoding\JoseEncoder; -use Lcobucci\JWT\Parser; -use Lcobucci\JWT\Signer; -use Lcobucci\JWT\Token; -use Lcobucci\JWT\Validation; -use Lcobucci\JWT\Validator; +use Lcobucci\JWT as BaseJwt; +use Lcobucci\JWT\Encoding; use yii\base\Component; use yii\base\InvalidConfigException; use yii\di\Instance; @@ -27,7 +17,7 @@ * This implementation allows developer to pick & choose JWT tools to use for example in order to only validate * a token (without issuing it first, so signing key does not need to be defined). * - * @author Paweł Bizley Brzozowski + * @author Paweł Bizley Brzozowski * @since 4.1.0 */ class JwtTools extends Component @@ -38,28 +28,28 @@ class JwtTools extends Component * the second is $params. */ public array $signers = [ - Jwt::HS256 => [Signer\Hmac\Sha256::class], - Jwt::HS384 => [Signer\Hmac\Sha384::class], - Jwt::HS512 => [Signer\Hmac\Sha512::class], - Jwt::RS256 => [Signer\Rsa\Sha256::class], - Jwt::RS384 => [Signer\Rsa\Sha384::class], - Jwt::RS512 => [Signer\Rsa\Sha512::class], - Jwt::ES256 => [Signer\Ecdsa\Sha256::class], - Jwt::ES384 => [Signer\Ecdsa\Sha384::class], - Jwt::ES512 => [Signer\Ecdsa\Sha512::class], - Jwt::EDDSA => [Signer\Eddsa::class], - Jwt::BLAKE2B => [Signer\Blake2b::class], + Jwt::HS256 => [BaseJwt\Signer\Hmac\Sha256::class], + Jwt::HS384 => [BaseJwt\Signer\Hmac\Sha384::class], + Jwt::HS512 => [BaseJwt\Signer\Hmac\Sha512::class], + Jwt::RS256 => [BaseJwt\Signer\Rsa\Sha256::class], + Jwt::RS384 => [BaseJwt\Signer\Rsa\Sha384::class], + Jwt::RS512 => [BaseJwt\Signer\Rsa\Sha512::class], + Jwt::ES256 => [BaseJwt\Signer\Ecdsa\Sha256::class], + Jwt::ES384 => [BaseJwt\Signer\Ecdsa\Sha384::class], + Jwt::ES512 => [BaseJwt\Signer\Ecdsa\Sha512::class], + Jwt::EDDSA => [BaseJwt\Signer\Eddsa::class], + Jwt::BLAKE2B => [BaseJwt\Signer\Blake2b::class], ]; /** - * @var string|array|Encoder|null Custom encoder. + * @var string|array|BaseJwt\Encoder|null Custom encoder. * It can be component's ID, configuration array, or instance of Encoder. * In case it's not an instance, it must be resolvable to an Encoder's instance. */ public $encoder; /** - * @var string|array|Decoder|null Custom decoder. + * @var string|array|BaseJwt\Decoder|null Custom decoder. * It can be component's ID, configuration array, or instance of Decoder. * In case it's not an instance, it must be resolvable to a Decoder's instance. */ @@ -77,7 +67,6 @@ class JwtTools extends Component /** * @param array|(callable(): mixed)|string> $config - * @return object * @throws InvalidConfigException */ private function buildObjectFromArray(array $config): object @@ -95,36 +84,36 @@ private function buildObjectFromArray(array $config): object * @see https://lcobucci-jwt.readthedocs.io/en/latest/issuing-tokens/ for details of using the builder. * @throws InvalidConfigException */ - public function getBuilder(?ClaimsFormatter $claimFormatter = null): Builder + public function getBuilder(?BaseJwt\ClaimsFormatter $claimFormatter = null): BaseJwt\Builder { - return new Token\Builder($this->prepareEncoder(), $claimFormatter ?? ChainedFormatter::default()); + return new BaseJwt\Token\Builder($this->prepareEncoder(), $claimFormatter ?? Encoding\ChainedFormatter::default()); } /** * @see https://lcobucci-jwt.readthedocs.io/en/latest/parsing-tokens/ for details of using the parser. * @throws InvalidConfigException */ - public function getParser(): Parser + public function getParser(): BaseJwt\Parser { - return new Token\Parser($this->prepareDecoder()); + return new BaseJwt\Token\Parser($this->prepareDecoder()); } /** * @see https://lcobucci-jwt.readthedocs.io/en/stable/validating-tokens/ for details of using the validator. */ - public function getValidator(): Validator + public function getValidator(): BaseJwt\Validator { - return new Validation\Validator(); + return new BaseJwt\Validation\Validator(); } /** * @param non-empty-string $jwt - * @throws CannotDecodeContent When something goes wrong while decoding. - * @throws Token\InvalidTokenStructure When token string structure is invalid. - * @throws Token\UnsupportedHeaderFound When parsed token has an unsupported header. + * @throws Encoding\CannotDecodeContent When something goes wrong while decoding. + * @throws BaseJwt\Token\InvalidTokenStructure When token string structure is invalid. + * @throws BaseJwt\Token\UnsupportedHeaderFound When parsed token has an unsupported header. * @throws InvalidConfigException */ - public function parse(string $jwt): Token + public function parse(string $jwt): BaseJwt\Token { return $this->getParser()->parse($jwt); } @@ -132,26 +121,26 @@ public function parse(string $jwt): Token /** * This method goes through every single constraint in the set, groups all the violations, and throws an exception * with the grouped violations. - * @param non-empty-string|Token $jwt JWT string or instance of Token - * @throws Validation\RequiredConstraintsViolated When constraint is violated - * @throws Validation\NoConstraintsGiven When no constraints are provided + * @param non-empty-string|BaseJwt\Token $jwt JWT string or instance of Token + * @throws BaseJwt\Validation\RequiredConstraintsViolated When constraint is violated + * @throws BaseJwt\Validation\NoConstraintsGiven When no constraints are provided * @throws InvalidConfigException */ public function assert($jwt): void { - $token = $jwt instanceof Token ? $jwt : $this->parse($jwt); + $token = $jwt instanceof BaseJwt\Token ? $jwt : $this->parse($jwt); $constraints = $this->prepareValidationConstraints(); $this->getValidator()->assert($token, ...$constraints); } /** * This method return false on first constraint violation - * @param non-empty-string|Token $jwt JWT string or instance of Token + * @param non-empty-string|BaseJwt\Token $jwt JWT string or instance of Token * @throws InvalidConfigException */ public function validate($jwt): bool { - $token = $jwt instanceof Token ? $jwt : $this->parse($jwt); + $token = $jwt instanceof BaseJwt\Token ? $jwt : $this->parse($jwt); $constraints = $this->prepareValidationConstraints(); return $this->getValidator()->validate($token, ...$constraints); @@ -159,13 +148,12 @@ public function validate($jwt): bool /** * Returns the key based on the definition. - * @param string|array|Signer\Key $key - * @return Signer\Key + * @param string|array|BaseJwt\Signer\Key $key * @throws InvalidConfigException */ - public function buildKey($key): Signer\Key + public function buildKey($key): BaseJwt\Signer\Key { - if ($key instanceof Signer\Key) { + if ($key instanceof BaseJwt\Signer\Key) { return $key; } @@ -173,12 +161,7 @@ public function buildKey($key): Signer\Key if ($key === '') { throw new InvalidConfigException('Empty string used as a key configuration!'); } - if (\str_starts_with($key, '@')) { - $keyConfig = [ - Jwt::KEY => \Yii::getAlias($key), - Jwt::METHOD => Jwt::METHOD_FILE, - ]; - } elseif (\str_starts_with($key, 'file://')) { + if (\str_starts_with($key, '@') || \str_starts_with($key, 'file://')) { $keyConfig = [ Jwt::KEY => $key, Jwt::METHOD => Jwt::METHOD_FILE, @@ -199,34 +182,38 @@ public function buildKey($key): Signer\Key $method = $keyConfig[Jwt::METHOD] ?? Jwt::METHOD_PLAIN; $passphrase = $keyConfig[Jwt::PASSPHRASE] ?? ''; - if (!\is_string($value) || $value === '') { - throw new InvalidConfigException('Invalid key value!'); - } if (!\in_array($method, [Jwt::METHOD_PLAIN, Jwt::METHOD_BASE64, Jwt::METHOD_FILE], true)) { throw new InvalidConfigException('Invalid key method!'); } if (!\is_string($passphrase)) { throw new InvalidConfigException('Invalid key passphrase!'); } + if (!\is_string($value) || $value === '') { + throw new InvalidConfigException('Invalid key value!'); + } + /** @var string $value */ + $value = \Yii::getAlias($value); + if ($value === '') { + throw new InvalidConfigException('Yii alias was resolved as an invalid key value!'); + } if ($method === Jwt::METHOD_BASE64) { - return Signer\Key\InMemory::base64Encoded($value, $passphrase); + return BaseJwt\Signer\Key\InMemory::base64Encoded($value, $passphrase); } if ($method === Jwt::METHOD_FILE) { - return Signer\Key\InMemory::file($value, $passphrase); + return BaseJwt\Signer\Key\InMemory::file($value, $passphrase); } - return Signer\Key\InMemory::plainText($value, $passphrase); + return BaseJwt\Signer\Key\InMemory::plainText($value, $passphrase); } /** - * @param string|Signer $signer - * @return Signer + * @param string|BaseJwt\Signer $signer * @throws InvalidConfigException */ - public function buildSigner($signer): Signer + public function buildSigner($signer): BaseJwt\Signer { - if ($signer instanceof Signer) { + if ($signer instanceof BaseJwt\Signer) { return $signer; } @@ -235,17 +222,17 @@ public function buildSigner($signer): Signer } if (\in_array($signer, [Jwt::ES256, Jwt::ES384, Jwt::ES512], true)) { - \Yii::$container->set(Signer\Ecdsa\SignatureConverter::class, Signer\Ecdsa\MultibyteStringConverter::class); + \Yii::$container->set(BaseJwt\Signer\Ecdsa\SignatureConverter::class, BaseJwt\Signer\Ecdsa\MultibyteStringConverter::class); } - /** @var Signer $signerInstance */ + /** @var BaseJwt\Signer $signerInstance */ $signerInstance = $this->buildObjectFromArray($this->signers[$signer]); return $signerInstance; } /** - * @return Validation\Constraint[] + * @return BaseJwt\Validation\Constraint[] * @throws InvalidConfigException */ protected function prepareValidationConstraints(): array @@ -254,10 +241,10 @@ protected function prepareValidationConstraints(): array $constraints = []; foreach ($this->validationConstraints as $constraint) { - if ($constraint instanceof Validation\Constraint) { + if ($constraint instanceof BaseJwt\Validation\Constraint) { $constraints[] = $constraint; } else { - /** @var Validation\Constraint $constraintInstance */ + /** @var BaseJwt\Validation\Constraint $constraintInstance */ $constraintInstance = $this->buildObjectFromArray($constraint); $constraints[] = $constraintInstance; } @@ -274,19 +261,19 @@ protected function prepareValidationConstraints(): array return []; } - private ?Encoder $builtEncoder = null; + private ?BaseJwt\Encoder $builtEncoder = null; /** * @throws InvalidConfigException */ - protected function prepareEncoder(): Encoder + protected function prepareEncoder(): BaseJwt\Encoder { if ($this->builtEncoder === null) { if ($this->encoder === null) { - $this->builtEncoder = new JoseEncoder(); + $this->builtEncoder = new Encoding\JoseEncoder(); } else { - /** @var Encoder $encoder */ - $encoder = Instance::ensure($this->encoder, Encoder::class); + /** @var BaseJwt\Encoder $encoder */ + $encoder = Instance::ensure($this->encoder, BaseJwt\Encoder::class); $this->builtEncoder = $encoder; } } @@ -294,19 +281,19 @@ protected function prepareEncoder(): Encoder return $this->builtEncoder; } - private ?Decoder $builtDecoder = null; + private ?BaseJwt\Decoder $builtDecoder = null; /** * @throws InvalidConfigException */ - protected function prepareDecoder(): Decoder + protected function prepareDecoder(): BaseJwt\Decoder { if ($this->builtDecoder === null) { if ($this->decoder === null) { - $this->builtDecoder = new JoseEncoder(); + $this->builtDecoder = new Encoding\JoseEncoder(); } else { - /** @var Decoder $decoder */ - $decoder = Instance::ensure($this->decoder, Decoder::class); + /** @var BaseJwt\Decoder $decoder */ + $decoder = Instance::ensure($this->decoder, BaseJwt\Decoder::class); $this->builtDecoder = $decoder; } } diff --git a/tests/ConsecutiveCalls.php b/tests/ConsecutiveCalls.php index bf5fbb6..54c3b7b 100644 --- a/tests/ConsecutiveCalls.php +++ b/tests/ConsecutiveCalls.php @@ -4,52 +4,34 @@ namespace bizley\tests; -use PHPUnit\Framework\Assert; use PHPUnit\Framework\Constraint\Constraint; -class ConsecutiveCalls extends Assert +class ConsecutiveCalls extends Constraint { /** - * @var array + * @var array */ - private array $data = []; - private int $internalCounter = -1; + private array $stack; + private int $call = 0; - /** - * @param mixed[] ...$args - */ - private function __construct(array ...$args) + public function __construct(mixed ...$args) { - foreach ($args as $arg) { - if (!\is_array($arg)) { - throw new \InvalidArgumentException('All arguments must be arrays'); - } - - $this->data[] = $arg; - } + $this->stack = $args; } - /** - * @param mixed[] ...$arguments - */ - public static function withArgs(array ...$arguments): self + protected function matches(mixed $other): bool { - return new self(...$arguments); + $this->call++; + $value = array_shift($this->stack); + if ($value instanceof Constraint) { + return $value->evaluate($other, '', true); // @phpstan-ignore-line + } + + return $value === $other; } - public function __invoke(mixed ...$args): void + public function toString(): string { - $testData = $this->data[++$this->internalCounter] ?? null; - if ($testData === null) { - $testData = $this->data[$this->internalCounter % \count($this->data)]; - } - - foreach ($testData as $key => $value) { - if ($value instanceof Constraint) { - $value->evaluate($args[$key]); - } else { - self::assertEquals($value, $args[$key]); - } - } + return sprintf('was called the #%d time', $this->call); } } diff --git a/tests/standard/BearerTest.php b/tests/standard/BearerTest.php index fb292a9..4560e5f 100644 --- a/tests/standard/BearerTest.php +++ b/tests/standard/BearerTest.php @@ -4,44 +4,37 @@ namespace bizley\tests\standard; -use bizley\jwt\Jwt; -use bizley\jwt\JwtHttpBearerAuth; -use bizley\jwt\JwtTools; +use bizley\jwt; use bizley\tests\ConsecutiveCalls; -use bizley\tests\stubs\TestAuthController; -use bizley\tests\stubs\TestJwtHttpBearerAuth; -use bizley\tests\stubs\TestStub2Controller; -use bizley\tests\stubs\TestStubController; -use bizley\tests\stubs\UserIdentity; +use bizley\tests\stubs; use Lcobucci\Clock\SystemClock; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint\IssuedBy; use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\NoConstraintsGiven; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; use yii\log\Logger; use yii\rest\Controller; -use yii\web\Application; -use yii\web\Response; -use yii\web\UnauthorizedHttpException; +use yii\web; -#[CoversClass(JwtHttpBearerAuth::class)] -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[CoversClass(jwt\JwtHttpBearerAuth::class)] +#[CoversClass(jwt\Jwt::class)] +#[CoversClass(jwt\JwtTools::class)] class BearerTest extends TestCase { protected function setUp(): void { - new Application( + new web\Application( [ 'id' => 'test', 'basePath' => __DIR__, 'vendorPath' => __DIR__ . '/../vendor', 'components' => [ 'user' => [ - 'identityClass' => UserIdentity::class, + 'identityClass' => stubs\UserIdentity::class, 'enableSession' => false, ], 'request' => [ @@ -50,35 +43,37 @@ protected function setUp(): void 'scriptUrl' => '/index.php', ], 'jwt' => [ - 'class' => Jwt::class, - 'signer' => Jwt::HS256, + 'class' => jwt\Jwt::class, + 'signer' => jwt\Jwt::HS256, 'signingKey' => 'c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M', ], ], 'controllerMap' => [ - 'test-auth' => TestAuthController::class, - 'test-stub' => TestStubController::class, - 'test-stub2' => TestStub2Controller::class, + 'test-auth' => stubs\TestAuthController::class, + 'test-stub' => stubs\TestStubController::class, + 'test-stub2' => stubs\TestStub2Controller::class, ], ] ); } - protected function getJwt(): Jwt + protected function getJwt(): jwt\Jwt { return \Yii::$app->jwt; } - public function testEmptyPattern(): void + #[Test] + public function emptyPattern(): void { $this->expectException(InvalidConfigException::class); $controller = \Yii::$app->createController('test-stub')[0]; $controller->run('test'); } - public function testHttpBearerAuthNoHeader(): void + #[Test] + public function httpBearerAuthNoHeader(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); /* @var $controller Controller */ @@ -86,7 +81,8 @@ public function testHttpBearerAuthNoHeader(): void $controller->run('filtered'); } - public function testHttpBearerAuthInvalidToken(): void + #[Test] + public function httpBearerAuthInvalidToken(): void { $this->expectException(Token\InvalidTokenStructure::class); $this->expectExceptionMessage('The JWT string must have two dots'); @@ -98,9 +94,10 @@ public function testHttpBearerAuthInvalidToken(): void $controller->run('filtered'); } - public function testHttpBearerAuthInvalidHeader(): void + #[Test] + public function httpBearerAuthInvalidHeader(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); \Yii::$app->request->headers->set('Authorization', 'InvalidHeaderValue'); @@ -110,9 +107,10 @@ public function testHttpBearerAuthInvalidHeader(): void $controller->run('filtered'); } - public function testHttpBearerAuthExpiredToken(): void + #[Test] + public function httpBearerAuthExpiredToken(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -134,7 +132,8 @@ public function testHttpBearerAuthExpiredToken(): void $controller->run('filtered'); } - public function testHttpBearerAuth(): void + #[Test] + public function httpBearerAuth(): void { $now = new \DateTimeImmutable(); @@ -150,7 +149,7 @@ public function testHttpBearerAuth(): void ->getToken($this->getJwt()->getConfiguration()->signer(), $this->getJwt()->getConfiguration()->signingKey()) ->toString(); - UserIdentity::$token = $token; + stubs\UserIdentity::$token = $token; \Yii::$app->request->headers->set('Authorization', "Bearer $token"); @@ -160,7 +159,8 @@ public function testHttpBearerAuth(): void self::assertEquals('test', $controller->run('filtered')); } - public function testHttpBearerAuthCustom(): void + #[Test] + public function httpBearerAuthCustom(): void { $now = new \DateTimeImmutable(); @@ -178,10 +178,10 @@ public function testHttpBearerAuthCustom(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { - $identity = UserIdentity::findIdentity($token->claims()->get('sub')); + $identity = stubs\UserIdentity::findIdentity($token->claims()->get('sub')); \Yii::$app->user->switchIdentity($identity); return $identity; }; @@ -189,9 +189,10 @@ public function testHttpBearerAuthCustom(): void self::assertEquals('test', $controller->run('filtered')); } - public function testHttpBearerAuthCustomNoIdentity(): void + #[Test] + public function httpBearerAuthCustomNoIdentity(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -208,7 +209,7 @@ public function testHttpBearerAuthCustomNoIdentity(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { return null; @@ -216,9 +217,10 @@ public function testHttpBearerAuthCustomNoIdentity(): void $controller->run('filtered'); } - public function testHttpBearerAuthCustomNotIdentityInterface(): void + #[Test] + public function httpBearerAuthCustomNotIdentityInterface(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -235,7 +237,7 @@ public function testHttpBearerAuthCustomNotIdentityInterface(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { return new \stdClass(); @@ -243,9 +245,10 @@ public function testHttpBearerAuthCustomNotIdentityInterface(): void $controller->run('filtered'); } - public function testMethodsVisibility(): void + #[Test] + public function methodsVisibility(): void { - $filter = new JwtHttpBearerAuth(['jwt' => $this->getJwt()]); + $filter = new jwt\JwtHttpBearerAuth(['jwt' => $this->getJwt()]); $jwt = $filter->getJwtComponent(); $jwt->getConfiguration()->setValidationConstraints(new IssuedBy('test')); @@ -257,25 +260,29 @@ public function testMethodsVisibility(): void )); } - public function testFailVisibility(): void + #[Test] + public function failVisibility(): void { - $filter = new TestJwtHttpBearerAuth(['jwt' => $this->getJwt()]); - $filter->fail($this->createMock(Response::class)); + $filter = new stubs\TestJwtHttpBearerAuth(['jwt' => $this->getJwt()]); + $filter->fail($this->createMock(web\Response::class)); self::assertSame(2, $filter->flag); } - public function testFailedToken(): void + #[Test] + public function failedToken(): void { $this->expectException(NoConstraintsGiven::class); $logger = $this->createMock(Logger::class); - $logger->expects(self::exactly(2))->method('log')->willReturnCallback( - ConsecutiveCalls::withArgs( - ['Route to run: test-stub2/test', 8, 'yii\base\Controller::runAction'], - ['No constraint given.', 2, 'JwtHttpBearerAuth'], - ) - ); + $logger + ->expects($this->exactly(2)) + ->method('log') + ->with( + new ConsecutiveCalls('Route to run: test-stub2/test', 'No constraint given.'), + new ConsecutiveCalls(8, 2), + new ConsecutiveCalls('yii\base\Controller::runAction', 'JwtHttpBearerAuth') + ); \Yii::setLogger($logger); $token = $this->getJwt()->getBuilder() @@ -290,9 +297,10 @@ public function testFailedToken(): void self::assertSame(14, $controller->flag); } - public function testSilentException(): void + #[Test] + public function silentException(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); // instead of 'The JWT string must have two dots' diff --git a/tests/standard/ConstraintsConfigTest.php b/tests/standard/ConstraintsConfigTest.php index 4054bf4..5369dd1 100644 --- a/tests/standard/ConstraintsConfigTest.php +++ b/tests/standard/ConstraintsConfigTest.php @@ -9,24 +9,19 @@ use bizley\tests\stubs\YiiConstraint; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint; -use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; -use Lcobucci\JWT\Validation\Constraint\RelatedTo; use Lcobucci\JWT\Validation\NoConstraintsGiven; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use yii\base\InvalidConfigException; #[CoversClass(Jwt::class)] #[CoversClass(JwtTools::class)] class ConstraintsConfigTest extends TestCase { - /** - * @param array|\Closure|null $validationConstraints - * @throws InvalidConfigException - */ - private function getJwt($validationConstraints): Jwt + private function getJwt(array|\Closure|null $validationConstraints): Jwt { - return \Yii::createObject( + /** @var Jwt $jwt */ + $jwt = \Yii::createObject( [ 'class' => Jwt::class, 'signer' => Jwt::HS256, @@ -34,6 +29,8 @@ private function getJwt($validationConstraints): Jwt 'validationConstraints' => $validationConstraints, ] ); + + return $jwt; } private function getToken(Jwt $jwt): Token @@ -44,37 +41,42 @@ private function getToken(Jwt $jwt): Token ); } - public function testArrayConfigWithObjects(): void + #[Test] + public function arrayConfigWithObjects(): void { - $jwt = $this->getJwt([new IdentifiedBy('test'), new RelatedTo('test')]); + $jwt = $this->getJwt([new Constraint\IdentifiedBy('test'), new Constraint\RelatedTo('test')]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithArray(): void + #[Test] + public function arrayConfigWithArray(): void { - $jwt = $this->getJwt([[IdentifiedBy::class, ['test']], [RelatedTo::class, ['test']]]); + $jwt = $this->getJwt([[Constraint\IdentifiedBy::class, ['test']], [Constraint\RelatedTo::class, ['test']]]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithYiiArray(): void + #[Test] + public function arrayConfigWithYiiArray(): void { $jwt = $this->getJwt([['class' => YiiConstraint::class, 'test' => 'yii']]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithClosure(): void + #[Test] + public function arrayConfigWithClosure(): void { $jwt = $this->getJwt(static function (Jwt $jwt) { - return [new IdentifiedBy('test'), new RelatedTo('test')]; + return [new Constraint\IdentifiedBy('test'), new Constraint\RelatedTo('test')]; }); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testDefaultConfig(): void + #[Test] + public function defaultConfig(): void { $jwt = $this->getJwt(null); @@ -82,23 +84,25 @@ public function testDefaultConfig(): void $jwt->validate($this->getToken($jwt)); } - public function testArrayConfigWithCustomConstraints(): void + #[Test] + public function arrayConfigWithCustomConstraints(): void { $constraint1 = $this->createMock(Constraint::class); - $constraint1->expects(self::once())->method('assert'); + $constraint1->expects($this->once())->method('assert'); $constraint2 = $this->createMock(Constraint::class); - $constraint2->expects(self::once())->method('assert'); + $constraint2->expects($this->once())->method('assert'); $jwt = $this->getJwt([$constraint1, $constraint2]); $jwt->validate($this->getToken($jwt)); } - public function testDirectConfigWithCustomConstraints(): void + #[Test] + public function directConfigWithCustomConstraints(): void { $constraint1 = $this->createMock(Constraint::class); - $constraint1->expects(self::once())->method('assert'); + $constraint1->expects($this->once())->method('assert'); $constraint2 = $this->createMock(Constraint::class); - $constraint2->expects(self::once())->method('assert'); + $constraint2->expects($this->once())->method('assert'); $jwt = $this->getJwt(null); $jwt->getConfiguration()->setValidationConstraints($constraint1, $constraint2); diff --git a/tests/standard/JwtTest.php b/tests/standard/JwtTest.php index 1912570..56e3850 100644 --- a/tests/standard/JwtTest.php +++ b/tests/standard/JwtTest.php @@ -7,19 +7,15 @@ use bizley\jwt\Jwt; use bizley\jwt\JwtTools; use bizley\tests\stubs\JwtStub; -use Lcobucci\JWT\Decoder; -use Lcobucci\JWT\Encoder; -use Lcobucci\JWT\Signer; +use Lcobucci\JWT as BaseJwt; use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[Attributes\CoversClass(Jwt::class)] +#[Attributes\CoversClass(JwtTools::class)] class JwtTest extends TestCase { private function getJwt(): Jwt @@ -32,7 +28,8 @@ private function getJwt(): Jwt ); } - public function testAvailableAlgorithmTypes(): void + #[Attributes\Test] + public function availableAlgorithmTypes(): void { self::assertSame( [ @@ -56,14 +53,16 @@ public function testAvailableAlgorithmTypes(): void ); } - public function testNoInit(): void + #[Attributes\Test] + public function noInit(): void { $this->expectException(InvalidConfigException::class); $jwtStub = new JwtStub(); $jwtStub->getConfiguration(); } - public function testValidateSuccess(): void + #[Attributes\Test] + public function validateSuccess(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -72,7 +71,8 @@ public function testValidateSuccess(): void self::assertTrue($jwt->validate($token)); } - public function testValidateSuccessWithStringToken(): void + #[Attributes\Test] + public function validateSuccessWithStringToken(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -84,7 +84,8 @@ public function testValidateSuccessWithStringToken(): void self::assertTrue($jwt->validate($token)); } - public function testValidateFail(): void + #[Attributes\Test] + public function validateFail(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -93,8 +94,9 @@ public function testValidateFail(): void self::assertFalse($jwt->validate($token)); } - #[DoesNotPerformAssertions] - public function testAssertSuccess(): void + #[Attributes\DoesNotPerformAssertions] + #[Attributes\Test] + public function assertSuccess(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -103,8 +105,9 @@ public function testAssertSuccess(): void $jwt->assert($token); } - #[DoesNotPerformAssertions] - public function testAssertSuccessWithStringToken(): void + #[Attributes\DoesNotPerformAssertions] + #[Attributes\Test] + public function assertSuccessWithStringToken(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -116,7 +119,8 @@ public function testAssertSuccessWithStringToken(): void $jwt->assert($token); } - public function testAssertFail(): void + #[Attributes\Test] + public function assertFail(): void { $jwt = $this->getJwt(); $config = $jwt->getConfiguration(); @@ -136,12 +140,17 @@ public static function providerForInvalidKey(): iterable yield 'int pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => 1], 'Invalid key passphrase!']; yield 'array pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => []], 'Invalid key passphrase!']; yield 'object pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => new \stdClass()], 'Invalid key passphrase!']; + yield 'empty string' => [[Jwt::KEY => ''], 'Invalid key value!']; + yield '@' => [[Jwt::KEY => '@'], 'Invalid path alias: @']; + yield 'empty alias' => [[Jwt::KEY => '@emptyString'], 'Yii alias was resolved as an invalid key value!']; } - #[DataProvider('providerForInvalidKey')] - public function testInvalidKey($key, string $message): void + #[Attributes\DataProvider('providerForInvalidKey')] + #[Attributes\Test] + public function invalidKey($key, string $message): void { $this->expectExceptionMessage($message); + \Yii::setAlias('@emptyString', ''); new Jwt( [ 'signer' => Jwt::HS256, @@ -150,10 +159,11 @@ public function testInvalidKey($key, string $message): void ); } - public function testCustomEncoder(): void + #[Attributes\Test] + public function customEncoder(): void { - $encoder = $this->createMock(Encoder::class); - $encoder->expects(self::exactly(3))->method('base64UrlEncode'); + $encoder = $this->createMock(BaseJwt\Encoder::class); + $encoder->expects($this->exactly(3))->method('base64UrlEncode'); $jwt = new Jwt( [ @@ -165,11 +175,12 @@ public function testCustomEncoder(): void $jwt->getBuilder()->getToken($jwt->getConfiguration()->signer(), $jwt->getConfiguration()->signingKey()); } - public function testCustomDecoder(): void + #[Attributes\Test] + public function customDecoder(): void { - $decoder = $this->createMock(Decoder::class); + $decoder = $this->createMock(BaseJwt\Decoder::class); $decoder->method('jsonDecode')->willReturn([]); - $decoder->expects(self::exactly(3))->method('base64UrlDecode'); + $decoder->expects($this->exactly(3))->method('base64UrlDecode'); $jwt = new Jwt( [ @@ -186,7 +197,8 @@ public function testCustomDecoder(): void ); } - public function testMethodsVisibility(): void + #[Attributes\Test] + public function methodsVisibility(): void { $jwt = $this->getJwt(); self::assertNotEmpty($jwt->getConfiguration()); @@ -200,8 +212,9 @@ public static function providerForWrongKeyNames(): iterable yield 'file://' => ['_file://' . __DIR__ . '/data/rs256.key']; } - #[DataProvider('providerForWrongKeyNames')] - public function testWrongFileKeyNameStartingCharacters(string $key): void + #[Attributes\DataProvider('providerForWrongKeyNames')] + #[Attributes\Test] + public function wrongFileKeyNameStartingCharacters(string $key): void { $jwt = new Jwt( [ @@ -216,11 +229,16 @@ public function testWrongFileKeyNameStartingCharacters(string $key): void public static function providerForRightKeyNames(): iterable { yield '@' => ['@bizley/tests/data/rs256.key']; + yield '@ in array' => [[ + Jwt::KEY => '@bizley/tests/data/rs256.key', + Jwt::METHOD => Jwt::METHOD_FILE, + ]]; yield 'file://' => ['file://' . __DIR__ . '/../data/rs256.key']; } - #[DataProvider('providerForRightKeyNames')] - public function testRightFileKeyNameStartingCharacters(string $key): void + #[Attributes\DataProvider('providerForRightKeyNames')] + #[Attributes\Test] + public function rightFileKeyNameStartingCharacters(string|array $key): void { $jwt = new Jwt( [ @@ -293,13 +311,14 @@ public static function providerForSignerSignatureConverter(): iterable yield Jwt::ES512 => [Jwt::ES512]; } - #[DataProvider('providerForSignerSignatureConverter')] - public function testPrepareSignatureConverter(string $signerId): void + #[Attributes\DataProvider('providerForSignerSignatureConverter')] + #[Attributes\Test] + public function prepareSignatureConverter(string $signerId): void { new Jwt(['signer' => $signerId, 'signingKey' => ' ', 'verifyingKey' => ' ']); $this->assertInstanceOf( - Signer\Ecdsa\MultibyteStringConverter::class, - \Yii::$container->get(Signer\Ecdsa\SignatureConverter::class) + BaseJwt\Signer\Ecdsa\MultibyteStringConverter::class, + \Yii::$container->get(BaseJwt\Signer\Ecdsa\SignatureConverter::class) ); } } diff --git a/tests/standard/SignerTest.php b/tests/standard/SignerTest.php index db22a77..1c21612 100644 --- a/tests/standard/SignerTest.php +++ b/tests/standard/SignerTest.php @@ -10,18 +10,14 @@ use Lcobucci\JWT\Signer\InvalidKeyProvided; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Validation\Constraint\SignedWith; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[Attributes\CoversClass(Jwt::class)] +#[Attributes\CoversClass(JwtTools::class)] class SignerTest extends TestCase { - /** - * @throws InvalidConfigException - */ public function getJwt(array $config = []): Jwt { /** @var Jwt $jwt */ @@ -181,8 +177,9 @@ public static function providerForSigners(): iterable ]; } - #[DataProvider('providerForSigners')] - public function testParseTokenWithSignature(array $config, string $algorithm): void + #[Attributes\DataProvider('providerForSigners')] + #[Attributes\Test] + public function parseTokenWithSignature(array $config, string $algorithm): void { $jwt = $this->getJwt($config); $signer = $jwt->getConfiguration()->signer(); @@ -197,14 +194,16 @@ public function testParseTokenWithSignature(array $config, string $algorithm): v ); } - public function testInvalidSignerId(): void + #[Attributes\Test] + public function invalidSignerId(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessage('Invalid signer ID!'); $this->getJwt(['signer' => 'Invalid']); } - public function testEmptyKey(): void + #[Attributes\Test] + public function emptyKey(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessage('Empty string used as a key configuration!'); @@ -216,7 +215,8 @@ public function testEmptyKey(): void ); } - public function testInvalidKeyWithNotEnoughBits(): void + #[Attributes\Test] + public function invalidKeyWithNotEnoughBits(): void { $this->expectException(InvalidKeyProvided::class); $this->expectExceptionMessage('Key provided is shorter than 256 bits, only 56 bits provided'); diff --git a/tests/toolset/BearerTest.php b/tests/toolset/BearerTest.php index ba4ca12..285b0a1 100644 --- a/tests/toolset/BearerTest.php +++ b/tests/toolset/BearerTest.php @@ -4,44 +4,37 @@ namespace bizley\tests\toolset; -use bizley\jwt\Jwt; -use bizley\jwt\JwtHttpBearerAuth; -use bizley\jwt\JwtTools; +use bizley\jwt; use bizley\tests\ConsecutiveCalls; -use bizley\tests\stubs\TestAuthController; -use bizley\tests\stubs\TestJwtHttpBearerAuth; -use bizley\tests\stubs\TestStub2Controller; -use bizley\tests\stubs\TestStubController; -use bizley\tests\stubs\UserIdentity; +use bizley\tests\stubs; use Lcobucci\Clock\SystemClock; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint\IssuedBy; use Lcobucci\JWT\Validation\Constraint\LooseValidAt; use Lcobucci\JWT\Validation\NoConstraintsGiven; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; use yii\log\Logger; use yii\rest\Controller; -use yii\web\Application; -use yii\web\Response; -use yii\web\UnauthorizedHttpException; +use yii\web; -#[CoversClass(JwtHttpBearerAuth::class)] -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[CoversClass(jwt\JwtHttpBearerAuth::class)] +#[CoversClass(jwt\Jwt::class)] +#[CoversClass(jwt\JwtTools::class)] class BearerTest extends TestCase { protected function setUp(): void { - new Application( + new web\Application( [ 'id' => 'test', 'basePath' => __DIR__, 'vendorPath' => __DIR__ . '/../vendor', 'components' => [ 'user' => [ - 'identityClass' => UserIdentity::class, + 'identityClass' => stubs\UserIdentity::class, 'enableSession' => false, ], 'request' => [ @@ -50,33 +43,35 @@ protected function setUp(): void 'scriptUrl' => '/index.php', ], 'jwt' => [ - 'class' => JwtTools::class, + 'class' => jwt\JwtTools::class, ], ], 'controllerMap' => [ - 'test-auth' => TestAuthController::class, - 'test-stub' => TestStubController::class, - 'test-stub2' => TestStub2Controller::class, + 'test-auth' => stubs\TestAuthController::class, + 'test-stub' => stubs\TestStubController::class, + 'test-stub2' => stubs\TestStub2Controller::class, ], ] ); } - protected function getJwt(): JwtTools + protected function getJwt(): jwt\JwtTools { return \Yii::$app->jwt; } - public function testEmptyPattern(): void + #[Test] + public function emptyPattern(): void { $this->expectException(InvalidConfigException::class); $controller = \Yii::$app->createController('test-stub')[0]; $controller->run('test'); } - public function testHttpBearerAuthNoHeader(): void + #[Test] + public function httpBearerAuthNoHeader(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); /* @var $controller Controller */ @@ -84,7 +79,8 @@ public function testHttpBearerAuthNoHeader(): void $controller->run('filtered'); } - public function testHttpBearerAuthInvalidToken(): void + #[Test] + public function httpBearerAuthInvalidToken(): void { $this->expectException(Token\InvalidTokenStructure::class); $this->expectExceptionMessage('The JWT string must have two dots'); @@ -96,9 +92,10 @@ public function testHttpBearerAuthInvalidToken(): void $controller->run('filtered'); } - public function testHttpBearerAuthInvalidHeader(): void + #[Test] + public function httpBearerAuthInvalidHeader(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); \Yii::$app->request->headers->set('Authorization', 'InvalidHeaderValue'); @@ -108,9 +105,10 @@ public function testHttpBearerAuthInvalidHeader(): void $controller->run('filtered'); } - public function testHttpBearerAuthExpiredToken(): void + #[Test] + public function httpBearerAuthExpiredToken(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -121,7 +119,7 @@ public function testHttpBearerAuthExpiredToken(): void ->issuedAt($now->modify('-10 minutes')) ->expiresAt($now->modify('-5 minutes')) ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ) ->toString(); @@ -133,7 +131,8 @@ public function testHttpBearerAuthExpiredToken(): void $controller->run('filtered'); } - public function testHttpBearerAuth(): void + #[Test] + public function httpBearerAuth(): void { $now = new \DateTimeImmutable(); @@ -147,12 +146,12 @@ public function testHttpBearerAuth(): void ->issuedBy('test') ->expiresAt($now->modify('+1 hour')) ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ) ->toString(); - UserIdentity::$token = $token; + stubs\UserIdentity::$token = $token; \Yii::$app->request->headers->set('Authorization', "Bearer $token"); @@ -162,7 +161,8 @@ public function testHttpBearerAuth(): void self::assertEquals('test', $controller->run('filtered')); } - public function testHttpBearerAuthCustom(): void + #[Test] + public function httpBearerAuthCustom(): void { $now = new \DateTimeImmutable(); @@ -173,7 +173,7 @@ public function testHttpBearerAuthCustom(): void ->issuedAt($now) ->expiresAt($now->modify('+1 hour')) ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ); @@ -181,10 +181,10 @@ public function testHttpBearerAuthCustom(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { - $identity = UserIdentity::findIdentity($token->claims()->get('sub')); + $identity = stubs\UserIdentity::findIdentity($token->claims()->get('sub')); \Yii::$app->user->switchIdentity($identity); return $identity; }; @@ -192,9 +192,10 @@ public function testHttpBearerAuthCustom(): void self::assertEquals('test', $controller->run('filtered')); } - public function testHttpBearerAuthCustomNoIdentity(): void + #[Test] + public function httpBearerAuthCustomNoIdentity(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -206,7 +207,7 @@ public function testHttpBearerAuthCustomNoIdentity(): void ->issuedAt($now) ->expiresAt($now->modify('+1 hour')) ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ); @@ -214,7 +215,7 @@ public function testHttpBearerAuthCustomNoIdentity(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { return null; @@ -222,9 +223,10 @@ public function testHttpBearerAuthCustomNoIdentity(): void $controller->run('filtered'); } - public function testHttpBearerAuthCustomNotIdentityInterface(): void + #[Test] + public function httpBearerAuthCustomNotIdentityInterface(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); $now = new \DateTimeImmutable(); @@ -236,7 +238,7 @@ public function testHttpBearerAuthCustomNotIdentityInterface(): void ->issuedAt($now) ->expiresAt($now->modify('+1 hour')) ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ); @@ -244,7 +246,7 @@ public function testHttpBearerAuthCustomNotIdentityInterface(): void \Yii::$app->request->headers->set('Authorization', "Bearer $JWT"); - /** @var TestAuthController $controller */ + /** @var stubs\TestAuthController $controller */ $controller = \Yii::$app->createController('test-auth')[0]; $controller->filterConfig['auth'] = static function (Token $token) { return new \stdClass(); @@ -252,44 +254,49 @@ public function testHttpBearerAuthCustomNotIdentityInterface(): void $controller->run('filtered'); } - public function testMethodsVisibility(): void + #[Test] + public function methodsVisibility(): void { - $filter = new JwtHttpBearerAuth(['jwt' => $this->getJwt()]); + $filter = new jwt\JwtHttpBearerAuth(['jwt' => $this->getJwt()]); $jwt = $filter->getJwtComponent(); $jwt->validationConstraints = [new IssuedBy('test')]; self::assertNotEmpty($filter->processToken( $jwt->getBuilder()->issuedBy('test')->getToken( - $jwt->buildSigner(Jwt::HS256), + $jwt->buildSigner(jwt\Jwt::HS256), $jwt->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') )->toString() )); } - public function testFailVisibility(): void + #[Test] + public function failVisibility(): void { - $filter = new TestJwtHttpBearerAuth(['jwt' => $this->getJwt()]); - $filter->fail($this->createMock(Response::class)); + $filter = new stubs\TestJwtHttpBearerAuth(['jwt' => $this->getJwt()]); + $filter->fail($this->createMock(web\Response::class)); self::assertSame(2, $filter->flag); } - public function testFailedToken(): void + #[Test] + public function failedToken(): void { $this->expectException(NoConstraintsGiven::class); $logger = $this->createMock(Logger::class); - $logger->expects(self::exactly(2))->method('log')->willReturnCallback( - ConsecutiveCalls::withArgs( - ['Route to run: test-stub2/test', 8, 'yii\base\Controller::runAction'], - ['No constraint given.', 2, 'JwtHttpBearerAuth'], - ) - ); + $logger + ->expects($this->exactly(2)) + ->method('log') + ->with( + new ConsecutiveCalls('Route to run: test-stub2/test', 'No constraint given.'), + new ConsecutiveCalls(8, 2), + new ConsecutiveCalls('yii\base\Controller::runAction', 'JwtHttpBearerAuth') + ); \Yii::setLogger($logger); $token = $this->getJwt()->getBuilder() ->getToken( - $this->getJwt()->buildSigner(Jwt::HS256), + $this->getJwt()->buildSigner(jwt\Jwt::HS256), $this->getJwt()->buildKey('c2VjcmV0MXNlY3JldDFzZWNyZXQxc2VjcmV0M') ) ->toString(); @@ -302,9 +309,10 @@ public function testFailedToken(): void self::assertSame(14, $controller->flag); } - public function testSilentException(): void + #[Test] + public function silentException(): void { - $this->expectException(UnauthorizedHttpException::class); + $this->expectException(web\UnauthorizedHttpException::class); $this->expectExceptionMessage('Your request was made with invalid or expired JSON Web Token.'); // instead of 'The JWT string must have two dots' diff --git a/tests/toolset/ConstraintsConfigTest.php b/tests/toolset/ConstraintsConfigTest.php index 1357f3e..1982dfa 100644 --- a/tests/toolset/ConstraintsConfigTest.php +++ b/tests/toolset/ConstraintsConfigTest.php @@ -9,29 +9,26 @@ use bizley\tests\stubs\YiiConstraint; use Lcobucci\JWT\Token; use Lcobucci\JWT\Validation\Constraint; -use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; -use Lcobucci\JWT\Validation\Constraint\RelatedTo; use Lcobucci\JWT\Validation\NoConstraintsGiven; use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use yii\base\InvalidConfigException; #[CoversClass(Jwt::class)] #[CoversClass(JwtTools::class)] class ConstraintsConfigTest extends TestCase { - /** - * @param array|\Closure|null $validationConstraints - * @throws InvalidConfigException - */ - private function getJwt($validationConstraints): JwtTools + private function getJwt(array|\Closure|null $validationConstraints): JwtTools { - return \Yii::createObject( + /** @var JwtTools $jwt */ + $jwt = \Yii::createObject( [ 'class' => JwtTools::class, 'validationConstraints' => $validationConstraints, ] ); + + return $jwt; } private function getToken(JwtTools $jwt): Token @@ -42,37 +39,42 @@ private function getToken(JwtTools $jwt): Token ); } - public function testArrayConfigWithObjects(): void + #[Test] + public function arrayConfigWithObjects(): void { - $jwt = $this->getJwt([new IdentifiedBy('test'), new RelatedTo('test')]); + $jwt = $this->getJwt([new Constraint\IdentifiedBy('test'), new Constraint\RelatedTo('test')]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithArray(): void + #[Test] + public function arrayConfigWithArray(): void { - $jwt = $this->getJwt([[IdentifiedBy::class, ['test']], [RelatedTo::class, ['test']]]); + $jwt = $this->getJwt([[Constraint\IdentifiedBy::class, ['test']], [Constraint\RelatedTo::class, ['test']]]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithYiiArray(): void + #[Test] + public function arrayConfigWithYiiArray(): void { $jwt = $this->getJwt([['class' => YiiConstraint::class, 'test' => 'yii']]); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testArrayConfigWithClosure(): void + #[Test] + public function arrayConfigWithClosure(): void { $jwt = $this->getJwt(static function (JwtTools $jwt) { - return [new IdentifiedBy('test'), new RelatedTo('test')]; + return [new Constraint\IdentifiedBy('test'), new Constraint\RelatedTo('test')]; }); self::assertTrue($jwt->validate($this->getToken($jwt))); } - public function testDefaultConfig(): void + #[Test] + public function defaultConfig(): void { $jwt = $this->getJwt(null); @@ -80,23 +82,25 @@ public function testDefaultConfig(): void $jwt->validate($this->getToken($jwt)); } - public function testArrayConfigWithCustomConstraints(): void + #[Test] + public function arrayConfigWithCustomConstraints(): void { $constraint1 = $this->createMock(Constraint::class); - $constraint1->expects(self::once())->method('assert'); + $constraint1->expects($this->once())->method('assert'); $constraint2 = $this->createMock(Constraint::class); - $constraint2->expects(self::once())->method('assert'); + $constraint2->expects($this->once())->method('assert'); $jwt = $this->getJwt([$constraint1, $constraint2]); $jwt->validate($this->getToken($jwt)); } - public function testDirectConfigWithCustomConstraints(): void + #[Test] + public function directConfigWithCustomConstraints(): void { $constraint1 = $this->createMock(Constraint::class); - $constraint1->expects(self::once())->method('assert'); + $constraint1->expects($this->once())->method('assert'); $constraint2 = $this->createMock(Constraint::class); - $constraint2->expects(self::once())->method('assert'); + $constraint2->expects($this->once())->method('assert'); $jwt = $this->getJwt(null); $jwt->validationConstraints = [$constraint1, $constraint2]; diff --git a/tests/toolset/JwtToolsTest.php b/tests/toolset/JwtToolsTest.php index a11f144..77398d2 100644 --- a/tests/toolset/JwtToolsTest.php +++ b/tests/toolset/JwtToolsTest.php @@ -6,19 +6,14 @@ use bizley\jwt\Jwt; use bizley\jwt\JwtTools; -use Lcobucci\JWT\ClaimsFormatter; -use Lcobucci\JWT\Decoder; -use Lcobucci\JWT\Encoder; -use Lcobucci\JWT\Signer; +use Lcobucci\JWT as BaseJwt; use Lcobucci\JWT\Validation\Constraint\IdentifiedBy; use Lcobucci\JWT\Validation\RequiredConstraintsViolated; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes; use PHPUnit\Framework\TestCase; -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[Attributes\CoversClass(Jwt::class)] +#[Attributes\CoversClass(JwtTools::class)] class JwtToolsTest extends TestCase { private function getJwt(): JwtTools @@ -26,27 +21,29 @@ private function getJwt(): JwtTools return new JwtTools(); } - public function testAvailableSigners(): void + #[Attributes\Test] + public function availableSigners(): void { self::assertSame( [ - Jwt::HS256 => [Signer\Hmac\Sha256::class], - Jwt::HS384 => [Signer\Hmac\Sha384::class], - Jwt::HS512 => [Signer\Hmac\Sha512::class], - Jwt::RS256 => [Signer\Rsa\Sha256::class], - Jwt::RS384 => [Signer\Rsa\Sha384::class], - Jwt::RS512 => [Signer\Rsa\Sha512::class], - Jwt::ES256 => [Signer\Ecdsa\Sha256::class], - Jwt::ES384 => [Signer\Ecdsa\Sha384::class], - Jwt::ES512 => [Signer\Ecdsa\Sha512::class], - Jwt::EDDSA => [Signer\Eddsa::class], - Jwt::BLAKE2B => [Signer\Blake2b::class], + Jwt::HS256 => [BaseJwt\Signer\Hmac\Sha256::class], + Jwt::HS384 => [BaseJwt\Signer\Hmac\Sha384::class], + Jwt::HS512 => [BaseJwt\Signer\Hmac\Sha512::class], + Jwt::RS256 => [BaseJwt\Signer\Rsa\Sha256::class], + Jwt::RS384 => [BaseJwt\Signer\Rsa\Sha384::class], + Jwt::RS512 => [BaseJwt\Signer\Rsa\Sha512::class], + Jwt::ES256 => [BaseJwt\Signer\Ecdsa\Sha256::class], + Jwt::ES384 => [BaseJwt\Signer\Ecdsa\Sha384::class], + Jwt::ES512 => [BaseJwt\Signer\Ecdsa\Sha512::class], + Jwt::EDDSA => [BaseJwt\Signer\Eddsa::class], + Jwt::BLAKE2B => [BaseJwt\Signer\Blake2b::class], ], $this->getJwt()->signers, ); } - public function testValidateSuccess(): void + #[Attributes\Test] + public function validateSuccess(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -57,7 +54,8 @@ public function testValidateSuccess(): void self::assertTrue($jwt->validate($token)); } - public function testValidateSuccessWithStringToken(): void + #[Attributes\Test] + public function validateSuccessWithStringToken(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -68,7 +66,8 @@ public function testValidateSuccessWithStringToken(): void self::assertTrue($jwt->validate($token)); } - public function testValidateFail(): void + #[Attributes\Test] + public function validateFail(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -79,8 +78,9 @@ public function testValidateFail(): void self::assertFalse($jwt->validate($token)); } - #[DoesNotPerformAssertions] - public function testAssertSuccess(): void + #[Attributes\DoesNotPerformAssertions] + #[Attributes\Test] + public function assertSuccess(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -91,8 +91,9 @@ public function testAssertSuccess(): void $jwt->assert($token); } - #[DoesNotPerformAssertions] - public function testAssertSuccessWithStringToken(): void + #[Attributes\DoesNotPerformAssertions] + #[Attributes\Test] + public function assertSuccessWithStringToken(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -103,7 +104,8 @@ public function testAssertSuccessWithStringToken(): void $jwt->assert($token); } - public function testAssertFail(): void + #[Attributes\Test] + public function assertFail(): void { $jwt = $this->getJwt(); $jwt->validationConstraints = [new IdentifiedBy('abc')]; @@ -125,19 +127,25 @@ public static function providerForInvalidKey(): iterable yield 'int pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => 1], 'Invalid key passphrase!']; yield 'array pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => []], 'Invalid key passphrase!']; yield 'object pass' => [[Jwt::KEY => 'k', Jwt::PASSPHRASE => new \stdClass()], 'Invalid key passphrase!']; + yield 'empty string' => [[Jwt::KEY => ''], 'Invalid key value!']; + yield '@' => [[Jwt::KEY => '@'], 'Invalid path alias: @']; + yield 'empty alias' => [[Jwt::KEY => '@emptyString'], 'Yii alias was resolved as an invalid key value!']; } - #[DataProvider('providerForInvalidKey')] - public function testInvalidKey($key, string $message): void + #[Attributes\DataProvider('providerForInvalidKey')] + #[Attributes\Test] + public function invalidKey($key, string $message): void { $this->expectExceptionMessage($message); + \Yii::setAlias('@emptyString', ''); (new JwtTools())->buildKey($key); } - public function testCustomEncoder(): void + #[Attributes\Test] + public function customEncoder(): void { - $encoder = $this->createMock(Encoder::class); - $encoder->expects(self::exactly(3))->method('base64UrlEncode'); + $encoder = $this->createMock(BaseJwt\Encoder::class); + $encoder->expects($this->exactly(3))->method('base64UrlEncode'); $jwt = new JwtTools(['encoder' => $encoder]); $jwt->getBuilder()->getToken( @@ -146,11 +154,12 @@ public function testCustomEncoder(): void ); } - public function testCustomDecoder(): void + #[Attributes\Test] + public function customDecoder(): void { - $decoder = $this->createMock(Decoder::class); + $decoder = $this->createMock(BaseJwt\Decoder::class); $decoder->method('jsonDecode')->willReturn([]); - $decoder->expects(self::exactly(3))->method('base64UrlDecode'); + $decoder->expects($this->exactly(3))->method('base64UrlDecode'); $jwt = new Jwt( [ @@ -167,7 +176,8 @@ public function testCustomDecoder(): void ); } - public function testMethodsVisibility(): void + #[Attributes\Test] + public function methodsVisibility(): void { $jwt = $this->getJwt(); self::assertNotEmpty($jwt->getBuilder()); @@ -180,8 +190,9 @@ public static function providerForWrongKeyNames(): iterable yield 'file://' => ['_file://' . __DIR__ . '/data/rs256.key']; } - #[DataProvider('providerForWrongKeyNames')] - public function testWrongFileKeyNameStartingCharacters(string $key): void + #[Attributes\DataProvider('providerForWrongKeyNames')] + #[Attributes\Test] + public function wrongFileKeyNameStartingCharacters(string $key): void { $jwt = new Jwt( [ @@ -196,11 +207,16 @@ public function testWrongFileKeyNameStartingCharacters(string $key): void public static function providerForRightKeyNames(): iterable { yield '@' => ['@bizley/tests/data/rs256.key']; + yield '@ in array' => [[ + Jwt::KEY => '@bizley/tests/data/rs256.key', + Jwt::METHOD => Jwt::METHOD_FILE, + ]]; yield 'file://' => ['file://' . __DIR__ . '/../data/rs256.key']; } - #[DataProvider('providerForRightKeyNames')] - public function testRightFileKeyNameStartingCharacters(string $key): void + #[Attributes\DataProvider('providerForRightKeyNames')] + #[Attributes\Test] + public function rightFileKeyNameStartingCharacters(string|array $key): void { $jwt = new Jwt( [ @@ -273,20 +289,22 @@ public static function providerForSignerSignatureConverter(): iterable yield Jwt::ES512 => [Jwt::ES512]; } - #[DataProvider('providerForSignerSignatureConverter')] - public function testPrepareSignatureConverter(string $signerId): void + #[Attributes\DataProvider('providerForSignerSignatureConverter')] + #[Attributes\Test] + public function prepareSignatureConverter(string $signerId): void { - \Yii::$container->clear(Signer\Ecdsa\SignatureConverter::class); + \Yii::$container->clear(BaseJwt\Signer\Ecdsa\SignatureConverter::class); new Jwt(['signer' => $signerId, 'signingKey' => ' ', 'verifyingKey' => ' ']); $this->assertInstanceOf( - Signer\Ecdsa\MultibyteStringConverter::class, - \Yii::$container->get(Signer\Ecdsa\SignatureConverter::class) + BaseJwt\Signer\Ecdsa\MultibyteStringConverter::class, + \Yii::$container->get(BaseJwt\Signer\Ecdsa\SignatureConverter::class) ); } - public function testBuilderWithCustomClaimsFormatter(): void + #[Attributes\Test] + public function builderWithCustomClaimsFormatter(): void { - $formatter = $this->createMock(ClaimsFormatter::class); + $formatter = $this->createMock(BaseJwt\ClaimsFormatter::class); $formatter->expects($this->once())->method('formatClaims'); $this->getJwt()->getBuilder($formatter)->getToken( $this->getJwt()->buildSigner(Jwt::HS256), diff --git a/tests/toolset/SignerTest.php b/tests/toolset/SignerTest.php index 459cef4..5283a5e 100644 --- a/tests/toolset/SignerTest.php +++ b/tests/toolset/SignerTest.php @@ -10,18 +10,14 @@ use Lcobucci\JWT\Signer\InvalidKeyProvided; use Lcobucci\JWT\Signer\Key\InMemory; use Lcobucci\JWT\Validation\Constraint\SignedWith; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes; use PHPUnit\Framework\TestCase; use yii\base\InvalidConfigException; -#[CoversClass(Jwt::class)] -#[CoversClass(JwtTools::class)] +#[Attributes\CoversClass(Jwt::class)] +#[Attributes\CoversClass(JwtTools::class)] class SignerTest extends TestCase { - /** - * @throws InvalidConfigException - */ public function getJwt(array $config = []): JwtTools { /** @var JwtTools $jwt */ @@ -156,8 +152,9 @@ public static function providerForSigners(): iterable ]; } - #[DataProvider('providerForSigners')] - public function testParseTokenWithSignature( + #[Attributes\DataProvider('providerForSigners')] + #[Attributes\Test] + public function parseTokenWithSignature( string|object $signer, string|array|object $signingKey, string|array|null $verifyingKey, @@ -179,21 +176,24 @@ public function testParseTokenWithSignature( ); } - public function testInvalidSignerId(): void + #[Attributes\Test] + public function invalidSignerId(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessage('Invalid signer ID!'); $this->getJwt()->buildSigner('Invalid'); } - public function testEmptyKey(): void + #[Attributes\Test] + public function emptyKey(): void { $this->expectException(InvalidConfigException::class); $this->expectExceptionMessage('Empty string used as a key configuration!'); $this->getJwt()->buildKey(''); } - public function testInvalidKeyWithNotEnoughBits(): void + #[Attributes\Test] + public function invalidKeyWithNotEnoughBits(): void { $this->expectException(InvalidKeyProvided::class); $this->expectExceptionMessage('Key provided is shorter than 256 bits, only 56 bits provided');