Skip to content

Commit 94f6565

Browse files
committed
feat: use ZonedDateTime instead of LocalDateTime to avoid unexpected results nearby daylight saving clock changes
WE2-458 Signed-off-by: Mart Somermaa <[email protected]>
1 parent 5ef19c6 commit 94f6565

File tree

11 files changed

+93
-53
lines changed

11 files changed

+93
-53
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,20 @@ import static javax.cache.configuration.FactoryBuilder.factoryOf;
8686
private static final long NONCE_TTL_MINUTES = 5;
8787
private static final String CACHE_NAME = "nonceCache";
8888

89-
private Cache<String, LocalDateTime> nonceCache() {
89+
private Cache<String, ZonedDateTime> nonceCache() {
9090
CacheManager cacheManager = Caching.getCachingProvider(CaffeineCachingProvider.class.getName())
9191
.getCacheManager();
92-
Cache<String, LocalDateTime> cache = cacheManager.getCache(CACHE_NAME);
92+
Cache<String, ZonedDateTime> cache = cacheManager.getCache(CACHE_NAME);
9393

9494
if (cache == null) {
9595
cache = createNonceCache(cacheManager);
9696
}
9797
return cache;
9898
}
9999

100-
private Cache<String, LocalDateTime> createNonceCache(CacheManager cacheManager) {
101-
CompleteConfiguration<String, LocalDateTime> cacheConfig = new MutableConfiguration<String, LocalDateTime>()
102-
.setTypes(String.class, LocalDateTime.class)
100+
private Cache<String, ZonedDateTime> createNonceCache(CacheManager cacheManager) {
101+
CompleteConfiguration<String, ZonedDateTime> cacheConfig = new MutableConfiguration<String, ZonedDateTime>()
102+
.setTypes(String.class, ZonedDateTime.class)
103103
.setExpiryPolicyFactory(factoryOf(new CreatedExpiryPolicy(
104104
new Duration(TimeUnit.MINUTES, NONCE_TTL_MINUTES + 1))));
105105
return cacheManager.createCache(CACHE_NAME, cacheConfig);

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<modelVersion>4.0.0</modelVersion>
66
<artifactId>authtoken-validation</artifactId>
77
<groupId>org.webeid.security</groupId>
8-
<version>1.0.2</version>
8+
<version>1.1.0</version>
99
<packaging>jar</packaging>
1010
<name>authtoken-validation</name>
1111
<description>Web eID authentication token validation library for Java</description>

src/main/java/org/webeid/security/nonce/NonceGeneratorBuilder.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,7 @@
2525
import javax.cache.Cache;
2626
import java.security.SecureRandom;
2727
import java.time.Duration;
28-
import java.time.LocalDateTime;
28+
import java.time.ZonedDateTime;
2929
import java.util.Objects;
3030

3131
/**
@@ -35,7 +35,7 @@ public class NonceGeneratorBuilder {
3535

3636
private static final SecureRandom RANDOM = new SecureRandom();
3737

38-
private Cache<String, LocalDateTime> cache;
38+
private Cache<String, ZonedDateTime> cache;
3939
private SecureRandom secureRandom = RANDOM;
4040
private Duration ttl = Duration.ofMinutes(5);
4141

@@ -60,7 +60,7 @@ public NonceGeneratorBuilder withNonceTtl(Duration duration) {
6060
* @param cache nonce cache
6161
* @return current builder instance
6262
*/
63-
public NonceGeneratorBuilder withNonceCache(Cache<String, LocalDateTime> cache) {
63+
public NonceGeneratorBuilder withNonceCache(Cache<String, ZonedDateTime> cache) {
6464
this.cache = cache;
6565
return this;
6666
}

src/main/java/org/webeid/security/nonce/NonceGeneratorImpl.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -22,19 +22,21 @@
2222

2323
package org.webeid.security.nonce;
2424

25+
import org.webeid.security.util.UtcDateTime;
26+
2527
import javax.cache.Cache;
2628
import java.security.SecureRandom;
2729
import java.time.Duration;
28-
import java.time.LocalDateTime;
30+
import java.time.ZonedDateTime;
2931
import java.util.Base64;
3032

3133
final class NonceGeneratorImpl implements NonceGenerator {
3234

33-
private final Cache<String, LocalDateTime> cache;
35+
private final Cache<String, ZonedDateTime> cache;
3436
private final SecureRandom secureRandom;
3537
private final Duration ttl;
3638

37-
NonceGeneratorImpl(Cache<String, LocalDateTime> cache, SecureRandom secureRandom, Duration ttl) {
39+
NonceGeneratorImpl(Cache<String, ZonedDateTime> cache, SecureRandom secureRandom, Duration ttl) {
3840
this.cache = cache;
3941
this.secureRandom = secureRandom;
4042
this.ttl = ttl;
@@ -44,7 +46,7 @@ final class NonceGeneratorImpl implements NonceGenerator {
4446
public String generateAndStoreNonce() {
4547
final byte[] nonceBytes = new byte[NONCE_LENGTH];
4648
secureRandom.nextBytes(nonceBytes);
47-
final LocalDateTime expirationTime = LocalDateTime.now().plus(ttl);
49+
final ZonedDateTime expirationTime = UtcDateTime.now().plus(ttl);
4850
final String base64StringNonce = Base64.getEncoder().encodeToString(nonceBytes);
4951
cache.put(base64StringNonce, expirationTime);
5052
return base64StringNonce;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) 2021 The Web eID Project
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
package org.webeid.security.util;
24+
25+
import java.time.ZoneOffset;
26+
import java.time.ZonedDateTime;
27+
28+
public final class UtcDateTime {
29+
30+
public static ZonedDateTime now() {
31+
return ZonedDateTime.now(ZoneOffset.UTC);
32+
}
33+
34+
private UtcDateTime() {
35+
throw new IllegalStateException("Utility class");
36+
}
37+
}

src/main/java/org/webeid/security/validator/AuthTokenValidationConfiguration.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -30,7 +30,7 @@
3030
import java.net.URI;
3131
import java.security.cert.X509Certificate;
3232
import java.time.Duration;
33-
import java.time.LocalDateTime;
33+
import java.time.ZonedDateTime;
3434
import java.util.Collection;
3535
import java.util.HashSet;
3636
import java.util.Objects;
@@ -45,7 +45,7 @@
4545
final class AuthTokenValidationConfiguration {
4646

4747
private URI siteOrigin;
48-
private Cache<String, LocalDateTime> nonceCache;
48+
private Cache<String, ZonedDateTime> nonceCache;
4949
private Collection<X509Certificate> trustedCACertificates = new HashSet<>();
5050
private boolean isUserCertificateRevocationCheckWithOcspEnabled = true;
5151
private Duration ocspRequestTimeout = Duration.ofSeconds(5);
@@ -86,11 +86,11 @@ URI getSiteOrigin() {
8686
return siteOrigin;
8787
}
8888

89-
void setNonceCache(Cache<String, LocalDateTime> nonceCache) {
89+
void setNonceCache(Cache<String, ZonedDateTime> nonceCache) {
9090
this.nonceCache = nonceCache;
9191
}
9292

93-
Cache<String, LocalDateTime> getNonceCache() {
93+
Cache<String, ZonedDateTime> getNonceCache() {
9494
return nonceCache;
9595
}
9696

src/main/java/org/webeid/security/validator/AuthTokenValidatorBuilder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,7 @@
3131
import java.net.URI;
3232
import java.security.cert.X509Certificate;
3333
import java.time.Duration;
34-
import java.time.LocalDateTime;
34+
import java.time.ZonedDateTime;
3535
import java.util.Collections;
3636

3737
/**
@@ -67,7 +67,7 @@ public AuthTokenValidatorBuilder withSiteOrigin(URI origin) {
6767
* @param cache nonce cache
6868
* @return the builder instance for method chaining
6969
*/
70-
public AuthTokenValidatorBuilder withNonceCache(Cache<String, LocalDateTime> cache) {
70+
public AuthTokenValidatorBuilder withNonceCache(Cache<String, ZonedDateTime> cache) {
7171
configuration.setNonceCache(cache);
7272
return this;
7373
}

src/main/java/org/webeid/security/validator/validators/NonceValidator.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -29,18 +29,19 @@
2929
import org.webeid.security.exceptions.TokenParseException;
3030
import org.webeid.security.exceptions.TokenValidationException;
3131
import org.webeid.security.nonce.NonceGenerator;
32+
import org.webeid.security.util.UtcDateTime;
3233
import org.webeid.security.validator.AuthTokenValidatorData;
3334

3435
import javax.cache.Cache;
35-
import java.time.LocalDateTime;
36+
import java.time.ZonedDateTime;
3637

3738
public final class NonceValidator {
3839

3940
private static final Logger LOG = LoggerFactory.getLogger(NonceValidator.class);
4041

41-
private final Cache<String, LocalDateTime> cache;
42+
private final Cache<String, ZonedDateTime> cache;
4243

43-
public NonceValidator(Cache<String, LocalDateTime> cache) {
44+
public NonceValidator(Cache<String, ZonedDateTime> cache) {
4445
this.cache = cache;
4546
}
4647

@@ -54,7 +55,7 @@ public NonceValidator(Cache<String, LocalDateTime> cache) {
5455
*/
5556
public void validateNonce(AuthTokenValidatorData actualTokenData) throws TokenValidationException {
5657
final String nonce = actualTokenData.getNonce();
57-
final LocalDateTime nonceExpirationTime = cache.getAndRemove(nonce);
58+
final ZonedDateTime nonceExpirationTime = cache.getAndRemove(nonce);
5859
if (nonceExpirationTime == null) {
5960
throw new NonceNotFoundException();
6061
}
@@ -64,7 +65,7 @@ public void validateNonce(AuthTokenValidatorData actualTokenData) throws TokenVa
6465
NonceGenerator.NONCE_LENGTH
6566
));
6667
}
67-
if (nonceExpirationTime.isBefore(LocalDateTime.now())) {
68+
if (nonceExpirationTime.isBefore(UtcDateTime.now())) {
6869
throw new NonceExpiredException();
6970
}
7071
LOG.debug("Nonce was found and has not expired.");

src/test/java/org/webeid/security/testutil/AbstractTestWithCache.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020 The Web eID Project
2+
* Copyright (c) 2020, 2021 The Web eID Project
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -25,13 +25,14 @@
2525
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider;
2626
import org.junit.jupiter.api.AfterEach;
2727
import org.junit.jupiter.api.BeforeEach;
28+
import org.webeid.security.util.UtcDateTime;
2829

2930
import javax.cache.Cache;
3031
import javax.cache.CacheManager;
3132
import javax.cache.Caching;
3233
import javax.cache.configuration.CompleteConfiguration;
3334
import javax.cache.configuration.MutableConfiguration;
34-
import java.time.LocalDateTime;
35+
import java.time.ZonedDateTime;
3536

3637
public abstract class AbstractTestWithCache {
3738

@@ -41,11 +42,11 @@ public abstract class AbstractTestWithCache {
4142
private static final String TOO_SHORT_TEST_NONCE_KEY = "1234567812345678123456781234567";
4243

4344
private static final CacheManager cacheManager = Caching.getCachingProvider(CaffeineCachingProvider.class.getName()).getCacheManager();
44-
private static final CompleteConfiguration<String, LocalDateTime> cacheConfig = new MutableConfiguration<String, LocalDateTime>().setTypes(String.class, LocalDateTime.class);
45+
private static final CompleteConfiguration<String, ZonedDateTime> cacheConfig = new MutableConfiguration<String, ZonedDateTime>().setTypes(String.class, ZonedDateTime.class);
4546

46-
protected Cache<String, LocalDateTime> cache;
47+
protected Cache<String, ZonedDateTime> cache;
4748

48-
public static Cache<String, LocalDateTime> createCache(String cacheName) {
49+
public static Cache<String, ZonedDateTime> createCache(String cacheName) {
4950
return cacheManager.createCache(cacheName, cacheConfig);
5051
}
5152

@@ -54,12 +55,17 @@ protected void setup() {
5455
cache = createCache(CACHE_NAME);
5556
}
5657

58+
@AfterEach
59+
protected void tearDown() {
60+
cacheManager.destroyCache(CACHE_NAME);
61+
}
62+
5763
public void putCorrectNonceToCache() {
5864
cache.putIfAbsent(CORRECT_TEST_NONCE_KEY, fiveMinutesFromNow());
5965
}
6066

6167
public void putExpiredNonceToCache() {
62-
cache.putIfAbsent(CORRECT_TEST_NONCE_KEY, LocalDateTime.now().minusMinutes(5));
68+
cache.putIfAbsent(CORRECT_TEST_NONCE_KEY, ZonedDateTime.now().minusMinutes(5));
6369
}
6470

6571
public void putIncorrectNonceToCache() {
@@ -70,13 +76,8 @@ public void putTooShortNonceToCache() {
7076
cache.putIfAbsent(TOO_SHORT_TEST_NONCE_KEY, fiveMinutesFromNow());
7177
}
7278

73-
@AfterEach
74-
public void tearDown() {
75-
cacheManager.destroyCache(CACHE_NAME);
76-
}
77-
78-
private LocalDateTime fiveMinutesFromNow() {
79-
return LocalDateTime.now().plusMinutes(5);
79+
private ZonedDateTime fiveMinutesFromNow() {
80+
return UtcDateTime.now().plusMinutes(5);
8081
}
8182

8283
}

0 commit comments

Comments
 (0)