Solution: Random Password#

Write a function that works as a Random Strong Password Generator.

Rules#

  1. Function returns a string

  2. Length between 8 and 24 characters

  3. At least one uppercase letter (A-Z)

  4. At least one lowercase letter (a-z)

  5. At least one digit (0-9)

  6. It contains at least one special character (!@#$%^&*()+=)

  7. Maximum of 2 repeated characters (regardless of order)

  8. Always return a different password

  9. You can use any random function from the random module

Function Definition#

from random import choice, choices

def password_generator():

    def password_validator(password):
        type = isinstance(password, str)  # rule 1: type: string
        length = 8 <= len(password) <= 24  # rule 2: length: 8-24
        upper = any(char.isupper() for char in password)  # rule 3: upper-letter
        lower = any(char.islower() for char in password)  # rule 4: lower-letter
        numeric = any(char.isdigit() for char in password)  # rule 5: numeric character
        special = any(char in "!@#$%^&*()+=" for char in password)  # rule 6: special character
        repeated = all(password.count(char)<3 for char in password)  # rule 7: max 2 repeated characters
        rules = (type, length, upper, lower, numeric, special, repeated)
        return all(rules) # if any rule is false, then return false.

    # Parameters definition
    min_length = 8
    upper_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"  # or "".join([chr(i) for i in range(ord("A"), ord("Z")+1, 1)])
    lower_letters = "abcdefghijklmnopqrstuvwxyz"  # or "".join([chr(i) for i in range(ord("a"), ord("z")+1, 1)])  # or upper_letters.lower()
    numbers = "0123456789"  # or "".join([chr(i) for i in range(ord("0"), ord("9")+1, 1)])
    special_characters = "!@#$%^&*()+="
    all_characters = "".join([upper_letters, lower_letters, numbers, special_characters])

    # Base password generation
    password = "".join(choices(all_characters, k=min_length))

    # Verify if all requirements are fulfilled
    strong = password_validator(password)

    # If not: Add a new character every iteration until fulfil all requirements
    while not strong:
        password += choice(all_characters)
        strong = password_validator(password)

        # Note that after a while the validator will always fail for length and repeated
        # To avoid infinity iterations, reset password after reach 24 chr size.
        if len(password)>24:
            password = "".join(choices(all_characters, k=min_length))

    return password

Testing#

Check if your function returns the expected value using the cell below.

import unittest

class UnitTests(unittest.TestCase):
    def setUp(self):
        self.password = password_generator()
        self.passwords = [password_generator() for _ in range(10)]

    def test_password_type(self):
        self.assertTrue(isinstance(self.password, str), 'The function should return a string')
    def test_password_min_size(self):
        self.assertGreater(len(self.password), 7, "The function should return a password with minimum length of 8 characters")
    def test_password_max_size(self):
        self.assertLess(len(self.password), 25, "The function should return a password with maximum length of 24 characters")
    def test_upper(self):
        self.assertTrue(any(char.isupper() for char in self.password), "The function should return a password with at least 1 upper-case letter")
    def test_lower(self):
        self.assertTrue(any(char.islower() for char in self.password), "The function should return a password with at least 1 lower-case letter")
    def test_numeric(self):
        self.assertTrue(any(char.isdigit() for char in self.password), "The function should return a password with at least 1 number")
    def test_special(self):
        self.assertTrue(any(char in "!@#$%^&*()+=" for char in self.password), "The function should return a password with at least 1 special character")     
    def test_repeated_char(self):
        self.assertTrue(all(self.password.count(char)<3 for char in self.password), "The function should return a password with a maximum of 2 repeated character")
    def test_unique_password(self):
        self.assertEqual(len(self.passwords), len(set(self.passwords)), "The function should return an unique password")

unittest.main(argv=[''], verbosity=2,exit=False)
test_lower (__main__.UnitTests) ... 
ok
test_numeric (__main__.UnitTests) ... 
ok
test_password_max_size (__main__.UnitTests) ... 
ok
test_password_min_size (__main__.UnitTests) ... 
ok
test_password_type (__main__.UnitTests) ... 
ok
test_repeated_char (__main__.UnitTests) ... 
ok
test_special (__main__.UnitTests) ... 
ok
test_unique_password (__main__.UnitTests) ... 
ok
test_upper (__main__.UnitTests) ... 
ok

----------------------------------------------------------------------
Ran 9 tests in 0.009s

OK
<unittest.main.TestProgram at 0x7ff7208edf50>