Validation - At least one element required

Hi there,

I’m facing the problem with password quality validation. I need to assure that password provided by user will:

  • be of any length.
  • contain at least one letter (no matter, capital or not),
  • contain at least one number,
  • contain at last one special character out of provided set of character.

What would be the best approach to achieve this:

a] CRegularExpressionValidator along with good regular expression,

b] custom validator,

c] custom validation rule in form model (similar to authenicate in LoginForm)

d] or maybe set of buili-in validators?

Unfortunately I’m both regular expressions and custom validators newbie.

My friend, good expert in regular expression told me that, reg. expr. for this would be hard, as at least one letter/number/special character is only required, without specifying position or order in which they’ll appear. If order would be specified (i.e. one or more letter, one or more number and one or more special characters - i.e. passwords qwe123!@# and a1@ match, but 123qwe!@# not) then there would be no problem doing this with regular expression. But since order or combination is fluent (i.e. both qwe123!@#, 123qwe!@#, !@#qwe123 and many more combinations are valid), this is beyond his knowledge.

I have no experience in using custom validator and don’t have idea where to start with, to solve this problem.

So I decided to use custom validation rule directly in form model and to use simple ifs and/or loop to check if password contains what it is supposed to contain.

But before I do this, I would be grateful for the opinions on this subject.

BTW: Can someone explain me difference between custom validator and custom validation rule (function) defined directly in form class. For me they seems to be the same, doing exactly the same. And the only difference is that custom validator is reusable and can be attached to many models, while custom validation rule sits in particular model and is hard to be use in others. Am I right? Are there any other differences?

Cheers.

Trejder

I would go for regex. With use of look aheads it shouldn’t be that hard. Try something like that:


.*(?=.*\d)(?=.*[a-zA-Z]).*

If you want some special characters list put another look ahead with characters specified like that:


.*(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@&]).*

You are right about differences between function and class validators, but there is more. As far as I know, there is no way to implement client side validation using member function as validator. Class has to be used. This may interest you: wiki entry

i will also suggest you a reg. expression method…It will help you a lot to achieve this task.

There is also a wikki article is available for custom validation rule. custom validation rule

Or you can define your own method in a model class for this validation. and call it on a particular action.it will work like a charm.

i have already done this kind of job so many times and it is working fine for me.

i hope it will work for you also…

cheers … :)

Here is a good article on how to create regular expressions for password strength - http://www.zorched.net/2009/05/08/password-strength-validation-with-regular-expressions/

Thanks (+1)! As I said, I’m reg.exp newbie (and this is such a nigthmare for me, that I fear, that even after reading mdomba’s article I won’t get any clever here! :]). So I’m only limited to copy-paste of your example and test it.

Seems that this one works nearly perfect! :]

I’ve put it into variable with slashes (heard, this is required in PHP, hope that this didn’t caused mentioned problems):


const REGEX_SPECIAL_CHARS = '!@#';

$regularExpressionPattern = '/.*(?=.*\d)(?=.*[a-zA-Z])(?=.*['.(self::REGEX_SPECIAL_CHARS).']).*/';


if(!isset($this->_quality)) $this->_getPasswordQuality();


return array

(

    	...

    	array('password_new_one', 'match', 'pattern'=>$regularExpressionPattern, 'skipOnError'=>true, 'message'=>'Must have at least one letter, number and one special character out of set ['.self::REGEX_SPECIAL_CHARS.']!'),

    	...

);

And tested. Seems that your regular expression catches every situation, except that, when there is at least one special character from defined set and another one outside this set.

For example (we have only !@& allowed, so we need one or more letter, one or more number and one or more characters from provided set):

  • qwe123!@& - works fine, validation catches that this one is valid,
  • qwe123! - same, as above, as good as different order: i.e.: 123qwe!@& or !@&123qwe all parsed as valid, which is OK,
  • qwe123$ or qwe123* - works fine, validation catches such as invalid (because of $ or *),
  • qwe123!@&$ or qwe123!@&* - wrong, validation passed those as valid, while they aren’t (again, because of $ or *),
  • q1!@&$ or q1!@&* - also wrong, for the same reason,
  • q1!$ - also wrong, as above.

This goes the same for every combination, order or number of characters. If only rule is satisfied, that there is at least one number, letter and one character from provided set of allowed character, you can have as many invalid characters as you wish and where you wish and password will always be validated as valid. Which is wrong.

Do you have any idea on, how to fix this?

jayant and mdomba: Thanks for information and links to further readout.

Trejder regex are very powerfull and once you learn them you will wonder how did you make it without them…

PHP documentation has a very nice explanation to get you going with those… when you have time read a bit that - http://www.php.net/manual/en/reference.pcre.pattern.syntax.php

Check these sites as well:

http://www.regular-e…o/tutorial.html

http://www.ruig.com/...um/regex-tester

http://xenon.stanford.edu/~xusch/regexp/analyzer.html

The reason regular expression I posted before matches special characters not defined in list is that it was meant to check if specified characters are present at least once. If you removed digit checking, it would still match passwords with digits present, but it would not require one. If you want to make sure only characters from list are present you will have to use a bit more complicated one:


^[\da-zA-Z!@&]*(?=[\da-zA-Z!@&]*\d)(?=[\da-zA-Z!@&]*[a-zA-Z])(?=[\da-zA-Z!@&]*[!@&])[\da-zA-Z!@&]*$

where !@& is a list of special characters required AND allowed - at least one of them must be present but not other.

@sidewinder: Thank you again, so much. This time, it seems, it works like a charm!

@mdomba: I’ve never doubted power of regular expression. I know that they are really, really powerful. Only, learning them seems to be a very big problem for me, even thought I took a few attempts. But links provided by both of you seems very interesting, as using them one can learn regular expressions by example, which should be by far easier. Thanks again!