welcome back to dyb-tech

This commit is contained in:
Daniel Guzman
2024-05-18 02:28:01 +02:00
parent 9513cdba09
commit 9f30bc98c7
6149 changed files with 668407 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
interface Constraint
{
/** @throws ConstraintViolation */
public function assert(Token $token): void;
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class CannotValidateARegisteredClaim extends InvalidArgumentException implements Exception
{
/** @param non-empty-string $claim */
public static function create(string $claim): self
{
return new self(
'The claim "' . $claim . '" is a registered claim, another constraint must be used to validate its value',
);
}
}
@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
use function in_array;
final class HasClaimWithValue implements Constraint
{
/** @param non-empty-string $claim */
public function __construct(private readonly string $claim, private readonly mixed $expectedValue)
{
if (in_array($claim, Token\RegisteredClaims::ALL, true)) {
throw CannotValidateARegisteredClaim::create($claim);
}
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$claims = $token->claims();
if (! $claims->has($this->claim)) {
throw ConstraintViolation::error('The token does not have the claim "' . $this->claim . '"', $this);
}
if ($claims->get($this->claim) !== $this->expectedValue) {
throw ConstraintViolation::error(
'The claim "' . $this->claim . '" does not have the expected value',
$this,
);
}
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class IdentifiedBy implements Constraint
{
/** @param non-empty-string $id */
public function __construct(private readonly string $id)
{
}
public function assert(Token $token): void
{
if (! $token->isIdentifiedBy($this->id)) {
throw ConstraintViolation::error(
'The token is not identified with the expected ID',
$this,
);
}
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class IssuedBy implements Constraint
{
/** @var non-empty-string[] */
private readonly array $issuers;
/** @param non-empty-string ...$issuers */
public function __construct(string ...$issuers)
{
$this->issuers = $issuers;
}
public function assert(Token $token): void
{
if (! $token->hasBeenIssuedBy(...$this->issuers)) {
throw ConstraintViolation::error(
'The token was not issued by the given issuers',
$this,
);
}
}
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use InvalidArgumentException;
use Lcobucci\JWT\Exception;
final class LeewayCannotBeNegative extends InvalidArgumentException implements Exception
{
public static function create(): self
{
return new self('Leeway cannot be negative');
}
}
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final class LooseValidAt implements ValidAtInterface
{
private readonly DateInterval $leeway;
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(Token $token, DateTimeInterface $now): void
{
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
{
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(Token $token, DateTimeInterface $now): void
{
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class PermittedFor implements Constraint
{
/** @param non-empty-string $audience */
public function __construct(private readonly string $audience)
{
}
public function assert(Token $token): void
{
if (! $token->isPermittedFor($this->audience)) {
throw ConstraintViolation::error(
'The token is not allowed to be used by this audience',
$this,
);
}
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolation;
final class RelatedTo implements Constraint
{
/** @param non-empty-string $subject */
public function __construct(private readonly string $subject)
{
}
public function assert(Token $token): void
{
if (! $token->isRelatedTo($this->subject)) {
throw ConstraintViolation::error(
'The token is not related to the expected subject',
$this,
);
}
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
final class SignedWith implements SignedWithInterface
{
public function __construct(private readonly Signer $signer, private readonly Signer\Key $key)
{
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
if ($token->headers()->get('alg') !== $this->signer->algorithmId()) {
throw ConstraintViolation::error('Token signer mismatch', $this);
}
if (! $this->signer->verify($token->signature()->hash(), $token->payload(), $this->key)) {
throw ConstraintViolation::error('Token signature mismatch', $this);
}
}
}
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use const PHP_EOL;
final class SignedWithOneInSet implements SignedWithInterface
{
/** @var array<SignedWithUntilDate> */
private readonly array $constraints;
public function __construct(SignedWithUntilDate ...$constraints)
{
$this->constraints = $constraints;
}
public function assert(Token $token): void
{
$errorMessage = 'It was not possible to verify the signature of the token, reasons:';
foreach ($this->constraints as $constraint) {
try {
$constraint->assert($token);
return;
} catch (ConstraintViolation $violation) {
$errorMessage .= PHP_EOL . '- ' . $violation->getMessage();
}
}
throw ConstraintViolation::error($errorMessage, $this);
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateTimeImmutable;
use DateTimeInterface;
use Lcobucci\JWT\Signer;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\SignedWith as SignedWithInterface;
use Psr\Clock\ClockInterface;
final class SignedWithUntilDate implements SignedWithInterface
{
private readonly SignedWith $verifySignature;
private readonly ClockInterface $clock;
public function __construct(
Signer $signer,
Signer\Key $key,
private readonly DateTimeImmutable $validUntil,
?ClockInterface $clock = null,
) {
$this->verifySignature = new SignedWith($signer, $key);
$this->clock = $clock ?? new class () implements ClockInterface {
public function now(): DateTimeImmutable
{
return new DateTimeImmutable();
}
};
}
public function assert(Token $token): void
{
if ($this->validUntil < $this->clock->now()) {
throw ConstraintViolation::error(
'This constraint was only usable until '
. $this->validUntil->format(DateTimeInterface::RFC3339),
$this,
);
}
$this->verifySignature->assert($token);
}
}
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation\Constraint;
use DateInterval;
use DateTimeInterface;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\UnencryptedToken;
use Lcobucci\JWT\Validation\ConstraintViolation;
use Lcobucci\JWT\Validation\ValidAt as ValidAtInterface;
use Psr\Clock\ClockInterface as Clock;
final class StrictValidAt implements ValidAtInterface
{
private readonly DateInterval $leeway;
public function __construct(private readonly Clock $clock, ?DateInterval $leeway = null)
{
$this->leeway = $this->guardLeeway($leeway);
}
private function guardLeeway(?DateInterval $leeway): DateInterval
{
if ($leeway === null) {
return new DateInterval('PT0S');
}
if ($leeway->invert === 1) {
throw LeewayCannotBeNegative::create();
}
return $leeway;
}
public function assert(Token $token): void
{
if (! $token instanceof UnencryptedToken) {
throw ConstraintViolation::error('You should pass a plain token', $this);
}
$now = $this->clock->now();
$this->assertIssueTime($token, $now->add($this->leeway));
$this->assertMinimumTime($token, $now->add($this->leeway));
$this->assertExpiration($token, $now->sub($this->leeway));
}
/** @throws ConstraintViolation */
private function assertExpiration(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::EXPIRATION_TIME)) {
throw ConstraintViolation::error('"Expiration Time" claim missing', $this);
}
if ($token->isExpired($now)) {
throw ConstraintViolation::error('The token is expired', $this);
}
}
/** @throws ConstraintViolation */
private function assertMinimumTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::NOT_BEFORE)) {
throw ConstraintViolation::error('"Not Before" claim missing', $this);
}
if (! $token->isMinimumTimeBefore($now)) {
throw ConstraintViolation::error('The token cannot be used yet', $this);
}
}
/** @throws ConstraintViolation */
private function assertIssueTime(UnencryptedToken $token, DateTimeInterface $now): void
{
if (! $token->claims()->has(Token\RegisteredClaims::ISSUED_AT)) {
throw ConstraintViolation::error('"Issued At" claim missing', $this);
}
if (! $token->hasBeenIssuedBefore($now)) {
throw ConstraintViolation::error('The token was issued in the future', $this);
}
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class ConstraintViolation extends RuntimeException implements Exception
{
/** @param class-string<Constraint>|null $constraint */
public function __construct(
string $message = '',
public readonly ?string $constraint = null,
) {
parent::__construct($message);
}
/** @param non-empty-string $message */
public static function error(string $message, Constraint $constraint): self
{
return new self(message: $message, constraint: $constraint::class);
}
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
final class NoConstraintsGiven extends RuntimeException implements Exception
{
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Exception;
use RuntimeException;
use function array_map;
use function implode;
final class RequiredConstraintsViolated extends RuntimeException implements Exception
{
/** @param ConstraintViolation[] $violations */
public function __construct(
string $message = '',
public readonly array $violations = [],
) {
parent::__construct($message);
}
public static function fromViolations(ConstraintViolation ...$violations): self
{
return new self(message: self::buildMessage($violations), violations: $violations);
}
/** @param ConstraintViolation[] $violations */
private static function buildMessage(array $violations): string
{
$violations = array_map(
static function (ConstraintViolation $violation): string {
return '- ' . $violation->getMessage();
},
$violations,
);
$message = "The token violates some mandatory constraints, details:\n";
$message .= implode("\n", $violations);
return $message;
}
/** @return ConstraintViolation[] */
public function violations(): array
{
return $this->violations;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface SignedWith extends Constraint
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
interface ValidAt extends Constraint
{
}
+56
View File
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Lcobucci\JWT\Validation;
use Lcobucci\JWT\Token;
final class Validator implements \Lcobucci\JWT\Validator
{
public function assert(Token $token, Constraint ...$constraints): void
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
$violations = [];
foreach ($constraints as $constraint) {
$this->checkConstraint($constraint, $token, $violations);
}
if ($violations) {
throw RequiredConstraintsViolated::fromViolations(...$violations);
}
}
/** @param ConstraintViolation[] $violations */
private function checkConstraint(
Constraint $constraint,
Token $token,
array &$violations,
): void {
try {
$constraint->assert($token);
} catch (ConstraintViolation $e) {
$violations[] = $e;
}
}
public function validate(Token $token, Constraint ...$constraints): bool
{
if ($constraints === []) {
throw new NoConstraintsGiven('No constraint given.');
}
try {
foreach ($constraints as $constraint) {
$constraint->assert($token);
}
return true;
} catch (ConstraintViolation) {
return false;
}
}
}