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..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 @@ -7,149 +7,107 @@ 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.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.validate.Validate; +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.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; 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; -@Log4j2 @Component @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 public void refreshToken(HttpServletRequest request, HttpServletResponse response) { - String authorizationHeader = request.getHeader(AUTHORIZATION); - // Guard - if (!tokenUtil.isValidBearer(authorizationHeader)) { + String authorizationHeader = request.getHeader(AUTHORIZATION); - throw new ApiRequestException("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 ApiRequestException("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 ApiRequestException("Refreshing token failed"); + throw new ApiException("Refreshing token failed"); } + + tokenUtil.addNewTokenToSecurity(user); } @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) { - @Override - public Boolean delete(Request userIn) { - return null; + return userChangeSubmissionProcessor.process(userIn).isOk(); } @Override - public void passwordForgotten(HttpServletRequest request, HttpServletResponse response) { - } - - private String createNewAccessToken(HttpServletRequest request, DecodedJWT decodedJWT) { - - User user = userRepo.findByUsername(decodedJWT.getSubject()) - .orElseThrow(() -> new ApiRequestException("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()); + public Boolean delete(String username) { - String newAccessToken = tokenUtil.generateNewAccessToken(subject, issuer, rolesClaim); + ObjectValidator.notNull("Body can not be null", username); - List authorities = user.getRoles().stream() - .map(role -> new SimpleGrantedAuthority(role.getName())) - .collect(Collectors.toList()); - - UsernamePasswordAuthenticationToken authenticationToken = - new UsernamePasswordAuthenticationToken(user.getUsername(), null, authorities); + User user = userRepository.findByUsername(username) + .orElseThrow(() -> new ApiException("User not found")); - SecurityContextHolder.getContext().setAuthentication(authenticationToken); + userRepository.delete(user); - return newAccessToken; + return userRepository.findByUsername(username).isEmpty(); } - private Optional saveUser(User user) { - - log.info("Saving user {}", user.getUsername()); - - user.setPassword(passwordEncoder.encode(user.getPassword())); - - return Optional.of(userRepo.save(user)); + @Override + public void passwordForgotten(HttpServletRequest request, HttpServletResponse response) { } } 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/processors/UserChangeSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserChangeSubmissionProcessor.java new file mode 100644 index 0000000..a2e8bce --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserChangeSubmissionProcessor.java @@ -0,0 +1,51 @@ +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; +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/processors/UserSubmissionProcessor.java b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserSubmissionProcessor.java new file mode 100644 index 0000000..6c45f81 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/processors/UserSubmissionProcessor.java @@ -0,0 +1,60 @@ +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; +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/UserChangeSubmissionValidator.java b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java new file mode 100644 index 0000000..db6b25c --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserChangeSubmissionValidator.java @@ -0,0 +1,35 @@ +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.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; +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(UserChangeValidationReason.NEW_EMAIL_NULL) + .field(UserChangeValidationField.NEW_EMAIL) + .finish(validationResult); + + Validator.reject(user.getNewPassword()).ifEmpty() + .reason(UserChangeValidationReason.NEW_PASSWORD_NULL) + .field(UserChangeValidationField.NEW_PASSWORD) + .finish(validationResult); + + Validator.reject(user.getNewPassword()).ifPasswordInvalid() + .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 new file mode 100644 index 0000000..a1e022f --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/UserSubmissionValidator.java @@ -0,0 +1,40 @@ +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; + +@Component +public class UserSubmissionValidator implements ValidatorBase { + + public ValidationResult validate(UserInDTO user) { + + ValidationResult validationResult = ValidationResult.ok(); + + Validator.reject(user.getUsername()).ifEmpty() + .reason(UserValidationReason.USERNAME_NULL) + .field(UserValidationField.USERNAME) + .finish(validationResult); + + Validator.reject(user.getEmail()).ifEmpty() + .reason(UserValidationReason.EMAIL_NULL) + .field(UserValidationField.EMAIL) + .finish(validationResult); + + Validator.reject(user.getPassword()).ifEmpty() + .reason(UserValidationReason.PASSWORD_NULL) + .field(UserValidationField.PASSWORD) + .finish(validationResult); + + Validator.reject(user.getPassword()).ifPasswordInvalid() + .reason(UserValidationReason.PASSWORD_INVALID) + .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 new file mode 100644 index 0000000..6164028 --- /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.types.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..6326c72 --- /dev/null +++ b/src/main/java/com/ohmyclass/api/components/user/service/validation/types/UserValidationReason.java @@ -0,0 +1,20 @@ +package com.ohmyclass.api.components.user.service.validation.types; + +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"); + + String reason; + + UserValidationReason(String reason) {} + + @Override + public String getReason() { + return reason; + } +} 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/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..7fc742e 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(), @@ -40,6 +40,25 @@ public ResponseEntity handleApiRequestException(ApiRequestException 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/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/SubmissionProcessor.java b/src/main/java/com/ohmyclass/api/util/SubmissionProcessor.java new file mode 100644 index 0000000..44e0a3d --- /dev/null +++ b/src/main/java/com/ohmyclass/api/util/SubmissionProcessor.java @@ -0,0 +1,38 @@ +package com.ohmyclass.api.util; + +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/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/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/api/util/validation/ValidationResult.java b/src/main/java/com/ohmyclass/api/util/validation/ValidationResult.java index f10a563..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; @@ -8,7 +8,6 @@ public class ValidationResult { - private ValidationStatus status; private Set infos = new 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 new file mode 100644 index 0000000..0197ccb --- /dev/null +++ b/src/main/java/com/ohmyclass/api/util/validation/ValidatorBase.java @@ -0,0 +1,10 @@ +package com.ohmyclass.api.util.validation; + +/** + * 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/security/filters/JwtAuthorizationFilter.java b/src/main/java/com/ohmyclass/security/filters/JwtAuthorizationFilter.java index ba8d534..bdaa1e9 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; @@ -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 ApiRequestException("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 ApiRequestException("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/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/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) { diff --git a/src/main/java/com/ohmyclass/util/other/Development.java b/src/main/java/com/ohmyclass/util/annotations/Development.java similarity index 76% rename from src/main/java/com/ohmyclass/util/other/Development.java rename to src/main/java/com/ohmyclass/util/annotations/Development.java index f922402..5ae7bcd 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; @@ -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/validate/Validate.java b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java similarity index 54% rename from src/main/java/com/ohmyclass/util/validate/Validate.java rename to src/main/java/com/ohmyclass/util/validators/ObjectValidator.java index bf37c4e..32880c0 100644 --- a/src/main/java/com/ohmyclass/util/validate/Validate.java +++ b/src/main/java/com/ohmyclass/util/validators/ObjectValidator.java @@ -1,21 +1,25 @@ -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 { +/** + * Validates objects of type + * @author Z-100 + */ +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..fd5a2ba --- /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.types.ValidationStatus; +import com.ohmyclass.util.validators.collectors.FieldCollector; +import com.ohmyclass.util.validators.collectors.ReasonCollector; +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 +@NoArgsConstructor +public class Pass implements ReasonCollector, FieldCollector { + + ValidationReason reason; + + ValidationField location; + + Outcome outcome; + + public static Pass reject() { + return new Pass(null, null, Outcome.REJECT); + } + + @Override + public ReasonCollector field(ValidationField location) { + this.location = location; + return this; + } + + @Override + public FieldCollector reason(ValidationReason reason) { + this.reason = reason; + return this; + } + + 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.getField()); + } + + } +} 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..6c4a8a3 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/Validator.java @@ -0,0 +1,37 @@ +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.BooleanRejector; +import com.ohmyclass.util.validators.rejectors.IntegerRejector; +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 { + + 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); + } + + 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..3914495 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/FieldCollector.java @@ -0,0 +1,12 @@ +package com.ohmyclass.util.validators.collectors; + +import com.ohmyclass.util.validators.types.ValidationField; + +/** + * Collects the passed {@link ValidationField} + * @author Z-100 + */ +public interface FieldCollector extends ResultCollector { + + 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 new file mode 100644 index 0000000..5867725 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ReasonCollector.java @@ -0,0 +1,12 @@ +package com.ohmyclass.util.validators.collectors; + +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 new file mode 100644 index 0000000..ecb8d42 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/collectors/ResultCollector.java @@ -0,0 +1,12 @@ +package com.ohmyclass.util.validators.collectors; + +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 new file mode 100644 index 0000000..de22714 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BaseRejector.java @@ -0,0 +1,41 @@ +package com.ohmyclass.util.validators.rejectors; + +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 +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..7eb1c91 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/BooleanRejector.java @@ -0,0 +1,33 @@ +package com.ohmyclass.util.validators.rejectors; + +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) { + 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/IntegerRejector.java b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java new file mode 100644 index 0000000..33da074 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/IntegerRejector.java @@ -0,0 +1,35 @@ +package com.ohmyclass.util.validators.rejectors; + +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) { + 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 new file mode 100644 index 0000000..8094c36 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/rejectors/StringRejector.java @@ -0,0 +1,37 @@ +package com.ohmyclass.util.validators.rejectors; + +import com.ohmyclass.util.validators.Pass; +import com.ohmyclass.util.validators.collectors.ReasonCollector; + +import java.util.regex.Pattern; + +/** + * Rejector to validate entered Strings + * @author Z-100 + */ +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; + } + + public ReasonCollector ifPasswordInvalid() { + + Pattern passwordPattern = Pattern.compile("^.*(?=.{6,})(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!&$%&? \"]).*$\n"); + + if (!passwordPattern.matcher(entry).matches()) { + this.reject(); + } + + return this.pass; + } +} diff --git a/src/main/java/com/ohmyclass/util/validators/types/Outcome.java b/src/main/java/com/ohmyclass/util/validators/types/Outcome.java new file mode 100644 index 0000000..ffb90b9 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/types/Outcome.java @@ -0,0 +1,17 @@ +package com.ohmyclass.util.validators.types; + +/** + * Determines if the validation was successful or not + * @author Z-100 + */ +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/types/ValidationField.java b/src/main/java/com/ohmyclass/util/validators/types/ValidationField.java new file mode 100644 index 0000000..a52b4d0 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/types/ValidationField.java @@ -0,0 +1,6 @@ +package com.ohmyclass.util.validators.types; + +public interface ValidationField { + + String getField(); +} diff --git a/src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java b/src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java new file mode 100644 index 0000000..a24aaf1 --- /dev/null +++ b/src/main/java/com/ohmyclass/util/validators/types/ValidationReason.java @@ -0,0 +1,6 @@ +package com.ohmyclass.util.validators.types; + +public interface ValidationReason { + + String getReason(); +} 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