diff options
author | Matteo Mara <matteo.mara@sonarsource.com> | 2022-06-30 10:44:29 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-07-01 20:03:06 +0000 |
commit | 5159eb8d7cda29c357aa06868a595d07fbc6f633 (patch) | |
tree | 7caf459539e67ed23199256d6df9ba18911ee069 /server/sonar-webserver-auth | |
parent | 05ebcc134533b03a324a2710c3fe6d711850a5c4 (diff) | |
download | sonarqube-5159eb8d7cda29c357aa06868a595d07fbc6f633.tar.gz sonarqube-5159eb8d7cda29c357aa06868a595d07fbc6f633.zip |
SONAR-16565 handle token expiration at the authentication level
Diffstat (limited to 'server/sonar-webserver-auth')
2 files changed, 50 insertions, 2 deletions
diff --git a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java index be066b6a422..8bdef338bf6 100644 --- a/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java +++ b/server/sonar-webserver-auth/src/main/java/org/sonar/server/usertoken/UserTokenAuthentication.java @@ -33,6 +33,7 @@ import org.sonar.server.authentication.event.AuthenticationEvent; import org.sonar.server.authentication.event.AuthenticationException; import org.sonar.server.exceptions.NotFoundException; +import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.server.authentication.BasicAuthentication.extractCredentialsFromHeader; public class UserTokenAuthentication { @@ -80,7 +81,7 @@ public class UserTokenAuthentication { } request.setAttribute(ACCESS_LOG_TOKEN_NAME, userToken.getName()); return new UserAuthResult(userDto, userToken, UserAuthResult.AuthType.TOKEN); - } catch (NotFoundException exception) { + } catch (NotFoundException | IllegalStateException exception ) { throw AuthenticationException.newBuilder() .setSource(AuthenticationEvent.Source.local(AuthenticationEvent.Method.BASIC_TOKEN)) .setMessage(exception.getMessage()) @@ -93,6 +94,9 @@ public class UserTokenAuthentication { if (userToken == null) { throw new NotFoundException("Token doesn't exist"); } + if (userToken.isExpired()) { + throw new IllegalStateException("The token expired on " + formatDateTime(userToken.getExpirationDate())); + } userLastConnectionDatesUpdater.updateLastConnectionDateIfNeeded(userToken); return userToken; } diff --git a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java index 3e353f20e28..44d3cc141d8 100644 --- a/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java +++ b/server/sonar-webserver-auth/src/test/java/org/sonar/server/usertoken/UserTokenAuthenticationTest.java @@ -19,6 +19,8 @@ */ package org.sonar.server.usertoken; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Base64; import java.util.Optional; import javax.servlet.http.HttpServletRequest; @@ -43,6 +45,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; +import static org.sonar.api.utils.DateUtils.formatDateTime; import static org.sonar.db.user.TokenType.GLOBAL_ANALYSIS_TOKEN; import static org.sonar.db.user.TokenType.PROJECT_ANALYSIS_TOKEN; import static org.sonar.db.user.TokenType.USER_TOKEN; @@ -105,6 +108,30 @@ public class UserTokenAuthenticationTest { } @Test + public void return_login_when_token_hash_found_in_db_and_future_expiration_date() { + String token = "known-token"; + String tokenHash = "123456789"; + + long expirationTimestamp = ZonedDateTime.now(ZoneId.systemDefault()).plusDays(10).toInstant().toEpochMilli(); + when(request.getHeader(AUTHORIZATION_HEADER)).thenReturn("Basic " + toBase64(token + ":")); + when(tokenGenerator.hash(token)).thenReturn(tokenHash); + UserDto user1 = db.users().insertUser(); + UserTokenDto userTokenDto = db.users().insertToken(user1, t -> t.setTokenHash(tokenHash).setExpirationDate(expirationTimestamp)); + UserDto user2 = db.users().insertUser(); + db.users().insertToken(user2, t -> t.setTokenHash("another-token-hash")); + + Optional<UserAuthResult> result = underTest.authenticate(request); + + assertThat(result).isPresent(); + assertThat(result.get().getTokenDto().getUuid()).isEqualTo(userTokenDto.getUuid()); + assertThat(result.get().getTokenDto().getExpirationDate()).isEqualTo(expirationTimestamp); + assertThat(result.get().getUserDto().getUuid()) + .isNotNull() + .contains(user1.getUuid()); + verify(userLastConnectionDatesUpdater).updateLastConnectionDateIfNeeded(any(UserTokenDto.class)); + } + + @Test public void return_absent_if_username_password_used() { when(request.getHeader(AUTHORIZATION_HEADER)).thenReturn("Basic " + toBase64("login:password")); @@ -116,7 +143,7 @@ public class UserTokenAuthenticationTest { } @Test - public void return_absent_if_token_hash_is_not_found() { + public void throw_authentication_exception_if_token_hash_is_not_found() { when(request.getHeader(AUTHORIZATION_HEADER)).thenReturn("Basic " + toBase64(EXAMPLE_OLD_USER_TOKEN + ":")); assertThatThrownBy(() -> underTest.authenticate(request)) @@ -127,6 +154,23 @@ public class UserTokenAuthenticationTest { } @Test + public void throw_authentication_exception_if_token_is_expired() { + String token = "known-token"; + String tokenHash = "123456789"; + long expirationTimestamp = System.currentTimeMillis(); + when(request.getHeader(AUTHORIZATION_HEADER)).thenReturn("Basic " + toBase64(token + ":")); + when(tokenGenerator.hash(token)).thenReturn(tokenHash); + UserDto user1 = db.users().insertUser(); + db.users().insertToken(user1, t -> t.setTokenHash(tokenHash).setExpirationDate(expirationTimestamp)); + + assertThatThrownBy(() -> underTest.authenticate(request)) + .hasMessageContaining("The token expired on " + formatDateTime(expirationTimestamp)) + .isInstanceOf(AuthenticationException.class); + verify(userLastConnectionDatesUpdater, never()).updateLastConnectionDateIfNeeded(any(UserTokenDto.class)); + verifyNoInteractions(authenticationEvent); + } + + @Test public void authenticate_givenGlobalToken_resultContainsUuid() { UserDto user = db.users().insertUser(); String tokenName = db.users().insertToken(user, t -> t.setTokenHash(GLOBAL_ANALYSIS_TOKEN_HASH).setType(GLOBAL_ANALYSIS_TOKEN.name())).getName(); |