|
|
|
@ -15,19 +15,27 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.service.security.auth; |
|
|
|
|
|
|
|
import org.junit.jupiter.api.BeforeEach; |
|
|
|
import org.junit.jupiter.api.Test; |
|
|
|
import org.springframework.cache.concurrent.ConcurrentMapCacheManager; |
|
|
|
import org.junit.Before; |
|
|
|
import org.junit.Test; |
|
|
|
import org.junit.runner.RunWith; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.boot.test.context.SpringBootContextLoader; |
|
|
|
import org.springframework.boot.test.context.SpringBootTest; |
|
|
|
import org.springframework.context.annotation.ComponentScan; |
|
|
|
import org.springframework.security.authentication.CredentialsExpiredException; |
|
|
|
import org.thingsboard.server.common.data.CacheConstants; |
|
|
|
import org.springframework.test.annotation.DirtiesContext; |
|
|
|
import org.springframework.test.context.ActiveProfiles; |
|
|
|
import org.springframework.test.context.ContextConfiguration; |
|
|
|
import org.springframework.test.context.TestPropertySource; |
|
|
|
import org.springframework.test.context.junit4.SpringRunner; |
|
|
|
import org.thingsboard.server.common.data.User; |
|
|
|
import org.thingsboard.server.common.data.id.UserId; |
|
|
|
import org.thingsboard.server.common.data.security.Authority; |
|
|
|
import org.thingsboard.server.common.data.security.UserCredentials; |
|
|
|
import org.thingsboard.server.common.data.security.event.UserAuthDataChangedEvent; |
|
|
|
import org.thingsboard.server.common.data.security.model.JwtToken; |
|
|
|
import org.thingsboard.server.config.JwtSettings; |
|
|
|
import org.thingsboard.server.dao.customer.CustomerService; |
|
|
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
|
|
|
import org.thingsboard.server.dao.user.UserService; |
|
|
|
import org.thingsboard.server.service.security.auth.jwt.JwtAuthenticationProvider; |
|
|
|
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenAuthenticationProvider; |
|
|
|
@ -39,13 +47,9 @@ import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; |
|
|
|
|
|
|
|
import java.util.UUID; |
|
|
|
|
|
|
|
import static java.util.concurrent.TimeUnit.DAYS; |
|
|
|
import static java.util.concurrent.TimeUnit.MINUTES; |
|
|
|
import static java.util.concurrent.TimeUnit.SECONDS; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertFalse; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertNotNull; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertNull; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertThrows; |
|
|
|
import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
|
import static org.mockito.ArgumentMatchers.any; |
|
|
|
@ -53,31 +57,33 @@ import static org.mockito.ArgumentMatchers.eq; |
|
|
|
import static org.mockito.Mockito.mock; |
|
|
|
import static org.mockito.Mockito.when; |
|
|
|
|
|
|
|
@ActiveProfiles("test") |
|
|
|
@RunWith(SpringRunner.class) |
|
|
|
@ContextConfiguration(classes = TokenOutdatingTest.class, loader = SpringBootContextLoader.class) |
|
|
|
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) |
|
|
|
@ComponentScan({"org.thingsboard.server"}) |
|
|
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) |
|
|
|
@DaoSqlTest |
|
|
|
@TestPropertySource(properties = { |
|
|
|
"security.jwt.tokenIssuer=test.io", |
|
|
|
"security.jwt.tokenSigningKey=secret", |
|
|
|
"security.jwt.tokenExpirationTime=600", |
|
|
|
"security.jwt.refreshTokenExpTime=10" |
|
|
|
}) |
|
|
|
public class TokenOutdatingTest { |
|
|
|
private JwtAuthenticationProvider accessTokenAuthenticationProvider; |
|
|
|
private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider; |
|
|
|
|
|
|
|
@Autowired |
|
|
|
private TokenOutdatingService tokenOutdatingService; |
|
|
|
private ConcurrentMapCacheManager cacheManager; |
|
|
|
@Autowired |
|
|
|
private JwtTokenFactory tokenFactory; |
|
|
|
private JwtSettings jwtSettings; |
|
|
|
private SecurityUser securityUser; |
|
|
|
|
|
|
|
private UserId userId; |
|
|
|
|
|
|
|
@BeforeEach |
|
|
|
@Before |
|
|
|
public void setUp() { |
|
|
|
jwtSettings = new JwtSettings(); |
|
|
|
jwtSettings.setTokenIssuer("test.io"); |
|
|
|
jwtSettings.setTokenExpirationTime((int) MINUTES.toSeconds(10)); |
|
|
|
jwtSettings.setRefreshTokenExpTime((int) DAYS.toSeconds(7)); |
|
|
|
jwtSettings.setTokenSigningKey("secret"); |
|
|
|
tokenFactory = new JwtTokenFactory(jwtSettings); |
|
|
|
|
|
|
|
cacheManager = new ConcurrentMapCacheManager(); |
|
|
|
tokenOutdatingService = new TokenOutdatingService(cacheManager, tokenFactory, jwtSettings); |
|
|
|
tokenOutdatingService.initCache(); |
|
|
|
|
|
|
|
userId = new UserId(UUID.randomUUID()); |
|
|
|
UserId userId = new UserId(UUID.randomUUID()); |
|
|
|
securityUser = createMockSecurityUser(userId); |
|
|
|
|
|
|
|
UserService userService = mock(UserService.class); |
|
|
|
|
|
|
|
@ -97,28 +103,28 @@ public class TokenOutdatingTest { |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testOutdateOldUserTokens() throws Exception { |
|
|
|
JwtToken jwtToken = createAccessJwtToken(userId); |
|
|
|
JwtToken jwtToken = tokenFactory.createAccessJwtToken(securityUser); |
|
|
|
|
|
|
|
SECONDS.sleep(1); // need to wait before outdating so that outdatage time is strictly after token issue time
|
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId)); |
|
|
|
assertTrue(tokenOutdatingService.isOutdated(jwtToken, userId)); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(securityUser.getId(), securityUser.getSessionId(), false)); |
|
|
|
assertTrue(tokenOutdatingService.isOutdated(jwtToken, securityUser.getId())); |
|
|
|
|
|
|
|
SECONDS.sleep(1); |
|
|
|
|
|
|
|
JwtToken newJwtToken = tokenFactory.createAccessJwtToken(createMockSecurityUser(userId)); |
|
|
|
assertFalse(tokenOutdatingService.isOutdated(newJwtToken, userId)); |
|
|
|
JwtToken newJwtToken = tokenFactory.createAccessJwtToken(securityUser); |
|
|
|
assertFalse(tokenOutdatingService.isOutdated(newJwtToken, securityUser.getId())); |
|
|
|
} |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testAuthenticateWithOutdatedAccessToken() throws InterruptedException { |
|
|
|
RawAccessJwtToken accessJwtToken = getRawJwtToken(createAccessJwtToken(userId)); |
|
|
|
RawAccessJwtToken accessJwtToken = getRawJwtToken(tokenFactory.createAccessJwtToken(securityUser)); |
|
|
|
|
|
|
|
assertDoesNotThrow(() -> { |
|
|
|
accessTokenAuthenticationProvider.authenticate(new JwtAuthenticationToken(accessJwtToken)); |
|
|
|
}); |
|
|
|
|
|
|
|
SECONDS.sleep(1); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId)); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(securityUser.getId(), securityUser.getSessionId(), false)); |
|
|
|
|
|
|
|
assertThrows(JwtExpiredTokenException.class, () -> { |
|
|
|
accessTokenAuthenticationProvider.authenticate(new JwtAuthenticationToken(accessJwtToken)); |
|
|
|
@ -127,14 +133,14 @@ public class TokenOutdatingTest { |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testAuthenticateWithOutdatedRefreshToken() throws InterruptedException { |
|
|
|
RawAccessJwtToken refreshJwtToken = getRawJwtToken(createRefreshJwtToken(userId)); |
|
|
|
RawAccessJwtToken refreshJwtToken = getRawJwtToken(tokenFactory.createRefreshToken(securityUser)); |
|
|
|
|
|
|
|
assertDoesNotThrow(() -> { |
|
|
|
refreshTokenAuthenticationProvider.authenticate(new RefreshAuthenticationToken(refreshJwtToken)); |
|
|
|
}); |
|
|
|
|
|
|
|
SECONDS.sleep(1); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId)); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(securityUser.getId(), securityUser.getSessionId(), false)); |
|
|
|
|
|
|
|
assertThrows(CredentialsExpiredException.class, () -> { |
|
|
|
refreshTokenAuthenticationProvider.authenticate(new RefreshAuthenticationToken(refreshJwtToken)); |
|
|
|
@ -143,32 +149,20 @@ public class TokenOutdatingTest { |
|
|
|
|
|
|
|
@Test |
|
|
|
public void testTokensOutdatageTimeRemovalFromCache() throws Exception { |
|
|
|
JwtToken jwtToken = createAccessJwtToken(userId); |
|
|
|
JwtToken jwtToken = tokenFactory.createAccessJwtToken(securityUser); |
|
|
|
|
|
|
|
SECONDS.sleep(1); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(userId)); |
|
|
|
|
|
|
|
int refreshTokenExpirationTime = 3; |
|
|
|
jwtSettings.setRefreshTokenExpTime(refreshTokenExpirationTime); |
|
|
|
tokenOutdatingService.onUserAuthDataChanged(new UserAuthDataChangedEvent(securityUser.getId(), securityUser.getSessionId(), false)); |
|
|
|
|
|
|
|
SECONDS.sleep(refreshTokenExpirationTime - 2); |
|
|
|
|
|
|
|
assertTrue(tokenOutdatingService.isOutdated(jwtToken, userId)); |
|
|
|
assertNotNull(cacheManager.getCache(CacheConstants.USERS_UPDATE_TIME_CACHE).get(userId.getId().toString())); |
|
|
|
SECONDS.sleep(1); |
|
|
|
|
|
|
|
SECONDS.sleep(3); |
|
|
|
assertTrue(tokenOutdatingService.isOutdated(jwtToken, securityUser.getId())); |
|
|
|
|
|
|
|
assertFalse(tokenOutdatingService.isOutdated(jwtToken, userId)); |
|
|
|
assertNull(cacheManager.getCache(CacheConstants.USERS_UPDATE_TIME_CACHE).get(userId.getId().toString())); |
|
|
|
} |
|
|
|
SECONDS.sleep(10); |
|
|
|
|
|
|
|
private JwtToken createAccessJwtToken(UserId userId) { |
|
|
|
return tokenFactory.createAccessJwtToken(createMockSecurityUser(userId)); |
|
|
|
assertFalse(tokenOutdatingService.isOutdated(jwtToken, securityUser.getId())); |
|
|
|
} |
|
|
|
|
|
|
|
private JwtToken createRefreshJwtToken(UserId userId) { |
|
|
|
return tokenFactory.createRefreshToken(createMockSecurityUser(userId)); |
|
|
|
} |
|
|
|
|
|
|
|
private RawAccessJwtToken getRawJwtToken(JwtToken token) { |
|
|
|
return new RawAccessJwtToken(token.getToken()); |
|
|
|
@ -180,6 +174,7 @@ public class TokenOutdatingTest { |
|
|
|
securityUser.setUserPrincipal(new UserPrincipal(UserPrincipal.Type.USER_NAME, securityUser.getEmail())); |
|
|
|
securityUser.setAuthority(Authority.CUSTOMER_USER); |
|
|
|
securityUser.setId(userId); |
|
|
|
securityUser.setSessionId(UUID.randomUUID().toString()); |
|
|
|
return securityUser; |
|
|
|
} |
|
|
|
} |
|
|
|
|