1

I'm currently testing out my spring boot app's API using Swagger, and when I input mismatching passwords, or even a password with a size outside the bounds (min=5 and max=15) of the size, I am not getting a 404 error code when it is expected. Other errors for other entity variables are getting properly caught, but seemingly nothing for the password. What may I have incorrect for plainPassword and repeatPassword to match in my entity below? Because the @PasswordMatch annotation doesn't seem to be doing the job I expect it to of comparing plainPassword and repeatPassword.

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import com.bcore.hw.validation.PasswordMatch;
import lombok.NoArgsConstructor;
import lombok.Data;


@Entity
@Table(name="users", uniqueConstraints={@UniqueConstraint(columnNames = {"email"})})
@PasswordMatch(message="{register.repeatPassword.mismatch}")
@NoArgsConstructor
@Data
public class SiteUser {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long userId;

    @Column(name="email", unique=true)
    @Email(message="{register.email.invalid}")
    @NotBlank(message="{register.email.invalid}")
    private String email;
    
    @Transient // meaning it will not be saved in DB
    @Size(min=5, max=15, message="{register.password.size}")
    private String plainPassword; //unencrytped
    
    @Transient
    private String repeatPassword;

    @Column(name="password", length=60)
    private String password;

    @Column(name="role", length=20)
    private String role;
    
    @Column(name="enabled")
    private Boolean enabled = false;
    
    public void setPlainPassword(String plainPassword) {
        //System.out.println("PASSWORD BEFORE " + plainPassword);
        this.password = new BCryptPasswordEncoder().encode(plainPassword);
        //System.out.println("HERE IS PASSWORD" + this.password + "PASSWORD LENGTH = " + (this.password).length());
        this.plainPassword = plainPassword;
    }

}

Updated with Custom Validator:

So here now I have PasswordMatch.java and PasswordMatchValidator.java, but have a little confusion as to how to set up the @PrePersist and @PreUpdate annotations in the entity:

package com.bcore.hw.validation;

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

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy=PasswordMatchValidator.class)
@Documented
public @interface PasswordMatch {
    String message() default "{error.password.mismatch}";
    
    Class<?>[] groups() default {};
    
    Class<? extends Payload>[] payload() default {};
}



package com.bcore.hw.validation;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.bcore.hw.model.SiteUser;

public class PasswordMatchValidator implements ConstraintValidator<PasswordMatch, SiteUser>{

    @Override
    public void initialize(PasswordMatch p) {
        
    }
    
    public boolean isValid(SiteUser user, ConstraintValidatorContext c) {
        String plainPassword = user.getPlainPassword();
        String repeatPassword = user.getRepeatPassword();
        
        if(plainPassword == null || !plainPassword.equals(repeatPassword)) {
            return false;
        }
        
        return true;
    }

}

Currently in the entity, you can see @PasswordMatch before the class definition, but this isn't working. So, @PrePersist and @PreUpdate are used only for entity method definitions, but what method would I place under them? A call to the isValid() method from the PasswordMatch interface? Not exactly sure what to do in the entity at this point.

2 Answers 2

2

It's seems to be this bug in open status because of @Transient field

Sign up to request clarification or add additional context in comments.

3 Comments

Ahhh yes this is the issue. So removing @Transient annotation does allow for the validation, but it looks like there isn't a workaround so that the attribute isn't actually saved to the DB(?)
You can implement your custom annotation for validation and then process customly-annotated items on hibernate prePersist/preUpdate listener
So at this point I've defined the PasswordMatch and PasswordMatchValidator classes which you can see in my edits above. Now, my question becomes how to use PrePersist and PreUpdate in the entity in order to get the isValid() method to work, because as of now even though I have PasswordMatch defined on the entity, it is still not working.
0
@PostMapping("/change-password")
public String changePassword(@RequestParam("newPassword") String newPassword,
        @RequestParam("confirmNewPassword") String confirmNewPassword, Model model, HttpSession session) {

    model.addAttribute("title", "Change Password Form");

    String email = (String) session.getAttribute("email");
    UserEntity userEntity = userRepository.getUserByUserName(email);
    if (newPassword.equals(confirmNewPassword)) {
        userEntity.setPassword(bCryptPasswordEncoder.encode(newPassword));
        userRepository.save(userEntity);
        return "redirect:/signin?change=Password Changed Successfully";
    } else {
        session.setAttribute("message", "Password doesn't matched !!");
        return "password_change_form";
    }

}

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.