From 4d02d9040282110cf107c038a2159aa3e2847b7c Mon Sep 17 00:00:00 2001 From: Lopez Date: Sat, 17 Feb 2024 23:45:49 +0100 Subject: [PATCH] draft of password generator --- .github/workflows/main.yml | 29 +++++++ .gitignore | 2 +- pwd_generator/__init__.py | 140 ++++++++++++++++++++++++++++++++ pwd_generator/tests/__init__.py | 74 +++++++++++++++++ setup.py | 26 ++++++ 5 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/main.yml create mode 100644 pwd_generator/__init__.py create mode 100644 pwd_generator/tests/__init__.py create mode 100644 setup.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..7f05322 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,29 @@ +name: Python Password Generator + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v1 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Test with unittest + run: | + python3 -m unittest discover . + - name: Creating package + run: | + python3 setup.py bdist_wheel diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/pwd_generator/__init__.py b/pwd_generator/__init__.py new file mode 100644 index 0000000..da5a185 --- /dev/null +++ b/pwd_generator/__init__.py @@ -0,0 +1,140 @@ +import string +import random +from functools import reduce + + +class Pwd: + LOWER_CASES = string.ascii_lowercase + UPPER_CASES = string.ascii_uppercase + LETTERS = string.ascii_letters + DIGITS = string.digits + SPECIALS = string.punctuation + + def __init__(self): + self.value = None + self.choices = [] + + def _set(self, length=None): + if length is not None and length <= 0: + raise ValueError('Password length cannot be less than or equal to 0') + if len(self.choices) == 0: + raise ValueError('You must select at least one parameter if you wish to generate a password') + + self.value = ''.join( + random.choices( + reduce(lambda x, y: x + y, self.choices), + k=length or 10 + ) + ) + return self + + +class LowerCasesPassword(Pwd): + def __init__(self): + super().__init__() + + def generate(self, length=None): + """ + Generate a password containing only lower-case letters + :param int length: optional parameter (10 by default) + """ + + self.choices = [self.LOWER_CASES] + return self._set(length=length) + + +class UpperCasesPassword(Pwd): + def __init__(self): + super().__init__() + + def generate(self, length=None): + """ + Generate a password containing only upper-case letters + :param int length: optional parameter (10 by default) + """ + + self.choices = [self.UPPER_CASES] + return self._set(length=length) + + +class LettersPassword(Pwd): + def __init__(self): + super().__init__() + + def generate(self, length=None): + """ + Generate a letter-only password + :param int length: optional parameter (10 by default) + """ + + self.choices = [self.LETTERS] + return self._set(length=length) + + +class DigitsPassword(Pwd): + def __init__(self): + super().__init__() + + def generate(self, length=None): + """ + Generate a number-only password + :param int length: optional parameter (10 by default) + """ + + self.choices = [self.DIGITS] + return self._set(length=length) + + +class SpecialsPassword(Pwd): + def __init__(self): + super().__init__() + + def generate(self, length=None): + """ + Generate a password containing only special characters + :param int length: optional parameter (10 by default) + """ + + self.choices = [self.SPECIALS] + return self._set(length=length) + + +class Password(Pwd): + def __init__(self): + super().__init__() + + def generate( + self, + length=None, + lower_cases=True, + upper_cases=True, + digits=True, + specials=True + ): + """ + Generate a password containing only special characters + :param int length: optional parameter (10 by default) + :param bool lower_cases: optional parameter (True by default) + :param bool upper_cases: optional parameter (True by default) + :param bool digits: optional parameter (True by default) + :param bool specials: optional parameter (True by default) + """ + + choices = [ + self.LOWER_CASES, + self.UPPER_CASES, + self.DIGITS, + self.SPECIALS + ] + + if not lower_cases: + choices.remove(self.LOWER_CASES) + if not upper_cases: + choices.remove(self.UPPER_CASES) + if not digits: + choices.remove(self.DIGITS) + if not specials: + choices.remove(self.SPECIALS) + + self.choices = choices + return self._set(length=length) diff --git a/pwd_generator/tests/__init__.py b/pwd_generator/tests/__init__.py new file mode 100644 index 0000000..82bb0e7 --- /dev/null +++ b/pwd_generator/tests/__init__.py @@ -0,0 +1,74 @@ +import unittest + +from pwd_generator import ( + LowerCasesPassword, + UpperCasesPassword, + LettersPassword, + DigitsPassword, + SpecialsPassword, + Password +) + + +class GeneratePasswordTestCase(unittest.TestCase): + def test_generate_lower_cases_password(self): + password = LowerCasesPassword() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate(length=20) + self.assertEqual(len(password.value), 20) + self.assertEqual(password.value.islower(), True) + + def test_generate_upper_cases_password(self): + password = UpperCasesPassword() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate(length=12) + self.assertEqual(len(password.value), 12) + self.assertEqual(password.value.isupper(), True) + + def test_generate_letters_password(self): + password = LettersPassword() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate(length=15) + self.assertEqual(len(password.value), 15) + + def test_generate_digits_password(self): + password = DigitsPassword() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate(length=17) + self.assertEqual(len(password.value), 17) + self.assertEqual(password.value.isnumeric(), True) + + def test_generate_specials_password(self): + password = SpecialsPassword() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate(length=19) + self.assertEqual(len(password.value), 19) + + def test_generate_custom_password(self): + password = Password() + + password.generate() + self.assertEqual(len(password.value), 10) + + password.generate( + length=20, + specials=False, + digits=True, + lower_cases=False + ) + self.assertEqual(len(password.value), 20) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e5ac997 --- /dev/null +++ b/setup.py @@ -0,0 +1,26 @@ +from pathlib import Path + +import setuptools +from pkg_resources import parse_requirements + +with open("README.md", "r") as fh: + long_description = fh.read() + +setuptools.setup( + name="pwd_generator", + version='0.0.1', + author="RĂ©mi Lopez", + author_email="contact.remilopez@gmail.com", + description="An open-source password generator", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/pwd-generator", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + ], + python_requires='>=3.8', + include_package_data=True, +)