3
\$\begingroup\$

I'm doing Python programming challenges and have come across one where the object of the challenge was to create a script to change passwords. The script must validate that the password contains, one upper, one lowercase, and one number. After everything has been validated the script should save the password to a text file. I added special characters and some basic encryption to the program to make it a little more interesting.

Source:

# -*- encoding: UTF-8 -*-
import re
import getpass
from random import randint
class PasswordValidator(object):
 """ A password validator, used to validate a password for
 specific characters, numbers, and special characters """
 validated = False
 def __init__(self, password):
 self.password = password
 def validate_length(self): # Check the length, gotta be over 8
 if len(self.password) > 8:
 return True
 else:
 return False
 def validate_upper_case(self): # Check for upper case characters
 return re.search("[A-Z]", self.password) is not None
 def validate_lower_case(self): # Check for lower case characters
 return re.search("[a-z]", self.password) is not None
 def validate_integers(self): # Check for integers
 return re.search("[0-9]", self.password) is not None
 def validate_spec_chars(self): # Check for special characters
 return re.search("[!$&*]", self.password) is not None
 def validate_all(self):
 """ Validate everything. """
 length = self.validate_length()
 upper = self.validate_upper_case()
 lower = self.validate_lower_case()
 digits = self.validate_integers()
 specs = self.validate_spec_chars()
 if specs is False:
 return "Failed special characters validation. Allowed special chars: (! $ & *)"
 elif digits is False:
 return "Failed integer validation. Allowed integers: (0 - 9)"
 elif lower is False:
 return "Failed lower case validation. (a - z)"
 elif upper is False:
 return "Failed uppercase validation. (A - Z)"
 elif length is False:
 return "Failed length validation. Allowed length: (> 8)"
 else:
 self.validated = True
 return "Password validated successfully."
def encrypt(password):
 """ Encrypt the password simply example: bíFuo <- says Test!23456
 :type password: String """
 arr = list(password)
 new_arr = []
 for i in arr:
 num = ord(i) + randint(0, 100)
 new_arr.append(chr(num))
 return ''.join(new_arr)
def obtain_password():
 """ Get the password, simple and easy with getpass library """
 return getpass.getpass('Change your password: ')
def second_authentication(password_to_verify):
 """ Verify that the password is what the user wants it to be,
 by asking for it a second time.
 :type password_to_verify: String """
 password = getpass.getpass('Enter password a second time: ')
 if password == password_to_verify:
 with open('passwords.txt', 'w') as f:
 f.write(encrypt(password_to_verify))
 return "Password changed."
 else:
 return "Password not changed."
if __name__ == '__main__':
 pass_verify = obtain_password()
 val = PasswordValidator(pass_verify)
 print val.validate_all()
 if val.validated is False:
 print "Password not validated."
 else:
 print second_authentication(pass_verify)

My question is, what can I do better to make this more efficient and more accurate?

Graipher
41.6k7 gold badges70 silver badges134 bronze badges
asked Aug 30, 2016 at 20:57
\$\endgroup\$
6
  • 2
    \$\begingroup\$ How are you going to read the password back when you need it if you encrypt it with random numbers you do not store? How can a test be meaningful without RNG seeding if randomness if used within the function? \$\endgroup\$ Commented Aug 30, 2016 at 21:30
  • \$\begingroup\$ @Caridorc that's a valid point, but the object was to just encrypt it, I hadn't gone through with reading it back and storing it and everything yet. My goal was just to get the program running. \$\endgroup\$ Commented Aug 31, 2016 at 14:44
  • 1
    \$\begingroup\$ Encryption does not make sense without decription, if something you "encrypt" is not recoverable anymore, than "encryption" becomes synonim with "deletion" \$\endgroup\$ Commented Aug 31, 2016 at 14:46
  • \$\begingroup\$ @Caridorc That's a valid point, and I see what you're saying. I'm going to rewrite this and add some more features into it such as, username, password has to be changed after so many attempts, decryption, etc. Thank you \$\endgroup\$ Commented Aug 31, 2016 at 14:48
  • 1
    \$\begingroup\$ You are welcome, thank to you for using this site, I will look forward to the improved version :) \$\endgroup\$ Commented Aug 31, 2016 at 14:50

1 Answer 1

7
\$\begingroup\$

It would be more intuitive if your validate_all method was just called validate.

In quite a few places you are doing something like this in validate_length:

def validate_length(self): # Check the length, gotta be over 8
 if len(self.password) > 8:
 return True
 else:
 return False

Just return the right value right away:

def validate_length(self): # Check the length, gotta be over 8
 return len(self.password) > 8

It would also make more sense if the Validator.validate method just returned True or False instead of setting a class variable and returning a string detailing the status:

def validate(self, password):
 return all(validator(password) for validator in [self.validate_length, self.validate_upper_case, ...])

I would also make failing a testcase raise an exception and catch it in validate:

class ValidatorError(Exception):
 pass
...
class PasswordValidator(object):
 def __init__(self):
 self.validators = [self.validate_length, self.validate_upper_case, ...]
 ...
 def validate_length(self, password): 
 """Check the length, gotta be over 8"""
 if len(password) > 8:
 return True
 raise ValidatorError("Failed length validation. Allowed length: (> 8)")
 def validate(self, password):
 try:
 return all(validator(password) for validator in self.validators)
 except ValidatorError as e:
 print e
 return False
...
if __name__ == '__main__':
 pass_verify = obtain_password()
 val = PasswordValidator()
 if not val.validate(pass_verify):
 print "Password not validated."
 else:
 print second_authentication(pass_verify)

Also your encrypt function seems to be one-way. There is no way to validate an entered password in the future (to see whether the entered password is equal to the password set by this program).

answered Aug 30, 2016 at 21:33
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.