Skip to content

Commit

Permalink
feat: bcrypt (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
izaiasmachado authored Feb 29, 2024
1 parent 75f7093 commit 08cd60e
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 2 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.0.7.RELEASE</version>
</dependency>

<!-- junit 5 -->
<dependency>
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/com/mandacarubroker/controller/AuthController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.mandacarubroker.controller;

import com.mandacarubroker.domain.auth.RequestAuthUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.service.AuthService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;

public AuthController(final AuthService receivedAuthService) {
this.authService = receivedAuthService;
}

@PostMapping("/login")
public ResponseEntity<String> login(@Valid @RequestBody final RequestAuthUserDTO requestAuthUserDTO) {
Optional<User> user = authService.login(requestAuthUserDTO);

if (user.isEmpty()) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}

return ResponseEntity.ok("User logged in successfully");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mandacarubroker.domain.auth;

import jakarta.validation.constraints.NotBlank;

public record RequestAuthUserDTO(
@NotBlank(message = "Username is required")
String username,
@NotBlank(message = "Password is required")
String password
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@

@Repository
public interface UserRepository extends JpaRepository<User, String> {
User findByUsername(String username);
}
40 changes: 40 additions & 0 deletions src/main/java/com/mandacarubroker/service/AuthService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.mandacarubroker.service;

import com.mandacarubroker.domain.auth.RequestAuthUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.domain.user.UserRepository;
import org.springframework.stereotype.Service;

import java.util.Optional;

import static com.mandacarubroker.validation.RecordValidation.validateRequestDTO;

@Service
public class AuthService {
private final UserRepository userRepository;
private final PasswordHashingService passwordHashingService = new PasswordHashingService();

public AuthService(final UserRepository receivedUserRepository) {
this.userRepository = receivedUserRepository;
}

public Optional<User> login(final RequestAuthUserDTO requestAuthUserDTO) {
validateRequestDTO(requestAuthUserDTO);

User user = userRepository.findByUsername(requestAuthUserDTO.username());

if (user == null) {
return Optional.empty();
}

final String givenPassword = requestAuthUserDTO.password();
final String storedPassword = user.getPassword();
final boolean isPasswordCorrect = passwordHashingService.matches(givenPassword, storedPassword);

if (!isPasswordCorrect) {
return Optional.empty();
}

return Optional.of(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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();

public String hashPassword(final String plainPassword) {
return passwordEncoder.encode(plainPassword);
}

public boolean matches(final String plainPassword, final String hashedPassword) {
return passwordEncoder.matches(plainPassword, hashedPassword);
}
}
16 changes: 15 additions & 1 deletion src/main/java/com/mandacarubroker/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
@Service
public class UserService {
private final UserRepository userRepository;
private final PasswordHashingService passwordHashingService = new PasswordHashingService();

public UserService(final UserRepository recievedUserRepository) {
this.userRepository = recievedUserRepository;
Expand All @@ -26,14 +27,26 @@ public Optional<User> getUserById(final String userId) {
return userRepository.findById(userId);
}

private User hashPassword(final User user) {
final String rawPassword = user.getPassword();
final String hashedPassword = passwordHashingService.hashPassword(rawPassword);
user.setPassword(hashedPassword);
return user;
}

public User createUser(final RequestUserDTO requestUserDTO) {
validateRequestDTO(requestUserDTO);
User newUser = new User(requestUserDTO);
return userRepository.save(newUser);
User hashedPasswordUser = hashPassword(newUser);
return userRepository.save(hashedPasswordUser);
}

public Optional<User> updateUser(final String userId, final RequestUserDTO requestUserDTO) {
validateRequestDTO(requestUserDTO);

final String rawPassword = requestUserDTO.password();
final String hashedPassword = passwordHashingService.hashPassword(rawPassword);

return userRepository.findById(userId)
.map(user -> {
user.setEmail(requestUserDTO.email());
Expand All @@ -43,6 +56,7 @@ public Optional<User> updateUser(final String userId, final RequestUserDTO reque
user.setLastName(requestUserDTO.lastName());
user.setBirthDate(requestUserDTO.birthDate());
user.setBalance(requestUserDTO.balance());
user.setPassword(hashedPassword);
return userRepository.save(user);
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.mandacarubroker.controller;

import com.mandacarubroker.domain.auth.RequestAuthUserDTO;
import com.mandacarubroker.domain.user.RequestUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.service.AuthService;
import com.mandacarubroker.service.PasswordHashingService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.time.LocalDate;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;

class AuthControllerTest {
@MockBean
private AuthService authService;
private AuthController authController;

private final PasswordHashingService passwordHashingService = new PasswordHashingService();

private final String validEmail = "[email protected]";
private final String validUsername = "username";
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 validFirstName = "Lara";
private final String validLastName = "Souza";
private final LocalDate validBirthDate = LocalDate.of(1997,4,5);
private final double validBalance = 90.50;

private final RequestUserDTO validRequestUserDTO = new RequestUserDTO(
validEmail,
validUsername,
validHashedPassword,
validFirstName,
validLastName,
validBirthDate,
validBalance
);

private final RequestAuthUserDTO validRequestAuthUserDTO = new RequestAuthUserDTO(
validUsername,
validPassword
);

@BeforeEach
void setUp() {
authService = Mockito.mock(AuthService.class);
User validUser = new User(validRequestUserDTO);
Optional<User> optionalValidUser = Optional.of(validUser);
Mockito.when(authService.login(validRequestAuthUserDTO)).thenReturn(optionalValidUser);
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);
}

@Test
void itShouldBeAbleToLoginWithValidUser() {
ResponseEntity<String> response = authController.login(validRequestAuthUserDTO);
assertEquals(ResponseEntity.ok("User logged in successfully"), response);
}

@Test
void itShouldNotBeAbleToLoginWithInvalidUser() {
RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO(
invalidUsername,
validPassword
);

ResponseEntity<String> response = authController.login(invalidRequestAuthUserDTO);
assertEquals(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(), response);
}

@Test
void itShouldNotBeAbleToLoginWithInvalidPassword() {
RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO(
validUsername,
invalidPassword
);

ResponseEntity<String> response = authController.login(invalidRequestAuthUserDTO);
assertEquals(ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(), response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ void tearDown() {
void assertRequestDTOEqualsUser(final RequestUserDTO userDTO, final User receivedUser) {
assertEquals(userDTO.email(), receivedUser.getEmail());
assertEquals(userDTO.username(), receivedUser.getUsername());
assertEquals(userDTO.password(), receivedUser.getPassword());
assertEquals(userDTO.firstName(), receivedUser.getFirstName());
assertEquals(userDTO.lastName(), receivedUser.getLastName());
assertEquals(userDTO.birthDate(), receivedUser.getBirthDate());
Expand Down
95 changes: 95 additions & 0 deletions src/test/java/com/mandacarubroker/service/AuthServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package com.mandacarubroker.service;

import com.mandacarubroker.domain.auth.RequestAuthUserDTO;
import com.mandacarubroker.domain.user.RequestUserDTO;
import com.mandacarubroker.domain.user.User;
import com.mandacarubroker.domain.user.UserRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.time.LocalDate;
import java.util.Optional;

import static org.junit.jupiter.api.Assertions.assertEquals;

class AuthServiceTest {
@MockBean
private UserRepository userRepository;
private AuthService authService;
private final PasswordHashingService passwordHashingService = new PasswordHashingService();

private final String validEmail = "[email protected]";
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 validFirstName = "Lara";
private final String validLastName = "Souza";
private final LocalDate validBirthDate = LocalDate.of(1997,4,5);
private final double validBalance = 90.50;

private final RequestUserDTO validRequestUserDTO = new RequestUserDTO(
validEmail,
validUsername,
validHashedPassword,
validFirstName,
validLastName,
validBirthDate,
validBalance
);

private User validUser;

@BeforeEach
void setUp() {
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);
}

private void assertUsersAreEqual(final User expected, final User actual) {
assertEquals(expected.getEmail(), actual.getEmail());
assertEquals(expected.getUsername(), actual.getUsername());
assertEquals(expected.getPassword(), actual.getPassword());
assertEquals(expected.getFirstName(), actual.getFirstName());
assertEquals(expected.getLastName(), actual.getLastName());
assertEquals(expected.getBirthDate(), actual.getBirthDate());
assertEquals(expected.getBalance(), actual.getBalance());
}

@Test
void itShouldBeAbleToLoginWithValidUser() {
RequestAuthUserDTO validRequestAuthUserDTO = new RequestAuthUserDTO(
validUsername,
validPassword
);

Optional<User> user = authService.login(validRequestAuthUserDTO);
assertEquals(true, user.isPresent());
assertUsersAreEqual(validUser, user.get());
}

@Test
void itShouldNotBeAbleToLoginWithInvalidPassword() {
RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO(
validUsername,
"invalidPassword"
);
Optional<User> user = authService.login(invalidRequestAuthUserDTO);
assertEquals(false, user.isPresent());
}

@Test
void itShouldNotBeAbleToLoginWithInvalidUsername() {
RequestAuthUserDTO invalidRequestAuthUserDTO = new RequestAuthUserDTO(
invalidUsername,
validPassword
);
Optional<User> user = authService.login(invalidRequestAuthUserDTO);
assertEquals(false, user.isPresent());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.mandacarubroker.service;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class PasswordHashingServiceTest {

@Test
void itShouldBeAbleToHashAndMatchRightPassword() {
final PasswordHashingService underTest = new PasswordHashingService();
final String hashedPassword = underTest.hashPassword("password");
final boolean matches = underTest.matches("password", hashedPassword);
assertTrue(matches);
}

@Test
void itShouldNotMatchWrongPassword() {
final PasswordHashingService underTest = new PasswordHashingService();
final String hashedPassword = underTest.hashPassword("password");
final boolean matches = underTest.matches("wrongPassword", hashedPassword);
assertFalse(matches);
}
}

0 comments on commit 08cd60e

Please sign in to comment.