From bf69f8d308301725855049ffd9243b3bfe829432 Mon Sep 17 00:00:00 2001 From: stephan Date: Tue, 4 Jul 2017 23:29:13 +0200 Subject: [PATCH] increased test coverage --- .../zerhusen/common/utils/TimeProvider.java | 19 ++++ .../org/zerhusen/security/JwtTokenUtil.java | 41 +++++---- .../common/utils/TimeProviderTest.java | 20 +++++ .../org/zerhusen/security/DeviceDummy.java | 46 ++++++++++ .../zerhusen/security/JwtTokenUtilTest.java | 89 +++++++++++++++++-- .../zerhusen/security/UserDetailsDummy.java | 53 +++++++++++ 6 files changed, 246 insertions(+), 22 deletions(-) create mode 100644 src/main/java/org/zerhusen/common/utils/TimeProvider.java create mode 100644 src/test/java/org/zerhusen/common/utils/TimeProviderTest.java create mode 100644 src/test/java/org/zerhusen/security/DeviceDummy.java create mode 100644 src/test/java/org/zerhusen/security/UserDetailsDummy.java diff --git a/src/main/java/org/zerhusen/common/utils/TimeProvider.java b/src/main/java/org/zerhusen/common/utils/TimeProvider.java new file mode 100644 index 0000000..5b2f9bf --- /dev/null +++ b/src/main/java/org/zerhusen/common/utils/TimeProvider.java @@ -0,0 +1,19 @@ +package org.zerhusen.common.utils; + +import org.springframework.stereotype.Component; + +import java.io.Serializable; +import java.util.Date; + +/** + * Created by stephan on 04.07.17. + */ +@Component +public class TimeProvider implements Serializable { + + private static final long serialVersionUID = -3301695478208950415L; + + public Date now() { + return new Date(); + } +} diff --git a/src/main/java/org/zerhusen/security/JwtTokenUtil.java b/src/main/java/org/zerhusen/security/JwtTokenUtil.java index bbeb8c4..79af219 100644 --- a/src/main/java/org/zerhusen/security/JwtTokenUtil.java +++ b/src/main/java/org/zerhusen/security/JwtTokenUtil.java @@ -3,10 +3,12 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.mobile.device.Device; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; +import org.zerhusen.common.utils.TimeProvider; import java.io.Serializable; import java.util.Date; @@ -21,11 +23,15 @@ public class JwtTokenUtil implements Serializable { static final String CLAIM_KEY_USERNAME = "sub"; static final String CLAIM_KEY_AUDIENCE = "audience"; static final String CLAIM_KEY_CREATED = "created"; + static final String CLAIM_KEY_EXPIRED = "exp"; - private static final String AUDIENCE_UNKNOWN = "unknown"; - private static final String AUDIENCE_WEB = "web"; - private static final String AUDIENCE_MOBILE = "mobile"; - private static final String AUDIENCE_TABLET = "tablet"; + static final String AUDIENCE_UNKNOWN = "unknown"; + static final String AUDIENCE_WEB = "web"; + static final String AUDIENCE_MOBILE = "mobile"; + static final String AUDIENCE_TABLET = "tablet"; + + @Autowired + private TimeProvider timeProvider; @Value("${jwt.secret}") private String secret; @@ -90,13 +96,9 @@ private Claims getClaimsFromToken(String token) { return claims; } - private Date generateExpirationDate() { - return new Date(System.currentTimeMillis() + expiration * 1000); - } - private Boolean isTokenExpired(String token) { final Date expiration = getExpirationDateFromToken(token); - return expiration.before(new Date()); + return expiration.before(timeProvider.now()); } private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) { @@ -122,16 +124,25 @@ private Boolean ignoreTokenExpiration(String token) { public String generateToken(UserDetails userDetails, Device device) { Map claims = new HashMap<>(); + claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername()); claims.put(CLAIM_KEY_AUDIENCE, generateAudience(device)); - claims.put(CLAIM_KEY_CREATED, new Date()); - return generateToken(claims); + + final Date createdDate = timeProvider.now(); + claims.put(CLAIM_KEY_CREATED, createdDate); + + return doGenerateToken(claims); } - String generateToken(Map claims) { + private String doGenerateToken(Map claims) { + final Date createdDate = (Date) claims.get(CLAIM_KEY_CREATED); + final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000); + + System.out.println("doGenerateToken " + createdDate); + return Jwts.builder() .setClaims(claims) - .setExpiration(generateExpirationDate()) + .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } @@ -146,8 +157,8 @@ public String refreshToken(String token) { String refreshedToken; try { final Claims claims = getClaimsFromToken(token); - claims.put(CLAIM_KEY_CREATED, new Date()); - refreshedToken = generateToken(claims); + claims.put(CLAIM_KEY_CREATED, timeProvider.now()); + refreshedToken = doGenerateToken(claims); } catch (Exception e) { refreshedToken = null; } diff --git a/src/test/java/org/zerhusen/common/utils/TimeProviderTest.java b/src/test/java/org/zerhusen/common/utils/TimeProviderTest.java new file mode 100644 index 0000000..6dd210e --- /dev/null +++ b/src/test/java/org/zerhusen/common/utils/TimeProviderTest.java @@ -0,0 +1,20 @@ +package org.zerhusen.common.utils; + +import org.assertj.core.api.Assertions; +import org.junit.Test; + +import java.util.Date; + +import static org.assertj.core.api.Assertions.*; +import static org.junit.Assert.*; + +/** + * Created by stephan on 04.07.17. + */ +public class TimeProviderTest { + @Test + public void now() throws Exception { + assertThat(new TimeProvider().now()).isCloseTo(new Date(), 1000); + } + +} \ No newline at end of file diff --git a/src/test/java/org/zerhusen/security/DeviceDummy.java b/src/test/java/org/zerhusen/security/DeviceDummy.java new file mode 100644 index 0000000..ee3cbcd --- /dev/null +++ b/src/test/java/org/zerhusen/security/DeviceDummy.java @@ -0,0 +1,46 @@ +package org.zerhusen.security; + +import org.springframework.mobile.device.Device; +import org.springframework.mobile.device.DevicePlatform; + +/** + * Created by stephan on 04.07.17. + */ +public class DeviceDummy implements Device { + + private boolean normal; + private boolean mobile; + private boolean tablet; + + @Override + public boolean isNormal() { + return normal; + } + + @Override + public boolean isMobile() { + return mobile; + } + + @Override + public boolean isTablet() { + return tablet; + } + + @Override + public DevicePlatform getDevicePlatform() { + return null; + } + + public void setNormal(boolean normal) { + this.normal = normal; + } + + public void setMobile(boolean mobile) { + this.mobile = mobile; + } + + public void setTablet(boolean tablet) { + this.tablet = tablet; + } +} diff --git a/src/test/java/org/zerhusen/security/JwtTokenUtilTest.java b/src/test/java/org/zerhusen/security/JwtTokenUtilTest.java index 9ec9b5a..0f2b1fa 100644 --- a/src/test/java/org/zerhusen/security/JwtTokenUtilTest.java +++ b/src/test/java/org/zerhusen/security/JwtTokenUtilTest.java @@ -1,49 +1,124 @@ package org.zerhusen.security; import org.assertj.core.util.DateUtil; +import org.assertj.core.util.Maps; import org.junit.Before; import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.util.ReflectionUtils; +import org.zerhusen.common.utils.TimeProvider; import java.lang.reflect.Field; +import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.within; +import static org.mockito.Mockito.*; /** * Created by stephan on 10.09.16. */ public class JwtTokenUtilTest { + private static final String TEST_USER = "testUser"; + + @Mock + private TimeProvider timeProviderMock; + + @InjectMocks private JwtTokenUtil jwtTokenUtil; @Before public void init() { - jwtTokenUtil = new JwtTokenUtil(); - ReflectionTestUtils.setField(jwtTokenUtil, "expiration", 3600000L); + MockitoAnnotations.initMocks(this); + + ReflectionTestUtils.setField(jwtTokenUtil, "expiration", 3600L); // one hour ReflectionTestUtils.setField(jwtTokenUtil, "secret", "mySecret"); } @Test public void testGenerateTokenGeneratesDifferentTokensForDifferentCreationDates() throws Exception { - final Map claims = createClaims("2016-09-08T03:00:00"); - final String token = jwtTokenUtil.generateToken(claims); + when(timeProviderMock.now()) + .thenReturn(DateUtil.yesterday()) + .thenReturn(DateUtil.now()); - final Map claimsForLaterToken = createClaims("2016-09-08T08:00:00"); - final String laterToken = jwtTokenUtil.generateToken(claimsForLaterToken); + final String token = createToken(); + final String laterToken = createToken(); assertThat(token).isNotEqualTo(laterToken); } + @Test + public void getUsernameFromToken() throws Exception { + when(timeProviderMock.now()).thenReturn(DateUtil.now()); + + final String token = createToken(); + + assertThat(jwtTokenUtil.getUsernameFromToken(token)).isEqualTo(TEST_USER); + } + + @Test + public void getCreatedDateFromToken() throws Exception { + final Date now = DateUtil.now(); + when(timeProviderMock.now()).thenReturn(now); + + final String token = createToken(); + + assertThat(jwtTokenUtil.getCreatedDateFromToken(token)).hasSameTimeAs(now); + } + + @Test + public void getExpirationDateFromToken() throws Exception { + final Date now = DateUtil.now(); + when(timeProviderMock.now()).thenReturn(now); + final String token = createToken(); + + final Date expirationDateFromToken = jwtTokenUtil.getExpirationDateFromToken(token); + assertThat(DateUtil.timeDifference(expirationDateFromToken, now)).isCloseTo(3600000L, within(1000L)); + } + + @Test + public void getAudienceFromToken() throws Exception { + when(timeProviderMock.now()).thenReturn(DateUtil.now()); + final String token = createToken(); + + assertThat(jwtTokenUtil.getAudienceFromToken(token)).isEqualTo(JwtTokenUtil.AUDIENCE_WEB); + } + + // TODO write tests +// @Test +// public void canTokenBeRefreshed() throws Exception { +// } +// +// @Test +// public void refreshToken() throws Exception { +// } +// +// @Test +// public void validateToken() throws Exception { +// } + private Map createClaims(String creationDate) { Map claims = new HashMap(); - claims.put(JwtTokenUtil.CLAIM_KEY_USERNAME, "testUser"); + claims.put(JwtTokenUtil.CLAIM_KEY_USERNAME, TEST_USER); claims.put(JwtTokenUtil.CLAIM_KEY_AUDIENCE, "testAudience"); claims.put(JwtTokenUtil.CLAIM_KEY_CREATED, DateUtil.parseDatetime(creationDate)); return claims; } + private String createToken() { + final DeviceDummy device = new DeviceDummy(); + device.setNormal(true); + + return jwtTokenUtil.generateToken(new UserDetailsDummy(TEST_USER), device); + } + } \ No newline at end of file diff --git a/src/test/java/org/zerhusen/security/UserDetailsDummy.java b/src/test/java/org/zerhusen/security/UserDetailsDummy.java new file mode 100644 index 0000000..c684760 --- /dev/null +++ b/src/test/java/org/zerhusen/security/UserDetailsDummy.java @@ -0,0 +1,53 @@ +package org.zerhusen.security; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; + +/** + * Created by stephan on 04.07.17. + */ +public class UserDetailsDummy implements UserDetails { + + private final String username; + + public UserDetailsDummy(String username) { + this.username = username; + } + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + return false; + } + + @Override + public boolean isAccountNonLocked() { + return false; + } + + @Override + public boolean isCredentialsNonExpired() { + return false; + } + + @Override + public boolean isEnabled() { + return false; + } +}