clauu
clauu

Reputation: 91

Password characters checker in JavaScript

I'm trying to build something that checks the password that I enter in a prompt has a upper case letter , one lower case , one special symbol and a length...but sometimes when I am entering just upper case cand lower case letters I get no alert ( you will see it in the code )..

Also I have the following error in the console : Cannot read property 'length' of null at hasUpperCase"

I will post the code here:

var parola = prompt('Introdu parola:');

function hasUpperCase(parola){
 for(i = 0; i < parola.length; i++){
    if(parola[i] === parola[i].toUpperCase()){
        return true;
    }
   }
  }

function hasLowerCase(parola){
 for(i = 0; i < parola.length; i++){
    if(parola[i] === parola[i].toLowerCase()){
        return true;
     }
   }
}

var minLength = 8;
  function isLongEnough(parola){
   if(parola.length >= minLength){
    return true;
  }
 }

function hasSpecialCharacter(parola){
  var specialCharacters = "£$%^&*@~";
    for(i = 0; i < parola.length; i++){
      for(j = 0; j < specialCharacters.length; j++){
        if(parola[i] === specialCharacters[j]){
            return true;
           }
        }
      }
   }

  function isPasswordValid(parola){
    if(!hasUpperCase(parola)){
      alert('The password requires a capital letter!');
      var parola = prompt('Introdu parola:');
   }

   if(!hasLowerCase(parola)){
      alert('The password requires a lower case letter!');
      var parola = prompt('Introdu parola:');
   }

  if(!isLongEnough(parola)){
     alert('The password is not long enough!');
     var parola = prompt('Introdu parola:');
   }

   if(!hasSpecialCharacter(parola)){
     alert('The password requires a special character');
     var parola = prompt('Introdu parola:');
   }

   if((hasSpecialCharacter(parola) && hasLowerCase(parola) && hasUpperCase(parola)  && isLongEnough(parola)){

   }

 }
 isPasswordValid(parola);

Upvotes: 0

Views: 502

Answers (5)

Joseph Webber
Joseph Webber

Reputation: 2173

isPasswordValid calls your 4 functions in sequence to check their validity, but does not re-check the prompt's value if any of them fail. It just asks you to enter something else and then continues on. You can re-check the prompt's validity by re-calling isPasswordValid at the end of the function. Calling it at the end of the function prevents additional prompts from appearing when you enter invalid inputs and then a valid one.

Note: calling toLowerCase() on a symbol returns that symbol, so @@@@AAAA would have been valid. To get around this I used regular expressions in your functions.

var parola = prompt('Introdu parola:');

function hasUpperCase(parola) {
  return /[A-Z]/.test(parola)
}

function hasLowerCase(parola) {
  return /[a-z]/.test(parola);
}

var minLength = 8;
function isLongEnough(parola) {
  return parola.length >= minLength;
}

function hasSpecialCharacter(parola) {
  return /[£$%^&*@~]/.test(parola);
}

var errorMessage;
function isPasswordValid(parola) {
  if (!hasUpperCase(parola)) {
    errorMessage = 'The password requires a capital letter!';
  }
  else if (!hasLowerCase(parola)) {
    errorMessage = 'The password requires a lower case letter!';
  }
  else if (!isLongEnough(parola)) {
    errorMessage = 'The password is not long enough!';
  }
  else if (!hasSpecialCharacter(parola)) {
    errorMessage = 'The password requires a special character';
  }
  else {
    alert('Password is valid');
    return;
  }

  parola = prompt(errorMessage);
  isPasswordValid(parola);
}

isPasswordValid(parola);

Upvotes: 0

Sujan Gainju
Sujan Gainju

Reputation: 4769

That error was cause of the prompt cancellation. So, i think you should be using the value in the text field and validate that

Here is the working way for doing that

  $('#form').on('submit', function() {
    event.preventDefault();
    var parola = $('#txt_name').val();
     isPasswordValid(parola);
  })


function hasUpperCase(parola){
 for(i = 0; i < parola.length; i++){
    if(parola[i] === parola[i].toUpperCase()){
        return true;
    }
   }
  }

function hasLowerCase(parola){
 for(i = 0; i < parola.length; i++){
    if(parola[i] === parola[i].toLowerCase()){
        return true;
     }
   }
}

var minLength = 8;
  function isLongEnough(parola){
   if(parola.length >= minLength){
    return true;
  }
 }

function hasSpecialCharacter(parola){
  var specialCharacters = "£$%^&*@~";
    for(i = 0; i < parola.length; i++){
      for(j = 0; j < specialCharacters.length; j++){
        if(parola[i] === specialCharacters[j]){
            return true;
           }
        }
      }
   }

  function isPasswordValid(parola){
    if(!hasUpperCase(parola)){
      alert('The password requires a capital letter!');
      return;
   }

   if(!hasLowerCase(parola)){
      alert('The password requires a lower case letter!');
     return;
   }

  if(!isLongEnough(parola)){
     alert('The password is not long enough!');
    return;
   }

   if(!hasSpecialCharacter(parola)){
     alert('The password requires a special character');
     return;
   }

   if(hasSpecialCharacter(parola) && hasLowerCase(parola) && hasUpperCase(parola)  && isLongEnough(parola)){
      alert('yayyy!!');
   }

 }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="form">
  <input type="text" name="text" value="" id="txt_name">
  <input type="submit" value="Check password">
</form>

Upvotes: 0

Patrick Roberts
Patrick Roberts

Reputation: 51886

To address the error you mentioned, namely

Cannot read property 'length' of null at hasUpperCase

You must check that the value returned from prompt() is not null as a result of the input being cancelled by the user.

Otherwise, you can read the string from an <input> to ensure that the value is always a string.

Some other issues are that you don't take advantage of the simplicity of regular expressions to enforce some of the constraints you implement with for loops, which would make your code a lot more readable, maintainable, and easier to digest.

Also as I suggested in the comments, it's better practice to allow isLongEnough() to accept a parameter, a local variable, or even the minlength attribute on the password to indicate the minimum length, rather than a scoped variable as is currently being used.

Finally, it would help to take advantage of the pattern attribute of the <input> element to automate some of the requirements that can be expressed in a regular expression. Note, I tried to include this in the solution below, but the look-aheads in pattern="(?=[a-z])(?=[A-Z])(?=[£$%^&*@~])" seemed to behave kind of buggy, so I've omitted this particular suggestion.

Working these suggestions into a solution might look something like this:

function hasUpperCase(parola) {
  return /[A-Z]/.test(parola);
}

function hasLowerCase(parola) {
  return /[a-z]/.test(parola);
}

function isLongEnough(parola, minLength) {
  return parola.length >= minLength;
}

function hasSpecialCharacter(parola) {
  return /[£$%^&*@~]/.test(parola);
}

function checkPasswordValidity() {
  var parola = this.value;
  var minLength = Number(this.getAttribute('minlength'));
  var errors = [];

  if (!isLongEnough(parola, minLength)) {
    errors.push('a minimum length of ' + minLength + ' characters');
  }

  if (!hasUpperCase(parola)) {
    errors.push('a capital letter');
  }

  if (!hasLowerCase(parola)) {
    errors.push('a lower case letter');
  }

  if (!hasSpecialCharacter(parola)) {
    errors.push('a special character');
  }

  if (errors.length > 0) {
    this.setCustomValidity('The password requires ' + errors.join(', '));
  } else {
    this.setCustomValidity('');
  }
}

document.querySelector('[name="password"]').addEventListener('input', checkPasswordValidity);
<form>
  <input name="password" type="text" placeholder="Introdu parola" minlength="8" required>
  <input type="submit" value="Submit">
</form>

Upvotes: 1

thatguy
thatguy

Reputation: 1279

You could use the the regex

/^(?=.{1,}[a-z])(?=.{1,}[A-Z])(?=.{1,}([£$%^&*@~])).{8,20}$/

to match all of those examples. Here's a brief explanation:

^                       // the start of the string
(?=.{1,}[a-z])             // use positive look ahead to see if at least one lower case letter exists
(?=.{1,}[A-Z])             // use positive look ahead to see if at least one upper case letter exists
(?=.{1,}[£$%^&*@~])  // use positive look ahead to see if at least one underscore or special character exists
.{8,20}                 // gobble up the entire string and match between 8 and 20
$                       // the end of the string

You would use it like this:

function isPasswordValid(parola)
{  
   if(!parola)
      return false;
   var reg = /^(?=.{1,}[a-z])(?=.{1,}[A-Z])(?=.{1,}([£$%^&*@~])).{8,20}$/g;
   if(parola && reg.test(parola)){
      //do stuff here
   }
}

if you don't want to do all of that, for your code its a very simple fix! remove the extra ( and add this to your test:

function isPasswordValid(parola){
    if(!parola)
        return false;
.
.
.
.
.
.

if(parola && (hasSpecialCharacter(parola) && hasLowerCase(parola) && hasUpperCase(parola)  && isLongEnough(parola))){
      // do stuff here
   }

checking just the varriable will evaluate as false if it is:

  • null
  • undefined
  • NaN
  • empty string ("")
  • 0
  • false

and as true otherwise.

This should fix all of your issues.

UPDATE
Thank you Patrick Roberts for pointing out the error in the regex. below is a working example.

const regex = /^(?=.{1,}[a-z])(?=.{1,}[A-Z])(?=.{1,}([£$%^&*@~])).{8,20}$/g;
const str = 'aA$';
let m;

if ((m = regex.exec(str)) !== null) {
    // The result can be accessed through the `m`-variable.
    m.forEach((match, groupIndex) => {
        console.log(`Found match, group ${groupIndex}: ${match}`);
    });
}
else{
  console.log('no match found')
}

Upvotes: 1

Nate Reynolds
Nate Reynolds

Reputation: 137

Aside from implementation concerns, first fix the error by checking for null or undefined first thing in your isPasswordValid function. Also, as wisely suggested by Patrick, use regex for these checks. I'd also recommend always returning a bool in your check functions by returning false after each for loop.

Upvotes: 1

Related Questions