简体   繁体   中英

Writing code to check if a password meets certain criteria

I am trying to write a function in Python that checks a password and returns True or False based on the following criteria:

  • it must be at least 8 characters long
  • it must contain at least one capital letter
  • it must contain at least one lower case letter
  • it must contain at least one number
  • it must contain at least one of the following special characters: ;@#$%&()-_[]{}:',".?/<>, The twist is that it MUST NOT contain special characters other than the ones listed. eg, a space. ~ or * or anything else.

I have been trying to come up with code for a week now and have tried different variations of the following:

def password_check(str):
    list = ['!', '@', '#', '$', '%', '&', '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>', '?']
    estr = True
    if len(str) >= 8:
        for i in str:
            if i in list:
                estr = True
            else:
                if i.isnumeric():
                    estr = True
                else:
                    if i.isupper():
                        estr = True
                    else:
                        if i.islower():
                            estr = True
                        else:
                            return False
    else:
        estr = False
    return estr

But the code does not work as intended because if there are, for example, only lower case letters it returns True. So I tried the following:

def password_check(str):
    list = ['!', '@', '#', '$', '%', '&', '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>', '?']
    if any(i.isupper() for i in str) and any(i.islower() for i in str) and any(i.isdigit() for i in str) and len(str) >= 8 and any(i in list for i in str):
        estr = True
    else:
        return False

But it doesn't return False when an invalid character is used (eg ~). The function calls below should return True, True, False, False, False, True, False and False.

print(password_check("tHIs1sag00d.p4ssw0rd."))
print(password_check("3@t7ENZ((T"))
print(password_check("2.shOrt"))
print(password_check("all.l0wer.case"))
print(password_check("inv4l1d CH4R4CTERS~"))
print(password_check('X)ndC@[?/fVkoN/[AkmA0'))
print(password_check(':>&BhEjGNcaSWotpAy@$tJ@j{*W8'))
print(password_check('ZW}VoVH.~VGz,D?()l0'))

I will be most grateful if anyone points me in the right direction.

The issue here is that on the occasion that any rule is true, it returns true. But what you want is for it to check that all rules are true. To do this, we'll need to make four different variables, one for each condition:

def password_check(str):
    list = ['!', '@', '#', '$', '%', '&', '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>', '?']
    hs = False # Has Symbols
    hn = False # Has Numbers
    hu = False # Has Uppercase
    hl = False # Has Lowercase
    if len(str) >= 8:
        for i in str:
            if i in list:
                hs = True
            elif i.isnumeric():
                hn = True
            elif i.isupper():
                hu = True
            elif i.islower():
                hl = True
            else:
                return False
    else:
        return False
    return hs and hn and hu and hl

I tested this and it gave me these results:

True
True
False
False
False
True
False
False

Note the last line there,

return hs and hn and hu and hl

This is basically shorthand for saying this:

if not hs:
    return False
if not hn:
    return False
if not hu:
    return False
if not hl:
    return False
return True

By the way, this is a really useful password checker, might come in handy one day!

Without steering you towards a rewrite (which IS a good idea) but only answering your direct question...

You are not checking for "the twist", the case where the password contains an invalid character. To do that, you need to add one more test to your conditional:

and all((i.isupper() or i.islower() or i.isdigit() or i in list) for i in str)

which says that ALL characters in the password must be in one of the valid ranges of characters. If you add this, you get the output you desire.

The full solution, including another minor fix, looks like this then:

def password_check(str):
    list = ['!', '@', '#', '$', '%', '&', '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>',
            '?']
    if any(i.isupper() for i in str) and any(i.islower() for i in str) and any(i.isdigit() for i in str) and len(
            str) >= 8 and any(i in list for i in str) and all((i.isupper() or i.islower() or i.isdigit() or i in list) for i in str):
        return True
    else:
        return False

and produces:

True
True
False
False
False
True
False
False

You can use this code, it also tells you why the password is incorrect. Simple if and else conditions. But I would prefer if you use RegEx Python

def password_check(password):

    SpecialSym = ['!', '@', '#', '$', '%', '&',
                  '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>', '?']

    if len(password) < 8:
        print('length should be at least 6')
        return False

    if not any(char.isdigit() for char in password):
        print('Password should have at least one numeral')
        return False

    if not any(char.isupper() for char in password):
        print('Password should have at least one uppercase letter')
        return False

    if not any(char.islower() for char in password):
        print('Password should have at least one lowercase letter')
        return False

    if not any(char in SpecialSym for char in password):
        print('Password should have at least one of the symbols $@#')
        return False

    for i in password:
        if not (('0' <= i <= '9') or ('a' <= i.lower() <= 'z')):
            # Special Char
            if i not in SpecialSym:
                return False

    return True

Firt off, please don't define a variable called list (or int or str for that matter) as this is a reserved word and a built-in function. Then, you don't need a nested if-block but just a boolean value that gets set to False if any of the conditions is not met. You can check the conditions independently:


