From 7031be4a3e7861cf187883891a43b27eb9a6676e Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Sat, 3 Dec 2022 17:33:12 +0100 Subject: [PATCH] Added test cases --- .../src/test/java/at/tuwien/BaseUnitTest.java | 75 +++++++++---- .../AuthenticationServiceIntegrationTest.java | 8 +- ...ationTest.java => TimeSecretUnitTest.java} | 14 ++- .../tuwien/service/TokenIntegrationTest.java | 101 ++++++++++++++++++ .../java/at/tuwien/auth/AuthTokenFilter.java | 6 +- .../main/java/at/tuwien/auth/JwtUtils.java | 5 + .../at/tuwien/config/WebSecurityConfig.java | 7 +- .../java/at/tuwien/service/TokenService.java | 9 ++ .../tuwien/service/impl/TokenServiceImpl.java | 17 +++ .../java/at/tuwien/entities/user/Token.java | 4 +- 10 files changed, 210 insertions(+), 36 deletions(-) rename fda-authentication-service/rest-service/src/test/java/at/tuwien/service/{TokenServiceIntegrationTest.java => TimeSecretUnitTest.java} (78%) create mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java index b228f91cb1..c3b8501f4b 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java @@ -1,13 +1,19 @@ package at.tuwien; -import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.user.UserDetailsDto; +import at.tuwien.entities.user.RoleType; import at.tuwien.entities.user.TimeSecret; import at.tuwien.entities.user.Token; import at.tuwien.entities.user.User; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.test.context.TestPropertySource; +import java.security.Principal; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.List; @TestPropertySource(locations = "classpath:application.properties") public abstract class BaseUnitTest { @@ -38,9 +44,21 @@ public abstract class BaseUnitTest { .emailVerified(USER_1_VERIFIED) .themeDark(USER_1_THEME_DARK) .created(USER_1_CREATED) + .roles(List.of(RoleType.ROLE_RESEARCHER)) .lastModified(USER_1_LAST_MODIFIED) .build(); + public final static UserDetails USER_1_DETAILS = UserDetailsDto.builder() + .username(USER_1_USERNAME) + .email(USER_1_EMAIL) + .password(USER_1_PASSWORD) + .authorities(List.of(new SimpleGrantedAuthority("ROLE_RESEARCHER"))) + .build(); + + public final static Principal USER_1_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_1_DETAILS, + USER_1_PASSWORD, USER_1_DETAILS.getAuthorities()); + + public final static Long USER_2_ID = 2L; public final static String USER_2_EMAIL = "jane.doe@example.com"; public final static String USER_2_USERNAME = "jdoe2"; @@ -59,37 +77,56 @@ public abstract class BaseUnitTest { .emailVerified(USER_2_VERIFIED) .themeDark(USER_2_THEME_DARK) .created(USER_2_CREATED) + .roles(List.of(RoleType.ROLE_RESEARCHER)) .lastModified(USER_2_LAST_MODIFIED) .build(); - public final static Long TOKEN_1_ID = 1L; - public final static Boolean TOKEN_1_PROCESSED = false; - public final static String TOKEN_1_TOKEN = "mysecrettokenrandomlygenerated"; - public final static Instant TOKEN_1_VALID_TO = Instant.now() + public final static UserDetails USER_2_DETAILS = UserDetailsDto.builder() + .username(USER_2_USERNAME) + .email(USER_2_EMAIL) + .password(USER_2_PASSWORD) + .authorities(List.of(new SimpleGrantedAuthority("ROLE_RESEARCHER"))) + .build(); + + public final static Principal USER_2_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_2_DETAILS, + USER_2_PASSWORD, USER_2_DETAILS.getAuthorities()); + + public final static Long TIME_SECRET_1_ID = 1L; + public final static Boolean TIME_SECRET_1_PROCESSED = false; + public final static String TIME_SECRET_1_TOKEN = "mysecrettokenrandomlygenerated"; + public final static Instant TIME_SECRET_1_VALID_TO = Instant.now() .plus(1, ChronoUnit.DAYS); - public final static Long TOKEN_2_ID = 2L; - public final static Boolean TOKEN_2_PROCESSED = true; - public final static String TOKEN_2_TOKEN = "blahblahblah"; - public final static Instant TOKEN_2_VALID_TO = Instant.now() + public final static Long TIME_SECRET_2_ID = 2L; + public final static Boolean TIME_SECRET_2_PROCESSED = true; + public final static String TIME_SECRET_2_TOKEN = "blahblahblah"; + public final static Instant TIME_SECRET_2_VALID_TO = Instant.now() .plus(1, ChronoUnit.DAYS); - public final static TimeSecret TOKEN_1 = TimeSecret.builder() - .id(TOKEN_1_ID) + public final static TimeSecret TIME_SECRET_1 = TimeSecret.builder() + .id(TIME_SECRET_1_ID) .uid(USER_1_ID) .user(USER_1) - .token(TOKEN_1_TOKEN) - .processed(TOKEN_1_PROCESSED) - .validTo(TOKEN_1_VALID_TO) + .token(TIME_SECRET_1_TOKEN) + .processed(TIME_SECRET_1_PROCESSED) + .validTo(TIME_SECRET_1_VALID_TO) .build(); - public final static TimeSecret TOKEN_2 = TimeSecret.builder() - .id(TOKEN_2_ID) + public final static TimeSecret TIME_SECRET_2 = TimeSecret.builder() + .id(TIME_SECRET_2_ID) .uid(USER_2_ID) .user(USER_2) - .token(TOKEN_2_TOKEN) - .processed(TOKEN_2_PROCESSED) - .validTo(TOKEN_2_VALID_TO) + .token(TIME_SECRET_2_TOKEN) + .processed(TIME_SECRET_2_PROCESSED) + .validTo(TIME_SECRET_2_VALID_TO) + .build(); + + public final static Long TOKEN_1_ID = 1L; + public final static Instant TOKEN_1_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS); + + public final static Token TOKEN_1 = Token.builder() + .id(TOKEN_1_ID) + .expires(TOKEN_1_EXPIRES) .build(); } diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java index e447b521ed..49608e0f1d 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java @@ -41,10 +41,10 @@ public class AuthenticationServiceIntegrationTest extends BaseUnitTest { public void beforeEach() { final User u1 = userRepository.save(USER_1); final User u2 = userRepository.save(USER_2); - TOKEN_1.setUser(u1); - tokenRepository.save(TOKEN_1); - TOKEN_2.setUser(u2); - tokenRepository.save(TOKEN_2); + TIME_SECRET_1.setUser(u1); + tokenRepository.save(TIME_SECRET_1); + TIME_SECRET_2.setUser(u2); + tokenRepository.save(TIME_SECRET_2); } @Test diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java similarity index 78% rename from fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenServiceIntegrationTest.java rename to fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java index 09d2a26554..9cbb9592dc 100644 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenServiceIntegrationTest.java +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java @@ -20,31 +20,29 @@ import static org.junit.jupiter.api.Assertions.assertThrows; @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @SpringBootTest @ExtendWith(SpringExtension.class) -public class TokenServiceIntegrationTest extends BaseUnitTest { +public class TimeSecretUnitTest extends BaseUnitTest { @MockBean private ReadyConfig readyConfig; @Autowired - private TimeSecretService tokenService; + private TimeSecretService timeSecretService; @Autowired - private TimeSecretRepository tokenRepository; + private TimeSecretRepository timeSecretRepository; @BeforeEach public void beforeEach() { - tokenRepository.save(TOKEN_1); + timeSecretRepository.save(TIME_SECRET_1); } @Test public void updateVerification_succeeds() throws SecretInvalidException { - /* mock */ - /* test */ - tokenService.invalidate(TOKEN_1_TOKEN); + timeSecretService.invalidate(TIME_SECRET_1_TOKEN); assertThrows(SecretInvalidException.class, () -> { - tokenService.invalidate(TOKEN_1_TOKEN); + timeSecretService.invalidate(TIME_SECRET_1_TOKEN); }); } diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java new file mode 100644 index 0000000000..1747c49003 --- /dev/null +++ b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java @@ -0,0 +1,101 @@ +package at.tuwien.service; + +import at.tuwien.BaseUnitTest; +import at.tuwien.auth.JwtUtils; +import at.tuwien.config.ReadyConfig; +import at.tuwien.entities.user.Token; +import at.tuwien.exception.TokenNotEligableException; +import at.tuwien.exception.TokenNotFoundException; +import at.tuwien.exception.UserNotFoundException; +import at.tuwien.repositories.TokenRepository; +import at.tuwien.repositories.UserRepository; +import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.servlet.ServletException; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Log4j2 +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +@SpringBootTest +@ExtendWith(SpringExtension.class) +public class TokenIntegrationTest extends BaseUnitTest { + + @MockBean + private ReadyConfig readyConfig; + + @Autowired + private TokenService tokenService; + + @Autowired + private UserRepository userRepository; + + @Autowired + private TokenRepository tokenRepository; + + @Autowired + private JwtUtils jwtUtils; + + @BeforeEach + public void beforeEach() { + userRepository.save(USER_1); + } + + @Test + public void check_succeeds() throws ServletException { + + /* mock */ + final String jwt = jwtUtils.generateJwtToken(USER_1_USERNAME, TOKEN_1_EXPIRES); + final Token entity = Token.builder() + .token(jwt) + .tokenHash(JwtUtils.toHash(jwt)) + .creator(USER_1_ID) + .expires(TOKEN_1_EXPIRES) + .build(); + final Token token = tokenRepository.save(entity); + + /* test */ + tokenService.check(jwt); + } + + @Test + public void check_revoked_fails() { + + /* mock */ + final String jwt = jwtUtils.generateJwtToken(USER_1_USERNAME, TOKEN_1_EXPIRES); + final Token entity = Token.builder() + .token(jwt) + .tokenHash(JwtUtils.toHash(jwt)) + .creator(USER_1_ID) + .expires(TOKEN_1_EXPIRES) + .deleted(Instant.now().minus(1, ChronoUnit.SECONDS)) + .build(); + final Token token = tokenRepository.save(entity); + + /* test */ + assertThrows(ServletException.class, () -> { + tokenService.check(jwt); + }); + } + + @Test + public void create_userNotFound_fails() { + + /* test */ + assertThrows(UserNotFoundException.class, () -> { + tokenService.create(USER_2_PRINCIPAL); + }); + } + +} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java index eb8d9548da..dac68f16ee 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java @@ -1,5 +1,6 @@ package at.tuwien.auth; +import at.tuwien.service.TokenService; import at.tuwien.service.impl.UserDetailsServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -20,10 +21,12 @@ import java.io.IOException; public class AuthTokenFilter extends OncePerRequestFilter { private final JwtUtils jwtUtils; + private final TokenService tokenService; private final UserDetailsServiceImpl userDetailsService; - public AuthTokenFilter(JwtUtils jwtUtils, UserDetailsServiceImpl userDetailsService) { + public AuthTokenFilter(JwtUtils jwtUtils, TokenService tokenService, UserDetailsServiceImpl userDetailsService) { this.jwtUtils = jwtUtils; + this.tokenService = tokenService; this.userDetailsService = userDetailsService; } @@ -32,6 +35,7 @@ public class AuthTokenFilter extends OncePerRequestFilter { throws ServletException, IOException { final String jwt = parseJwt(request); if (jwt != null && jwtUtils.validateJwtToken(jwt)) { + tokenService.check(jwt); final String username = jwtUtils.getUserNameFromJwtToken(jwt); final UserDetails userDetails; try { diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java index 36fcb3b5e2..12e93c0eb1 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java @@ -5,6 +5,7 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTDecodeException; import com.auth0.jwt.interfaces.DecodedJWT; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @@ -40,6 +41,10 @@ public class JwtUtils { .getSubject(); } + public static String toHash(String token) { + return DigestUtils.sha256Hex(token); + } + public boolean validateJwtToken(String authToken) { try { final DecodedJWT jwt = JWT.decode(authToken); diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java index ac11ba38db..56851d0743 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java @@ -2,6 +2,7 @@ package at.tuwien.config; import at.tuwien.auth.AuthTokenFilter; import at.tuwien.auth.JwtUtils; +import at.tuwien.service.TokenService; import at.tuwien.service.impl.UserDetailsServiceImpl; import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; import io.swagger.v3.oas.annotations.security.SecurityScheme; @@ -35,20 +36,22 @@ import javax.servlet.http.HttpServletResponse; public class WebSecurityConfig extends WebSecurityConfigurerAdapter { private final JwtUtils jwtUtils; + private final TokenService tokenService; private final SecurityConfig securityConfig; private final UserDetailsServiceImpl userDetailsService; @Autowired - public WebSecurityConfig(JwtUtils jwtUtils, SecurityConfig securityConfig, + public WebSecurityConfig(JwtUtils jwtUtils, TokenService tokenService, SecurityConfig securityConfig, UserDetailsServiceImpl userDetailsService) { this.jwtUtils = jwtUtils; + this.tokenService = tokenService; this.securityConfig = securityConfig; this.userDetailsService = userDetailsService; } @Bean public AuthTokenFilter authTokenFilter() { - return new AuthTokenFilter(jwtUtils, userDetailsService); + return new AuthTokenFilter(jwtUtils, tokenService, userDetailsService); } @Override diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java index 26d5e4f33d..4915baaf36 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java @@ -5,7 +5,9 @@ import at.tuwien.exception.TokenNotEligableException; import at.tuwien.exception.TokenNotFoundException; import at.tuwien.exception.UserNotFoundException; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import javax.servlet.ServletException; import java.security.Principal; import java.util.List; @@ -49,4 +51,11 @@ public interface TokenService { * @throws UserNotFoundException The user does not exist in the metadata database. */ void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException; + + /** + * Checks if the developer token has not been marked as deleted + * + * @param jwt The token + */ + void check(String jwt) throws ServletException; } diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java index e7f34b4cf7..33456b3299 100644 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java +++ b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java @@ -15,6 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.servlet.ServletException; import java.security.Principal; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -94,4 +95,20 @@ public class TokenServiceImpl implements TokenService { log.debug("deleted token {}", token); } + @Override + @Transactional + public void check(String jwt) throws ServletException { + final Optional<Token> optional = tokenRepository.findByTokenHash(JwtUtils.toHash(jwt)); + if (optional.isEmpty()) { + return; + } + final Token token = optional.get(); + if (token.getDeleted() != null) { + log.error("Token was marked as deleted on {}", token.getDeleted()); + throw new ServletException("Token was marked as deleted"); + } + token.setLastUsed(Instant.now()); + tokenRepository.save(token); + } + } diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Token.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Token.java index 7a6cca310f..17811e2efe 100644 --- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Token.java +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/Token.java @@ -51,10 +51,10 @@ public class Token { @Column(nullable = false, updatable = false) private Instant expires; - @Column(nullable = false, updatable = false) + @Column private Instant lastUsed; - @Column(nullable = false, updatable = false) + @Column(updatable = false) private Instant deleted; } -- GitLab