Dealing with different password validation schemes in a single app
May 20, 2009 – 3:25 pm Tags: Design Patterns, PHP, Software design/architectureIf your application is well thought out, you would not want to save any data that isn’t valid. So what do you do, when you need different validation schemes, say for passwords, depending on some special case?
For example: Your user passwords need to be at least 8 characters long and contain upper and lower case letters, a number and a special character, but when one of your users forget their password, you would like to generate a new one which does not have to conform to those conditions.
There are some approaches to this, such as using more than one validation function in your user object, or using some boolean flag to enable or disable the validation. These are kind of clunky, though, and there is a better approach: Using a “policy” – Policies can be used for other things than this too, but let’s look at how to use a policy for managing password validation.
An example scenario
First, let’s look at the example scenario of user passwords a bit more closely.
We have a User object, which contains code that will validate a newly set password. The code that’s responsible for saving users to the DB will not allow us to store anything that does not validate, because having invalid data stored is not a very good idea.
The user works something like this:
//set a valid password $user->setPassword('Xyz123!'); //true $user->isValid(); //set an invalid password $user->setPassword('zxcqwe'); //no longer true $user->isValid();
In the case where we have a simpler, invalid, password it could be generated by some algorithm to create random simple passwords for users when they forget their own.
As mentioned before, we could add some flag to isValid or have a separate method for setting the random password which makes it ignore the validation. While these are easy to implement, they aren’t very obvious: A boolean parameter to isValid could mean a lot more than “ignore password validation”, and using two methods has similar problems. It would also make it a bit more difficult to modify our password validation rules later.
Instead, we’ll create some policy objects and use those.
What is a policy?
In our example, a policy means the rules used to validate a password. We could call it a password validator too, but I think a policy is a quite descriptive word for this, as we only want to set a specific policy like “RandomPasswordPolicy” – we don’t really care how it validates the password, as long as randomly generated passwords validate with it. In fact, it might even just not validate it at all and always return that it’s valid.
The policy object will define a method to call to check if a password validates with the policy. This way we can define a policy, assign it to the user, and then the password will get validated based on the policy.
By default, we can have a policy like this:
//Let's just assume the IPasswordPolicy interface defines the validatePassword method class ComplexPasswordPolicy implements IPasswordPolicy { public function validatePassword($password) { //password must be at least 8 chars long if(strlen($password) < 8) { return false; } //password must contain an upper case letter if(preg_match('/[A-Z]/', $password) == 0) { return false; } //password must contain a number if(preg_match('/[0-9]/', $password) == 0) { return false; } //and a special character if(preg_match('/[^A-Za-z0-9]/', $password) == 0) { return false; } return true; } }
For the randomly generated password, we’ll have this very simple policy:
class RandomPasswordPolicy implements IPasswordPolicy { public function validatePassword($password) { return true; } }
Using the policy with the user
Now that we have a policy with the validation rules, we will need to change the user’s isValid method to test the password using the policy.
By default, the user object will come with the ComplexPasswordPolicy, so any new user that is created will need a good password and if a user wishes to change their password, it will also need to pass the complex validation.
However, in our code which generates a new password for users who forget theirs, we can now easily swap the validation policy to the simple one:
//We'll assume $user is the user who forgot their password $user->setPasswordPolicy( new RandomPasswordPolicy() ); $user->setPassword($randomPassword); //Now the user will validate even if the randomly generated password isn't very complex $user->isValid();
Conclusion
Using a swappable policy is useful if you need to be able to change some aspect of your code. This is quite similar to the strategy pattern too, it just has a different name.


6 Responses to “Dealing with different password validation schemes in a single app”
Nice, straight to the point article.
PS. “policy” is yet another name for “Dependency injection” IMO
By Orkan on May 20, 2009
Hi Jani
I need to ask U. Why bound validation with model… or with forms/form->model objects (like sf did)?
I know that it look easy and nice but it will introduce difficulties later on. Common character of input data (POST, GT, headers in Http or *args in CLI) is that its validable in single point place. Why make your life harder?
Cheers, Alan
By Alan on May 20, 2009
I don’t see how separating some validation logic into a policy object will make it more difficult at any point.
You are correct that often the validation is done in a single point, but not always. For example, you might have a CLI script, unit tests and the actual web page, which all need the validation code to be available, thus it’s best placed into the model or some object usable in the model. Not to mention that it’s a part of the “business logic” so it’s a natural place for it.
Orkan: I guess you could say “injecting” the validation algorithm like this is a bit like DI, but I would not really call it that.
By Jani Hartikainen on May 20, 2009
It will be more flexible to make policies as a private array in the class and then use foreach loop to check all of them. In this case you can make the object more universal and define methods like addPolicy() for instance…
By oleg on May 20, 2009
That is a nice use of the strategy pattern, although I’d argue that you should use dependency injection to specify the policy rather than an out-of-band setPasswordPolicy() call.
By Alan Pinstein on May 23, 2009