Validate new passwords with ORM and ValidateThis
My new project is using CF9’s ORM, FW/1 and ValidateThis, all for the first time – so naturally I’m having to work a few things out. There will probably be a few posts like this one over the next few weeks…
I have a user edit page, on which you can edit your personal details and change your password. It’s set up something like this:
user.cfc
component output="false" persistent="true"
{
// identifier
property name="userid" fieldtype="id";
// properties
property name="firstname";
property name="lastname";
property name="uuid";
property name="password";
}
user.xml (ValidateThis rules)
<?xml version="1.0" encoding="UTF-8"?>
<validateThis xsi:noNamespaceSchemaLocation="http://www.validatethis.org/validateThis.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<objectProperties>
<property name="firstname" desc="First name">
<rule type="required" />
</property>
<property name="lastname" desc="Last name">
<rule type="required" />
</property>
</objectProperties>
</validateThis>
…and the (simplified) code to run the validation:
user = variables.userService.get( rc.userID );
user.setFirstName( rc.firstname );
user.setLastName( rc.lastname );
result = application.ValidateThis.validate( user );
if ( result.hasErrors() ) {
// show error messages
} else {
// save user
}
Which is all well and good, but the problem comes with the password. My user object has a password field which stores a hash of the user’s password and uuid. The update form has two fields, newpassword1 and newpassword2, which have to match in order to set the password. But these are not persisted values; they are only used to set the actual persisted password value. So how to make them part of the user object so it can be validated in one easy command using ValidateThis?
The answer is simple, and will apply to other situations as well. All the persisted values in the user object are set up using properties, and so also get the auto-generated getters and setters. But if we add a couple of explicit getters and setters for our non-persisted values, ValidateThis will be able to access the values from the object, but they will not be a part of the persisted data. So our new user.cfc object will look like this:
component output="false" persistent="true"
{
// identifier
property name="userid" fieldtype="id";
// properties
property name="firstname";
property name="lastname";
property name="uuid";
property name="password";
variables.newpassword1 = "";
variables.newpassword2 = "";
function getVariables() {
return variables;
}
function setNewPassword1(string newpassword1) {
variables.newpassword1 = newpassword1;
}
function getNewPassword1() {
return variables.newpassword1;
}
function setNewPassword2(string newpassword2) {
variables.newpassword2 = newpassword2;
}
function getNewPassword2() {
return variables.newpassword2;
}
function updatePassword() {
setPassword( hash( getNewPassword1() & getUUID() ) );
}
}
And we can add the following rules to our user.xml (in the real code we would probably make newpassword1 required only under certain contexts, such as the initial user creation):
<property name="newpassword1" desc="New password">
<rule type="required" />
</property>
<property name="newpassword2" desc="New password confirmation">
<rule type="equalTo" failureMessage="Please make sure your passwords match">
<param DependentPropertyName="newpassword1" />
<param ComparePropertyName="newpassword1" />
</rule>
</property>
And then, when the user validates successfully, we simply call the updatePassword() method before persisting the object, and it will be updated with the new hashed password:
user = variables.userService.get( rc.userID );
user.setFirstName( rc.firstname );
user.setLastName( rc.lastname );
user.setNewPassword1( rc.newpassword1 );
user.setNewPassword2( rc.newpassword2 );
result = application.ValidateThis.validate( user );
if ( result.hasErrors() ) {
// show error messages
} else {
user.updatePassword();
// save user
}
I hope this is useful to others who are just starting to use these technologies…
You could use the eventhandling in CF ORM, so that preUpdate() and preInsert methods are called in User.cfc. These would be the ones that encrypt the password (if not already encrypted). That way you don't need to remember to call updatePassword.
@John: I thought of doing that, but realised I'd have to do various tests in the eventHandler to see what type of object I'm working with, before encrypting the password.
As it's an action that's so specific to the user object, I'd rather keep it in my main code logic.
What would be nice is if each persistent object could have its own preUpdate() and preInsert() methods.
What I also discovered (not strictly relevant to this case) is that those eventhandler methods are applied AFTER the data in the entity has been validated as appropriate for the table columns - I might write a longer blog post about this...
@Seb, each persistent object _can_ have its own preUpdate() and preInsert() methods; you don't need to use a global event handler. Here's an example:
http://www.bennadel.com/blog/1691-Learning-ColdFusion-9-Understand-ORM-Events-Thanks-John-Whish-.htm
Yes, the events only fire directly before or after a CRUD operation.
Excellent! Not sure how I missed that...