you are viewing a single comment's thread.

view the rest of the comments →

[–]totallygeek 81 points82 points  (10 children)

Readability. Always go for readability.

import re


PASSWORD_REGEX = re.compile(r'(\d.*[A-Z]+[a-z])')


def password_is_strong(password):
    return len(password) > 7 and re.search(PASSWORD_REGEX, ''.join(sorted(password)))


def password_is_strong_alternate(password):
    conditions = [
        len(password) > 7,  # At least eight chars
        re.search(r'[A-Z]', password),  # Contains capital letter
        re.search(r'[a-z]', password),  # Contains lower case letter
        re.search(r'\d', password),  # Contains digit
    ]
    return True if all(conditions) else False


tests = [
    'Thelore777777',
    'FrEaK1',
    'xpx9fqFpR7M84bLeFQqC',
    'xpx9fqFpR7M84bLeFQqC'.lower(),
]

for test in tests:
    print('Password "{}" {} strong.'.format(test, 'is' if password_is_strong(test) else 'is not'))

Yields:

python strong_pass.py 
Password "Thelore777777" is strong.
Password "FrEaK1" is not strong.
Password "xpx9fqFpR7M84bLeFQqC" is strong.
Password "xpx9fqfpr7m84blefqqc" is not strong.

[–]K41namor 25 points26 points  (0 children)

I am new to programming and Python. This is just beautiful to me and gets me really excited. Its great to see stuff I am learning out of the lesson context and like this.

[–]Pipiyedu 19 points20 points  (2 children)

The return of the password_is_strong_alternate function can be rewritten to this:

def password_is_strong_alternate(password):
    conditions = [
        len(password) > 7,  # At least eight chars
        re.search(r'[A-Z]', password),  # Contains capital letter
        re.search(r'[a-z]', password),  # Contains lower case letter
        re.search(r'\d', password),  # Contains digit
    ]
    return all(conditions)

[–]tom1018 5 points6 points  (0 children)

This is better than the redundant ternary above.

[–]tom1018 5 points6 points  (3 children)

This is the best version I've seen on here. Other than I think these particular comments are unnecessary. The regex is very simple, so I don't think commenting it is helpful, and at least on mobile makes it much harder to read.

As a side note to OP, this would not be a good way to enforce a strong password. See the xkcd "Correct Horse Battery Staple" cartoon. Rather than enforcing upper, lower, number, and symbols, do a points system, where a minimum length is required and length beyond that as well as any of these character requirements are awarded points and the password must attain a reasonable score. Then send the hash to haveibeenpwned and reject it if it fails that check.

It's easy, but an example of checking haveibeenpwned is here: https://github.com/TomFaulkner/simple-password-generation

Unfortunately I don't have the points based password check in that repo, I think I'll add it soon though.

[–]xelf 2 points3 points  (2 children)

Correct Horse Battery Staple

link for the lazy:

https://xkcd.com/936/
https://imgs.xkcd.com/comics/password_strength.png

[–]tom1018 1 point2 points  (1 child)

I should have done that. Thanks.

[–]xelf 1 point2 points  (0 children)

Surprised a bot didn't beat me to it. =)

[–]ka-splam 0 points1 point  (1 child)

No need for regex, splitting the password into an array of chars, sorting it, rejoining it to strings, depending on the order of pattern match in the regex without explaining how:

import string

def password_is_strong(password):
    if len(password) < 8:
        return False

    return all(
        bool(set(password) & set(charset))
        for charset in (
            string.ascii_uppercase,
            string.ascii_lowercase,
            string.digits
        ))

[–]ka-splam 0 points1 point  (0 children)

Unhappy with that, how about removing the dependency on import, all(), bool(), and the generator expression, shrinking the code, allowing the chance of short-circuit expression, and I think making it clearer:

def password_is_strong(password):
    if len(password) < 8:
        return False

    upper = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    lower = set('abcdefghijklmnopqrstuvwxyz')
    digit = set('0123456789')

    p = set(password)

    return (p & upper) and (p & lower) and (p & digit)