diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..63e9001
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8159baf..d2e2350 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,17 +3,56 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.3.2
+
+
+
it.aboutbits
spring-boot-toolbox
BUILD-SNAPSHOT
Utility library for Spring Boot projects.
+ jar
21
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+
+ org.passay
+ passay
+ 1.6.5
+
+
+
+
+ org.jetbrains
+ annotations
+ 24.1.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
diff --git a/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnum.java b/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnum.java
new file mode 100644
index 0000000..3f53b46
--- /dev/null
+++ b/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnum.java
@@ -0,0 +1,28 @@
+package it.aboutbits.springboot.toolbox.validation.enumeration;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Documented
+@Constraint(validatedBy = ValidEnumValidator.class)
+@Target({FIELD, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+public @interface ValidEnum {
+
+ String message() default "{shared.error.validation.invalidEnum}";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+
+ Class extends Enum>> enumClass();
+
+}
diff --git a/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnumValidator.java b/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnumValidator.java
new file mode 100644
index 0000000..d90d53c
--- /dev/null
+++ b/src/main/java/it/aboutbits/springboot/toolbox/validation/enumeration/ValidEnumValidator.java
@@ -0,0 +1,30 @@
+package it.aboutbits.springboot.toolbox.validation.enumeration;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class ValidEnumValidator implements ConstraintValidator {
+ private Set enumValues;
+
+ @Override
+ public void initialize(ValidEnum validEnum) {
+ var enumClass = validEnum.enumClass();
+
+ enumValues = new HashSet<>();
+ for (Enum> anEnum : enumClass.getEnumConstants()) {
+ enumValues.add(anEnum.name());
+ }
+ }
+
+ @Override
+ public boolean isValid(String value, ConstraintValidatorContext context) {
+ if (value == null) {
+ return true;
+ }
+
+ return enumValues.contains(value);
+ }
+}
diff --git a/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPassword.java b/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPassword.java
new file mode 100644
index 0000000..f2097f4
--- /dev/null
+++ b/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPassword.java
@@ -0,0 +1,26 @@
+package it.aboutbits.springboot.toolbox.validation.password;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+@Documented
+@Constraint(validatedBy = ValidPasswordValidator.class)
+@Target({FIELD, ANNOTATION_TYPE})
+@Retention(RUNTIME)
+public @interface ValidPassword {
+ String message() default "{shared.error.validation.invalidPassword}";
+
+ String lengthMessage() default "{shared.error.validation.invalidPassword.length}";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPasswordValidator.java b/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPasswordValidator.java
new file mode 100644
index 0000000..1914a72
--- /dev/null
+++ b/src/main/java/it/aboutbits/springboot/toolbox/validation/password/ValidPasswordValidator.java
@@ -0,0 +1,56 @@
+package it.aboutbits.springboot.toolbox.validation.password;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+import org.passay.LengthRule;
+import org.passay.PasswordData;
+import org.passay.PasswordValidator;
+import org.passay.RuleResultDetail;
+
+import java.util.List;
+
+public class ValidPasswordValidator implements ConstraintValidator {
+ private String message;
+ private String lengthMessage;
+
+ @Override
+ public void initialize(ValidPassword constraintAnnotation) {
+ this.message = constraintAnnotation.message();
+ this.lengthMessage = constraintAnnotation.lengthMessage();
+ }
+
+ @Override
+ public boolean isValid(String password, ConstraintValidatorContext context) {
+ if (password == null) {
+ return true;
+ }
+
+ var validator = new PasswordValidator(List.of(
+ // at least 8 characters
+ new LengthRule(8, 50)
+ ));
+
+ var result = validator.validate(new PasswordData(password));
+
+ if (!password.isBlank() && result.isValid()) {
+ return true;
+ }
+ context.disableDefaultConstraintViolation();
+ context.buildConstraintViolationWithTemplate(getMessageTemplate(result.getDetails()))
+ .addConstraintViolation();
+
+ return false;
+ }
+
+ private String getMessageTemplate(List ruleResultDetails) {
+ if (ruleResultDetails.isEmpty()) {
+ return message;
+ }
+
+ return switch (ruleResultDetails.getFirst().getErrorCode()) {
+ case "TOO_SHORT" -> lengthMessage;
+ case "TOO_LONG" -> lengthMessage;
+ default -> message;
+ };
+ }
+}