Grails tries to validate encoded password (Spring Security Core) - spring

I spent hours on this problem without finding the solution. Other questions come close but none of the suggested solutions work for me.
I'm running on
- Grails 2.1.1 installed with
- Groovy 2.0.8 and
- Oracle Java v 1.6.0_45 (also tried with 1.7 already)
I added the Spring Security Core Plugin v 2.0-RC2.
I'm a Grails beginner and all I want to do is create a "Runner" with a password using my own password validator.
This is my Runner.groovy domain class (did not change very much from the default Spring Security User template apart from the renaming):
package de.muden.runnerbase
class Runner {
transient springSecurityService
String username
String password
boolean enabled = true
boolean accountExpired
boolean accountLocked
boolean passwordExpired
Date dateCreated
Profile profile
public static final int MIN_PASS_LENGTH = 6
public static final int MAX_PASS_LENGTH = 20
static transients = ['springSecurityService']
static constraints = {
username(size:3..20,unique:true)
password(nullable:false, blank:false, minSize:6, validator: { passwd, runner ->
return (passwd != runner.username && validatePasswordInplace(passwd))
})
dateCreated()
profile(nullable:true)
}
static mapping = {
profile lazy:false
runs sort:'dateCreated'
password column: '`password`'
}
Set<Role> getAuthorities() {
UserRole.findAllByUser(this).collect { it.role } as Set
}
def beforeInsert() {
encodePassword()
}
def beforeUpdate() {
if (isDirty('password')) {
encodePassword()
}
}
String toString() {
return "Runner '$username'"
}
protected void encodePassword() {
password = springSecurityService.encodePassword(password)
}
protected static boolean validatePasswordInplace(String passToValidate) {
println "VALIDATING PASS $passToValidate"
return passToValidate ==~ /([A-Za-z0-9äöüÄÖÜß.!\?_-]){$MIN_PASS_LENGTH,$MAX_PASS_LENGTH}/
}
static hasMany = [runs: Run, shoes: Shoe]
}
So the validator allows passwords between 6 and 20 characters long, upper and lower case letters, numbers and a few special characters.
Simple unit tests just testing this method work as expected.
Now a simple integration test:
void testValidRunner() {
Runner r = new Runner(username:'dummy',password:'foobar')
assertTrue r.validate() // OK
assertFalse r.hasErrors() // OK
assertNotNull r.save(flush:true,failOnError:true) // OK
Runner foundRunner = Runner.findByUsername("dummy")
assertNotNull foundRunner // fails, foundRunner = null
assertEquals('dummy',foundRunner.username)
}
And the console (with -echoOut) says:
VALIDATING PASS foobar
VALIDATING PASS $2a$10$Q5RYaDrCFFxdXEqYqV4J2OJWHzgOJZJ3wljqVK1jNP4Sqm6ZUOPam
It is obvious that the second validation fails. But why is grails validating the encoded password again? And why doesn't r.validate() complain? Where exactly does that second validation happen?
I have the feeling that I'm doing really basic user password encryption wrong here...
First I thought it had to do with the Spring Security fields "accountExpired" etc. being added and not in the constraints block. But when I remove the custom validator everything works fine.
Any help is appreciated :-)
Thanks,
Matt

Below is what I think is going on ...
The second validation happens when you call r.save
The beforeInsert method is calling the encodePassword method, which is encoding the PW to the long string $2a$10$Q5RYaDrCFFxdXEqYqV4J2OJWHzgOJZJ3wljqVK1jNP4Sqm6ZUOPam
That is the string that will be validated and saved in the DB not 'foobar'
I believe that your regex does not allow the dollar sign, which is part of the encoded string resulting in the failure.
I do not think that using constraints will work for what you want to do. You need to validate before it is encoded, so you probably need to add separate validation code (in the domain class or elsewhere) and validate the PW before assigning to the PW field and saving the object.

Related

In Gradle, how do you perform validation of lazily evaluated properties (on extensions)?

