From 0f8ad7b8b29a1f37be4e99c0835ef065314b1a2b Mon Sep 17 00:00:00 2001 From: Marvin Date: Fri, 11 Nov 2022 16:39:13 +0100 Subject: [PATCH 1/4] [sync] enhanced validation & better persisting. [Error: Validator not working] --- .../role/controller/impl/RoleController.java | 5 +- .../user/controller/IUserController.java | 6 +- .../user/controller/impl/UserController.java | 6 +- .../user/dto/in/UserChangeInDTO.java | 3 +- ...serRepository.java => UserRepository.java} | 2 +- .../user/service/crud/IUserService.java | 9 +- .../user/service/crud/impl/UserService.java | 82 +++++++++---------- .../{AUserMapper.java => UserMapper.java} | 3 +- .../validation/SubmissionProcessor.java | 38 +++++++++ .../UserChangeSubmissionProcessor.java | 49 +++++++++++ .../UserChangeSubmissionValidator.java | 27 ++++++ .../validation/UserSubmissionProcessor.java | 58 +++++++++++++ .../validation/UserSubmissionValidator.java | 32 ++++++++ .../validation/UserValidationLocation.java | 23 ++++++ .../validation/UserValidationReason.java | 18 ++++ .../service/validation/ValidatorBase.java | 8 ++ ...equestException.java => ApiException.java} | 8 +- .../api/exceptions/ApiExceptionHandler.java | 4 +- .../api/util/communication/InDTO.java | 9 +- .../filters/JwtAuthorizationFilter.java | 6 +- .../services/JwtUserDetailsService.java | 4 +- .../{other => annotations}/Development.java | 2 +- .../ObjectValidator.java} | 10 +-- .../com/ohmyclass/util/validators/Pass.java | 55 +++++++++++++ .../ohmyclass/util/validators/Validator.java | 22 +++++ .../collectors/LocationCollector.java | 8 ++ .../collectors/ReasonCollector.java | 8 ++ .../collectors/ResultCollector.java | 9 ++ .../util/validators/other/Outcome.java | 13 +++ .../validators/other/ValidationLocation.java | 6 ++ .../validators/other/ValidationReason.java | 6 ++ .../validators/rejectors/BaseRejector.java | 37 +++++++++ .../validators/rejectors/BooleanRejector.java | 29 +++++++ .../validators/rejectors/StringRejector.java | 20 +++++ src/main/resources/application.yml | 2 +- 35 files changed, 548 insertions(+), 79 deletions(-) rename src/main/java/com/ohmyclass/api/components/user/repository/{IUserRepository.java => UserRepository.java} (89%) rename src/main/java/com/ohmyclass/api/components/user/service/mapper/{AUserMapper.java => UserMapper.java} (85%) create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java rename src/main/java/com/ohmyclass/api/exceptions/{ApiRequestException.java => ApiException.java} (55%) rename src/main/java/com/ohmyclass/util/{other => annotations}/Development.java (90%) rename src/main/java/com/ohmyclass/util/{validate/Validate.java => validators/ObjectValidator.java} (59%) create mode 100644 src/main/java/com/ohmyclass/util/validators/Pass.java create mode 100644 src/main/java/com/ohmyclass/util/validators/Validator.java create mode 100644 src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/other/Outcome.java create mode 100644 src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java create mode 100644 src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java create mode 100644 src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java diff --git a/src/main/java/com/ohmyclass/api/components/role/controller/impl/RoleController.java b/src/main/java/com/ohmyclass/api/components/role/controller/impl/RoleController.java index 6a61792..ddfedc0 100644 --- a/src/main/java/com/ohmyclass/api/components/role/controller/impl/RoleController.java +++ b/src/main/java/com/ohmyclass/api/components/role/controller/impl/RoleController.java @@ -5,11 +5,10 @@ import com.ohmyclass.api.components.role.entity.Role; import com.ohmyclass.api.components.role.repository.IRoleRepository; import com.ohmyclass.api.components.user.entity.User; -import com.ohmyclass.api.components.user.repository.IUserRepository; +import com.ohmyclass.api.components.user.repository.UserRepository; import com.ohmyclass.api.util.communication.Request; import com.ohmyclass.api.util.communication.Response; import lombok.AllArgsConstructor; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.Optional; @@ -39,7 +38,7 @@ public Response deleteRole(Request roleIdPayload) { } private final IRoleRepository roleRepository; - private final IUserRepository userRepository; + private final UserRepository userRepository; @Override public Response setRoleToUser(String username) { diff --git a/src/main/java/com/ohmyclass/api/components/user/controller/IUserController.java b/src/main/java/com/ohmyclass/api/components/user/controller/IUserController.java index 62963bc..3441140 100644 --- a/src/main/java/com/ohmyclass/api/components/user/controller/IUserController.java +++ b/src/main/java/com/ohmyclass/api/components/user/controller/IUserController.java @@ -61,10 +61,10 @@ public interface IUserController { @PutMapping("/user") @Operation(summary = "Edit user details in database") @ApiResponse(responseCode = "200", description = "The updated user", content = { - @Content(mediaType = "application/json", schema = @Schema(implementation = UserOutDTO.class)) }) + @Content(mediaType = "application/json", schema = @Schema(implementation = Boolean.class)) }) @ApiResponse(responseCode = "401", description = "Authentication failed") @ApiResponse(responseCode = "500", description = "General server error") - Response updateUser(@RequestBody Request userChangeIn); + Response updateUser(@RequestBody UserChangeInDTO userChangeIn); @Secured("ROLE_USER") @DeleteMapping("/user") @@ -73,5 +73,5 @@ public interface IUserController { @Content(mediaType = "application/json", schema = @Schema(implementation = Boolean.class)) }) @ApiResponse(responseCode = "401", description = "Authentication failed") @ApiResponse(responseCode = "500", description = "General server error") - Response deleteUser(@RequestBody Request user); + Response deleteUser(@RequestBody String username); } diff --git a/src/main/java/com/ohmyclass/api/components/user/controller/impl/UserController.java b/src/main/java/com/ohmyclass/api/components/user/controller/impl/UserController.java index 1380cbe..f9ebc3b 100644 --- a/src/main/java/com/ohmyclass/api/components/user/controller/impl/UserController.java +++ b/src/main/java/com/ohmyclass/api/components/user/controller/impl/UserController.java @@ -43,12 +43,12 @@ public Response getUser(String username) { } @Override - public Response updateUser(Request userChangeIn) { + public Response updateUser(UserChangeInDTO userChangeIn) { return new Response<>(userService.update(userChangeIn), ValidationResult.ok()); } @Override - public Response deleteUser(Request user) { - return new Response<>(userService.delete(user), ValidationResult.ok()); + public Response deleteUser(String username) { + return new Response<>(userService.delete(username), ValidationResult.ok()); } } diff --git a/src/main/java/com/ohmyclass/api/components/user/dto/in/UserChangeInDTO.java b/src/main/java/com/ohmyclass/api/components/user/dto/in/UserChangeInDTO.java index 00e7f2c..71f528f 100644 --- a/src/main/java/com/ohmyclass/api/components/user/dto/in/UserChangeInDTO.java +++ b/src/main/java/com/ohmyclass/api/components/user/dto/in/UserChangeInDTO.java @@ -1,11 +1,12 @@ package com.ohmyclass.api.components.user.dto.in; +import com.ohmyclass.api.util.communication.InDTO; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class UserChangeInDTO extends UserInDTO { +public class UserChangeInDTO extends InDTO { private String newEmail; diff --git a/src/main/java/com/ohmyclass/api/components/user/repository/IUserRepository.java b/src/main/java/com/ohmyclass/api/components/user/repository/UserRepository.java similarity index 89% rename from src/main/java/com/ohmyclass/api/components/user/repository/IUserRepository.java rename to src/main/java/com/ohmyclass/api/components/user/repository/UserRepository.java index 2ba6fd0..24aea9a 100644 --- a/src/main/java/com/ohmyclass/api/components/user/repository/IUserRepository.java +++ b/src/main/java/com/ohmyclass/api/components/user/repository/UserRepository.java @@ -7,7 +7,7 @@ import java.util.Optional; @Repository -public interface IUserRepository extends CrudRepository { +public interface UserRepository extends CrudRepository { Optional findById(Long id); diff --git a/src/main/java/com/ohmyclass/api/components/user/service/crud/IUserService.java b/src/main/java/com/ohmyclass/api/components/user/service/crud/IUserService.java index f80cf39..7598c8f 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/crud/IUserService.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/crud/IUserService.java @@ -3,24 +3,29 @@ import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; import com.ohmyclass.api.components.user.dto.in.UserInDTO; import com.ohmyclass.api.components.user.dto.out.UserOutDTO; +import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.api.util.communication.Request; import com.ohmyclass.api.util.communication.Response; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import javax.transaction.Transactional; import java.util.Map; public interface IUserService { + @Transactional(rollbackOn = ApiException.class) Map register(UserInDTO user); void refreshToken(HttpServletRequest request, HttpServletResponse response); UserOutDTO getUser(String username); - UserOutDTO update(Request user); + @Transactional(rollbackOn = ApiException.class) + Boolean update(UserChangeInDTO user); - Boolean delete(Request user); + @Transactional(rollbackOn = ApiException.class) + Boolean delete(String username); void passwordForgotten(HttpServletRequest request, HttpServletResponse response); } diff --git a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java index be5ac7e..02b4e7e 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java @@ -7,13 +7,14 @@ import com.ohmyclass.api.components.user.dto.in.UserInDTO; import com.ohmyclass.api.components.user.dto.out.UserOutDTO; import com.ohmyclass.api.components.user.entity.User; -import com.ohmyclass.api.components.user.repository.IUserRepository; +import com.ohmyclass.api.components.user.repository.UserRepository; import com.ohmyclass.api.components.user.service.crud.IUserService; -import com.ohmyclass.api.components.user.service.mapper.AUserMapper; -import com.ohmyclass.api.exceptions.ApiRequestException; -import com.ohmyclass.api.util.communication.Request; +import com.ohmyclass.api.components.user.service.mapper.UserMapper; +import com.ohmyclass.api.components.user.service.validation.UserChangeSubmissionProcessor; +import com.ohmyclass.api.components.user.service.validation.UserSubmissionProcessor; +import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.security.util.JwtTokenUtil; -import com.ohmyclass.util.validate.Validate; +import com.ohmyclass.util.validators.*; import lombok.AllArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -24,11 +25,9 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import javax.transaction.Transactional; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.function.BiPredicate; import java.util.stream.Collectors; import static org.springframework.http.HttpHeaders.AUTHORIZATION; @@ -38,35 +37,28 @@ @AllArgsConstructor public class UserService implements IUserService { - private final IUserRepository userRepo; + private final UserRepository userRepository; - private final AUserMapper userMapper; - - private final PasswordEncoder passwordEncoder; + private final UserMapper userMapper; private final JwtTokenUtil tokenUtil; - @Override - @Transactional - public Map register(UserInDTO inDTO) { + private final UserSubmissionProcessor userSubmissionProcessor; - Validate.notNull( "No Payload found", inDTO); + private final UserChangeSubmissionProcessor userChangeSubmissionProcessor; - if (userRepo.findByUsername(inDTO.getUsername()).isPresent()) - throw new ApiRequestException("Username already exists"); + @Override + public Map register(UserInDTO inDTO) { - User user = userMapper.inDTOToEntity(inDTO); - user.addRole(new Role("ROLE_USER")); - Optional user1 = saveUser(user); + userSubmissionProcessor.process(inDTO); - if (user1.isEmpty()) - throw new ApiRequestException("Registration failed"); + User persistedUser = userSubmissionProcessor.getPersistedEntity(); - List roles = user.getRoles().stream() + List roles = persistedUser.getRoles().stream() .map(Role::getName) .collect(Collectors.toList()); - return tokenUtil.generateNewTokenMap(user.getUsername(), "Registration", roles); + return tokenUtil.generateNewTokenMap(persistedUser.getUsername(), "Registration", roles); } @Override @@ -76,7 +68,7 @@ public void refreshToken(HttpServletRequest request, HttpServletResponse respons // Guard if (!tokenUtil.isValidBearer(authorizationHeader)) { - throw new ApiRequestException("Invalid bearer token."); + throw new ApiException("Invalid bearer token."); } try { @@ -84,7 +76,7 @@ public void refreshToken(HttpServletRequest request, HttpServletResponse respons if (tokenUtil.isTokenExpired(decodedJWT)) { - throw new ApiRequestException("Token expired. Please login again"); + throw new ApiException("Token expired. Please login again"); } String newAccessToken = createNewAccessToken(request, decodedJWT); @@ -92,27 +84,36 @@ public void refreshToken(HttpServletRequest request, HttpServletResponse respons new ObjectMapper().writeValue(response.getOutputStream(), newAccessToken); } catch (Exception e) { - throw new ApiRequestException("Refreshing token failed"); + throw new ApiException("Refreshing token failed"); } } @Override public UserOutDTO getUser(String username) { - Validate.notNull(username); + ObjectValidator.notNull("Supplied username was null", username); - return userMapper.entityToOutDTO(userRepo.findByUsername(username) - .orElseThrow(() -> new ApiRequestException("User not found"))); + return userMapper.entityToOutDTO(userRepository.findByUsernameOrEmail(username, username) + .orElseThrow(() -> new ApiException("User not found"))); } @Override - public UserOutDTO update(Request userIn) { - return null; + public Boolean update(UserChangeInDTO userIn) { + + return userChangeSubmissionProcessor.process(userIn).isOk(); } @Override - public Boolean delete(Request userIn) { - return null; + public Boolean delete(String username) { + + ObjectValidator.notNull("Body can not be null", username); + + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new ApiException("User not found")); + + userRepository.delete(user); + + return userRepository.findByUsername(username).isEmpty(); } @Override @@ -121,8 +122,8 @@ public void passwordForgotten(HttpServletRequest request, HttpServletResponse re private String createNewAccessToken(HttpServletRequest request, DecodedJWT decodedJWT) { - User user = userRepo.findByUsername(decodedJWT.getSubject()) - .orElseThrow(() -> new ApiRequestException("User not found for token refresh")); + User user = userRepository.findByUsername(decodedJWT.getSubject()) + .orElseThrow(() -> new ApiException("User not found for token refresh")); String subject = user.getUsername(); String issuer = request.getRequestURI(); @@ -143,13 +144,4 @@ private String createNewAccessToken(HttpServletRequest request, DecodedJWT decod return newAccessToken; } - - private Optional saveUser(User user) { - - log.info("Saving user {}", user.getUsername()); - - user.setPassword(passwordEncoder.encode(user.getPassword())); - - return Optional.of(userRepo.save(user)); - } } diff --git a/src/main/java/com/ohmyclass/api/components/user/service/mapper/AUserMapper.java b/src/main/java/com/ohmyclass/api/components/user/service/mapper/UserMapper.java similarity index 85% rename from src/main/java/com/ohmyclass/api/components/user/service/mapper/AUserMapper.java rename to src/main/java/com/ohmyclass/api/components/user/service/mapper/UserMapper.java index 9d2ade5..497954c 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/mapper/AUserMapper.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/mapper/UserMapper.java @@ -6,9 +6,10 @@ import com.ohmyclass.api.components.user.entity.User; import com.sun.istack.NotNull; import org.mapstruct.*; +import org.springframework.security.crypto.password.PasswordEncoder; @Mapper(componentModel = "spring", uses = APreferencesMapper.class) -public abstract class AUserMapper { +public abstract class UserMapper { @Mapping(source = "preferences", target = "preferencesOut") public abstract UserOutDTO entityToOutDTO(@NotNull User user); diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java new file mode 100644 index 0000000..3e18bb4 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java @@ -0,0 +1,38 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.util.validation.ValidationResult; + +public abstract class SubmissionProcessor { + + protected T submission; + + private boolean isOk = true; + + public SubmissionProcessor process(T t) { + + if (validate(t).isOk()) { + + prePersistOperations(t); + + persist(t); + + postPersistOperations(t); + } else { + isOk = false; + } + + return this; + } + + public boolean isOk() { + return isOk; + } + + protected abstract ValidationResult validate(T t); + + protected abstract void persist(T t); + + protected abstract void postPersistOperations(T t); + + protected abstract void prePersistOperations(T t); +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java new file mode 100644 index 0000000..c1f1c12 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java @@ -0,0 +1,49 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; +import com.ohmyclass.api.components.user.entity.User; +import com.ohmyclass.api.components.user.repository.UserRepository; +import com.ohmyclass.api.exceptions.ApiException; +import com.ohmyclass.api.util.validation.ValidationResult; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserChangeSubmissionProcessor extends SubmissionProcessor { + + private final UserChangeSubmissionValidator userChangeSubmissionValidator; + + private final UserRepository userRepository; + + @Override + protected ValidationResult validate(UserChangeInDTO userIn) { + return userChangeSubmissionValidator.validate(userIn); + } + + @Override + protected void persist(UserChangeInDTO userIn) { + + User user = userRepository.findByUsername(userIn.resolveHeaderById("username")) + .orElseThrow(() -> new ApiException("User to edit not found")); + + user.setEmail(userIn.getNewEmail()); + user.setPassword(userIn.getNewPassword()); + + userRepository.save(user); + } + + @Override + protected void prePersistOperations(UserChangeInDTO userIn) { + if (userRepository.findByEmail(userIn.getNewEmail()).isPresent()) { + throw new ApiException("Email already exists"); + } + } + + @Override + protected void postPersistOperations(UserChangeInDTO userIn) { + if (userRepository.findByEmailAndPassword(userIn.getNewEmail(), userIn.getNewPassword()).isEmpty()) { + throw new ApiException("User not updated correctly"); + } + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java new file mode 100644 index 0000000..621338e --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java @@ -0,0 +1,27 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; +import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.util.validators.Validator; +import org.springframework.stereotype.Component; + +@Component +public class UserChangeSubmissionValidator implements ValidatorBase { + + public ValidationResult validate(UserChangeInDTO user) { + + ValidationResult validationResult = ValidationResult.ok(); + + Validator.reject(user.getNewEmail()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .location(UserValidationLocation.USERNAME) + .finish(validationResult); + + Validator.reject(user.getNewPassword()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .location(UserValidationLocation.USERNAME) + .finish(validationResult); + + return validationResult; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java new file mode 100644 index 0000000..e15d059 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java @@ -0,0 +1,58 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.components.user.dto.in.UserInDTO; +import com.ohmyclass.api.components.user.entity.User; +import com.ohmyclass.api.components.user.repository.UserRepository; +import com.ohmyclass.api.components.user.service.mapper.UserMapper; +import com.ohmyclass.api.exceptions.ApiException; +import com.ohmyclass.api.util.validation.ValidationResult; +import lombok.RequiredArgsConstructor; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class UserSubmissionProcessor extends SubmissionProcessor { + + private final UserSubmissionValidator userSubmissionValidator; + + private final UserRepository userRepository; + + private final UserMapper userMapper; + + private final PasswordEncoder passwordEncoder; + + public User getPersistedEntity() { + return userRepository.findByUsername(submission.getUsername()) + .orElseThrow(() -> new ApiException("User not get-able, due to error")); + } + + @Override + protected ValidationResult validate(UserInDTO userIn) { + return userSubmissionValidator.validate(userIn); + } + + @Override + protected void persist(UserInDTO userIn) { + + User user = userMapper.inDTOToEntity(userIn); + + user.setPassword(passwordEncoder.encode(user.getPassword())); + + userRepository.save(user); + } + + @Override + protected void prePersistOperations(UserInDTO userIn) { + if (userRepository.findByUsername(userIn.getUsername()).isPresent()) { + throw new ApiException("Username already exists"); + } + } + + @Override + protected void postPersistOperations(UserInDTO userIn) { + if (userRepository.findByUsername(userIn.getUsername()).isEmpty()) { + throw new ApiException("User not persisted correctly"); + } + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java new file mode 100644 index 0000000..db22d26 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java @@ -0,0 +1,32 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.components.user.dto.in.UserInDTO; +import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.util.validators.Validator; +import org.springframework.stereotype.Component; + +@Component +public class UserSubmissionValidator implements ValidatorBase { + + public ValidationResult validate(UserInDTO user) { + + ValidationResult validationResult = ValidationResult.ok(); + + Validator.reject(user.getUsername()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .location(UserValidationLocation.USERNAME) + .finish(validationResult); + + Validator.reject(user.getEmail()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .location(UserValidationLocation.USERNAME) + .finish(validationResult); + + Validator.reject(user.getPassword()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .location(UserValidationLocation.USERNAME) + .finish(validationResult); + + return validationResult; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java new file mode 100644 index 0000000..1985b59 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java @@ -0,0 +1,23 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.util.validators.other.ValidationLocation; + +public enum UserValidationLocation implements ValidationLocation { + + EMAIL("email"), + USERNAME("username"), + PASSWORD("password"), + NAME("name"), + ROLE("role"); + + private final String location; + + UserValidationLocation(String location) { + this.location = location; + } + + @Override + public String getLocation() { + return location; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java new file mode 100644 index 0000000..6604edf --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java @@ -0,0 +1,18 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.util.validators.other.ValidationReason; + +public enum UserValidationReason implements ValidationReason { + USERNAME_NULL("Username cannot be null"); + + String reason; + + UserValidationReason(String reason) { + this.reason = reason; + } + + @Override + public String getReason() { + return reason; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java new file mode 100644 index 0000000..87f96b4 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java @@ -0,0 +1,8 @@ +package com.ohmyclass.api.components.user.service.validation; + +import com.ohmyclass.api.util.validation.ValidationResult; + +public interface ValidatorBase { + + ValidationResult validate(T t); +} diff --git a/src/main/java/com/ohmyclass/api/exceptions/ApiRequestException.java b/src/main/java/com/ohmyclass/api/exceptions/ApiException.java similarity index 55% rename from src/main/java/com/ohmyclass/api/exceptions/ApiRequestException.java rename to src/main/java/com/ohmyclass/api/exceptions/ApiException.java index 06c5102..f3c4ead 100644 --- a/src/main/java/com/ohmyclass/api/exceptions/ApiRequestException.java +++ b/src/main/java/com/ohmyclass/api/exceptions/ApiException.java @@ -7,20 +7,20 @@ * * @author z-100 */ -public class ApiRequestException extends RuntimeException { +public class ApiException extends RuntimeException { @Getter private String message; - public ApiRequestException() { + public ApiException() { super(); } - public ApiRequestException(String message) { + public ApiException(String message) { this.message = message; } - public ApiRequestException(String message, Throwable cause) { + public ApiException(String message, Throwable cause) { super(message, cause); } } diff --git a/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java b/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java index 45143cf..0f5aa9f 100644 --- a/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java +++ b/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java @@ -22,10 +22,10 @@ @Order(Ordered.HIGHEST_PRECEDENCE) public class ApiExceptionHandler extends ResponseEntityExceptionHandler { - @ExceptionHandler(value = { ApiRequestException.class }) + @ExceptionHandler(value = { ApiException.class }) @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) - public ResponseEntity handleApiRequestException(ApiRequestException e) { + public ResponseEntity handleApiRequestException(ApiException e) { ApiError error = new ApiError( e.getMessage(), diff --git a/src/main/java/com/ohmyclass/api/util/communication/InDTO.java b/src/main/java/com/ohmyclass/api/util/communication/InDTO.java index ee200aa..8eb9b09 100644 --- a/src/main/java/com/ohmyclass/api/util/communication/InDTO.java +++ b/src/main/java/com/ohmyclass/api/util/communication/InDTO.java @@ -3,10 +3,15 @@ import lombok.Getter; import lombok.Setter; +import java.util.Map; + @Getter @Setter public abstract class InDTO { - //TODO Implement proper inDTO, to make validation more complex but easier - private String header; + private Map headers; + + public String resolveHeaderById(String id) { + return headers.get(id); + } } diff --git a/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java b/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java index ba8d534..5f3d803 100644 --- a/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java +++ b/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java @@ -1,7 +1,7 @@ package com.ohmyclass.security.filters; import com.auth0.jwt.interfaces.DecodedJWT; -import com.ohmyclass.api.exceptions.ApiRequestException; +import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.security.util.JwtTokenUtil; import com.ohmyclass.server.properties.JwtConstants; import lombok.extern.log4j.Log4j2; @@ -62,7 +62,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse // Guard if (!tokenUtil.isValidBearer(authorizationHeader)) { - throw new ApiRequestException("Invalid bearer token"); + throw new ApiException("Invalid bearer token"); } createSessionFrom(authorizationHeader); @@ -77,7 +77,7 @@ private void createSessionFrom(String authorizationHeader) { try { decodedJWT = tokenUtil.extractBearer.apply(authorizationHeader); } catch (Exception e) { - throw new ApiRequestException("Invalid token"); + throw new ApiException("Invalid token"); } String username = decodedJWT.getSubject(); diff --git a/src/main/java/com/ohmyclass/security/services/JwtUserDetailsService.java b/src/main/java/com/ohmyclass/security/services/JwtUserDetailsService.java index 69e808d..16aa5a8 100644 --- a/src/main/java/com/ohmyclass/security/services/JwtUserDetailsService.java +++ b/src/main/java/com/ohmyclass/security/services/JwtUserDetailsService.java @@ -1,7 +1,7 @@ package com.ohmyclass.security.services; import com.ohmyclass.api.components.user.entity.User; -import com.ohmyclass.api.components.user.repository.IUserRepository; +import com.ohmyclass.api.components.user.repository.UserRepository; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.springframework.security.core.authority.SimpleGrantedAuthority; @@ -25,7 +25,7 @@ @RequiredArgsConstructor public class JwtUserDetailsService implements UserDetailsService { - private final IUserRepository userRepo; + private final UserRepository userRepo; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { diff --git a/src/main/java/com/ohmyclass/util/other/Development.java b/src/main/java/com/ohmyclass/util/annotations/Development.java similarity index 90% rename from src/main/java/com/ohmyclass/util/other/Development.java rename to src/main/java/com/ohmyclass/util/annotations/Development.java index f922402..29d7d25 100644 --- a/src/main/java/com/ohmyclass/util/other/Development.java +++ b/src/main/java/com/ohmyclass/util/annotations/Development.java @@ -1,4 +1,4 @@ -package com.ohmyclass.util.other; +package com.ohmyclass.util.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/src/main/java/com/ohmyclass/util/validate/Validate.java b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java similarity index 59% rename from src/main/java/com/ohmyclass/util/validate/Validate.java rename to src/main/java/com/ohmyclass/util/validators/ObjectValidator.java index bf37c4e..1fc7b34 100644 --- a/src/main/java/com/ohmyclass/util/validate/Validate.java +++ b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java @@ -1,21 +1,21 @@ -package com.ohmyclass.util.validate; +package com.ohmyclass.util.validators; -import com.ohmyclass.api.exceptions.ApiRequestException; +import com.ohmyclass.api.exceptions.ApiException; import java.util.Arrays; import java.util.Objects; -public class Validate { +public class ObjectValidator { @SafeVarargs public static void notNull(T... t) { if (Arrays.stream(t).anyMatch(Objects::isNull)) - throw new ApiRequestException(); + throw new ApiException(); } @SafeVarargs public static void notNull(String message, T... t) { if (Arrays.stream(t).anyMatch(Objects::isNull)) - throw new ApiRequestException(message); + throw new ApiException(message); } } diff --git a/src/main/java/com/ohmyclass/util/validators/Pass.java b/src/main/java/com/ohmyclass/util/validators/Pass.java new file mode 100644 index 0000000..7d5dab2 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/Pass.java @@ -0,0 +1,55 @@ +package com.ohmyclass.util.validators; + +import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.api.util.validation.http.ValidationStatus; +import com.ohmyclass.util.validators.collectors.LocationCollector; +import com.ohmyclass.util.validators.collectors.ReasonCollector; +import com.ohmyclass.util.validators.other.Outcome; +import com.ohmyclass.util.validators.other.ValidationLocation; +import com.ohmyclass.util.validators.other.ValidationReason; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Pass implements ReasonCollector, LocationCollector { + + ValidationReason reason; + + ValidationLocation location; + + Outcome outcome; + + public static Pass reject() { + return new Pass(null, null, Outcome.REJECT); + } + + @Override + public ReasonCollector location(ValidationLocation location) { + this.location = location; + return this; + } + + @Override + public LocationCollector reason(ValidationReason reason) { + this.reason = reason; + return this; + } + + public Outcome finish(ValidationResult validationResult) { + + if (this.outcome == null) { + throw new RuntimeException("Pass outcome is null"); + } + + if (this.outcome.isReject()) { + validationResult.add(ValidationStatus.ERROR, this.reason.getReason(), this.location.getLocation()); + } + + return this.outcome; + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/Validator.java b/src/main/java/com/ohmyclass/util/validators/Validator.java new file mode 100644 index 0000000..93961fe --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/Validator.java @@ -0,0 +1,22 @@ +package com.ohmyclass.util.validators; + +import com.ohmyclass.util.validators.collectors.ReasonCollector; +import com.ohmyclass.util.validators.rejectors.BaseRejector; +import com.ohmyclass.util.validators.rejectors.StringRejector; +import org.springframework.stereotype.Component; + +@Component +public class Validator { + + public static ReasonCollector reject() { + return Pass.reject(); + } + + public static BaseRejector reject(Object entry) { + return new BaseRejector<>(new Pass(), entry); + } + + public static StringRejector reject(String entry) { + return new StringRejector(new Pass(), entry); + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java new file mode 100644 index 0000000..148abea --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java @@ -0,0 +1,8 @@ +package com.ohmyclass.util.validators.collectors; + +import com.ohmyclass.util.validators.other.ValidationLocation; + +public interface LocationCollector extends ResultCollector { + + ReasonCollector location(ValidationLocation location); +} diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java new file mode 100644 index 0000000..e77b719 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java @@ -0,0 +1,8 @@ +package com.ohmyclass.util.validators.collectors; + +import com.ohmyclass.util.validators.other.ValidationReason; + +public interface ReasonCollector extends ResultCollector{ + + LocationCollector reason(ValidationReason reason); +} diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java new file mode 100644 index 0000000..da13ae2 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java @@ -0,0 +1,9 @@ +package com.ohmyclass.util.validators.collectors; + +import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.util.validators.other.Outcome; + +public interface ResultCollector { + + Outcome finish(ValidationResult validationResult); +} diff --git a/src/main/java/com/ohmyclass/util/validators/other/Outcome.java b/src/main/java/com/ohmyclass/util/validators/other/Outcome.java new file mode 100644 index 0000000..7f7d09d --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/other/Outcome.java @@ -0,0 +1,13 @@ +package com.ohmyclass.util.validators.other; + +public enum Outcome { + ACCEPT, REJECT; + + public boolean isAccept() { + return this == ACCEPT; + } + + public boolean isReject() { + return this == REJECT; + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java b/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java new file mode 100644 index 0000000..75b6730 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java @@ -0,0 +1,6 @@ +package com.ohmyclass.util.validators.other; + +public interface ValidationLocation { + + String getLocation(); +} diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java b/src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java new file mode 100644 index 0000000..dc51b11 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java @@ -0,0 +1,6 @@ +package com.ohmyclass.util.validators.other; + +public interface ValidationReason { + + String getReason(); +} diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java new file mode 100644 index 0000000..f7c1230 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java @@ -0,0 +1,37 @@ +package com.ohmyclass.util.validators.rejectors; + +import com.ohmyclass.util.validators.other.Outcome; +import com.ohmyclass.util.validators.Pass; +import com.ohmyclass.util.validators.collectors.ReasonCollector; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +public class BaseRejector { + + protected Pass pass; + + protected T entry; + + public ReasonCollector ifNull() { + + if (entry == null) { + reject(); + } else { + accept(); + } + + return this.pass; + } + + protected void accept() { + this.pass.setOutcome(Outcome.ACCEPT); + } + + protected void reject() { + this.pass.setOutcome(Outcome.REJECT); + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java new file mode 100644 index 0000000..f3e2075 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java @@ -0,0 +1,29 @@ +package com.ohmyclass.util.validators.rejectors; + +import com.ohmyclass.util.validators.Pass; +import com.ohmyclass.util.validators.collectors.ReasonCollector; + +public class BooleanRejector extends BaseRejector { + + public BooleanRejector(Pass pass, Boolean entry) { + super(pass, entry); + } + + public ReasonCollector ifTrue() { + + if (Boolean.TRUE.equals(entry)) { + this.reject(); + } + + return this.pass; + } + + public ReasonCollector ifFalse() { + + if (Boolean.FALSE.equals(entry)) { + this.reject(); + } + + return this.pass; + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java new file mode 100644 index 0000000..b9dc0b7 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java @@ -0,0 +1,20 @@ +package com.ohmyclass.util.validators.rejectors; + +import com.ohmyclass.util.validators.Pass; +import com.ohmyclass.util.validators.collectors.ReasonCollector; + +public class StringRejector extends BaseRejector { + + public StringRejector(Pass pass, String entry) { + super(pass, entry); + } + + public ReasonCollector ifEmpty() { + + if (entry == null || entry.isEmpty()) { + this.reject(); + } + + return this.pass; + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index db6e167..ffa61f4 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -16,7 +16,7 @@ spring: driver-class-name: org.mariadb.jdbc.Driver password: root username: root - url: jdbc:mariadb://localhost:3307/ohmyclass + url: jdbc:mariadb://localhost:3306/ohmyclass springdoc: api-docs: enabled: true From 6ee834a444b7740e9ae077c5a79c0a4e1212ed1b Mon Sep 17 00:00:00 2001 From: Marvin Zeiter Date: Tue, 15 Nov 2022 15:52:51 +0100 Subject: [PATCH 2/4] Added enhanced functionality to Validator.java & implemented further functionality to Validator.java --- .../user/service/crud/impl/UserService.java | 6 ++-- .../UserChangeSubmissionProcessor.java | 4 ++- .../UserSubmissionProcessor.java | 4 ++- .../UserChangeSubmissionValidator.java | 14 +++++++-- .../validation/UserSubmissionValidator.java | 14 +++++++-- .../validation/UserValidationLocation.java | 23 -------------- .../validation/UserValidationReason.java | 18 ----------- .../validation/types/UserValidationField.java | 23 ++++++++++++++ .../types/UserValidationReason.java | 19 ++++++++++++ .../SubmissionProcessor.java | 2 +- .../api/util/validation/ValidationResult.java | 1 - .../validation/ValidatorBase.java | 2 +- .../com/ohmyclass/util/validators/Pass.java | 19 ++++++------ .../ohmyclass/util/validators/Validator.java | 11 +++++++ .../validators/collectors/FieldCollector.java | 8 +++++ .../collectors/LocationCollector.java | 8 ----- .../collectors/ReasonCollector.java | 4 +-- .../collectors/ResultCollector.java | 3 +- .../validators/other/ValidationField.java | 6 ++++ .../validators/other/ValidationLocation.java | 6 ---- .../validators/other/{ => types}/Outcome.java | 2 +- .../validators/rejectors/BaseRejector.java | 2 +- .../validators/rejectors/IntegerRejector.java | 31 +++++++++++++++++++ .../validators/rejectors/StringRejector.java | 14 +++++++++ 24 files changed, 158 insertions(+), 86 deletions(-) rename src/main/java/com/ohmyclass/api/components/user/service/{validation => processors}/UserChangeSubmissionProcessor.java (88%) rename src/main/java/com/ohmyclass/api/components/user/service/{validation => processors}/UserSubmissionProcessor.java (89%) delete mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java delete mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java rename src/main/java/com/ohmyclass/api/{components/user/service/validation => util}/SubmissionProcessor.java (90%) rename src/main/java/com/ohmyclass/api/{components/user/service => util}/validation/ValidatorBase.java (68%) create mode 100644 src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java delete mode 100644 src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java create mode 100644 src/main/java/com/ohmyclass/util/validators/other/ValidationField.java delete mode 100644 src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java rename src/main/java/com/ohmyclass/util/validators/other/{ => types}/Outcome.java (75%) create mode 100644 src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java diff --git a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java index 02b4e7e..742a6d9 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java @@ -10,8 +10,8 @@ import com.ohmyclass.api.components.user.repository.UserRepository; import com.ohmyclass.api.components.user.service.crud.IUserService; import com.ohmyclass.api.components.user.service.mapper.UserMapper; -import com.ohmyclass.api.components.user.service.validation.UserChangeSubmissionProcessor; -import com.ohmyclass.api.components.user.service.validation.UserSubmissionProcessor; +import com.ohmyclass.api.components.user.service.processors.UserChangeSubmissionProcessor; +import com.ohmyclass.api.components.user.service.processors.UserSubmissionProcessor; import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.security.util.JwtTokenUtil; import com.ohmyclass.util.validators.*; @@ -20,14 +20,12 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.stream.Collectors; import static org.springframework.http.HttpHeaders.AUTHORIZATION; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserChangeSubmissionProcessor.java similarity index 88% rename from src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java rename to src/main/java/com/ohmyclass/api/components/user/service/processors/UserChangeSubmissionProcessor.java index c1f1c12..a2e8bce 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionProcessor.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserChangeSubmissionProcessor.java @@ -1,8 +1,10 @@ -package com.ohmyclass.api.components.user.service.validation; +package com.ohmyclass.api.components.user.service.processors; import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; import com.ohmyclass.api.components.user.entity.User; import com.ohmyclass.api.components.user.repository.UserRepository; +import com.ohmyclass.api.util.SubmissionProcessor; +import com.ohmyclass.api.components.user.service.validation.UserChangeSubmissionValidator; import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.api.util.validation.ValidationResult; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserSubmissionProcessor.java similarity index 89% rename from src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java rename to src/main/java/com/ohmyclass/api/components/user/service/processors/UserSubmissionProcessor.java index e15d059..6c45f81 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionProcessor.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserSubmissionProcessor.java @@ -1,9 +1,11 @@ -package com.ohmyclass.api.components.user.service.validation; +package com.ohmyclass.api.components.user.service.processors; import com.ohmyclass.api.components.user.dto.in.UserInDTO; import com.ohmyclass.api.components.user.entity.User; import com.ohmyclass.api.components.user.repository.UserRepository; import com.ohmyclass.api.components.user.service.mapper.UserMapper; +import com.ohmyclass.api.util.SubmissionProcessor; +import com.ohmyclass.api.components.user.service.validation.UserSubmissionValidator; import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.api.util.validation.ValidationResult; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java index 621338e..6e10be5 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java @@ -1,7 +1,10 @@ package com.ohmyclass.api.components.user.service.validation; import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; +import com.ohmyclass.api.components.user.service.validation.types.UserValidationField; +import com.ohmyclass.api.components.user.service.validation.types.UserValidationReason; import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.api.util.validation.ValidatorBase; import com.ohmyclass.util.validators.Validator; import org.springframework.stereotype.Component; @@ -14,12 +17,17 @@ public ValidationResult validate(UserChangeInDTO user) { Validator.reject(user.getNewEmail()).ifEmpty() .reason(UserValidationReason.USERNAME_NULL) - .location(UserValidationLocation.USERNAME) + .field(UserValidationField.USERNAME) .finish(validationResult); Validator.reject(user.getNewPassword()).ifEmpty() - .reason(UserValidationReason.USERNAME_NULL) - .location(UserValidationLocation.USERNAME) + .reason(UserValidationReason.PASSWORD_NULL) + .field(UserValidationField.PASSWORD) + .finish(validationResult); + + Validator.reject(user.getNewPassword()).ifPasswordInvalid() + .reason(UserValidationReason.PASSWORD_INVALID) + .field(UserValidationField.USERNAME) .finish(validationResult); return validationResult; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java index db22d26..1dc3b5c 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java @@ -1,7 +1,10 @@ package com.ohmyclass.api.components.user.service.validation; import com.ohmyclass.api.components.user.dto.in.UserInDTO; +import com.ohmyclass.api.components.user.service.validation.types.UserValidationField; +import com.ohmyclass.api.components.user.service.validation.types.UserValidationReason; import com.ohmyclass.api.util.validation.ValidationResult; +import com.ohmyclass.api.util.validation.ValidatorBase; import com.ohmyclass.util.validators.Validator; import org.springframework.stereotype.Component; @@ -14,17 +17,22 @@ public ValidationResult validate(UserInDTO user) { Validator.reject(user.getUsername()).ifEmpty() .reason(UserValidationReason.USERNAME_NULL) - .location(UserValidationLocation.USERNAME) + .field(UserValidationField.USERNAME) .finish(validationResult); Validator.reject(user.getEmail()).ifEmpty() .reason(UserValidationReason.USERNAME_NULL) - .location(UserValidationLocation.USERNAME) + .field(UserValidationField.USERNAME) .finish(validationResult); Validator.reject(user.getPassword()).ifEmpty() .reason(UserValidationReason.USERNAME_NULL) - .location(UserValidationLocation.USERNAME) + .field(UserValidationField.USERNAME) + .finish(validationResult); + + Validator.reject(user.getPassword()).ifPasswordInvalid() + .reason(UserValidationReason.PASSWORD_INVALID) + .field(UserValidationField.USERNAME) .finish(validationResult); return validationResult; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java deleted file mode 100644 index 1985b59..0000000 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationLocation.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.ohmyclass.api.components.user.service.validation; - -import com.ohmyclass.util.validators.other.ValidationLocation; - -public enum UserValidationLocation implements ValidationLocation { - - EMAIL("email"), - USERNAME("username"), - PASSWORD("password"), - NAME("name"), - ROLE("role"); - - private final String location; - - UserValidationLocation(String location) { - this.location = location; - } - - @Override - public String getLocation() { - return location; - } -} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java deleted file mode 100644 index 6604edf..0000000 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserValidationReason.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.ohmyclass.api.components.user.service.validation; - -import com.ohmyclass.util.validators.other.ValidationReason; - -public enum UserValidationReason implements ValidationReason { - USERNAME_NULL("Username cannot be null"); - - String reason; - - UserValidationReason(String reason) { - this.reason = reason; - } - - @Override - public String getReason() { - return reason; - } -} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java new file mode 100644 index 0000000..20f5f38 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java @@ -0,0 +1,23 @@ +package com.ohmyclass.api.components.user.service.validation.types; + +import com.ohmyclass.util.validators.other.ValidationField; + +public enum UserValidationField implements ValidationField { + + EMAIL("email"), + USERNAME("username"), + PASSWORD("password"), + NAME("name"), + ROLE("role"); + + private final String location; + + UserValidationField(String location) { + this.location = location; + } + + @Override + public String getField() { + return location; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java new file mode 100644 index 0000000..c3db3d4 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java @@ -0,0 +1,19 @@ +package com.ohmyclass.api.components.user.service.validation.types; + +import com.ohmyclass.util.validators.other.ValidationReason; + +public enum UserValidationReason implements ValidationReason { + + USERNAME_NULL("Username cannot be null"), + PASSWORD_NULL("Password cannot be null"), + PASSWORD_INVALID("Password doesn't match regex"); + + String reason; + + UserValidationReason(String reason) {} + + @Override + public String getReason() { + return reason; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java b/src/main/java/com/ohmyclass/api/util/SubmissionProcessor.java similarity index 90% rename from src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java rename to src/main/java/com/ohmyclass/api/util/SubmissionProcessor.java index 3e18bb4..44e0a3d 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/SubmissionProcessor.java +++ b/src/main/java/com/ohmyclass/api/util/SubmissionProcessor.java @@ -1,4 +1,4 @@ -package com.ohmyclass.api.components.user.service.validation; +package com.ohmyclass.api.util; import com.ohmyclass.api.util.validation.ValidationResult; diff --git a/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java b/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java index f10a563..06e5c34 100644 --- a/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java @@ -8,7 +8,6 @@ public class ValidationResult { - private ValidationStatus status; private Set infos = new HashSet<>(); diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java b/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java similarity index 68% rename from src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java rename to src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java index 87f96b4..de3716f 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/ValidatorBase.java +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java @@ -1,4 +1,4 @@ -package com.ohmyclass.api.components.user.service.validation; +package com.ohmyclass.api.util.validation; import com.ohmyclass.api.util.validation.ValidationResult; diff --git a/src/main/java/com/ohmyclass/util/validators/Pass.java b/src/main/java/com/ohmyclass/util/validators/Pass.java index 7d5dab2..418cec1 100644 --- a/src/main/java/com/ohmyclass/util/validators/Pass.java +++ b/src/main/java/com/ohmyclass/util/validators/Pass.java @@ -2,10 +2,10 @@ import com.ohmyclass.api.util.validation.ValidationResult; import com.ohmyclass.api.util.validation.http.ValidationStatus; -import com.ohmyclass.util.validators.collectors.LocationCollector; +import com.ohmyclass.util.validators.collectors.FieldCollector; import com.ohmyclass.util.validators.collectors.ReasonCollector; -import com.ohmyclass.util.validators.other.Outcome; -import com.ohmyclass.util.validators.other.ValidationLocation; +import com.ohmyclass.util.validators.other.types.Outcome; +import com.ohmyclass.util.validators.other.ValidationField; import com.ohmyclass.util.validators.other.ValidationReason; import lombok.AllArgsConstructor; import lombok.Getter; @@ -16,11 +16,11 @@ @Setter @AllArgsConstructor @NoArgsConstructor -public class Pass implements ReasonCollector, LocationCollector { +public class Pass implements ReasonCollector, FieldCollector { ValidationReason reason; - ValidationLocation location; + ValidationField location; Outcome outcome; @@ -29,27 +29,26 @@ public static Pass reject() { } @Override - public ReasonCollector location(ValidationLocation location) { + public ReasonCollector field(ValidationField location) { this.location = location; return this; } @Override - public LocationCollector reason(ValidationReason reason) { + public FieldCollector reason(ValidationReason reason) { this.reason = reason; return this; } - public Outcome finish(ValidationResult validationResult) { + public void finish(ValidationResult validationResult) { if (this.outcome == null) { throw new RuntimeException("Pass outcome is null"); } if (this.outcome.isReject()) { - validationResult.add(ValidationStatus.ERROR, this.reason.getReason(), this.location.getLocation()); + validationResult.add(ValidationStatus.ERROR, this.reason.getReason(), this.location.getField()); } - return this.outcome; } } diff --git a/src/main/java/com/ohmyclass/util/validators/Validator.java b/src/main/java/com/ohmyclass/util/validators/Validator.java index 93961fe..c10ad65 100644 --- a/src/main/java/com/ohmyclass/util/validators/Validator.java +++ b/src/main/java/com/ohmyclass/util/validators/Validator.java @@ -2,6 +2,8 @@ import com.ohmyclass.util.validators.collectors.ReasonCollector; import com.ohmyclass.util.validators.rejectors.BaseRejector; +import com.ohmyclass.util.validators.rejectors.BooleanRejector; +import com.ohmyclass.util.validators.rejectors.IntegerRejector; import com.ohmyclass.util.validators.rejectors.StringRejector; import org.springframework.stereotype.Component; @@ -19,4 +21,13 @@ public static BaseRejector reject(Object entry) { public static StringRejector reject(String entry) { return new StringRejector(new Pass(), entry); } + + public static BooleanRejector reject(Boolean entry) { + return new BooleanRejector(new Pass(), entry); + } + + public static IntegerRejector reject(Integer entry) { + return new IntegerRejector(new Pass(), entry); + } + } diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java new file mode 100644 index 0000000..c899490 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java @@ -0,0 +1,8 @@ +package com.ohmyclass.util.validators.collectors; + +import com.ohmyclass.util.validators.other.ValidationField; + +public interface FieldCollector extends ResultCollector { + + ReasonCollector field(ValidationField location); +} diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java deleted file mode 100644 index 148abea..0000000 --- a/src/main/java/com/ohmyclass/util/validators/collectors/LocationCollector.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.ohmyclass.util.validators.collectors; - -import com.ohmyclass.util.validators.other.ValidationLocation; - -public interface LocationCollector extends ResultCollector { - - ReasonCollector location(ValidationLocation location); -} diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java index e77b719..bb1b28c 100644 --- a/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java @@ -2,7 +2,7 @@ import com.ohmyclass.util.validators.other.ValidationReason; -public interface ReasonCollector extends ResultCollector{ +public interface ReasonCollector extends ResultCollector { - LocationCollector reason(ValidationReason reason); + FieldCollector reason(ValidationReason reason); } diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java index da13ae2..8ade902 100644 --- a/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java @@ -1,9 +1,8 @@ package com.ohmyclass.util.validators.collectors; import com.ohmyclass.api.util.validation.ValidationResult; -import com.ohmyclass.util.validators.other.Outcome; public interface ResultCollector { - Outcome finish(ValidationResult validationResult); + void finish(ValidationResult validationResult); } diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationField.java b/src/main/java/com/ohmyclass/util/validators/other/ValidationField.java new file mode 100644 index 0000000..2bf3451 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/other/ValidationField.java @@ -0,0 +1,6 @@ +package com.ohmyclass.util.validators.other; + +public interface ValidationField { + + String getField(); +} diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java b/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java deleted file mode 100644 index 75b6730..0000000 --- a/src/main/java/com/ohmyclass/util/validators/other/ValidationLocation.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.ohmyclass.util.validators.other; - -public interface ValidationLocation { - - String getLocation(); -} diff --git a/src/main/java/com/ohmyclass/util/validators/other/Outcome.java b/src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java similarity index 75% rename from src/main/java/com/ohmyclass/util/validators/other/Outcome.java rename to src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java index 7f7d09d..4d7a015 100644 --- a/src/main/java/com/ohmyclass/util/validators/other/Outcome.java +++ b/src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java @@ -1,4 +1,4 @@ -package com.ohmyclass.util.validators.other; +package com.ohmyclass.util.validators.other.types; public enum Outcome { ACCEPT, REJECT; diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java index f7c1230..4d5ebcb 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java @@ -1,6 +1,6 @@ package com.ohmyclass.util.validators.rejectors; -import com.ohmyclass.util.validators.other.Outcome; +import com.ohmyclass.util.validators.other.types.Outcome; import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java new file mode 100644 index 0000000..1b4941f --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java @@ -0,0 +1,31 @@ +package com.ohmyclass.util.validators.rejectors; + +import com.ohmyclass.util.validators.Pass; +import com.ohmyclass.util.validators.collectors.ReasonCollector; + +public class IntegerRejector extends BaseRejector { + + public IntegerRejector(Pass pass, Integer entry) { + super(pass, entry); + } + + public ReasonCollector ifNegative() { + + if (entry > 0) { + this.reject(); + } + + return this.pass; + } + + public ReasonCollector ifPositive() { + + if (entry.toString().charAt(0) == '-') { + this.reject(); + } + + return this.pass; + } + +} + diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java index b9dc0b7..6621d17 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java @@ -2,6 +2,9 @@ import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; +import org.apache.tomcat.util.file.Matcher; + +import java.util.regex.Pattern; public class StringRejector extends BaseRejector { @@ -17,4 +20,15 @@ public ReasonCollector ifEmpty() { return this.pass; } + + public ReasonCollector ifPasswordInvalid() { + + Pattern passwordPattern = Pattern.compile("^.*(?=.{6,})(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!&$%&? \"]).*$\n"); + + if (!passwordPattern.matcher(entry).matches()) { + this.reject(); + } + + return this.pass; + } } From a4f985f40f537a6207ed543a2cb714d3026e9495 Mon Sep 17 00:00:00 2001 From: Marvin Zeiter Date: Tue, 15 Nov 2022 16:24:24 +0100 Subject: [PATCH 3/4] Refactoring & JavaDoc --- .../UserChangeSubmissionValidator.java | 16 ++++++++-------- .../validation/UserSubmissionValidator.java | 10 +++++----- .../types/UserChangeValidationField.java | 19 +++++++++++++++++++ .../types/UserChangeValidationReason.java | 19 +++++++++++++++++++ .../validation/types/UserValidationField.java | 2 +- .../types/UserValidationReason.java | 3 ++- .../ohmyclass/api/exceptions/ApiError.java | 5 +---- .../java/com/ohmyclass/api/util/ApiConst.java | 4 +++- .../communication/CreateResponseService.java | 2 +- .../api/util/validation/ValidationResult.java | 2 +- .../validation/ValidationResultEntry.java | 4 +--- .../api/util/validation/ValidatorBase.java | 6 ++++-- .../{http => types}/ValidationStatus.java | 2 +- .../util/annotations/Development.java | 2 +- .../util/validators/ObjectValidator.java | 4 ++++ .../com/ohmyclass/util/validators/Pass.java | 19 ++++++++++--------- .../ohmyclass/util/validators/Validator.java | 4 ++++ .../validators/collectors/FieldCollector.java | 8 ++++++-- .../collectors/ReasonCollector.java | 6 +++++- .../collectors/ResultCollector.java | 4 ++++ .../validators/rejectors/BaseRejector.java | 6 +++++- .../validators/rejectors/BooleanRejector.java | 4 ++++ .../validators/rejectors/IntegerRejector.java | 4 ++++ .../validators/rejectors/StringRejector.java | 5 ++++- .../validators/{other => }/types/Outcome.java | 6 +++++- .../{other => types}/ValidationField.java | 2 +- .../{other => types}/ValidationReason.java | 2 +- 27 files changed, 124 insertions(+), 46 deletions(-) create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationField.java create mode 100644 src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationReason.java rename src/main/java/com/ohmyclass/api/util/validation/{http => types}/ValidationStatus.java (93%) rename src/main/java/com/ohmyclass/util/validators/{other => }/types/Outcome.java (56%) rename src/main/java/com/ohmyclass/util/validators/{other => types}/ValidationField.java (56%) rename src/main/java/com/ohmyclass/util/validators/{other => types}/ValidationReason.java (57%) diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java index 6e10be5..db6b25c 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java @@ -1,8 +1,8 @@ package com.ohmyclass.api.components.user.service.validation; import com.ohmyclass.api.components.user.dto.in.UserChangeInDTO; -import com.ohmyclass.api.components.user.service.validation.types.UserValidationField; -import com.ohmyclass.api.components.user.service.validation.types.UserValidationReason; +import com.ohmyclass.api.components.user.service.validation.types.UserChangeValidationField; +import com.ohmyclass.api.components.user.service.validation.types.UserChangeValidationReason; import com.ohmyclass.api.util.validation.ValidationResult; import com.ohmyclass.api.util.validation.ValidatorBase; import com.ohmyclass.util.validators.Validator; @@ -16,18 +16,18 @@ public ValidationResult validate(UserChangeInDTO user) { ValidationResult validationResult = ValidationResult.ok(); Validator.reject(user.getNewEmail()).ifEmpty() - .reason(UserValidationReason.USERNAME_NULL) - .field(UserValidationField.USERNAME) + .reason(UserChangeValidationReason.NEW_EMAIL_NULL) + .field(UserChangeValidationField.NEW_EMAIL) .finish(validationResult); Validator.reject(user.getNewPassword()).ifEmpty() - .reason(UserValidationReason.PASSWORD_NULL) - .field(UserValidationField.PASSWORD) + .reason(UserChangeValidationReason.NEW_PASSWORD_NULL) + .field(UserChangeValidationField.NEW_PASSWORD) .finish(validationResult); Validator.reject(user.getNewPassword()).ifPasswordInvalid() - .reason(UserValidationReason.PASSWORD_INVALID) - .field(UserValidationField.USERNAME) + .reason(UserChangeValidationReason.NEW_PASSWORD_INVALID) + .field(UserChangeValidationField.NEW_PASSWORD) .finish(validationResult); return validationResult; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java index 1dc3b5c..a1e022f 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java @@ -21,18 +21,18 @@ public ValidationResult validate(UserInDTO user) { .finish(validationResult); Validator.reject(user.getEmail()).ifEmpty() - .reason(UserValidationReason.USERNAME_NULL) - .field(UserValidationField.USERNAME) + .reason(UserValidationReason.EMAIL_NULL) + .field(UserValidationField.EMAIL) .finish(validationResult); Validator.reject(user.getPassword()).ifEmpty() - .reason(UserValidationReason.USERNAME_NULL) - .field(UserValidationField.USERNAME) + .reason(UserValidationReason.PASSWORD_NULL) + .field(UserValidationField.PASSWORD) .finish(validationResult); Validator.reject(user.getPassword()).ifPasswordInvalid() .reason(UserValidationReason.PASSWORD_INVALID) - .field(UserValidationField.USERNAME) + .field(UserValidationField.PASSWORD) .finish(validationResult); return validationResult; diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationField.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationField.java new file mode 100644 index 0000000..a4a773f --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationField.java @@ -0,0 +1,19 @@ +package com.ohmyclass.api.components.user.service.validation.types; + +import com.ohmyclass.util.validators.types.ValidationField; + +public enum UserChangeValidationField implements ValidationField { + + NEW_EMAIL("newEmail"), + NEW_PASSWORD("newPassword"); + private final String location; + + UserChangeValidationField(String location) { + this.location = location; + } + + @Override + public String getField() { + return location; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationReason.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationReason.java new file mode 100644 index 0000000..a6eccb7 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserChangeValidationReason.java @@ -0,0 +1,19 @@ +package com.ohmyclass.api.components.user.service.validation.types; + +import com.ohmyclass.util.validators.types.ValidationReason; + +public enum UserChangeValidationReason implements ValidationReason { + + NEW_EMAIL_NULL("New e-mail cannot be null"), + NEW_PASSWORD_NULL("New password cannot be null"), + NEW_PASSWORD_INVALID("New password doesn't match regex"); + + String reason; + + UserChangeValidationReason(String reason) {} + + @Override + public String getReason() { + return reason; + } +} diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java index 20f5f38..6164028 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationField.java @@ -1,6 +1,6 @@ package com.ohmyclass.api.components.user.service.validation.types; -import com.ohmyclass.util.validators.other.ValidationField; +import com.ohmyclass.util.validators.types.ValidationField; public enum UserValidationField implements ValidationField { diff --git a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java index c3db3d4..6326c72 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java @@ -1,10 +1,11 @@ package com.ohmyclass.api.components.user.service.validation.types; -import com.ohmyclass.util.validators.other.ValidationReason; +import com.ohmyclass.util.validators.types.ValidationReason; public enum UserValidationReason implements ValidationReason { USERNAME_NULL("Username cannot be null"), + EMAIL_NULL("E-mail cannot be null"), PASSWORD_NULL("Password cannot be null"), PASSWORD_INVALID("Password doesn't match regex"); diff --git a/src/main/java/com/ohmyclass/api/exceptions/ApiError.java b/src/main/java/com/ohmyclass/api/exceptions/ApiError.java index 88db344..014692e 100644 --- a/src/main/java/com/ohmyclass/api/exceptions/ApiError.java +++ b/src/main/java/com/ohmyclass/api/exceptions/ApiError.java @@ -1,13 +1,10 @@ package com.ohmyclass.api.exceptions; import com.ohmyclass.api.util.validation.ValidationResultEntry; -import com.ohmyclass.api.util.validation.http.ValidationStatus; -import lombok.Getter; -import lombok.RequiredArgsConstructor; +import com.ohmyclass.api.util.validation.types.ValidationStatus; import org.springframework.http.HttpStatus; import java.time.ZonedDateTime; -import java.util.List; public record ApiError(String message, HttpStatus status, Throwable cause, ZonedDateTime timestamp) { diff --git a/src/main/java/com/ohmyclass/api/util/ApiConst.java b/src/main/java/com/ohmyclass/api/util/ApiConst.java index 0e101d0..dee1b48 100644 --- a/src/main/java/com/ohmyclass/api/util/ApiConst.java +++ b/src/main/java/com/ohmyclass/api/util/ApiConst.java @@ -1,6 +1,8 @@ package com.ohmyclass.api.util; -//TODO Do some sort of cfg +import com.ohmyclass.util.annotations.Development; + +@Development public class ApiConst { // ? Account diff --git a/src/main/java/com/ohmyclass/api/util/communication/CreateResponseService.java b/src/main/java/com/ohmyclass/api/util/communication/CreateResponseService.java index 9870b84..d8ac212 100644 --- a/src/main/java/com/ohmyclass/api/util/communication/CreateResponseService.java +++ b/src/main/java/com/ohmyclass/api/util/communication/CreateResponseService.java @@ -1,7 +1,7 @@ package com.ohmyclass.api.util.communication; import com.ohmyclass.api.util.validation.ValidationResult; -import com.ohmyclass.api.util.validation.http.ValidationStatus; +import com.ohmyclass.api.util.validation.types.ValidationStatus; import javax.servlet.http.HttpServletResponse; import java.io.IOException; diff --git a/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java b/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java index 06e5c34..335c196 100644 --- a/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java @@ -1,6 +1,6 @@ package com.ohmyclass.api.util.validation; -import com.ohmyclass.api.util.validation.http.ValidationStatus; +import com.ohmyclass.api.util.validation.types.ValidationStatus; import java.util.Arrays; import java.util.HashSet; diff --git a/src/main/java/com/ohmyclass/api/util/validation/ValidationResultEntry.java b/src/main/java/com/ohmyclass/api/util/validation/ValidationResultEntry.java index 02ab494..d2a332e 100644 --- a/src/main/java/com/ohmyclass/api/util/validation/ValidationResultEntry.java +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidationResultEntry.java @@ -1,9 +1,7 @@ package com.ohmyclass.api.util.validation; -import com.ohmyclass.api.util.validation.http.ValidationStatus; -import lombok.AllArgsConstructor; +import com.ohmyclass.api.util.validation.types.ValidationStatus; import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.Setter; import java.util.List; diff --git a/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java b/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java index de3716f..0197ccb 100644 --- a/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java @@ -1,7 +1,9 @@ package com.ohmyclass.api.util.validation; -import com.ohmyclass.api.util.validation.ValidationResult; - +/** + * Base of a {@link com.ohmyclass.api.util.SubmissionProcessor}-validator + * @param Type of to be passed object + */ public interface ValidatorBase { ValidationResult validate(T t); diff --git a/src/main/java/com/ohmyclass/api/util/validation/http/ValidationStatus.java b/src/main/java/com/ohmyclass/api/util/validation/types/ValidationStatus.java similarity index 93% rename from src/main/java/com/ohmyclass/api/util/validation/http/ValidationStatus.java rename to src/main/java/com/ohmyclass/api/util/validation/types/ValidationStatus.java index 9c29c3b..bc599e2 100644 --- a/src/main/java/com/ohmyclass/api/util/validation/http/ValidationStatus.java +++ b/src/main/java/com/ohmyclass/api/util/validation/types/ValidationStatus.java @@ -1,4 +1,4 @@ -package com.ohmyclass.api.util.validation.http; +package com.ohmyclass.api.util.validation.types; import lombok.Getter; diff --git a/src/main/java/com/ohmyclass/util/annotations/Development.java b/src/main/java/com/ohmyclass/util/annotations/Development.java index 29d7d25..5ae7bcd 100644 --- a/src/main/java/com/ohmyclass/util/annotations/Development.java +++ b/src/main/java/com/ohmyclass/util/annotations/Development.java @@ -10,6 +10,6 @@ * Remove in releases! */ @Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.METHOD) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE}) public @interface Development { } diff --git a/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java index 1fc7b34..32880c0 100644 --- a/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java +++ b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java @@ -5,6 +5,10 @@ import java.util.Arrays; import java.util.Objects; +/** + * Validates objects of type + * @author Z-100 + */ public class ObjectValidator { @SafeVarargs diff --git a/src/main/java/com/ohmyclass/util/validators/Pass.java b/src/main/java/com/ohmyclass/util/validators/Pass.java index 418cec1..fd5a2ba 100644 --- a/src/main/java/com/ohmyclass/util/validators/Pass.java +++ b/src/main/java/com/ohmyclass/util/validators/Pass.java @@ -1,17 +1,18 @@ package com.ohmyclass.util.validators; import com.ohmyclass.api.util.validation.ValidationResult; -import com.ohmyclass.api.util.validation.http.ValidationStatus; +import com.ohmyclass.api.util.validation.types.ValidationStatus; import com.ohmyclass.util.validators.collectors.FieldCollector; import com.ohmyclass.util.validators.collectors.ReasonCollector; -import com.ohmyclass.util.validators.other.types.Outcome; -import com.ohmyclass.util.validators.other.ValidationField; -import com.ohmyclass.util.validators.other.ValidationReason; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - +import com.ohmyclass.util.validators.types.Outcome; +import com.ohmyclass.util.validators.types.ValidationField; +import com.ohmyclass.util.validators.types.ValidationReason; +import lombok.*; + +/** + * Pass, passed by the rejectors in order to determine the {@link Outcome} + * @author Z-100 + */ @Getter @Setter @AllArgsConstructor diff --git a/src/main/java/com/ohmyclass/util/validators/Validator.java b/src/main/java/com/ohmyclass/util/validators/Validator.java index c10ad65..6c4a8a3 100644 --- a/src/main/java/com/ohmyclass/util/validators/Validator.java +++ b/src/main/java/com/ohmyclass/util/validators/Validator.java @@ -7,6 +7,10 @@ import com.ohmyclass.util.validators.rejectors.StringRejector; import org.springframework.stereotype.Component; +/** + * Extendable reject-validator. See {@link BaseRejector} + * @author z-100 + */ @Component public class Validator { diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java index c899490..3914495 100644 --- a/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java +++ b/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java @@ -1,8 +1,12 @@ package com.ohmyclass.util.validators.collectors; -import com.ohmyclass.util.validators.other.ValidationField; +import com.ohmyclass.util.validators.types.ValidationField; +/** + * Collects the passed {@link ValidationField} + * @author Z-100 + */ public interface FieldCollector extends ResultCollector { - ReasonCollector field(ValidationField location); + ReasonCollector field(ValidationField field); } diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java index bb1b28c..5867725 100644 --- a/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java @@ -1,7 +1,11 @@ package com.ohmyclass.util.validators.collectors; -import com.ohmyclass.util.validators.other.ValidationReason; +import com.ohmyclass.util.validators.types.ValidationReason; +/** + * Collects the passed {@link ValidationReason} + * @author Z-100 + */ public interface ReasonCollector extends ResultCollector { FieldCollector reason(ValidationReason reason); diff --git a/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java index 8ade902..ecb8d42 100644 --- a/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java @@ -2,6 +2,10 @@ import com.ohmyclass.api.util.validation.ValidationResult; +/** + * Adds errors/warnings/infos to the passed {@link ValidationResult} on reject + * @author Z-100 + */ public interface ResultCollector { void finish(ValidationResult validationResult); diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java index 4d5ebcb..de22714 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java @@ -1,12 +1,16 @@ package com.ohmyclass.util.validators.rejectors; -import com.ohmyclass.util.validators.other.types.Outcome; +import com.ohmyclass.util.validators.types.Outcome; import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; +/** + * Rejector to validate entered Objects, which do not have their own rejector yet. + * @author Z-100 + */ @Getter @Setter @AllArgsConstructor diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java index f3e2075..7eb1c91 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java @@ -3,6 +3,10 @@ import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; +/** + * Rejector to validate entered Booleans + * @author Z-100 + */ public class BooleanRejector extends BaseRejector { public BooleanRejector(Pass pass, Boolean entry) { diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java index 1b4941f..33da074 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java @@ -3,6 +3,10 @@ import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; +/** + * Rejector to validate entered Integers + * @author Z-100 + */ public class IntegerRejector extends BaseRejector { public IntegerRejector(Pass pass, Integer entry) { diff --git a/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java index 6621d17..8094c36 100644 --- a/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java @@ -2,10 +2,13 @@ import com.ohmyclass.util.validators.Pass; import com.ohmyclass.util.validators.collectors.ReasonCollector; -import org.apache.tomcat.util.file.Matcher; import java.util.regex.Pattern; +/** + * Rejector to validate entered Strings + * @author Z-100 + */ public class StringRejector extends BaseRejector { public StringRejector(Pass pass, String entry) { diff --git a/src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java b/src/main/java/com/ohmyclass/util/validators/types/Outcome.java similarity index 56% rename from src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java rename to src/main/java/com/ohmyclass/util/validators/types/Outcome.java index 4d7a015..ffb90b9 100644 --- a/src/main/java/com/ohmyclass/util/validators/other/types/Outcome.java +++ b/src/main/java/com/ohmyclass/util/validators/types/Outcome.java @@ -1,5 +1,9 @@ -package com.ohmyclass.util.validators.other.types; +package com.ohmyclass.util.validators.types; +/** + * Determines if the validation was successful or not + * @author Z-100 + */ public enum Outcome { ACCEPT, REJECT; diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationField.java b/src/main/java/com/ohmyclass/util/validators/types/ValidationField.java similarity index 56% rename from src/main/java/com/ohmyclass/util/validators/other/ValidationField.java rename to src/main/java/com/ohmyclass/util/validators/types/ValidationField.java index 2bf3451..a52b4d0 100644 --- a/src/main/java/com/ohmyclass/util/validators/other/ValidationField.java +++ b/src/main/java/com/ohmyclass/util/validators/types/ValidationField.java @@ -1,4 +1,4 @@ -package com.ohmyclass.util.validators.other; +package com.ohmyclass.util.validators.types; public interface ValidationField { diff --git a/src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java b/src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java similarity index 57% rename from src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java rename to src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java index dc51b11..a24aaf1 100644 --- a/src/main/java/com/ohmyclass/util/validators/other/ValidationReason.java +++ b/src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java @@ -1,4 +1,4 @@ -package com.ohmyclass.util.validators.other; +package com.ohmyclass.util.validators.types; public interface ValidationReason { From 86db0155c8f8e93f781dc2a65dbcea0ab3d400e2 Mon Sep 17 00:00:00 2001 From: Marvin Date: Wed, 16 Nov 2022 16:07:09 +0100 Subject: [PATCH 4/4] Improved refresh token validation --- .../user/service/crud/impl/UserService.java | 52 ++++--------------- .../api/exceptions/ApiExceptionHandler.java | 19 +++++++ .../filters/JwtAuthorizationFilter.java | 20 ++----- .../ohmyclass/security/util/JwtTokenUtil.java | 42 +++++++++++++-- 4 files changed, 71 insertions(+), 62 deletions(-) diff --git a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java index 742a6d9..d00b149 100644 --- a/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java +++ b/src/main/java/com/ohmyclass/api/components/user/service/crud/impl/UserService.java @@ -16,10 +16,6 @@ import com.ohmyclass.security.util.JwtTokenUtil; import com.ohmyclass.util.validators.*; import lombok.AllArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @@ -30,7 +26,6 @@ import static org.springframework.http.HttpHeaders.AUTHORIZATION; -@Log4j2 @Component @AllArgsConstructor public class UserService implements IUserService { @@ -61,29 +56,27 @@ public Map register(UserInDTO inDTO) { @Override public void refreshToken(HttpServletRequest request, HttpServletResponse response) { - String authorizationHeader = request.getHeader(AUTHORIZATION); - // Guard - if (!tokenUtil.isValidBearer(authorizationHeader)) { + String authorizationHeader = request.getHeader(AUTHORIZATION); - throw new ApiException("Invalid bearer token."); - } + tokenUtil.validateBearer(authorizationHeader); - try { - DecodedJWT decodedJWT = tokenUtil.extractBearer.apply(authorizationHeader); + DecodedJWT decodedJWT = tokenUtil.extractBearer.apply(authorizationHeader); - if (tokenUtil.isTokenExpired(decodedJWT)) { + tokenUtil.validateTokenExpiration(decodedJWT); - throw new ApiException("Token expired. Please login again"); - } + User user = userRepository.findByUsername(decodedJWT.getSubject()) + .orElseThrow(() -> new ApiException("User not found from token refresh")); - String newAccessToken = createNewAccessToken(request, decodedJWT); + String newAccessToken = tokenUtil.generateNewAccessToken(user); + try { new ObjectMapper().writeValue(response.getOutputStream(), newAccessToken); - } catch (Exception e) { throw new ApiException("Refreshing token failed"); } + + tokenUtil.addNewTokenToSecurity(user); } @Override @@ -117,29 +110,4 @@ public Boolean delete(String username) { @Override public void passwordForgotten(HttpServletRequest request, HttpServletResponse response) { } - - private String createNewAccessToken(HttpServletRequest request, DecodedJWT decodedJWT) { - - User user = userRepository.findByUsername(decodedJWT.getSubject()) - .orElseThrow(() -> new ApiException("User not found for token refresh")); - - String subject = user.getUsername(); - String issuer = request.getRequestURI(); - List rolesClaim = user.getRoles().stream() - .map(Role::getName) - .collect(Collectors.toList()); - - String newAccessToken = tokenUtil.generateNewAccessToken(subject, issuer, rolesClaim); - - List authorities = user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority(role.getName())) - .collect(Collectors.toList()); - - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities); - - SecurityContextHolder.getContext().setAuthentication(authenticationToken); - - return newAccessToken; - } } diff --git a/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java b/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java index 0f5aa9f..7fc742e 100644 --- a/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java +++ b/src/main/java/com/ohmyclass/api/exceptions/ApiExceptionHandler.java @@ -40,6 +40,25 @@ public ResponseEntity handleApiRequestException(ApiException e) { return buildResponseEntity(error); } + @ExceptionHandler(value = { Exception.class }) + @ResponseBody + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ResponseEntity handleGeneralException(Exception e) { + + ApiError error = new ApiError( + e.getMessage(), + HttpStatus.BAD_REQUEST, + null, + ZonedDateTime.now(ZoneId.of("Z")) + ); + + // ValidationResult validationResult = ValidationResult.ok(); + // validationResult.add(error.toValidationResultEntry()); + + return buildResponseEntity(error); + } + + private ResponseEntity buildResponseEntity(ApiError apiError) { return new ResponseEntity<>(apiError, apiError.status()); diff --git a/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java b/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java index 5f3d803..bdaa1e9 100644 --- a/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java +++ b/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java @@ -57,28 +57,18 @@ protected boolean shouldNotFilter(HttpServletRequest request) { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String authorizationHeader = request.getHeader(AUTHORIZATION); - - // Guard - if (!tokenUtil.isValidBearer(authorizationHeader)) { - - throw new ApiException("Invalid bearer token"); - } - - createSessionFrom(authorizationHeader); + createSessionFrom(request.getHeader(AUTHORIZATION)); filterChain.doFilter(request, response); } private void createSessionFrom(String authorizationHeader) { - DecodedJWT decodedJWT; + tokenUtil.validateBearer(authorizationHeader); + + DecodedJWT decodedJWT = tokenUtil.extractBearer.apply(authorizationHeader); - try { - decodedJWT = tokenUtil.extractBearer.apply(authorizationHeader); - } catch (Exception e) { - throw new ApiException("Invalid token"); - } + tokenUtil.validateTokenExpiration(decodedJWT); String username = decodedJWT.getSubject(); String[] roles = decodedJWT.getClaim(constants.getClaims().get("roles")).asArray(String.class); diff --git a/src/main/java/com/ohmyclass/security/util/JwtTokenUtil.java b/src/main/java/com/ohmyclass/security/util/JwtTokenUtil.java index f1c121e..dc234d7 100644 --- a/src/main/java/com/ohmyclass/security/util/JwtTokenUtil.java +++ b/src/main/java/com/ohmyclass/security/util/JwtTokenUtil.java @@ -5,7 +5,13 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; +import com.ohmyclass.api.components.role.entity.Role; +import com.ohmyclass.api.components.user.entity.User; +import com.ohmyclass.api.exceptions.ApiException; import com.ohmyclass.server.properties.JwtConstants; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; @@ -14,6 +20,7 @@ import java.util.List; import java.util.Map; import java.util.function.Function; +import java.util.stream.Collectors; /** * Util to handle JWT tokens @@ -63,9 +70,20 @@ public String generateNewRefreshToken(String subject, String issuer) { .sign(algorithm()); } + public String generateNewAccessToken(User user) { + + String subject = user.getUsername(); + String issuer = "/api/auth/refresh-token"; + List rolesClaim = user.getRoles().stream() + .map(Role::getName) + .collect(Collectors.toList()); + + return generateNewAccessToken(subject, issuer, rolesClaim); + } + public String getUsernameFromToken(DecodedJWT token) { - return token.getSignature(); + return token.getSubject(); } public Date getExpirationDateFromToken(DecodedJWT token) { @@ -73,16 +91,30 @@ public Date getExpirationDateFromToken(DecodedJWT token) { return token.getExpiresAt(); } - public boolean isTokenExpired(DecodedJWT token) { + public void validateTokenExpiration(DecodedJWT token) { final Date expiration = getExpirationDateFromToken(token); - return expiration.before(new Date()); + if (expiration.after(new Date())) + throw new ApiException("Token expired. Please login again"); } - public boolean isValidBearer(String token) { + public void validateBearer(String token) { + + if (token != null && token.startsWith(constants.getTokenPrefix())) + throw new ApiException("Invalid bearer token"); + } + + public void addNewTokenToSecurity(User user) { + + List authorities = user.getRoles().stream() + .map(role -> new SimpleGrantedAuthority(role.getName())) + .collect(Collectors.toList()); + + UsernamePasswordAuthenticationToken authenticationToken = + new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities); - return token != null && token.startsWith(constants.getTokenPrefix()); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); } private Map getAllClaimsFromToken(DecodedJWT token) {