2

I have set up a program to change a "password". I have it checking to see if it is at least 8 characters, contains a capital letter and has a number, and if it does not meet this criteria, it asks for the password again. I have everything working except the checking for a number and I was wondering if someone could help.

npwv = 1

while npwv == 1:

    npw = input("Please enter new password.")
    npwc = input ("Please confirm new password")

    if npwc == npw:
        if npwc.isupper()== False:
            if npwc.islower()== False:
                if len(npwc) >= 8:
                    if str.isdigit(npwc) == True:
                        npw=npwc
                        print("Your password has been changed")
                    else:
                        print("Your password must contain a number")
                        npwv = 1
                else:
                    print("Your password must contain at least 8 characters.")
                    npwv = 1
            else:
                print("Your password must contain at least 1 upper case character.")
                npwv = 1

    else:
        print ("Passwords don't match")
        npwv = 1
DeepSpace
  • 78,697
  • 11
  • 109
  • 154
user7410930
  • 21
  • 1
  • 3
  • 2
    `isupper()` and `islower()` also don't do what you think they do. Please read their [documentation](https://docs.python.org/3.6/library/stdtypes.html#str.isupper) – Felk Jan 30 '17 at 17:05
  • 1
    `npwc.isupper()` will check if ALL characters are uppercase, not just one. – cdarke Jan 30 '17 at 17:06
  • 1
    same with `islower()` and `isdigit()`... – David Zemens Jan 30 '17 at 17:06
  • I suggest you use a regular expression instead. `[A-Z]` for the upper-case test and `\d` for a digit. – cdarke Jan 30 '17 at 17:07
  • Have you considered using regular expressions? You may find them more flexible than isdigit(). – StockB Jan 30 '17 at 17:07

5 Answers5

2

You are checking if the password itself is fully uppercase or composed of numbers. What you need to check if if the characters in the password match this criteria.

has_upper = any([c.isupper() for c in npwc])
has_digit = any([c.isdigit() for c in npwc])

You can also use regular expressions.

By the way, you should prefer getpass to get the password from the user.

Srikanth
  • 973
  • 2
  • 9
  • 19
  • 1
    you do not need the list-comprehensions in the `any`. removing the braces will turn them into a generator expression which should be faster. (irrelevant for this example here) – Ma0 Jan 30 '17 at 17:18
1

Have you considered using .isalnum()?

>>> foo =  "123asd"
>>> foo
'123asd'
>>> foo.isalnum()
True
>>> 

Edit: Judging by the other answers, I am not sure what are you looking for, could explain it with examples?

Simeon Aleksov
  • 1,275
  • 1
  • 13
  • 25
1

I would suggest using sets, and the string package from stdlib for your list of acceptable characters.

I would also suggest refactoring a bit to remove a lot of the nesting with if / else branches.

import string
upper = set(list(string.uppercase))
lower = set(list(string.lowercase))
numbers = set(list(string.digits))

while True:

    npw = input("Please enter new password: ")
    npwc = input("Please confirm new password: ")

    if npwc != npw:
        print("Passwords don't match")
        continue

    if len(npcw) < 8:
        print("Your password must contain at least 8 characters.")
        continue

    chars = set(list(npwc))

    if not upper.intersection(chars):
        print("Your password must contain at least 1 upper case character.")
        continue

    if not lower.intersection(chars):
        print("Your password must contain at least 1 lower case character.")
        continue

    if not numbers.intersection(chars):
        print("Your password must contain a number")
        continue

    npw = npwc
    print("Your password has been changed")
    break
sberry
  • 128,281
  • 18
  • 138
  • 165
0

This can be made more compact but yeah..

while True:    
    npw = input("Please enter new password.")
    npwc = input ("Please confirm new password")    
    if npwc == npw:
        if any(x.isupper() for x in npwc):
            if any(x.islower() for x in npwc):
                if len(npwc) >= 8:
                    if any (x.isdigit() for x in npwc):
                        npw=npwc
                        print("Your password has been changed")
                        #break  # you probably want to exit the loop at this point
                    else:
                        print("Your password must contain a number")
                else:
                    print("Your password must contain at least 8 characters.")
            else:
                print("Your password must contain at least 1 upper case character.")
        else:
            print("Your password must contain at least 1 lower case character.")
    else:
        print("Passwords don't match")
Ma0
  • 15,057
  • 4
  • 35
  • 65
0

This looks like a job for regular expressions. Solution below:

import re

def password_validator(password):
    if len(password) < 8:
        return False, "Your password must contain at least 8 characters"
    if not re.match('.*[0-9]', password):
        return False, "Your password must contain a number"
    if not re.match('.*[A-Z]', password):
        return False, "Your password must contain at least 1 upper case character."
    if not re.match('.*[a-z]', password):
        return False, "Your password must contain at least 1 lower case character." 
    return True, "Your password has been changed"

while True:
    npw = input("Please enter new password.")
    npwc = input("Please confirm new password")
    if npw != npwc:
        print("Passwords don't match")
        continue

    is_valid, message = password_validator(npw) 
    print(message)
    if is_valid:
        break

You could also validate the whole thing in one go as:

pattern = """
(?=.*[a-z]) # use positive lookahead to see if at least one lower case letter exists
(?=.*[A-Z]) # use positive lookahead to see if at least one upper case letter exists
(?=.*\d)    # use positive lookahead to see if at least one digit exists
[A-Za-z0-9@#$%^&+=]{8,}  # match any character in [] at least 8 times
"""
pwd_validator = re.compile(pattern, re.VERBOSE)    
if pwd_validator.match(password):
    # legit password
else:
    # no match ask again

Hope this helps. The re.VERBOSE just makes this regular expression self-documenting so a lot easier to understand in future.

JanHak
  • 1,645
  • 1
  • 10
  • 9
  • While this can be matched using a regex, this doesn't provide the information that needs to be conveyed to the user when they provide a password that does not match the requirements. – sberry Jan 30 '17 at 17:28