package fr.openwide.core.basicapp.web.application.common.validator; import static com.google.common.base.Preconditions.checkNotNull; import java.util.List; import java.util.Map; import org.apache.wicket.Component; import org.apache.wicket.behavior.Behavior; import org.apache.wicket.injection.Injector; import org.apache.wicket.model.IModel; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.validation.IValidatable; import org.apache.wicket.validation.IValidator; import org.apache.wicket.validation.ValidationError; import org.passay.LengthRule; import org.passay.PasswordData; import org.passay.PasswordValidator; import org.passay.Rule; import org.passay.RuleResult; import org.passay.RuleResultDetail; import org.passay.UsernameRule; import org.springframework.security.crypto.password.PasswordEncoder; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import fr.openwide.core.basicapp.core.business.user.model.User; import fr.openwide.core.basicapp.core.property.BasicApplicationCorePropertyIds; import fr.openwide.core.basicapp.core.security.service.ISecurityManagementService; import fr.openwide.core.basicapp.web.application.common.typedescriptor.user.UserTypeDescriptor; import fr.openwide.core.spring.property.service.IPropertyService; import fr.openwide.core.spring.util.StringUtils; public class UserPasswordValidator extends Behavior implements IValidator<String> { private static final long serialVersionUID = 5619802188558408589L; private static final List<String> RULES_CUSTOM_ERROR = Lists.newArrayList( LengthRule.ERROR_CODE_MIN, LengthRule.ERROR_CODE_MAX ); private static final String HISTORY_VIOLATION = "HISTORY_VIOLATION"; private static final String COMMON_ERROR = "COMMON_ERROR"; private final UserTypeDescriptor<?> typeDescriptor; private IModel<? extends User> userModel; @SpringBean private ISecurityManagementService securityManagementService; @SpringBean private PasswordEncoder passwordEncoder; @SpringBean private IPropertyService propertyService; public UserPasswordValidator(UserTypeDescriptor<?> typeDescriptor) { super(); Injector.get().inject(this); this.typeDescriptor = checkNotNull(typeDescriptor); } @Override public void detach(Component component) { super.detach(component); if (userModel != null) { userModel.detach(); } } @Override public void validate(IValidatable<String> validatable) { String password = validatable.getValue(); if (Boolean.FALSE.equals(propertyService.get(BasicApplicationCorePropertyIds.SECURITY_PASSWORD_VALIDATOR_ENABLED)) || !StringUtils.hasText(password)) { return; } User user = userModel != null ? userModel.getObject() : null; PasswordData passwordData = new PasswordData(password); List<Rule> passwordRules = Lists.newArrayList(securityManagementService.getOptions(typeDescriptor.getEntityClass()).getPasswordRules()); if (user != null && StringUtils.hasText(user.getUserName())) { passwordData.setUsername(user.getUserName()); } else { passwordRules.removeAll(Lists.newArrayList(Iterables.filter(passwordRules, UsernameRule.class))); } PasswordValidator validator = new PasswordValidator(passwordRules); RuleResult result = validator.validate(passwordData); boolean valid = true; if (!result.isValid()) { valid = false; for (RuleResultDetail detail : result.getDetails()) { if (RULES_CUSTOM_ERROR.contains(detail.getErrorCode())) { validatable.error( new ValidationError(this, detail.getErrorCode()) .setVariables((Map<String, Object>) detail.getParameters()) ); } } } if (user != null && securityManagementService.getOptions(user).isPasswordHistoryEnabled() && user.getPasswordInformation().getHistory() != null && !user.getPasswordInformation().getHistory().isEmpty()) { for (String historyPasswordHash : user.getPasswordInformation().getHistory()) { if (passwordEncoder.matches(password, historyPasswordHash)) { valid = false; validatable.error( new ValidationError(this, HISTORY_VIOLATION) ); break; } } } if (!valid) { validatable.error( new ValidationError(this, COMMON_ERROR) ); } } public UserPasswordValidator userModel(IModel<? extends User> userModel) { this.userModel = checkNotNull(userModel); return this; } }