tlawo
tlawo

Reputation: 53

python - password must contain at least one upper or lower case and a number

as a school work, I am trying to make a password encryption/decryption program. I need consider the following rules:

  1. Ensure that every password contains at least one letter (A-Z) or (a-z), one nonalphabetic character (from #, @, %), and at least one digit. The program must reject passwords that violate this rule.
  2. Restrict the characters that are allowed in a password to letters (A-Z) and (a-z), digits (0-9) and the three characters (#, @, %). The program must reject passwords that violate this rule.

If the password violates those conditions, I will terminate the program with:

print('Invalid password!')
sys.exit()

I've been stuck for hours trying to add these conditions... I don't get where to add these conditions, wherever I add them, my program just terminates even though I've input a valid password T-T

Here is what I have so far (I've removed the part for decryption so that I can try to figure out that part for myself afterwards):

# import sys module for termination
import sys

# init
password_out = ''
case_changer = ord('a') - ord('A')
encryption_key = (('a','m'), ('b','h'), ('c','t'), ('d','f'), ('e','g'),
  ('f','k'), ('g','b'), ('h','p'), ('i','j'), ('j','w'), ('k','e'),('l','r'),
  ('m','q'), ('n','s'), ('o','l'), ('p','n'), ('q','i'), ('r','u'), ('s','o'),
  ('t','x'), ('u','z'), ('v','y'), ('w','v'), ('x','d'), ('y','c'), ('z','a'),
  ('#', '!'), ('@', '('), ('%', ')'), ('0'), ('1'), ('2'), ('3'), ('4'), ('5'),
  ('6'), ('7'), ('8'), ('9'))

encrypting = True

# get password
password_in = input('Enter password: ')

# perform encryption / decryption
if encrypting:
    from_index = 0
    to_index = 1
else:
    from_index = 1 
    to_index = 0

case_changer = ord('a') - ord('A')

for ch in password_in:
    letter_found = False

    for t in encryption_key:
        if ('a' <= ch and ch <= 'z') and ch == t[from_index]:  
            password_out = password_out + t[to_index]
            letter_found = True
        elif ('A' <= ch and ch <= 'Z') and chr(ord(ch) + 32) == t[from_index]:
            password_out = password_out + chr(ord(t[to_index]) - case_changer)
            letter_found = True
        elif (ch == '#' or ch == '@' or ch == '%') and ch == t[from_index]:
            password_out = password_out + t[to_index]
        elif (ch >= '0' and ch <= '9') and ch == t[from_index]:
            password_out = password_out + ch

# output
if encrypting:
    print('Your encrypted password is:', password_out)
else:
    print('Your decrypted password is:', password_out)

Upvotes: 5

Views: 6944

Answers (4)

Azat Ibrakov
Azat Ibrakov

Reputation: 10990

No regular expressions required

import string
import sys

NON_ALPHABETIC_CHARACTERS = {'#', '@', '%'}
DIGITS_CHARACTERS = set(string.digits)
LETTERS_CHARACTERS = set(string.ascii_letters)


def validate_password_1(password,
                        non_alphabetic_characters=NON_ALPHABETIC_CHARACTERS,
                        digits_characters=DIGITS_CHARACTERS,
                        letters_characters=LETTERS_CHARACTERS):
    if not any(character in password
               for character in non_alphabetic_characters):
        err_msg = ('Password should contain at least '
                   'one non-alphabetic character.')
        print(err_msg)
        print('Invalid password!')
        sys.exit()

    if not any(character in password
               for character in digits_characters):
        err_msg = ('Password should contain at least '
                   'one digit character.')
        print(err_msg)
        print('Invalid password!')
        sys.exit()

    if not any(character in password
               for character in letters_characters):
        err_msg = ('Password should contain at least '
                   'one letter character.')
        print(err_msg)
        print('Invalid password!')
        sys.exit()


ALLOWED_CHARACTERS = (NON_ALPHABETIC_CHARACTERS
                      | DIGITS_CHARACTERS
                      | LETTERS_CHARACTERS)


def validate_password_2(password,
                        allowed_characters=ALLOWED_CHARACTERS):
    if not all(character in allowed_characters
               for character in password):
        print('Invalid password!')
        sys.exit()

Added additional messages to see what exactly is wrong with given password

Upvotes: 2

Till
Till

Reputation: 4523

I will do something like that using regexp:

import re

def test(x):
    regexp = r'[@#%]+[0-9]+@*[a-zA-Z]+'
    sorted_x = ''.join(sorted(x))
    if '@' in sorted_x:
        sorted_x = '@%s' % sorted_x
    p = re.compile(regexp)
    return p.match(sorted_x) is not None

Then the function gives :

In [34]: test("dfj")
Out[34]: False

In [35]: test("dfj23")
Out[35]: False

In [36]: test("dfj23#")
Out[36]: True

If you need one upper AND one lower case letter you can change the regexp to:

regexp = r'[@#%]+[0-9]+@*[A-Z]+[a-z]+'

The sorted function will put the # and the % first, then the numbers, then the @ and finally the letters (first the uper case and then the lower case). Because the @ is put in the middle, I put one first if I find one in the string.

So at the end you want a string with at least a special character : [@#%]+, at least a number [0-9]+, optionally there can be a @ in the middle, and finally a letter [a-zA-Z]+ (or if you want upper and lower [A-Z]+[a-z]+).

Upvotes: 0

Thierry Lathuille
Thierry Lathuille

Reputation: 24282

This certainly won't qualify as answer for your homework, but you can test that kind of conditions easily with sets:

import string
alpha = set(string.ascii_lowercase + string.ascii_uppercase)
digits = set(string.digits)
non_alpha = set('#@%')

def is_valid(password):
    password_chars = set(password)

    # We substract the set of letters (resp. digits, non_alpha)
    # from the set of chars used in password
    # If any of the letters is used in password, this should be
    # smaller than the original set 
    all_classes_used = all([len(password_chars - char_class) != len(password_chars) 
                            for char_class in [alpha, digits, non_alpha] ])

    # We remove all letters, digits and non_alpha from the
    # set of chars composing the password, nothing should be left.
    all_chars_valid = len(password_chars - alpha - digits - non_alpha) == 0

    return all_classes_used and all_chars_valid

for pw in ['a', 'a2', 'a2%', 'a2%!']:
    print(pw, is_valid(pw))

# a False
# a2 False
# a2% True
# a2%! False

Upvotes: 1

Jacob G.
Jacob G.

Reputation: 29710

One possible way to check for both cases would be to do the following:

if password == password.lower() or password == password.upper():
    # Reject password here.

We're not going to write the rest of the encryption for you, though!

Upvotes: 0

Related Questions