Skip to content

Commit

Permalink
added username and password validations
Browse files Browse the repository at this point in the history
  • Loading branch information
Yerish26 committed May 13, 2024
1 parent 435b892 commit 6a0a6c5
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 5 deletions.
14 changes: 14 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@
<scope>runtime</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>3.2.1</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.passay/passay -->
<dependency>
<groupId>org.passay</groupId>
<artifactId>passay</artifactId>
<version>1.6.3</version>
</dependency>

</dependencies>


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.llm_service.llm_service.constraint;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import org.passay.*;

public class PasswordConstraintValidator implements ConstraintValidator<ValidPassword, String> {

@Override
public void initialize(ValidPassword arg0) {}

@Override
public boolean isValid(String password, ConstraintValidatorContext context) {
PasswordValidator validator = new PasswordValidator(Arrays.asList(
new LengthRule(8, 30),
new CharacterRule(EnglishCharacterData.UpperCase, 1),
new CharacterRule(EnglishCharacterData.LowerCase, 1),
new CharacterRule(EnglishCharacterData.Digit, 1),
new CharacterRule(EnglishCharacterData.Special, 1),
new WhitespaceRule()));

RuleResult result = validator.validate(new PasswordData(password));
if (result.isValid()) {
return true;
}
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(String.join(" ", validator.getMessages(result)))
.addConstraintViolation();
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.llm_service.llm_service.constraint;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.HashSet;
import java.util.Set;

public class UsernameConstraintValidator implements ConstraintValidator<ValidUsername, String> {
@Override
public void initialize(ValidUsername constraintAnnotation) {}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return false;
}

Set<String> errorMessages = new HashSet<>();

if (!value.matches("^[A-Za-z].*")) {
errorMessages.add("Username must start with a letter.");
}

if (!value.matches(".{8,30}")) {
errorMessages.add("Username must be between 8 and 30 characters long.");
}

if (!value.matches("[A-Za-z0-9_]*")) {
errorMessages.add("Username must consist of letters, digits, or underscores only.");
}

if (!errorMessages.isEmpty()) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(String.join(" ", errorMessages))
.addConstraintViolation();
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.llm_service.llm_service.constraint;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Constraint(validatedBy = PasswordConstraintValidator.class)
@Target({TYPE, FIELD, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ValidPassword {

String message() default "Invalid Password";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.llm_service.llm_service.constraint;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Documented
@Constraint(validatedBy = UsernameConstraintValidator.class)
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE})
@Retention(RUNTIME)
public @interface ValidUsername {
String message() default "Invalid username";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import java.util.HashMap;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;

@RestController
Expand Down Expand Up @@ -49,8 +53,15 @@ public ResponseEntity<UserResponse> register() throws UnAuthorizedException {
})
@Operation(summary = "register the user")
@PostMapping("/api/v1/register")
public ResponseEntity<UserResponse> register(@RequestBody UserRequest userRequest)
public ResponseEntity<?> register(@Valid @RequestBody UserRequest userRequest, BindingResult bindingResult)
throws UsernameAlreadyExistsException {
if (bindingResult.hasErrors()) {
ValidationErrorResponse errorResponse = new ValidationErrorResponse(new HashMap<>());
for (FieldError error : bindingResult.getFieldErrors()) {
errorResponse.addError(error.getField(), error.getDefaultMessage());
}
return ResponseEntity.badRequest().body(errorResponse);
}
User user = authenticationService.register(userRequest);
return ResponseEntity.status(HttpStatus.OK).body(userApiMapper.map(user));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
package com.llm_service.llm_service.controller.user;

import com.llm_service.llm_service.constraint.ValidPassword;
import com.llm_service.llm_service.constraint.ValidUsername;
import com.llm_service.llm_service.persistance.entities.Role;
import jakarta.validation.constraints.NotBlank;
import lombok.*;
import lombok.extern.jackson.Jacksonized;

@Value
@Builder
@Jacksonized
public class UserRequest {
@NonNull
@ValidUsername
String username;

@NonNull
@ValidPassword
String password;

@NonNull
@NotBlank(message = "firstname is required")
String firstName;

@NonNull
@NotBlank(message = "lastname is required")
String lastName;

@NonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.llm_service.llm_service.controller.user;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.HashMap;
import java.util.Map;
import lombok.Builder;
import lombok.extern.jackson.Jacksonized;

@Builder
@Jacksonized
public record ValidationErrorResponse(Map<String, String> errors) {
@JsonCreator
public ValidationErrorResponse(@JsonProperty("errors") Map<String, String> errors) {
this.errors = errors != null ? new HashMap<>(errors) : new HashMap<>();
}

public void addError(String field, String message) {
errors.put(field, message);
}
}

0 comments on commit 6a0a6c5

Please sign in to comment.