diff --git a/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java b/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java index 676c500..739f476 100644 --- a/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java +++ b/src/main/java/net/kapcake/bankingservice/controllers/AuthController.java @@ -1,19 +1,48 @@ package net.kapcake.bankingservice.controllers; +import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; +import net.kapcake.bankingservice.exceptions.ValidationException; +import net.kapcake.bankingservice.model.dtos.UserUpdateDTO; import net.kapcake.bankingservice.security.UserDetailsImpl; +import net.kapcake.bankingservice.services.UserService; import org.springframework.security.core.Authentication; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import static net.kapcake.bankingservice.controllers.ControllerUtils.getErrorString; + @RestController @Slf4j public class AuthController { + private final UserService userService; + + public AuthController(UserService userService) { + this.userService = userService; + } + @PostMapping("/login") public void login(HttpServletRequest request) { Authentication auth = (Authentication) request.getUserPrincipal(); UserDetailsImpl user = (UserDetailsImpl) auth.getPrincipal(); log.info("User {} logged in.", user.getUsername()); } + + @PutMapping("/update-user") + public void updateUser(HttpServletRequest request, @AuthenticationPrincipal UserDetailsImpl authenticatedUser, @RequestBody @Valid UserUpdateDTO userUpdateDTO, BindingResult bindingResult) throws ServletException { + if (bindingResult.hasErrors()) { + String errorString = getErrorString(bindingResult); + throw new ValidationException("User update request invalid: " + errorString); + } + boolean needsLogout = userService.updateUser(authenticatedUser, userUpdateDTO); + if (needsLogout) { + request.logout(); + } + } } diff --git a/src/main/java/net/kapcake/bankingservice/model/domain/User.java b/src/main/java/net/kapcake/bankingservice/model/domain/User.java index f78080f..1a44cac 100644 --- a/src/main/java/net/kapcake/bankingservice/model/domain/User.java +++ b/src/main/java/net/kapcake/bankingservice/model/domain/User.java @@ -1,8 +1,7 @@ package net.kapcake.bankingservice.model.domain; import jakarta.persistence.*; -import lombok.Getter; -import lombok.Setter; +import lombok.*; import lombok.experimental.Accessors; import java.util.List; @@ -12,6 +11,9 @@ import java.util.List; @Accessors(chain = true) @Getter @Setter +@Builder(toBuilder = true) +@AllArgsConstructor +@NoArgsConstructor public class User { @Id @GeneratedValue diff --git a/src/main/java/net/kapcake/bankingservice/model/dtos/UserUpdateDTO.java b/src/main/java/net/kapcake/bankingservice/model/dtos/UserUpdateDTO.java new file mode 100644 index 0000000..e3012ba --- /dev/null +++ b/src/main/java/net/kapcake/bankingservice/model/dtos/UserUpdateDTO.java @@ -0,0 +1,21 @@ +package net.kapcake.bankingservice.model.dtos; + +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import lombok.experimental.Accessors; +import net.kapcake.bankingservice.validation.AtLeastOneFieldNotEmpty; + +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@AtLeastOneFieldNotEmpty(fieldNames = {"password", "street", "number", "numberExtension", "postalCode", "country"}) +public class UserUpdateDTO implements Serializable { + @Pattern(regexp = "^[^\\s]+$") + private String password; + private String street; + private Integer number; + private String numberExtension; + private Integer postalCode; + private String country; +} diff --git a/src/main/java/net/kapcake/bankingservice/services/UserService.java b/src/main/java/net/kapcake/bankingservice/services/UserService.java new file mode 100644 index 0000000..4887991 --- /dev/null +++ b/src/main/java/net/kapcake/bankingservice/services/UserService.java @@ -0,0 +1,53 @@ +package net.kapcake.bankingservice.services; + +import net.kapcake.bankingservice.model.domain.User; +import net.kapcake.bankingservice.model.dtos.UserUpdateDTO; +import net.kapcake.bankingservice.repositories.UserRepository; +import net.kapcake.bankingservice.security.UserDetailsImpl; +import org.modelmapper.ModelMapper; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class UserService { + private final UserRepository userRepository; + private final ModelMapper modelMapper; + private final PasswordEncoder passwordEncoder; + + public UserService(UserRepository userRepository, ModelMapper modelMapper, PasswordEncoder passwordEncoder) { + this.userRepository = userRepository; + this.modelMapper = modelMapper; + this.passwordEncoder = passwordEncoder; + } + + public boolean updateUser(UserDetailsImpl authenticatedUser, UserUpdateDTO userUpdateDTO) { + boolean needsLogout = false; + User user = userRepository.findByUsername(authenticatedUser.getUsername()).orElseThrow(); + User.UserBuilder builder = user.toBuilder(); + + if (userUpdateDTO.getCountry() != null) { + builder.country(userUpdateDTO.getCountry()); + } + if (userUpdateDTO.getStreet() != null) { + builder.street(userUpdateDTO.getStreet()); + } + if (userUpdateDTO.getPostalCode() != null) { + builder.postalCode(userUpdateDTO.getPostalCode()); + } + if (userUpdateDTO.getNumber() != null) { + builder.number(userUpdateDTO.getNumber()); + } + if (userUpdateDTO.getNumberExtension() != null) { + builder.numberExtension(userUpdateDTO.getNumberExtension()); + } + if (userUpdateDTO.getPassword() != null) { + builder.password(passwordEncoder.encode(userUpdateDTO.getPassword())); + needsLogout = true; + } + + User updatedUser = builder.build(); + userRepository.save(updatedUser); + + return needsLogout; + } +}