@Tushar's idea isn't bad, but it is NOT optimal for re-usability and extensability.
What I propose is to filter out the rules that failed. This way, you can count how many failed a bit faster, instead of having a counter.
Even if it isn't that much faster, it is still easier to add rules.
function validate(string) {
if(string.length < 8)
{
return false;
}
var rules = [
/[a-z]/, //letters (lower-case)
/[A-Z]/, //letters (upper-case)
/\d/, //numbers (similar to /[0-9]/)
/[!@#$&*]/ //some symbols
];
return rules.filter(function(rule){
return rule.test(string);
}).length >= 3;
}
Yes, there is a magic number. I don't see any other way to improve it. If you want, you can do the oposite and count how many rules failed.
This may be a bit more flexible, if you want to allow some rules to fail.
Speed-wise, it is up to 5 times slower, but sometimes it is faster.
I guess is has something to do with the .filter
method.
Here's how I tested the speed:
function validate_mine(string) {
if(string.length < 8)
{
return false;
}
var rules = [
/[a-z]/, //letters (lower-case)
/[A-Z]/, //letters (upper-case)
/\d/, //numbers (similar to /[0-9]/)
/[!@#$&*]/ //some symbols
];
return rules.filter(function(rule){
return rule.test(string);
}).length >= 3;
}
function validate_Tusha(string) {
// Initialize counter to zero
var counter = 0;
// On each test that is passed, increment the counter
if (/[a-z]/.test(string)) {
// If string contain at least one lowercase alphabet character
counter++;
}
if (/[A-Z]/.test(string)) {
counter++;
}
if (/[0-9]/.test(string)) {
counter++;
}
if (/[!@#$&*]/.test(string)) {
counter++;
}
// Check if at least three rules are satisfied
return counter >= 3;
}
console.time('validate_mine');
[
'test it',
'Me want this!',
'Th1s_is_cool',
'$tuff!_should-be-fast',
'now! run it!'
].map(validate_mine);
console.timeEnd('validate_mine');
console.time('validate_Tusha');
[
'test it',
'Me want this!',
'Th1s_is_cool',
'$tuff!_should-be-fast',
'now! run it!'
].map(validate_Tusha);
console.timeEnd('validate_Tusha');
If you want to make something extremelly extensible, one could follow @Vld's idea and pass a list of rules to a function, and an optional parameter to relax how many rules can be missed.
Something similar to this:
function validate(string, rules, min_pass) {
'use strict';
if(min_pass < 0 || min_pass > rules.length)
{
throw new RangeError('min_pass should be between 0 and ' + rules.length);
}
return rules.filter(function(rule){
if(rule instanceof Function)
{
return !!rule.bind(string)(string);
}
else if(rule instanceof RegExp)
{
return rule.test(string);
}
return false;
}).length >= min_pass;
}
This accepts 3 parameters:
string
: The string to be tested,
rules
: An array of regular expressions or functions,
min_pass
: Minimum number of rules to pass.
To obtain the results you want, using this new method, you could do like this:
validate(
'string here',
[
/[a-b]/,
/[A-B]/,
/\d/,
/[!@#$&*]/
],
3
);
You still have to validate the length before, since I don't have a way to say that a rule is important or something.
The formatting still isn't optimal, but you get the idea.
Also, this is not a good way to check security. With your rules, a password like Passw0rd!
is valid, but we all know how crappy that is. Any password cracker worth it's salt will have rules to try these permutations. Heck, it may even be the one of the very first words in the dictionary list!
This will leave you prone to dictionary-based attacks.
Just make sure you use a good and unique salt, with a strong hash (like SHA256) instead of relying on this type of rules to check password strength.
This fails for passwords like ñó wÿn fõr ýôü
, which may be far more secure than some of the passwords you validate.
Password1
\$\endgroup\$