-1

My requirements are this:

  • at least 8 characters,
  • must contain at least 1 lowercase
  • must contain at least 1 uppercase
  • must contain at least one numeral
  • must contain at least one of the following set: !@#$%^&*
  • must not contain "and" or "end"

That last part is the part that is tripping me up I googled a bunch and came up with:

^(?=.*[^a-zA-Z])(?=.*[a-z])(?=.*[A-Z])\S{8,}$

But I believe that it will allow ALL special characters because of \S if I understand that post. I can't have that. I also need to check to see if the string contains and or end.

Currently I have this. and it works, but it's not elegant.

package testing;

import java.util.Scanner;

public class PasswordTest {

public static void main(String args[]){
    System.out.println("Start");
    Scanner sc = new Scanner(System.in);
    boolean done = false;       
    while(!done){
        boolean size = false;
        boolean upper = false;
        boolean lower = false;
        boolean special = false;
        boolean andEnd = false;
        boolean numeric = false;
        String password = sc.nextLine();
        if(password.length()>=8){
            size=true;
        }
        for(int x = 0;x < password.length();x++){
            if(password.charAt(x)>= 65 && password.charAt(x)<=90){
                upper = true;
            }
            if(password.charAt(x)>= 97 && password.charAt(x)<=122){
                lower = true;
            }
            if(password.charAt(x)>= 48 && password.charAt(x)<=67){
                numeric = true;
            }
            if(password.charAt(x)>= 33 && password.charAt(x)<=42){
                special = true;
            }
        }
        if(!password.contains("and") && !password.contains("end")){
            andEnd = true;
        }
        if(!size){
            System.out.println("Password too short");
        }
        if(!upper){
            System.out.println("Password does not contain an uppercase character");
        }
        if(!lower){
            System.out.println("Password does not contain an lowercase character");
        }
        if(!numeric){
            System.out.println("Password does not contain a number");
        }
        if(!special){
            System.out.println("Password does not contain a Special character");
        }
        if(!andEnd){
            System.out.println("Password does contain 'and' or 'end'");
        }
        done = size && upper && lower && numeric && special&& andEnd;
        if(done){
            System.out.println("Valid "+password);
        }else{
            System.out.println("Invalid "+password);
        }
    }
    sc.close();
}
}
Alan Moore
  • 73,866
  • 12
  • 100
  • 156
Horologe
  • 11
  • 1

2 Answers2

1

This is not a problem for a single regular expression, instead you should validate each condition separately, only using a regex as appropriate.

Check that it contains a lowercase letter /[a-z]/
Check that it contains an upper case letter /[A-Z]/
Check the length password.length()

And so forth...

You want to make your code clear and easy to understand -- checking each condition separately does that. Your approach with named booleans is a good start, it's the implementation that is incorrect.

Edit: I had originally missed the requirement to reject some special characters. I added that in. Making the test explicit will make changing and validating these conditions easier.

public static void main(String args[]){

    System.out.println("Start");
    String password = getPassword()
    System.out.println("Valid "+password);

}

public static String getPassword(){
    boolean done = false;       
    String passwordCandidate;
    while(!done){
       Scanner sc = new Scanner(System.in);
       passwordCandidate = sc.nextLine();
       List<String> validationErrors = validate(passwordCandidate);

       done = validationErrors.size() == 0;
       if(!done){
            System.out.println("Invalid "+passwordCandidate);
            for(String errMsg : validationErrors) {
                 System.out.println(errMsg);
             }
        }
    }
    sc.close();
    return passwordCandidate;
}

public static List<String> validate(String passwordCandidate){
    List<String> errMsgs = new ArrayList<String>();

    boolean size = passwordCandidate.length()>=8;
    boolean upper = passwordCandidate.matches(".*[A-Z].*");
    boolean lower = passwordCandidate.matches(".*[a-z].*");
    boolean requiredSpecial = passwordCandidate.matches(".*[!@#$%^&*].*");
    boolean andEnd = !passwordCandidate.matches(".*[ae]nd.*");
    boolean numeric = passwordCandidate.matches(".*[0-9].*");
    boolean forbiddenSpecial = !passwordCandidate.matches(".*[^a-zA-Z0-9!@#$%^&*].*");

    if(!size){
        errMsgs.add("Password too short");
    }
    if(!upper){
        errMsgs.add"Password does not contain an uppercase character");
    }
    if(!lower){
        errMsgs.add("Password does not contain an lowercase character");
    }
    if(!numeric){
        errMsgs.add("Password does not contain a number");
    }
    if(!requiredSpecial){
        errMsgs.add("Password does not contain a Special character");
    }
    if(!forbiddenSpecial){
        errMsgs.add("Password contains a forbidden character");
    }
    if(!andEnd){
        errMsgs.add("Password contains 'and' or 'end'");
    }
    return errMsgs

}
jmoreno
  • 12,752
  • 4
  • 60
  • 91
  • This can easily be done with one regular expression: see my comment. If you actually want to tell the user what's wrong, then I agree, this shouldn't be done with one regex. – Steve P. Jul 05 '14 at 20:48
  • 1
    @SteveP.: I didn't say it couldn't be solved with a single regex, but it's not a problem *best* solved with a single regular expression. – jmoreno Jul 06 '14 at 02:01
  • The `matches()` method requires the regex to describe the whole string, so you need to "pad" all those regexes like so: `".*[A-Z].*"`, `".*[a-z].*"`, etc. Also, you didn't address the requirement that characters other than `[A-Za-z0-9!@#$%^&*]` are not allowed. – Alan Moore Jul 08 '14 at 03:23
1

The way you described the password, you can use this:

if (subjectString.matches("(?=.*[a-z])(?=[A-Z])(?=[0-9])(?!.*[ae]nd)(?=.*[!@#$%^&*])\\S{8,}")) {
    // It matched!
  } 
else {  // nah, it didn't match...  
     } 
  • For the allowable character set, I would advise coming up for something more specific than \S. For instance, if you are happy to only have ASCII characters, instead S{8,} at the end, you could use [!-~]{8,} which allows any printable ASCII chars
  • Each of the (?=...) is a lookahead that assert that something can be matched.
  • The (?!...) is a negative lookahead that assert that something cannot be matched.

Reference

zx81
  • 41,100
  • 9
  • 89
  • 105