Is there a way to validate a property value when the property is evaluated? I can't do it in the getter because that returns the Property object - I want the validation to run only when the actual value is calculated (i.e. I want to be lazy evaluation friendly).
They show extensions using the Property object here:
https://docs.gradle.org/current/userguide/lazy_configuration.html#connecting_properties_together
However, they don't explain how to do property validation when the value is calculated. Here is the snipet of code from the Gradle documentation provided example:
// A project extension
class MessageExtension {
// A configurable greeting
final Property<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
greeting = objects.property(String)
}
}
If I wanted to make sure the value of greeting was not equal to test, then how would I do that when it is evaluated?
For most use cases, it should be sufficient to just validate the property value once you resolve it in your task or in other internal parts of your plugin. Only a few extensions are actually designed to be consumed by other plugins or the build script.
Gradle does not provide some validation that can be attached to a property, however you can build this functionality on your own like in the example below:
class MessageExtension {
private final Property<String> _greeting
final Provider<String> greeting
#javax.inject.Inject
MessageExtension(ObjectFactory objects) {
_greeting = objects.property(String)
greeting = _greeting.map { value ->
if (value.equals('test'))
throw new RuntimeException('Invalid greeting')
return value
}
}
def setGreeting(String value) {
_greeting.set(value)
}
def setGreeting(Provider<String> value) {
_greeting.set(value)
}
}
project.extensions.create('message', MessageExtension)
message {
greeting = 'test'
}
println message.greeting.get()
I turned the Property into a backing field for a Provider that runs the validation when resolved. If you do not want to throw an exception, but just return an empty Provider, you may replace the map with a flatMap.

CaffeineCache is not refreshed immediately after put operation in Spring Boot application

I have a random issue with CaffeineCache in my Spring Boot application. The problem is with an integration test that makes next
find user
delete it
find it again
It seems that sometimes cache doesn't not refreshes on time before the second call of find that comes immediately after delete.
Here is a simplified signature of find method
#Cacheable(cacheNames = "name", key = "{#config.id, #userId}", unless = "#result == null")
public User find(SomeConfig config, String userId) {
// ...
}
And a simplified signature of delete
#Caching(put = {
#CachePut(cacheNames = "someOtherCache", key = "#userId.technicalId"),
#CachePut(cacheNames = "name", key = "{#config.id, #userId}")
})
public User delete(SomeConfig config, String userId) {
// ...
}
I suppose that after call delete cache is not updated immediately and that's why method find is not called the second time. It happens 1 times from 10.
Any ideas about fix?

ControlsFX complex validation

I need to validate simple JavaFX form with password and password confirmation.
validationSupport.registerValidator(passwordInput,
Validator.createEmptyValidator("Password required!"));
validationSupport.registerValidator(confirmPasswordInput,
new EqualsToValidator(passwordInput.textProperty(),
"Password differs from confirmation"));
In EqualsToValidator I simply compare property.getValue() and value passed to validator.
If I change only password EqualsToValidator is not called because it is listening to confirmPasswordInput not passwordInput.
I have only found ugly solution:
passwordInput.textProperty().addListener((observable, oldValue, newValue) -> {
String oldText = confirmPasswordInput.getText();
confirmPasswordInput.setText(null);
confirmPasswordInput.setText(oldText);
});
How to invalidate one field when another field changes?
My implementation
Validator.java - base class for validator.
TextValidator.java and PasswordValidator.java - implementations.
ValidationSupport.java tracks all validators.
Usage:
validationSupport.addValidator(new TextValidator(usernameInput, "Username is required!"));
validationSupport.addValidator(new TextValidator(passwordInput, "Password is required!"));
validationSupport.addValidator(new PasswordValidator(passwordInput, confirmPasswordInput, "Password differs from confirmation"));
This worked for me. I simply copied the ValidationSupport class and added the folowing code.
#SuppressWarnings("unchecked")
public <T> void reaplyValidator(Control target){
if(!controls.containsKey(target)){
throw new NullPointerException("The given control was not registered");
}
ValueExtractor.getObservableValueExtractor(target).map( e -> {
ObservableValue<T> observable = (ObservableValue<T>) e.call(target);
Validator<T> validator = (Validator<T>) controls.get(target);
Platform.runLater(() -> validationResults.put(target, validator.apply(target, observable.getValue())));
dataChanged.set(true);
return e;
});
}
public void revalidate(){
for(Control c : getRegisteredControls()){
reaplyValidator(c);
}
}
You also need to change the controls Set to a Map in order to keep the validator.
private ObservableMap<Control, Validator<?>> controls = FXCollections.observableMap(new HashMap<>());

