Skip to content

Commit

Permalink
Use a time provider instead on ValidAt constraint
Browse files Browse the repository at this point in the history
So that we can separate the constraint initialisation from the the
validation itself and then be able to map validation constraints in a
dependency injection container.

Closes #139
  • Loading branch information
lcobucci committed May 7, 2017
1 parent 9ce7588 commit 3ef710a
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 36 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"require": {
"php": "^7.1",
"ext-openssl": "*",
"lcobucci/clock": "^1.0",
"lcobucci/jose-parsing": "~2.1"
},
"require-dev": {
Expand Down
27 changes: 15 additions & 12 deletions src/Validation/Constraint/ValidAt.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Lcobucci\JWT\Validation\Constraint;

use DateTimeInterface;
use Lcobucci\Clock\Clock;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
use Lcobucci\JWT\Validation\ConstraintViolationException;
Expand All @@ -23,49 +24,51 @@ final class ValidAt implements Constraint
/**
* @var DateTimeInterface
*/
private $now;
private $clock;

public function __construct(DateTimeInterface $now)
public function __construct(Clock $clock)
{
$this->now = $now;
$this->clock = $clock;
}

/**
* {@inheritdoc}
*/
public function assert(Token $token): void
{
$this->assertIssueTime($token);
$this->assertMinimumTime($token);
$this->assertExpiration($token);
$now = $this->clock->now();

$this->assertIssueTime($token, $now);
$this->assertMinimumTime($token, $now);
$this->assertExpiration($token, $now);
}

/**
* @throws ConstraintViolationException
*/
private function assertExpiration(Token $token): void
private function assertExpiration(Token $token, DateTimeInterface $now): void
{
if ($token->isExpired($this->now)) {
if ($token->isExpired($now)) {
throw new ConstraintViolationException('The token is expired');
}
}

/**
* @throws ConstraintViolationException
*/
private function assertMinimumTime(Token $token): void
private function assertMinimumTime(Token $token, DateTimeInterface $now): void
{
if (!$token->isMinimumTimeBefore($this->now)) {
if (! $token->isMinimumTimeBefore($now)) {
throw new ConstraintViolationException('The token cannot be used yet');
}
}

/**
* @throws ConstraintViolationException
*/
private function assertIssueTime(Token $token): void
private function assertIssueTime(Token $token, DateTimeInterface $now): void
{
if (!$token->hasBeenIssuedBefore($this->now)) {
if (! $token->hasBeenIssuedBefore($now)) {
throw new ConstraintViolationException('The token was issued in the future');
}
}
Expand Down
5 changes: 4 additions & 1 deletion test/functional/UnsignedTokenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Lcobucci\JWT\FunctionalTests;

use DateTimeImmutable;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Token;
use Lcobucci\JWT\Validation\Constraint;
Expand Down Expand Up @@ -117,11 +118,13 @@ public function parserCanReadAToken(Token $generated): void
*/
public function tokenValidationShouldPassWhenEverythingIsFine(Token $generated): void
{
$clock = new FrozenClock(new DateTimeImmutable('@' . self::CURRENT_TIME));

$constraints = [
new IdentifiedBy('1'),
new PermittedFor('http://client.abc.com'),
new IssuedBy('http://issuer.abc.com', 'http://api.abc.com'),
new ValidAt(new DateTimeImmutable('@' . self::CURRENT_TIME))
new ValidAt($clock)
];

self::assertTrue($this->config->getValidator()->validate($generated, ...$constraints));
Expand Down
55 changes: 32 additions & 23 deletions test/unit/Validation/Constraint/ValidAtTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,23 @@
namespace Lcobucci\JWT\Validation\Constraint;

use DateTimeImmutable;
use Lcobucci\Clock\Clock;
use Lcobucci\Clock\FrozenClock;
use Lcobucci\JWT\Token\RegisteredClaims;

final class ValidAtTest extends ConstraintTestCase
{
/**
* @var DateTimeImmutable
* @var Clock
*/
private $now;
private $clock;

/**
* @before
*/
public function createDependencies(): void
{
$this->now = new DateTimeImmutable();
$this->clock = new FrozenClock(new DateTimeImmutable());
}

/**
Expand All @@ -42,13 +44,15 @@ public function createDependencies(): void
*/
public function assertShouldRaiseExceptionWhenTokenIsExpired(): void
{
$now = $this->clock->now();

$claims = [
RegisteredClaims::ISSUED_AT => $this->now->modify('-20 seconds'),
RegisteredClaims::NOT_BEFORE => $this->now->modify('-10 seconds'),
RegisteredClaims::EXPIRATION_TIME => $this->now->modify('-10 seconds'),
RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'),
RegisteredClaims::NOT_BEFORE => $now->modify('-10 seconds'),
RegisteredClaims::EXPIRATION_TIME => $now->modify('-10 seconds'),
];

$constraint = new ValidAt($this->now);
$constraint = new ValidAt($this->clock);
$constraint->assert($this->buildToken($claims));
}

Expand All @@ -69,13 +73,15 @@ public function assertShouldRaiseExceptionWhenTokenIsExpired(): void
*/
public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void
{
$now = $this->clock->now();

$claims = [
RegisteredClaims::ISSUED_AT => $this->now->modify('-20 seconds'),
RegisteredClaims::NOT_BEFORE => $this->now->modify('+40 seconds'),
RegisteredClaims::EXPIRATION_TIME => $this->now->modify('+60 seconds'),
RegisteredClaims::ISSUED_AT => $now->modify('-20 seconds'),
RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'),
RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'),
];

$constraint = new ValidAt($this->now);
$constraint = new ValidAt($this->clock);
$constraint->assert($this->buildToken($claims));
}

Expand All @@ -95,13 +101,15 @@ public function assertShouldRaiseExceptionWhenMinimumTimeIsNotMet(): void
*/
public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void
{
$now = $this->clock->now();

$claims = [
RegisteredClaims::ISSUED_AT => $this->now->modify('+20 seconds'),
RegisteredClaims::NOT_BEFORE => $this->now->modify('+40 seconds'),
RegisteredClaims::EXPIRATION_TIME => $this->now->modify('+60 seconds'),
RegisteredClaims::ISSUED_AT => $now->modify('+20 seconds'),
RegisteredClaims::NOT_BEFORE => $now->modify('+40 seconds'),
RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'),
];

$constraint = new ValidAt($this->now);
$constraint = new ValidAt($this->clock);
$constraint->assert($this->buildToken($claims));
}

Expand All @@ -120,23 +128,24 @@ public function assertShouldRaiseExceptionWhenTokenWasIssuedInTheFuture(): void
*/
public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment(): void
{
$constraint = new ValidAt($this->now);
$constraint = new ValidAt($this->clock);
$now = $this->clock->now();

$token = $this->buildToken(
[
RegisteredClaims::ISSUED_AT => $this->now->modify('-40 seconds'),
RegisteredClaims::NOT_BEFORE => $this->now->modify('-20 seconds'),
RegisteredClaims::EXPIRATION_TIME => $this->now->modify('+60 seconds'),
RegisteredClaims::ISSUED_AT => $now->modify('-40 seconds'),
RegisteredClaims::NOT_BEFORE => $now->modify('-20 seconds'),
RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'),
]
);

self::assertNull($constraint->assert($token));

$token = $this->buildToken(
[
RegisteredClaims::ISSUED_AT => $this->now,
RegisteredClaims::NOT_BEFORE => $this->now,
RegisteredClaims::EXPIRATION_TIME => $this->now->modify('+60 seconds'),
RegisteredClaims::ISSUED_AT => $now,
RegisteredClaims::NOT_BEFORE => $now,
RegisteredClaims::EXPIRATION_TIME => $now->modify('+60 seconds'),
]
);

Expand All @@ -159,7 +168,7 @@ public function assertShouldNotRaiseExceptionWhenTokenIsUsedInTheRightMoment():
public function assertShouldNotRaiseExceptionWhenTokenDoesNotHaveTimeClaims(): void
{
$token = $this->buildToken();
$constraint = new ValidAt($this->now);
$constraint = new ValidAt($this->clock);
self::assertNull($constraint->assert($token));
}
}

0 comments on commit 3ef710a

Please sign in to comment.