From 72e0e3b23c4f363fd367220945e5549c8f3564d3 Mon Sep 17 00:00:00 2001 From: Les Hazlewood Date: Fri, 1 Apr 2016 18:15:37 -0700 Subject: [PATCH] 109: enabled injection of a time source - a 'Clock' --- src/main/java/io/jsonwebtoken/Clock.java | 15 ++++++++++++ .../java/io/jsonwebtoken/DefaultClock.java | 10 -------- src/main/java/io/jsonwebtoken/JwtParser.java | 9 +++++--- .../io/jsonwebtoken/impl/DefaultClock.java | 23 +++++++++++++++++++ .../jsonwebtoken/impl/DefaultJwtParser.java | 11 +++------ .../java/io/jsonwebtoken/impl/FixedClock.java | 17 ++++++++++++++ .../io/jsonwebtoken/JwtParserTest.groovy | 11 ++++----- .../jsonwebtoken/impl/FixedClockTest.groovy | 19 +++++++++++++++ 8 files changed, 87 insertions(+), 28 deletions(-) delete mode 100644 src/main/java/io/jsonwebtoken/DefaultClock.java create mode 100644 src/main/java/io/jsonwebtoken/impl/DefaultClock.java create mode 100644 src/test/groovy/io/jsonwebtoken/impl/FixedClockTest.groovy diff --git a/src/main/java/io/jsonwebtoken/Clock.java b/src/main/java/io/jsonwebtoken/Clock.java index 69d6547b..7e57df80 100644 --- a/src/main/java/io/jsonwebtoken/Clock.java +++ b/src/main/java/io/jsonwebtoken/Clock.java @@ -1,7 +1,22 @@ package io.jsonwebtoken; +import io.jsonwebtoken.impl.DefaultClock; + import java.util.Date; +/** + * A clock represents a time source that can be used when creating and verifying JWTs. + * + * @since 0.7.0 + */ public interface Clock { + + public static final Clock DEFAULT = new DefaultClock(); + + /** + * Returns the clock's current timestamp at the instant the method is invoked. + * + * @return the clock's current timestamp at the instant the method is invoked. + */ Date now(); } diff --git a/src/main/java/io/jsonwebtoken/DefaultClock.java b/src/main/java/io/jsonwebtoken/DefaultClock.java deleted file mode 100644 index ddd47bc6..00000000 --- a/src/main/java/io/jsonwebtoken/DefaultClock.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.jsonwebtoken; - -import java.util.Date; - -public class DefaultClock implements Clock { - @Override - public Date now() { - return new Date(); - } -} diff --git a/src/main/java/io/jsonwebtoken/JwtParser.java b/src/main/java/io/jsonwebtoken/JwtParser.java index 58caee3b..e0373093 100644 --- a/src/main/java/io/jsonwebtoken/JwtParser.java +++ b/src/main/java/io/jsonwebtoken/JwtParser.java @@ -15,6 +15,8 @@ */ package io.jsonwebtoken; +import io.jsonwebtoken.impl.DefaultClock; + import java.security.Key; import java.util.Date; @@ -125,11 +127,12 @@ public interface JwtParser { JwtParser require(String claimName, Object value); /** - * Replace the {@code clock} used by the parser to determine the current time-of-day to use when validating - * the parsed JWT. If {@code null}, will reset the behavior to use the system clock. + * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT. + * The parser uses a {@link DefaultClock DefaultClock} instance by default. * - * @param clock a {@code Clock} object to return the time-of-day or {@code null} + * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT. * @return the builder instance for method chaining. + * @since 0.7.0 */ JwtParser setClock(Clock clock); diff --git a/src/main/java/io/jsonwebtoken/impl/DefaultClock.java b/src/main/java/io/jsonwebtoken/impl/DefaultClock.java new file mode 100644 index 00000000..14d1eed7 --- /dev/null +++ b/src/main/java/io/jsonwebtoken/impl/DefaultClock.java @@ -0,0 +1,23 @@ +package io.jsonwebtoken.impl; + +import io.jsonwebtoken.Clock; + +import java.util.Date; + +/** + * Default {@link Clock} implementation. + * + * @since 0.7.0 + */ +public class DefaultClock implements Clock { + + /** + * Simply returns new {@link Date}(). + * + * @return a new {@link Date} instance. + */ + @Override + public Date now() { + return new Date(); + } +} diff --git a/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java b/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java index 8c4c7947..8f8c02c8 100644 --- a/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java +++ b/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java @@ -21,7 +21,6 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Clock; import io.jsonwebtoken.CompressionCodec; import io.jsonwebtoken.CompressionCodecResolver; -import io.jsonwebtoken.DefaultClock; import io.jsonwebtoken.ExpiredJwtException; import io.jsonwebtoken.Header; import io.jsonwebtoken.IncorrectClaimException; @@ -71,7 +70,7 @@ public class DefaultJwtParser implements JwtParser { Claims expectedClaims = new DefaultClaims(); - private Clock clock = new DefaultClock(); + private Clock clock = Clock.DEFAULT; @Override public JwtParser requireIssuedAt(Date issuedAt) { @@ -133,12 +132,8 @@ public class DefaultJwtParser implements JwtParser { @Override public JwtParser setClock(Clock clock) { - if (clock == null) { - this.clock = new DefaultClock(); - } else { - this.clock = clock; - } - + Assert.notNull(clock, "Clock instance cannot be null."); + this.clock = clock; return this; } diff --git a/src/main/java/io/jsonwebtoken/impl/FixedClock.java b/src/main/java/io/jsonwebtoken/impl/FixedClock.java index 83007370..88de45dc 100644 --- a/src/main/java/io/jsonwebtoken/impl/FixedClock.java +++ b/src/main/java/io/jsonwebtoken/impl/FixedClock.java @@ -4,13 +4,30 @@ import io.jsonwebtoken.Clock; import java.util.Date; +/** + * A {@code Clock} implementation that is constructed with a seed timestamp and always reports that same + * timestamp. + * + * @since 0.7.0 + */ public class FixedClock implements Clock { + private final Date now; + /** + * Creates a new fixed clock using new {@link Date Date}() as the seed timestamp. All calls to + * {@link #now now()} will always return this seed Date. + */ public FixedClock() { this(new Date()); } + /** + * Creates a new fixed clock using the specified seed timestamp. All calls to + * {@link #now now()} will always return this seed Date. + * + * @param now the specified Date to always return from all calls to {@link #now now()}. + */ public FixedClock(Date now) { this.now = now; } diff --git a/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy b/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy index c5451cf2..40dba0c7 100644 --- a/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy +++ b/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy @@ -15,6 +15,7 @@ */ package io.jsonwebtoken +import io.jsonwebtoken.impl.DefaultClock import io.jsonwebtoken.impl.FixedClock import io.jsonwebtoken.impl.TextCodec import org.junit.Test @@ -1407,15 +1408,11 @@ class JwtParserTest { @Test void testParseClockManipulationWithNullClock() { - Date expiry = new Date(System.currentTimeMillis() - 1000) - - String compact = Jwts.builder().setSubject('Joe').setExpiration(expiry).compact() - + JwtParser parser = Jwts.parser(); try { - Jwts.parser().setClock(null).parse(compact) + parser.setClock(null) fail() - } catch (ExpiredJwtException e) { - assertTrue e.getMessage().startsWith('JWT expired at ') + } catch (IllegalArgumentException expected) { } } diff --git a/src/test/groovy/io/jsonwebtoken/impl/FixedClockTest.groovy b/src/test/groovy/io/jsonwebtoken/impl/FixedClockTest.groovy new file mode 100644 index 00000000..fc092e6a --- /dev/null +++ b/src/test/groovy/io/jsonwebtoken/impl/FixedClockTest.groovy @@ -0,0 +1,19 @@ +package io.jsonwebtoken.impl + +import org.junit.Test +import static org.junit.Assert.* + +class FixedClockTest { + + @Test + void testFixedClockDefaultConstructor() { + + def clock = new FixedClock() + + def date1 = clock.now() + Thread.sleep(100) + def date2 = clock.now() + + assertSame date1, date2 + } +}