Web API validation error

I have a View Model called SignUp with the EmailAddress property set like this:
[Required]
[DuplicateEmailAddressAttribute(ErrorMessage = "This email address already exists")]
public string EmailAddress { get; set; }
and the custom validator looks like this:
public class DuplicateEmailAddressAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
PestControlContext _db = new PestControlContext();
int hash = value.ToString().GetHashCode();
if (value == null)
{
return true;
}
if (_db.Users.Where(x => x.EmailAddressHash == hash).Count() > 0)
return false;
else
return true;
}
}
The problem I'm having is that if the user leaves the email address field blank on the sign up form the application is throwing a null reference exception error (I think it's looking for "" in the database and can't find it). What I don't understand is why this isn't being handled by the Required attribute - why is it jumping straight into the custom validator?
The Required attribute would have resulted in an error being added to the model state. It will not short-circuit the execution though. The framework continues to run other validators for the simple reason that all the errors about the request need to be sent out in a single shot. Ideally, you wouldn't want the service to say something is wrong to start with and when the user re-submits the request after making a correction, the service comes back and say some other thing is wrong and so on. It will be an annoyance, I guess.
The NullReferenceException is thrown because value.ToString() is called before the check against null. As you need the hash variable only after the check, you can solve this by reordering the statements:
if (value == null)
{
return true;
}
int hash = value.ToString().GetHashCode();
In addition, you could also move the PestControlContext after the check against null and use a using statement to dispose of it properly.
As also #Baldri pointed out, each validator can add Error messages and all of them are run, even if a previous one already signaled the data to be invalid. Furthermore, I'd not rely on that the validations are run in the order that you specify when marking the property with the attributes (some frameworks implement their own attribute ordering mechanism in order to assert that the order is deterministic, e.g. priorities or preceding attributes).
Therefore, I suggest reordering the code in the custom validator is the best solution.

Imported unique constraints doesn't get validated

I have a Spring Security User class which has a unique constraint for username and email. In a Command class I imported all constraints from this class with "importFrom User". All constraints work as expected EXCEPT the unique ones.
However when saving the User the unique constraints get validated and errors are shown. But it would be nice if they get validated BEFORE saving like all other constraints.
UPDATE
I added this to the controller:
user.errors.fieldErrors.each {
command.errors.rejectValue(it.getField(), it.getCode())
}
Seems like a dirty workaround, but it works.
Good question #Chris, and your solution is best since the goal of sharing constraints between domain classes and command objects is to avoid duplicating validation logic.
I'll just add that to avoid duplicating field errors and to handle nested field paths in domain objects, something like the following might be necessary.
def save(EntityCreateCommand cmd) {
def entity = new Entity(cmd.properties)
def someAssociation = new Something(cmd.properties)
entity.someAssociation = someAssociation
entity.validate()
entity.errors.fieldErrors.each {
def fieldName = it.field.split("\\.").last()
def flattenedCodes = cmd.errors.getFieldErrors(fieldName).codes.flatten()
if(cmd.hasProperty(fieldName) && (!flattenedCodes.contains(it.code))) {
cmd.errors.rejectValue(fieldName,
"entityCreateCommand.${fieldName}.${it.code}")
}
}
if(cmd.errors.hasErrors()) {
error handling stuff...
} else {
business stuff...
}
}
I had problems with unique constraint before, so I made a custom validator in my command object to test and see if it's unique:
Command Object:
class wateverCommand{
....
String username
static constraints = {
username validator:{value, command ->
if(value){
if(User.findByUsername(value){
return 'wateverCommand.username.unique'
}
}
}
}
}
within your messages.properties add a custom error message:
wateverCommand.username.unique The username is taken, please pick a new username
I agree the unique constraint doesn't always seem to import properly. Since I like to avoid clutter in the constraint body I like the one liner approach:
validator: {value, command -> (User.findByUsername(value) ? false : true ) }
Then in your message.properties it would be:
accountCommand.username.validator.error=That username already exists

Resources