Checking strength of password with regex

Viewed 3581

The function is to find out how strong password is. It's considered strong if:

  1. length is greater than or equal to 10 characters
  2. it contains at least one digit
  3. at least one uppercase letter
  4. one lowercase letter
  5. the password may only contain ASCII latin letters or digits

Is there a way to reduce the amount of code in the function? Please help me make the code of function shorter than 200 characters (try to solve without assigning values to variables)

import re
def golf(password):
    if len(password) >=  10 \
    and re.search("^[a-zA-Z0-9]+", password) \
    and re.search("[a-z]+", password) \
    and re.search("[A-Z]+", password) \
    and re.search("[0-9]+", password):
        print(password, True)
        return True
    else:
        print(password, False)
        return False


if __name__ == '__main__':
    golf('A1213pokl') == False
    golf('bAse730onE') == True
    golf('asasasasasasasaas') == False
    golf('QWERTYqwerty') == False
    golf('123456123456') == False
    golf('QwErTy911poqqqq') == True
    golf('..........') == False
4 Answers

While you already have your answer, I'd even try to optimize the pattern. Instead of .* and then backtracking, I'd apply the principle of contrast directly:

(?=\D*\d)          # NOT a number, 0+ times, then one number
(?=[^A-Z]*[A-Z])   # NOT an UPPERCASE, 0+times, then an UPPERCASE
(?=[^a-z]*[a-z])   # same with lowercase
^[A-Za-z0-9]{10,}$ # allowed characters, 10+, with anchors on both sides

Condensed and demo:

(?=\D*\d)(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])[A-Za-z0-9]{10,}$

The idea here is, that while .* brings you down the line and then backtracks, the pattern above is likely to come to an end faster.


At last the Python snippet:

import re

def golf(password=None):
    rx = re.compile(r'(?=\D*\d)(?=[^A-Z]*[A-Z])(?=[^a-z]*[a-z])[A-Za-z0-9]{10,}$')
    return True if rx.match(password) else False

passwords = ['A1213pokl', 'bAse730onE', 'asasasasasasasaas', 'QWERTYqwerty', '123456123456', 'QwErTy911poqqqq', '..........']
vectors = [golf(password) for password in passwords]
print(vectors)
# [False, True, False, False, False, True, False]

The (not so :) stupid way:

from re import search as s
def golf(p):
    return all([len(p)>=10,s("^[a-zA-Z0-9]+",p),s("[a-z]+",p),s("[A-Z]+",p),s("[0-9]+",p)])

And BTW, you probably want some assert statements in your if __name__ == '__main__': block...

This regex should do:

 (?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])^[a-zA-Z0-9]{10,}$

You can use one regular expression as bellow to check password strength.

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

It will validate for all your cases.

import re
def golf(password):
    if re.search("^(?=(.*\d){2})(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z\d]).{,}$", password):
        print(password, True)
        return True
    else:
        print(password, False)
        return False
Related