From 979e84ed64640c1eaf4b8e4f0b4b6b5a0b9d3403 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 14:22:08 -0300 Subject: [PATCH 01/11] feat: generate jwt token after login --- .env.example | 1 + pom.xml | 7 ++- .../controller/AuthController.java | 10 +-- .../domain/auth/ResponseAuthUserDTO.java | 8 +++ .../MissingSecuritySecretException.java | 7 +++ .../security/SecuritySecrets.java | 16 +++++ .../mandacarubroker/service/AuthService.java | 20 +++++- .../service/PasswordHashingService.java | 2 - .../mandacarubroker/service/TokenService.java | 63 +++++++++++++++++++ .../controller/AuthControllerTest.java | 29 ++++++--- .../security/SecuritySecretsMock.java | 20 ++++++ .../service/AuthServiceTest.java | 12 ++-- 12 files changed, 173 insertions(+), 22 deletions(-) create mode 100644 src/main/java/com/mandacarubroker/domain/auth/ResponseAuthUserDTO.java create mode 100644 src/main/java/com/mandacarubroker/security/MissingSecuritySecretException.java create mode 100644 src/main/java/com/mandacarubroker/security/SecuritySecrets.java create mode 100644 src/main/java/com/mandacarubroker/service/TokenService.java create mode 100644 src/test/java/com/mandacarubroker/security/SecuritySecretsMock.java diff --git a/.env.example b/.env.example index 84520fe7..ec441f9d 100644 --- a/.env.example +++ b/.env.example @@ -6,6 +6,7 @@ MANDACARU_POSTGRES_PASSWORD=mandacaru # Change value to "never" for production environment MANDACARU_SQL_INIT_MODE=always MANDACARU_API_PORT=8080 +MANDACARU_JWT_SECRET=mandacaru-jwt-secret # Integration test environment MANDACARU_TEST_POSTGRES_HOST=127.0.0.1 diff --git a/pom.xml b/pom.xml index a87548f5..c65ec1ae 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ 17 mandacaru-broker https://sonarcloud.io + 2.0.2 @@ -70,6 +71,11 @@ + + com.auth0 + java-jwt + 4.4.0 + org.springframework.security spring-security-core @@ -88,7 +94,6 @@ junit-jupiter-engine test - com.puppycrawl.tools checkstyle diff --git a/src/main/java/com/mandacarubroker/controller/AuthController.java b/src/main/java/com/mandacarubroker/controller/AuthController.java index 7d7abf4d..a83f3968 100644 --- a/src/main/java/com/mandacarubroker/controller/AuthController.java +++ b/src/main/java/com/mandacarubroker/controller/AuthController.java @@ -1,7 +1,7 @@ package com.mandacarubroker.controller; import com.mandacarubroker.domain.auth.RequestAuthUserDTO; -import com.mandacarubroker.domain.user.User; +import com.mandacarubroker.domain.auth.ResponseAuthUserDTO; import com.mandacarubroker.service.AuthService; import jakarta.validation.Valid; import org.springframework.http.HttpStatus; @@ -23,13 +23,13 @@ public AuthController(final AuthService receivedAuthService) { } @PostMapping("/login") - public ResponseEntity login(@Valid @RequestBody final RequestAuthUserDTO requestAuthUserDTO) { - Optional user = authService.login(requestAuthUserDTO); + public ResponseEntity login(@Valid @RequestBody final RequestAuthUserDTO requestAuthUserDTO) { + Optional responseAuthUserDTO = authService.login(requestAuthUserDTO); - if (user.isEmpty()) { + if (responseAuthUserDTO.isEmpty()) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } - return ResponseEntity.ok("User logged in successfully"); + return ResponseEntity.ok(responseAuthUserDTO.orElseThrow()); } } diff --git a/src/main/java/com/mandacarubroker/domain/auth/ResponseAuthUserDTO.java b/src/main/java/com/mandacarubroker/domain/auth/ResponseAuthUserDTO.java new file mode 100644 index 00000000..4c4e5355 --- /dev/null +++ b/src/main/java/com/mandacarubroker/domain/auth/ResponseAuthUserDTO.java @@ -0,0 +1,8 @@ +package com.mandacarubroker.domain.auth; + +public record ResponseAuthUserDTO( + String token, + int expiresIn, + String tokenType +) { +} diff --git a/src/main/java/com/mandacarubroker/security/MissingSecuritySecretException.java b/src/main/java/com/mandacarubroker/security/MissingSecuritySecretException.java new file mode 100644 index 00000000..4fbe5150 --- /dev/null +++ b/src/main/java/com/mandacarubroker/security/MissingSecuritySecretException.java @@ -0,0 +1,7 @@ +package com.mandacarubroker.security; + +public class MissingSecuritySecretException extends RuntimeException { + public MissingSecuritySecretException(final String message) { + super("Missing security secret: " + message); + } +} diff --git a/src/main/java/com/mandacarubroker/security/SecuritySecrets.java b/src/main/java/com/mandacarubroker/security/SecuritySecrets.java new file mode 100644 index 00000000..db1a5d06 --- /dev/null +++ b/src/main/java/com/mandacarubroker/security/SecuritySecrets.java @@ -0,0 +1,16 @@ +package com.mandacarubroker.security; + +public final class SecuritySecrets { + private SecuritySecrets() { + } + + public static String getJWTSecret() { + final String secret = System.getenv("MANDACARU_JWT_SECRET"); + + if (secret == null) { + throw new MissingSecuritySecretException("JWT secret not found"); + } + + return secret; + } +} diff --git a/src/main/java/com/mandacarubroker/service/AuthService.java b/src/main/java/com/mandacarubroker/service/AuthService.java index 02c92eda..554ef845 100644 --- a/src/main/java/com/mandacarubroker/service/AuthService.java +++ b/src/main/java/com/mandacarubroker/service/AuthService.java @@ -1,6 +1,7 @@ package com.mandacarubroker.service; import com.mandacarubroker.domain.auth.RequestAuthUserDTO; +import com.mandacarubroker.domain.auth.ResponseAuthUserDTO; import com.mandacarubroker.domain.user.User; import com.mandacarubroker.domain.user.UserRepository; import org.springframework.stereotype.Service; @@ -12,13 +13,28 @@ @Service public class AuthService { private final UserRepository userRepository; - private final PasswordHashingService passwordHashingService = new PasswordHashingService(); + private final PasswordHashingService passwordHashingService; + private final TokenService tokenService; public AuthService(final UserRepository receivedUserRepository) { this.userRepository = receivedUserRepository; + this.passwordHashingService = new PasswordHashingService(); + this.tokenService = new TokenService(); } - public Optional login(final RequestAuthUserDTO requestAuthUserDTO) { + public Optional login(final RequestAuthUserDTO requestAuthUserDTO) { + Optional user = getUserGivenCredentials(requestAuthUserDTO); + + if (user.isEmpty()) { + return Optional.empty(); + } + + String userId = user.get().getId(); + ResponseAuthUserDTO responseAuthUserDTO = tokenService.encodeToken(userId); + return Optional.of(responseAuthUserDTO); + } + + public Optional getUserGivenCredentials(final RequestAuthUserDTO requestAuthUserDTO) { validateRequestDTO(requestAuthUserDTO); User user = userRepository.findByUsername(requestAuthUserDTO.username()); diff --git a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java index 437e1273..7d2937e7 100644 --- a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java +++ b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java @@ -1,9 +1,7 @@ package com.mandacarubroker.service; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.stereotype.Service; -@Service public class PasswordHashingService { private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); diff --git a/src/main/java/com/mandacarubroker/service/TokenService.java b/src/main/java/com/mandacarubroker/service/TokenService.java new file mode 100644 index 00000000..af0370ba --- /dev/null +++ b/src/main/java/com/mandacarubroker/service/TokenService.java @@ -0,0 +1,63 @@ +package com.mandacarubroker.service; + +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.exceptions.JWTCreationException; +import com.auth0.jwt.interfaces.DecodedJWT; +import com.mandacarubroker.domain.auth.ResponseAuthUserDTO; +import com.mandacarubroker.security.SecuritySecrets; +import org.springframework.stereotype.Service; + +import java.util.Date; + +@Service +public class TokenService { + private static final String TOKEN_ISSUER = "mandacaru-broker"; + private static final int EXPIRATION_TIME = 864 * 1000 * 1000; + private static final int EXPIRATION_TIME_IN_SECONDS = EXPIRATION_TIME / 1000; + private static final String TOKEN_TYPE = "Bearer"; + private final Algorithm algorithm; + + public TokenService() { + String secret = SecuritySecrets.getJWTSecret(); + algorithm = Algorithm.HMAC512(secret.getBytes()); + } + + public ResponseAuthUserDTO encodeToken(final String subject) { + try { + return tryToEncodeToken(subject); + } catch (JWTCreationException exception) { + return null; + } + } + + private ResponseAuthUserDTO tryToEncodeToken(final String subject) { + Date expirationDate = new Date(System.currentTimeMillis() + EXPIRATION_TIME); + + String generatedToken = JWT.create() + .withSubject(subject) + .withIssuer(TOKEN_ISSUER) + .withExpiresAt(expirationDate) + .sign(algorithm); + + String tokenWithPrefix = TOKEN_TYPE + " " + generatedToken; + return new ResponseAuthUserDTO(tokenWithPrefix, EXPIRATION_TIME_IN_SECONDS, TOKEN_TYPE); + } + + public String getTokenSubject(final String token) { + DecodedJWT decodedToken = decodeUserToken(token); + return decodedToken.getSubject(); + } + + public DecodedJWT decodeUserToken(final String token) { + try { + return tryToDecodeUserToken(token); + } catch (Exception exception) { + return null; + } + } + + private DecodedJWT tryToDecodeUserToken(final String token) { + return JWT.require(algorithm).build().verify(token); + } +} diff --git a/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java b/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java index df50199f..60107fb5 100644 --- a/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java +++ b/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java @@ -1,8 +1,9 @@ package com.mandacarubroker.controller; import com.mandacarubroker.domain.auth.RequestAuthUserDTO; +import com.mandacarubroker.domain.auth.ResponseAuthUserDTO; import com.mandacarubroker.domain.user.RequestUserDTO; -import com.mandacarubroker.domain.user.User; +import com.mandacarubroker.security.SecuritySecretsMock; import com.mandacarubroker.service.AuthService; import com.mandacarubroker.service.PasswordHashingService; import org.junit.jupiter.api.BeforeEach; @@ -22,6 +23,8 @@ class AuthControllerTest { private AuthService authService; private AuthController authController; + private static final String TOKEN_TYPE = "Bearer"; + private final PasswordHashingService passwordHashingService = new PasswordHashingService(); private final String validEmail = "lara.souza@gmail.com"; @@ -50,12 +53,18 @@ class AuthControllerTest { validPassword ); + private final ResponseAuthUserDTO validResponseAuthUserDTO = new ResponseAuthUserDTO( + "Bearer token", + 86400, + "Bearer" + ); + @BeforeEach void setUp() { + SecuritySecretsMock.mockStatic(); + authService = Mockito.mock(AuthService.class); - User validUser = new User(validRequestUserDTO); - Optional optionalValidUser = Optional.of(validUser); - Mockito.when(authService.login(validRequestAuthUserDTO)).thenReturn(optionalValidUser); + Mockito.when(authService.login(validRequestAuthUserDTO)).thenReturn(Optional.of(validResponseAuthUserDTO)); Mockito.when(authService.login(new RequestAuthUserDTO(invalidUsername, validPassword))).thenReturn(Optional.empty()); Mockito.when(authService.login(new RequestAuthUserDTO(validUsername, invalidPassword))).thenReturn(Optional.empty()); authController = new AuthController(authService); @@ -63,8 +72,12 @@ void setUp() { @Test void itShouldBeAbleToLoginWithValidUser() { - ResponseEntity response = authController.login(validRequestAuthUserDTO); - assertEquals(ResponseEntity.ok("User logged in successfully"), response); + ResponseEntity response = authController.login(validRequestAuthUserDTO); + ResponseAuthUserDTO responseAuthUserDTO = response.getBody(); + + assertEquals(ResponseEntity.ok().build().getStatusCode(), response.getStatusCode()); + assertEquals(ResponseAuthUserDTO.class, responseAuthUserDTO.getClass()); + assertEquals(TOKEN_TYPE, responseAuthUserDTO.tokenType()); } @Test @@ -74,7 +87,7 @@ void itShouldNotBeAbleToLoginWithInvalidUser() { validPassword ); - ResponseEntity response = authController.login(invalidRequestAuthUserDTO); + ResponseEntity response = authController.login(invalidRequestAuthUserDTO); assertEquals(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(), response); } @@ -85,7 +98,7 @@ void itShouldNotBeAbleToLoginWithInvalidPassword() { invalidPassword ); - ResponseEntity response = authController.login(invalidRequestAuthUserDTO); + ResponseEntity response = authController.login(invalidRequestAuthUserDTO); assertEquals(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(), response); } } diff --git a/src/test/java/com/mandacarubroker/security/SecuritySecretsMock.java b/src/test/java/com/mandacarubroker/security/SecuritySecretsMock.java new file mode 100644 index 00000000..f681983e --- /dev/null +++ b/src/test/java/com/mandacarubroker/security/SecuritySecretsMock.java @@ -0,0 +1,20 @@ +package com.mandacarubroker.security; + +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +public final class SecuritySecretsMock { + private static MockedStatic securitySecretsMockedStatic = null; + + private SecuritySecretsMock() { + } + + public static void mockStatic() { + if (securitySecretsMockedStatic != null) { + return; + } + + securitySecretsMockedStatic = Mockito.mockStatic(SecuritySecrets.class); + securitySecretsMockedStatic.when(SecuritySecrets::getJWTSecret).thenReturn("secret"); + } +} diff --git a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java index d27d6455..38500bc6 100644 --- a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java +++ b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java @@ -4,6 +4,7 @@ import com.mandacarubroker.domain.user.RequestUserDTO; import com.mandacarubroker.domain.user.User; import com.mandacarubroker.domain.user.UserRepository; +import com.mandacarubroker.security.SecuritySecretsMock; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -44,10 +45,13 @@ class AuthServiceTest { @BeforeEach void setUp() { + SecuritySecretsMock.mockStatic(); + userRepository = Mockito.mock(UserRepository.class); validUser = new User(validRequestUserDTO); Mockito.when(userRepository.findByUsername(validUsername)).thenReturn(validUser); Mockito.when(userRepository.findByUsername(invalidUsername)).thenReturn(null); + authService = new AuthService(userRepository); } @@ -68,7 +72,7 @@ void itShouldBeAbleToLoginWithValidUser() { validPassword ); - Optional user = authService.login(validRequestAuthUserDTO); + Optional user = authService.getUserGivenCredentials(validRequestAuthUserDTO); assertEquals(true, user.isPresent()); assertUsersAreEqual(validUser, user.get()); } @@ -79,7 +83,7 @@ void itShouldNotBeAbleToLoginWithInvalidPassword() { validUsername, "invalidPassword" ); - Optional user = authService.login(invalidRequestAuthUserDTO); + Optional user = authService.getUserGivenCredentials(invalidRequestAuthUserDTO); assertEquals(false, user.isPresent()); } @@ -89,7 +93,7 @@ void itShouldNotBeAbleToLoginWithInvalidUsername() { invalidUsername, validPassword ); - Optional user = authService.login(invalidRequestAuthUserDTO); + Optional user = authService.getUserGivenCredentials(invalidRequestAuthUserDTO); assertEquals(false, user.isPresent()); } -} \ No newline at end of file +} From f44946eda4864a28e508dae837a23e736c527e6c Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 14:46:01 -0300 Subject: [PATCH 02/11] fix: autowire auth service dependencies --- .../java/com/mandacarubroker/service/AuthService.java | 9 +++++---- .../mandacarubroker/service/PasswordHashingService.java | 2 ++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/mandacarubroker/service/AuthService.java b/src/main/java/com/mandacarubroker/service/AuthService.java index 554ef845..0fed7d12 100644 --- a/src/main/java/com/mandacarubroker/service/AuthService.java +++ b/src/main/java/com/mandacarubroker/service/AuthService.java @@ -13,13 +13,14 @@ @Service public class AuthService { private final UserRepository userRepository; + private final PasswordHashingService passwordHashingService; private final TokenService tokenService; - public AuthService(final UserRepository receivedUserRepository) { - this.userRepository = receivedUserRepository; - this.passwordHashingService = new PasswordHashingService(); - this.tokenService = new TokenService(); + public AuthService(final UserRepository receivedUserRepostory, final PasswordHashingService receivedPasswordHashingService, final TokenService receivedTokenService) { + this.userRepository = receivedUserRepostory; + this.passwordHashingService = receivedPasswordHashingService; + this.tokenService = receivedTokenService; } public Optional login(final RequestAuthUserDTO requestAuthUserDTO) { diff --git a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java index 7d2937e7..437e1273 100644 --- a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java +++ b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java @@ -1,7 +1,9 @@ package com.mandacarubroker.service; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +@Service public class PasswordHashingService { private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); From 635e5303a5344c3c4fbdb4ed3c6b9e1872cfcb46 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 14:47:35 -0300 Subject: [PATCH 03/11] test: mock autowired auth service dependencies --- .../service/AuthServiceTest.java | 68 +++++++++++++++---- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java index 38500bc6..5bf2b7c8 100644 --- a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java +++ b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java @@ -1,6 +1,7 @@ package com.mandacarubroker.service; import com.mandacarubroker.domain.auth.RequestAuthUserDTO; +import com.mandacarubroker.domain.auth.ResponseAuthUserDTO; import com.mandacarubroker.domain.user.RequestUserDTO; import com.mandacarubroker.domain.user.User; import com.mandacarubroker.domain.user.UserRepository; @@ -19,13 +20,15 @@ class AuthServiceTest { @MockBean private UserRepository userRepository; private AuthService authService; - private final PasswordHashingService passwordHashingService = new PasswordHashingService(); + private PasswordHashingService passwordHashingService; + private TokenService tokenService; private final String validEmail = "lara.souza@gmail.com"; private final String validUsername = "username"; private final String invalidUsername = "invalidUsername"; private final String validPassword = "password"; - private final String validHashedPassword = passwordHashingService.hashPassword(validPassword); + private final String invalidPassword = "invalidPassword"; + private final String validHashedPassword = "hashedPassword"; private final String validFirstName = "Lara"; private final String validLastName = "Souza"; private final LocalDate validBirthDate = LocalDate.of(1997,4,5); @@ -41,6 +44,16 @@ class AuthServiceTest { validBalance ); + private final RequestAuthUserDTO validRequestAuthUserDTO = new RequestAuthUserDTO( + validUsername, + validPassword + ); + + private final String validToken = "Bearer token"; + private final int expiresIn = 86400; + private final String tokenType = "Bearer"; + + private User validUser; @BeforeEach @@ -52,7 +65,14 @@ void setUp() { Mockito.when(userRepository.findByUsername(validUsername)).thenReturn(validUser); Mockito.when(userRepository.findByUsername(invalidUsername)).thenReturn(null); - authService = new AuthService(userRepository); + passwordHashingService = Mockito.mock(PasswordHashingService.class); + Mockito.when(passwordHashingService.matches(validPassword, validHashedPassword)).thenReturn(true); + Mockito.when(passwordHashingService.matches(invalidPassword, validHashedPassword)).thenReturn(false); + + tokenService = Mockito.mock(TokenService.class); + Mockito.when(tokenService.encodeToken(validUser.getId())).thenReturn(new ResponseAuthUserDTO(validToken, expiresIn, tokenType)); + + authService = new AuthService(userRepository, passwordHashingService, tokenService); } private void assertUsersAreEqual(final User expected, final User actual) { @@ -66,29 +86,24 @@ private void assertUsersAreEqual(final User expected, final User actual) { } @Test - void itShouldBeAbleToLoginWithValidUser() { - RequestAuthUserDTO validRequestAuthUserDTO = new RequestAuthUserDTO( - validUsername, - validPassword - ); - + void itShouldBeAbleGetUserGivenValidCredentials() { Optional user = authService.getUserGivenCredentials(validRequestAuthUserDTO); assertEquals(true, user.isPresent()); assertUsersAreEqual(validUser, user.get()); } @Test - void itShouldNotBeAbleToLoginWithInvalidPassword() { + void itShouldNotBeAbleToGetUserGivenInvalidPassword() { RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO( validUsername, - "invalidPassword" + invalidPassword ); Optional user = authService.getUserGivenCredentials(invalidRequestAuthUserDTO); assertEquals(false, user.isPresent()); } @Test - void itShouldNotBeAbleToLoginWithInvalidUsername() { + void itShouldNotBeAbleToGetUserGivenInvalidUsername() { RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO( invalidUsername, validPassword @@ -96,4 +111,33 @@ void itShouldNotBeAbleToLoginWithInvalidUsername() { Optional user = authService.getUserGivenCredentials(invalidRequestAuthUserDTO); assertEquals(false, user.isPresent()); } + + @Test + void itShouldBeAbleToGetJwtTokenGivenValidUser() { + Optional responseAuthUserDTO = authService.login(validRequestAuthUserDTO); + assertEquals(true, responseAuthUserDTO.isPresent()); + assertEquals(validToken, responseAuthUserDTO.get().token()); + assertEquals(expiresIn, responseAuthUserDTO.get().expiresIn()); + assertEquals(tokenType, responseAuthUserDTO.get().tokenType()); + } + + @Test + void itShouldNotBeAbleToGetJwtTokenGivenInvalidUsername() { + RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO( + invalidUsername, + validPassword + ); + Optional responseAuthUserDTO = authService.login(invalidRequestAuthUserDTO); + assertEquals(false, responseAuthUserDTO.isPresent()); + } + + @Test + void itShouldNotBeAbleToGetJwtTokenGivenInvalidPassword() { + RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO( + validUsername, + invalidPassword + ); + Optional responseAuthUserDTO = authService.login(invalidRequestAuthUserDTO); + assertEquals(false, responseAuthUserDTO.isPresent()); + } } From 35be9f383c7b8a9ce559a5959bf642b4a0ce7404 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 14:57:27 -0300 Subject: [PATCH 04/11] chore: add jwt secret variable to workflows --- .github/workflows/build.yml | 1 + .github/workflows/test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2080a3a0..51a8ee81 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,7 @@ jobs: MANDACARU_POSTGRES_USER: "time_dez" MANDACARU_POSTGRES_PASSWORD: "mandacaru" MANDACARU_POSTGRES_PORT: 5432 + MANDACARU_JWT_SECRET: mandacaru-jwt-secret MANDACARU_TEST_POSTGRES_DB: "mandacaru_broker_test" MANDACARU_TEST_POSTGRES_USER: "time_dez_test" MANDACARU_TEST_POSTGRES_PASSWORD: "mandacaru" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 78e4a0b1..1c7ea80f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -34,6 +34,7 @@ jobs: MANDACARU_POSTGRES_USER: "time_dez" MANDACARU_POSTGRES_PASSWORD: "mandacaru" MANDACARU_POSTGRES_PORT: 5432 + MANDACARU_JWT_SECRET: mandacaru-jwt-secret MANDACARU_TEST_POSTGRES_DB: "mandacaru_broker_test" MANDACARU_TEST_POSTGRES_USER: "time_dez_test" MANDACARU_TEST_POSTGRES_PASSWORD: "mandacaru" From 6d425663cb62c8f3bbcf087dbcd5923fa652b50f Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 21:08:09 -0300 Subject: [PATCH 05/11] feat: make all routes private except those in whitelist --- pom.xml | 24 +++++++---- .../config/ApplicationConfig.java | 24 +++++++++++ .../com/mandacarubroker/domain/user/User.java | 34 +++++++++++++++- .../security/SecurityConfiguration.java | 40 +++++++++++++++++++ .../service/PasswordHashingService.java | 9 +++-- .../mandacarubroker/service/UserService.java | 23 +++++++++-- .../controller/AuthControllerTest.java | 2 +- .../service/PasswordHashingServiceTest.java | 6 +-- 8 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/mandacarubroker/config/ApplicationConfig.java create mode 100644 src/main/java/com/mandacarubroker/security/SecurityConfiguration.java diff --git a/pom.xml b/pom.xml index c65ec1ae..1a7cbbcc 100644 --- a/pom.xml +++ b/pom.xml @@ -43,6 +43,22 @@ org.springframework.boot spring-boot-starter-validation + + org.springframework.security + spring-security-core + 6.2.2 + + + org.springframework.security + spring-security-web + 6.2.2 + + + org.springframework.security + spring-security-config + 6.2.2 + + org.flywaydb flyway-core @@ -76,12 +92,6 @@ java-jwt 4.4.0 - - org.springframework.security - spring-security-core - 5.0.7.RELEASE - - org.junit.jupiter @@ -99,7 +109,7 @@ checkstyle 10.13.0 - + diff --git a/src/main/java/com/mandacarubroker/config/ApplicationConfig.java b/src/main/java/com/mandacarubroker/config/ApplicationConfig.java new file mode 100644 index 00000000..efa208a3 --- /dev/null +++ b/src/main/java/com/mandacarubroker/config/ApplicationConfig.java @@ -0,0 +1,24 @@ +package com.mandacarubroker.config; + +import com.mandacarubroker.service.PasswordHashingService; +import com.mandacarubroker.service.UserService; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; + +@Configuration +@RequiredArgsConstructor +public class ApplicationConfig { + private final UserService userService; + private final PasswordHashingService passwordHashingService; + + @Bean + public AuthenticationProvider authenticationProvider() { + DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(userService); + authProvider.setPasswordEncoder(passwordHashingService); + return authProvider; + } +} diff --git a/src/main/java/com/mandacarubroker/domain/user/User.java b/src/main/java/com/mandacarubroker/domain/user/User.java index e08e4f05..45ad39db 100644 --- a/src/main/java/com/mandacarubroker/domain/user/User.java +++ b/src/main/java/com/mandacarubroker/domain/user/User.java @@ -9,8 +9,11 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import java.time.LocalDate; +import java.util.Collection; @Table(name = "users") @Entity(name = "users") @@ -18,8 +21,7 @@ @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(of = "id") -public class User { - +public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.UUID) private String id; @@ -40,4 +42,32 @@ public User(final RequestUserDTO requestUserDTO) { this.birthDate = requestUserDTO.birthDate(); this.balance = requestUserDTO.balance(); } + + public Collection getAuthorities() { + return null; + } + + public String getPassword() { + return password; + } + + public String getUsername() { + return username; + } + + public boolean isAccountNonExpired() { + return true; + } + + public boolean isAccountNonLocked() { + return true; + } + + public boolean isCredentialsNonExpired() { + return true; + } + + public boolean isEnabled() { + return true; + } } diff --git a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java new file mode 100644 index 00000000..6f22936e --- /dev/null +++ b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java @@ -0,0 +1,40 @@ +package com.mandacarubroker.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; + +@Configuration +@EnableWebSecurity +@RequiredArgsConstructor +@EnableMethodSecurity +public class SecurityConfiguration { + private static final String[] WHITE_LIST_URL = { + "/", + "/auth/login", + "/auth/register", + }; + + private final AuthenticationProvider authenticationProvider; + + @Bean + public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { + http.csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(req -> + req.requestMatchers(WHITE_LIST_URL) + .permitAll() + ) + .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) + .authenticationProvider(authenticationProvider); + + return http.build(); + } +} diff --git a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java index 437e1273..3c360123 100644 --- a/src/main/java/com/mandacarubroker/service/PasswordHashingService.java +++ b/src/main/java/com/mandacarubroker/service/PasswordHashingService.java @@ -1,17 +1,18 @@ package com.mandacarubroker.service; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @Service -public class PasswordHashingService { +public class PasswordHashingService implements PasswordEncoder { private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); - public String hashPassword(final String plainPassword) { + public String encode(final CharSequence plainPassword) { return passwordEncoder.encode(plainPassword); } - public boolean matches(final String plainPassword, final String hashedPassword) { - return passwordEncoder.matches(plainPassword, hashedPassword); + public boolean matches(final CharSequence rawPassword, final String encodedPassword) { + return passwordEncoder.matches(rawPassword, encodedPassword); } } diff --git a/src/main/java/com/mandacarubroker/service/UserService.java b/src/main/java/com/mandacarubroker/service/UserService.java index 51723de0..00b6af96 100644 --- a/src/main/java/com/mandacarubroker/service/UserService.java +++ b/src/main/java/com/mandacarubroker/service/UserService.java @@ -3,6 +3,9 @@ import com.mandacarubroker.domain.user.RequestUserDTO; import com.mandacarubroker.domain.user.User; import com.mandacarubroker.domain.user.UserRepository; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import java.util.List; @@ -11,7 +14,7 @@ import static com.mandacarubroker.validation.RecordValidation.validateRequestDTO; @Service -public class UserService { +public class UserService implements UserDetailsService { private final UserRepository userRepository; private final PasswordHashingService passwordHashingService = new PasswordHashingService(); @@ -29,7 +32,7 @@ public Optional getUserById(final String userId) { private User hashPassword(final User user) { final String rawPassword = user.getPassword(); - final String hashedPassword = passwordHashingService.hashPassword(rawPassword); + final String hashedPassword = passwordHashingService.encode(rawPassword); user.setPassword(hashedPassword); return user; } @@ -45,7 +48,7 @@ public Optional updateUser(final String userId, final RequestUserDTO reque validateRequestDTO(requestUserDTO); final String rawPassword = requestUserDTO.password(); - final String hashedPassword = passwordHashingService.hashPassword(rawPassword); + final String hashedPassword = passwordHashingService.encode(rawPassword); return userRepository.findById(userId) .map(user -> { @@ -64,4 +67,18 @@ public Optional updateUser(final String userId, final RequestUserDTO reque public void deleteUser(final String id) { userRepository.deleteById(id); } + + public UserDetails loadUserByUsername(final String username) { + if (username == null || username.isEmpty()) { + throw new UsernameNotFoundException("Username is required"); + } + + User user = userRepository.findByUsername(username); + + if (user == null) { + throw new UsernameNotFoundException("User not found"); + } + + return user; + } } diff --git a/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java b/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java index 60107fb5..5555ceee 100644 --- a/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java +++ b/src/test/java/com/mandacarubroker/controller/AuthControllerTest.java @@ -32,7 +32,7 @@ class AuthControllerTest { private final String invalidUsername = "invalidUsername"; private final String validPassword = "password"; private final String invalidPassword = "invalidPassword"; - private final String validHashedPassword = passwordHashingService.hashPassword(validPassword); + private final String validHashedPassword = passwordHashingService.encode(validPassword); private final String validFirstName = "Lara"; private final String validLastName = "Souza"; private final LocalDate validBirthDate = LocalDate.of(1997,4,5); diff --git a/src/test/java/com/mandacarubroker/service/PasswordHashingServiceTest.java b/src/test/java/com/mandacarubroker/service/PasswordHashingServiceTest.java index bf7efc33..29371ddb 100644 --- a/src/test/java/com/mandacarubroker/service/PasswordHashingServiceTest.java +++ b/src/test/java/com/mandacarubroker/service/PasswordHashingServiceTest.java @@ -9,7 +9,7 @@ class PasswordHashingServiceTest { @Test void itShouldBeAbleToHashAndMatchRightPassword() { final PasswordHashingService underTest = new PasswordHashingService(); - final String hashedPassword = underTest.hashPassword("password"); + final String hashedPassword = underTest.encode("password"); final boolean matches = underTest.matches("password", hashedPassword); assertTrue(matches); } @@ -17,8 +17,8 @@ void itShouldBeAbleToHashAndMatchRightPassword() { @Test void itShouldNotMatchWrongPassword() { final PasswordHashingService underTest = new PasswordHashingService(); - final String hashedPassword = underTest.hashPassword("password"); + final String hashedPassword = underTest.encode("password"); final boolean matches = underTest.matches("wrongPassword", hashedPassword); assertFalse(matches); } -} \ No newline at end of file +} From 463611f92e1f3c19d6606c25fb5ab909575f4483 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 22:15:27 -0300 Subject: [PATCH 06/11] feat: ask for jwt to access all private routes --- .../security/JwtAuthFilter.java | 50 +++++++++++++++++++ .../security/SecurityConfiguration.java | 5 +- 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/mandacarubroker/security/JwtAuthFilter.java diff --git a/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java new file mode 100644 index 00000000..cbf27019 --- /dev/null +++ b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java @@ -0,0 +1,50 @@ +package com.mandacarubroker.security; + +import com.mandacarubroker.domain.user.UserRepository; +import com.mandacarubroker.service.TokenService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +@Component +@RequiredArgsConstructor +public class JwtAuthFilter extends OncePerRequestFilter { + private final TokenService tokenService; + private final UserRepository userRepository; + + @Override + protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { + final String authHeader = request.getHeader("Authorization"); + + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + } + + final String token = authHeader.substring(7); + final String userId = tokenService.getTokenSubject(token); + UserDetails user = userRepository.getById(userId); + + if (userId == null || user == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + filterChain.doFilter(request, response); + return; + } + + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user,null); + authToken.setDetails( + new WebAuthenticationDetailsSource().buildDetails(request) + ); + SecurityContextHolder.getContext().setAuthentication(authToken); + } +} diff --git a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java index 6f22936e..f9efdb31 100644 --- a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java +++ b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java @@ -9,6 +9,7 @@ import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS; @@ -24,6 +25,7 @@ public class SecurityConfiguration { }; private final AuthenticationProvider authenticationProvider; + private final JwtAuthFilter jwtAuthFilter; @Bean public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { @@ -33,7 +35,8 @@ public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws E .permitAll() ) .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) - .authenticationProvider(authenticationProvider); + .authenticationProvider(authenticationProvider) + .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } From fa78a7ac1107c59cd19002b516768a735878ab57 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 22:47:09 -0300 Subject: [PATCH 07/11] test: remove security in integration tests --- .../com/mandacarubroker/controller/StockControllerIT.java | 7 ++++++- .../com/mandacarubroker/controller/UserControllerIT.java | 6 +++--- .../java/com/mandacarubroker/service/StockServiceIT.java | 3 +++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/mandacarubroker/controller/StockControllerIT.java b/src/test/java/com/mandacarubroker/controller/StockControllerIT.java index 19120898..ece402f7 100644 --- a/src/test/java/com/mandacarubroker/controller/StockControllerIT.java +++ b/src/test/java/com/mandacarubroker/controller/StockControllerIT.java @@ -16,18 +16,23 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.ResultMatcher; +import java.util.Collections; import java.util.List; import java.util.Optional; @SpringBootTest -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) class StockControllerIT { @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/mandacarubroker/controller/UserControllerIT.java b/src/test/java/com/mandacarubroker/controller/UserControllerIT.java index 9606aabd..b18a40fd 100644 --- a/src/test/java/com/mandacarubroker/controller/UserControllerIT.java +++ b/src/test/java/com/mandacarubroker/controller/UserControllerIT.java @@ -10,6 +10,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; @@ -26,11 +28,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - @SpringBootTest -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) class UserControllerIT { - @Autowired private MockMvc mockMvc; diff --git a/src/test/java/com/mandacarubroker/service/StockServiceIT.java b/src/test/java/com/mandacarubroker/service/StockServiceIT.java index beeb8f71..8306a580 100644 --- a/src/test/java/com/mandacarubroker/service/StockServiceIT.java +++ b/src/test/java/com/mandacarubroker/service/StockServiceIT.java @@ -3,11 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mandacarubroker.domain.stock.RequestStockDTO; import com.mandacarubroker.domain.stock.Stock; +import com.mandacarubroker.security.SecurityConfiguration; import jakarta.validation.ConstraintViolationException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; From 0d9803aa613e3c0ad5ccfd5234423b684c708bf5 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 23:01:38 -0300 Subject: [PATCH 08/11] refactor: remove unused imports --- .../com/mandacarubroker/controller/StockControllerIT.java | 5 ----- .../com/mandacarubroker/controller/UserControllerIT.java | 3 --- .../java/com/mandacarubroker/service/StockServiceIT.java | 3 --- 3 files changed, 11 deletions(-) diff --git a/src/test/java/com/mandacarubroker/controller/StockControllerIT.java b/src/test/java/com/mandacarubroker/controller/StockControllerIT.java index ece402f7..564168ea 100644 --- a/src/test/java/com/mandacarubroker/controller/StockControllerIT.java +++ b/src/test/java/com/mandacarubroker/controller/StockControllerIT.java @@ -16,18 +16,13 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.RequestBuilder; import org.springframework.test.web.servlet.ResultMatcher; -import java.util.Collections; import java.util.List; import java.util.Optional; diff --git a/src/test/java/com/mandacarubroker/controller/UserControllerIT.java b/src/test/java/com/mandacarubroker/controller/UserControllerIT.java index b18a40fd..f50dd461 100644 --- a/src/test/java/com/mandacarubroker/controller/UserControllerIT.java +++ b/src/test/java/com/mandacarubroker/controller/UserControllerIT.java @@ -2,7 +2,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; - import com.mandacarubroker.domain.user.RequestUserDTO; import com.mandacarubroker.domain.user.User; import com.mandacarubroker.service.UserService; @@ -10,8 +9,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; diff --git a/src/test/java/com/mandacarubroker/service/StockServiceIT.java b/src/test/java/com/mandacarubroker/service/StockServiceIT.java index 8306a580..beeb8f71 100644 --- a/src/test/java/com/mandacarubroker/service/StockServiceIT.java +++ b/src/test/java/com/mandacarubroker/service/StockServiceIT.java @@ -3,14 +3,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.mandacarubroker.domain.stock.RequestStockDTO; import com.mandacarubroker.domain.stock.Stock; -import com.mandacarubroker.security.SecurityConfiguration; import jakarta.validation.ConstraintViolationException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; From e4302cd1aedd80679a162ea2b28537a4c47cc5af Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Thu, 29 Feb 2024 23:03:21 -0300 Subject: [PATCH 09/11] chore: add spring security version properties --- pom.xml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 1a7cbbcc..0774bb25 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,7 @@ mandacaru-broker https://sonarcloud.io 2.0.2 + 6.2.2 @@ -46,17 +47,17 @@ org.springframework.security spring-security-core - 6.2.2 + ${spring.security.version} org.springframework.security spring-security-web - 6.2.2 + ${spring.security.version} org.springframework.security spring-security-config - 6.2.2 + ${spring.security.version} From d57ef96d7648f119c534f4a27361271539fcad76 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Fri, 1 Mar 2024 09:31:19 -0300 Subject: [PATCH 10/11] fix: jwt auth filter --- .../com/mandacarubroker/security/JwtAuthFilter.java | 10 +++++++++- .../security/SecurityConfiguration.java | 8 ++++---- .../java/com/mandacarubroker/service/TokenService.java | 5 +++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java index cbf27019..f598ea70 100644 --- a/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java +++ b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java @@ -33,9 +33,16 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ final String token = authHeader.substring(7); final String userId = tokenService.getTokenSubject(token); + + if (userId == null) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + filterChain.doFilter(request, response); + return; + } + UserDetails user = userRepository.getById(userId); - if (userId == null || user == null) { + if (user == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); filterChain.doFilter(request, response); return; @@ -46,5 +53,6 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ new WebAuthenticationDetailsSource().buildDetails(request) ); SecurityContextHolder.getContext().setAuthentication(authToken); + filterChain.doFilter(request, response); } } diff --git a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java index f9efdb31..3c57331d 100644 --- a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java +++ b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java @@ -30,11 +30,11 @@ public class SecurityConfiguration { @Bean public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws Exception { http.csrf(AbstractHttpConfigurer::disable) - .authorizeHttpRequests(req -> - req.requestMatchers(WHITE_LIST_URL) - .permitAll() - ) .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) + .authorizeHttpRequests(req -> { + req.requestMatchers(WHITE_LIST_URL).permitAll(); + req.anyRequest().permitAll(); + }) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/src/main/java/com/mandacarubroker/service/TokenService.java b/src/main/java/com/mandacarubroker/service/TokenService.java index af0370ba..ee70332c 100644 --- a/src/main/java/com/mandacarubroker/service/TokenService.java +++ b/src/main/java/com/mandacarubroker/service/TokenService.java @@ -46,6 +46,11 @@ private ResponseAuthUserDTO tryToEncodeToken(final String subject) { public String getTokenSubject(final String token) { DecodedJWT decodedToken = decodeUserToken(token); + + if (decodedToken == null) { + return null; + } + return decodedToken.getSubject(); } From 78b0a4093b0d3dd48bd269d703ddc77cd1a487a3 Mon Sep 17 00:00:00 2001 From: Izaias Machado Date: Fri, 1 Mar 2024 10:06:44 -0300 Subject: [PATCH 11/11] fix: encode username in jwt token --- .../java/com/mandacarubroker/security/JwtAuthFilter.java | 8 ++++---- .../mandacarubroker/security/SecurityConfiguration.java | 2 +- .../java/com/mandacarubroker/service/AuthService.java | 2 +- .../java/com/mandacarubroker/service/AuthServiceTest.java | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java index f598ea70..b92a657a 100644 --- a/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java +++ b/src/main/java/com/mandacarubroker/security/JwtAuthFilter.java @@ -1,7 +1,7 @@ package com.mandacarubroker.security; -import com.mandacarubroker.domain.user.UserRepository; import com.mandacarubroker.service.TokenService; +import com.mandacarubroker.service.UserService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -20,7 +20,7 @@ @RequiredArgsConstructor public class JwtAuthFilter extends OncePerRequestFilter { private final TokenService tokenService; - private final UserRepository userRepository; + private final UserService userService; @Override protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException { @@ -40,7 +40,7 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ return; } - UserDetails user = userRepository.getById(userId); + UserDetails user = userService.loadUserByUsername(userId); if (user == null) { response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); @@ -48,7 +48,7 @@ protected void doFilterInternal(final HttpServletRequest request, final HttpServ return; } - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user,null); + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(user,null, user.getAuthorities()); authToken.setDetails( new WebAuthenticationDetailsSource().buildDetails(request) ); diff --git a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java index 3c57331d..5a032a22 100644 --- a/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java +++ b/src/main/java/com/mandacarubroker/security/SecurityConfiguration.java @@ -33,7 +33,7 @@ public SecurityFilterChain securityFilterChain(final HttpSecurity http) throws E .sessionManagement(session -> session.sessionCreationPolicy(STATELESS)) .authorizeHttpRequests(req -> { req.requestMatchers(WHITE_LIST_URL).permitAll(); - req.anyRequest().permitAll(); + req.anyRequest().authenticated(); }) .authenticationProvider(authenticationProvider) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/src/main/java/com/mandacarubroker/service/AuthService.java b/src/main/java/com/mandacarubroker/service/AuthService.java index 0fed7d12..daefe76a 100644 --- a/src/main/java/com/mandacarubroker/service/AuthService.java +++ b/src/main/java/com/mandacarubroker/service/AuthService.java @@ -30,7 +30,7 @@ public Optional login(final RequestAuthUserDTO requestAuthU return Optional.empty(); } - String userId = user.get().getId(); + String userId = user.get().getUsername(); ResponseAuthUserDTO responseAuthUserDTO = tokenService.encodeToken(userId); return Optional.of(responseAuthUserDTO); } diff --git a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java index 5bf2b7c8..a76a7865 100644 --- a/src/test/java/com/mandacarubroker/service/AuthServiceTest.java +++ b/src/test/java/com/mandacarubroker/service/AuthServiceTest.java @@ -70,7 +70,7 @@ void setUp() { Mockito.when(passwordHashingService.matches(invalidPassword, validHashedPassword)).thenReturn(false); tokenService = Mockito.mock(TokenService.class); - Mockito.when(tokenService.encodeToken(validUser.getId())).thenReturn(new ResponseAuthUserDTO(validToken, expiresIn, tokenType)); + Mockito.when(tokenService.encodeToken(validUser.getUsername())).thenReturn(new ResponseAuthUserDTO(validToken, expiresIn, tokenType)); authService = new AuthService(userRepository, passwordHashingService, tokenService); }