From 17dcc0ac4ff8db308e4bda5f75aa7d1da328687b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beatrice=20Dellac=C3=A0?= Date: Fri, 9 Aug 2024 04:56:54 +0200 Subject: [PATCH] implement registration validity checks --- .../config/ApplicationConfiguration.java | 5 ++- .../config/InternalConfiguration.java | 8 ++++ .../releasehive/exceptions/HiveException.java | 9 +++++ .../exceptions/RestExceptionHandler.java | 6 +++ ...AccountResource.java => AuthResource.java} | 9 +++-- .../releasehive/services/AccountService.java | 3 +- .../services/AccountServiceImpl.java | 37 ++++++++++++++++++- .../releasehive/services/UserService.java | 24 ++---------- .../releasehive/services/UserServiceImpl.java | 34 +++++++++++++++++ 9 files changed, 108 insertions(+), 27 deletions(-) create mode 100644 src/main/java/wtf/beatrice/releasehive/config/InternalConfiguration.java create mode 100644 src/main/java/wtf/beatrice/releasehive/exceptions/HiveException.java rename src/main/java/wtf/beatrice/releasehive/resources/{AccountResource.java => AuthResource.java} (90%) create mode 100644 src/main/java/wtf/beatrice/releasehive/services/UserServiceImpl.java diff --git a/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java b/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java index 5393c52..b277195 100644 --- a/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java +++ b/src/main/java/wtf/beatrice/releasehive/config/ApplicationConfiguration.java @@ -7,11 +7,14 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import wtf.beatrice.releasehive.repositories.UserRepository; +import java.util.function.Supplier; + @Configuration public class ApplicationConfiguration { @@ -24,7 +27,7 @@ public class ApplicationConfiguration @Bean UserDetailsService userDetailsService() { - return email -> userRepository.findByEmail(email) + return authParameter -> userRepository.findByEmail(authParameter) .orElseThrow(() -> new UsernameNotFoundException("User not found")); } diff --git a/src/main/java/wtf/beatrice/releasehive/config/InternalConfiguration.java b/src/main/java/wtf/beatrice/releasehive/config/InternalConfiguration.java new file mode 100644 index 0000000..86045f8 --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/config/InternalConfiguration.java @@ -0,0 +1,8 @@ +package wtf.beatrice.releasehive.config; + +public class InternalConfiguration +{ + public static final String EMAIL_REGEX_RCF = "(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])"; + public static final String USERNAME_REGEX = "^[a-zA-Z0-9_-]{3,16}$"; + public static final String PASSWORD_REGEX = "^.{6,128}$"; +} diff --git a/src/main/java/wtf/beatrice/releasehive/exceptions/HiveException.java b/src/main/java/wtf/beatrice/releasehive/exceptions/HiveException.java new file mode 100644 index 0000000..bc59113 --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/exceptions/HiveException.java @@ -0,0 +1,9 @@ +package wtf.beatrice.releasehive.exceptions; + +public class HiveException extends Exception +{ + public HiveException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/src/main/java/wtf/beatrice/releasehive/exceptions/RestExceptionHandler.java b/src/main/java/wtf/beatrice/releasehive/exceptions/RestExceptionHandler.java index 57cccb7..d047d97 100644 --- a/src/main/java/wtf/beatrice/releasehive/exceptions/RestExceptionHandler.java +++ b/src/main/java/wtf/beatrice/releasehive/exceptions/RestExceptionHandler.java @@ -1,6 +1,7 @@ package wtf.beatrice.releasehive.exceptions; import io.jsonwebtoken.ExpiredJwtException; +import org.apache.coyote.BadRequestException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.http.HttpStatusCode; @@ -25,6 +26,11 @@ public class RestExceptionHandler LOGGER.error(exception); + if(exception instanceof BadRequestException) { + errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(400), exception.getMessage()); + errorDetail.setProperty(DESCRIPTION_PROPERTY, "Bad request"); + } + if (exception instanceof BadCredentialsException) { errorDetail = ProblemDetail.forStatusAndDetail(HttpStatusCode.valueOf(401), exception.getMessage()); errorDetail.setProperty(DESCRIPTION_PROPERTY, "Invalid email or password"); diff --git a/src/main/java/wtf/beatrice/releasehive/resources/AccountResource.java b/src/main/java/wtf/beatrice/releasehive/resources/AuthResource.java similarity index 90% rename from src/main/java/wtf/beatrice/releasehive/resources/AccountResource.java rename to src/main/java/wtf/beatrice/releasehive/resources/AuthResource.java index bef4c90..73ac835 100644 --- a/src/main/java/wtf/beatrice/releasehive/resources/AccountResource.java +++ b/src/main/java/wtf/beatrice/releasehive/resources/AuthResource.java @@ -1,6 +1,8 @@ package wtf.beatrice.releasehive.resources; +import org.apache.coyote.BadRequestException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -15,12 +17,12 @@ import wtf.beatrice.releasehive.services.JWTService; @RestController @RequestMapping("/api/v1/auth") -public class AccountResource { +public class AuthResource { private final AccountService accountService; private final JWTService jwtService; - public AccountResource( + public AuthResource( @Autowired AccountService accountService, @Autowired JWTService jwtService) { this.accountService = accountService; @@ -30,8 +32,7 @@ public class AccountResource { @PostMapping( value="/register", produces="application/json") - public ResponseEntity register(@RequestBody RegisterUserDto userDto) - { + public ResponseEntity register(@RequestBody RegisterUserDto userDto) throws BadRequestException { User user = accountService.register(userDto); return ResponseEntity.ok(user); } diff --git a/src/main/java/wtf/beatrice/releasehive/services/AccountService.java b/src/main/java/wtf/beatrice/releasehive/services/AccountService.java index 4badb3c..2a2b3aa 100644 --- a/src/main/java/wtf/beatrice/releasehive/services/AccountService.java +++ b/src/main/java/wtf/beatrice/releasehive/services/AccountService.java @@ -1,5 +1,6 @@ package wtf.beatrice.releasehive.services; +import org.apache.coyote.BadRequestException; import wtf.beatrice.releasehive.dtos.LoginUserDto; import wtf.beatrice.releasehive.dtos.RegisterUserDto; import wtf.beatrice.releasehive.models.User; @@ -7,7 +8,7 @@ import wtf.beatrice.releasehive.models.User; public interface AccountService { - User register(RegisterUserDto user); + User register(RegisterUserDto user) throws BadRequestException; User login(LoginUserDto user); } diff --git a/src/main/java/wtf/beatrice/releasehive/services/AccountServiceImpl.java b/src/main/java/wtf/beatrice/releasehive/services/AccountServiceImpl.java index 01cd354..b5887d8 100644 --- a/src/main/java/wtf/beatrice/releasehive/services/AccountServiceImpl.java +++ b/src/main/java/wtf/beatrice/releasehive/services/AccountServiceImpl.java @@ -1,12 +1,17 @@ package wtf.beatrice.releasehive.services; +import org.apache.coyote.BadRequestException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import wtf.beatrice.releasehive.config.InternalConfiguration; import wtf.beatrice.releasehive.dtos.LoginUserDto; import wtf.beatrice.releasehive.dtos.RegisterUserDto; +import wtf.beatrice.releasehive.exceptions.HiveException; import wtf.beatrice.releasehive.models.User; import wtf.beatrice.releasehive.repositories.UserRepository; @@ -28,7 +33,37 @@ public class AccountServiceImpl implements AccountService { } @Override - public User register(RegisterUserDto userDto) { + public User register(RegisterUserDto userDto) throws BadRequestException { + + if (userDto.getEmail() == null || + userDto.getEmail().isEmpty() || + userDto.getPassword() == null || + userDto.getPassword().isEmpty() || + userDto.getUsername() == null || + userDto.getUsername().isEmpty()) { + + throw new BadRequestException("Please provide a valid email, password, and username"); + } + + if(!userDto.getEmail().matches(InternalConfiguration.EMAIL_REGEX_RCF)) { + throw new BadRequestException("Invalid email format"); + } + + if(!userDto.getUsername().matches(InternalConfiguration.USERNAME_REGEX)) { + throw new BadRequestException("Username contains invalid characters"); + } + + if(!userDto.getPassword().matches(InternalConfiguration.PASSWORD_REGEX)) { + throw new BadRequestException("Invalid password format"); + } + + if (userRepository.findByEmail(userDto.getEmail()).isPresent()) { + throw new BadRequestException("An account already exists with this email"); + } + + if(userRepository.findByUsername(userDto.getUsername()).isPresent()) { + throw new BadRequestException("Username already in use"); + } User user = new User(); user.setUsername(userDto.getUsername()); diff --git a/src/main/java/wtf/beatrice/releasehive/services/UserService.java b/src/main/java/wtf/beatrice/releasehive/services/UserService.java index 2eddd87..5176b0c 100644 --- a/src/main/java/wtf/beatrice/releasehive/services/UserService.java +++ b/src/main/java/wtf/beatrice/releasehive/services/UserService.java @@ -1,31 +1,15 @@ package wtf.beatrice.releasehive.services; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; import wtf.beatrice.releasehive.models.User; -import wtf.beatrice.releasehive.repositories.UserRepository; import java.util.List; -@Service -public class UserService +public interface UserService { - private final UserRepository userRepository; + List getAllUsers(); - public UserService(@Autowired UserRepository userRepository) { - this.userRepository = userRepository; - } + User loadUserByUsername(String username) throws UsernameNotFoundException; - public List getAllUsers() { - return userRepository.findAll(); - } - - public User loadUserByUsername(String username) throws UsernameNotFoundException { - return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username)); - } - - public User loadUserByEmail(String email) throws UsernameNotFoundException { - return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email)); - } + User loadUserByEmail(String email) throws UsernameNotFoundException; } diff --git a/src/main/java/wtf/beatrice/releasehive/services/UserServiceImpl.java b/src/main/java/wtf/beatrice/releasehive/services/UserServiceImpl.java new file mode 100644 index 0000000..21b0bb1 --- /dev/null +++ b/src/main/java/wtf/beatrice/releasehive/services/UserServiceImpl.java @@ -0,0 +1,34 @@ +package wtf.beatrice.releasehive.services; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import wtf.beatrice.releasehive.models.User; +import wtf.beatrice.releasehive.repositories.UserRepository; + +import java.util.List; + +@Service +public class UserServiceImpl implements UserService +{ + private final UserRepository userRepository; + + public UserServiceImpl(@Autowired UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + public List getAllUsers() { + return userRepository.findAll(); + } + + @Override + public User loadUserByUsername(String username) throws UsernameNotFoundException { + return userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException(username)); + } + + @Override + public User loadUserByEmail(String email) throws UsernameNotFoundException { + return userRepository.findByEmail(email).orElseThrow(() -> new UsernameNotFoundException(email)); + } +}