def password_check(p):
    print('\nchecking password: ',p)
    chlist = ['!', '@', '#', '$', '%', '&', '(', ')', '-', '_', '[', ']', '{', '}', ';', ':', '"', '.', '/', '<', '>', '?']
    good_password = True ## Set to true and try to disprove

    nums = False
    letters = False
    special = False

    for c in p:
        if not (c.isalnum() or c in chlist):
            good_password = False
            print("Invalid character: "+c)
        elif c.isdigit():
            nums = True
        elif c.isalpha():
            letters = True
        elif c in chlist:
            special = True
    if not letters:
        good_password = False
        print("There are no letters")
    if not nums:
        good_password = False
        print("There are no numbers")
    if not special:
        good_password = False
        print("There are no special characters")
    if p == p.lower() or p==p.upper():
        good_password = False
        print("Please use upper and lower case letters")
    if len(p) < 8:
        good_password = False
        print("Too short")

    return good_password

By checking each condition subsequently, you don't have to nest your conditions and can detect the exact problem of the password. You don't need to print them, of course, but this might help in debugging and testing for specific violations.

Try this:

import string 
sc = "!@#$%&()-_[]{};:,./<>?"
uc = string.ascii_uppercase
lc = uc.lower()
num = string.digits

def password_checker(password):
     if len(password) >= 8:
       sn = 0 #numbers of special character
       un = 0 #......... uppercase letters
       ln = 0 #........ lowercase 
       dn = 0 #.........digits
       for i in password:
           if i in uc:
              un += 1
           elif i in lc:
              ln += 1
           elif i in num:
             dn += 1
           elif i in sc and sn == 0:
             sn += 1
           else:
             break
       else:
             print("Valid Password")
     else:
        print("Invalid Password")   



password_checker(input()) 

I feel validating each condition step by step is a better approach to avoid confusions. This way we can let the user know what mistake they are committing to correct, rather than to just say if a password is valid or not. Checking the password length and disallowed punctuation will be better ones to start validation.

import re


def password_check(password):
    return_value = True

    if len(password) < 8:
        # print('Password length is less than 8 characters')
        return_value = False

    punctuation_not_allowed = '[\s+*+=\^`|~]'
    if re.search(punctuation_not_allowed, password):
        # print(f'Whitespaces or punctuations "*+=\^`|~" is not allowed')
        return_value = False

    if not any(char.isupper() for char in password) or \
            not any(char.islower() for char in password) or \
            not any(char.isdigit() for char in password):
        # print('Password requires at least one upper case letter, one lower case letter and one digit')
        return_value = False

    if not re.search(r"""[!@#$%&()\-_\[\]{};':",./<>?]""", password):
        # print("""At least special char in "[!@#$%&()-_[]{};':",./<>?]" is required""")
        return_value = False

    return return_value

Test Cases

print(password_check("tHIs1sag00d.p4ssw0rd."))
print(password_check("3@t7ENZ((T"))
print(password_check("2.shOrt"))
print(password_check("all.l0wer.case"))
print(password_check("inv4l1d CH4R4CTERS~"))
print(password_check('X)ndC@[?/fVkoN/[AkmA0'))
print(password_check(':>&BhEjGNcaSWotpAy@$tJ@j{*W8'))
print(password_check('ZW}VoVH.~VGz,D?()l0'))

Result

True True False False False True False False

I have updated my answer and made it a bit simpler for you to understand. Let me know if this is easier to follow through. The whole program loops through the string only once while checking for everything needed. That way you don't have looping thru many times.

pwd = input('Password :')

#set uppercase, lowercase, digits, special to False
#if password has these, then set to True

ucase = lcase = digit = scase = False

#for loop when applied to a string will pick each char for processing
#this will allow you to  check for conditions on the char

for ch in pwd:

    #check if char is uppercase, set ucase to True

    if ch.isupper(): ucase = True

    #check if char is lowercase, set lcase to True

    elif ch.islower(): lcase = True

    #check if char is a number, set digit to True

    elif ch.isdigit(): digit = True

    #check if char is special char, set scase to True
    #by using in (...), it checks against each item in the list

    elif ch in ('!@#$%&()-_[]{};\':",./<>?'): scase = True

    #if it is not one of these, then it is not valid password
    else:
        break

#check if everything is true

if len(pwd) >= 8 and ucase and lcase and digit and scase:
    print ('Valid Password')
else:
    print ('Invalid Password')

The output for this will be as follows. I ran it multiple times:

Password :thisisnotagoodpassword
Invalid Password

Password :thisis notaG00dPassw0#d
Invalid Password

Password :thisisaG00dPassw0$d
Valid Password

Password :Abcd123$
Valid Password

Password :Abc123$
Invalid Password

How to convert this to a Function:

You can always replace the print statement with a return True or return False statement when you convert this code into a function. Then use the def to create.

def pword(pwd):
    #the whole code from #set uppercase... (except first line)
    #to the final if statement
    #if you want to return True or False, you can use
    #return True instead of print ('Valid Password')

To call the function, you can do:

check = pword(input('Password. :'))

That will return the value of True or False to check.

Hopefully it was helpful for you to understand the implementation.

I think using regular expression library "re" is the best way of doing this. so simple, so clean.

import re

while True:
    p = input('enter a new password: ')
    if (len(p) < 6 or len(p)>16):
        print('Your password should be between 6-16 characters.')
    
    elif not re.search("[A-Z]", p):
        print('Your password should include at least one capital letter.')
        
    elif not re.search('[a-z]', p):
        print('Your password should include at least one letter.')

    elif not re.search('[0-9]', p):
        print('Your password should include at least one number.')

    elif not re.search('[@#$%]', p):
        print('Your password should include at least one of these signs: @#$%')
    
    else:
        print('Your password is valid.')
        break

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM