From dd171e45809d8fe3aeb56cd22129ebd42d47de88 Mon Sep 17 00:00:00 2001 From: Ron Date: Fri, 24 Nov 2023 14:16:32 +0100 Subject: [PATCH] v9.3.0 (#13) --- CHANGES.md | 3 ++ ambient_toolbox/__init__.py | 2 +- ambient_toolbox/validators/__init__.py | 0 .../validators/auth_password/__init__.py | 0 .../validators/auth_password/special_chars.py | 20 +++++++++++ docs/features/validators.md | 25 ++++++++++++++ docs/index.rst | 1 + settings.py | 4 +++ tests/validators/__init__.py | 0 tests/validators/auth_password/__init__.py | 0 .../auth_password/test_special_chars.py | 34 +++++++++++++++++++ 11 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 ambient_toolbox/validators/__init__.py create mode 100644 ambient_toolbox/validators/auth_password/__init__.py create mode 100644 ambient_toolbox/validators/auth_password/special_chars.py create mode 100644 docs/features/validators.md create mode 100644 tests/validators/__init__.py create mode 100644 tests/validators/auth_password/__init__.py create mode 100644 tests/validators/auth_password/test_special_chars.py diff --git a/CHANGES.md b/CHANGES.md index 016154f..d5839d9 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,8 @@ # Changelog +**9.3.0** (2023-11-21) + * Added auth password validator for special chars `SpecialCharValidator` + **9.2.1** (2023-11-21) * Fixed crash in GitLab coverage service due to `check=True` diff --git a/ambient_toolbox/__init__.py b/ambient_toolbox/__init__.py index 4c030db..d01271b 100644 --- a/ambient_toolbox/__init__.py +++ b/ambient_toolbox/__init__.py @@ -1,3 +1,3 @@ """Python toolbox of Ambient Digital containing an abundance of useful tools and gadgets.""" -__version__ = "9.2.1" +__version__ = "9.3.0" diff --git a/ambient_toolbox/validators/__init__.py b/ambient_toolbox/validators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ambient_toolbox/validators/auth_password/__init__.py b/ambient_toolbox/validators/auth_password/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ambient_toolbox/validators/auth_password/special_chars.py b/ambient_toolbox/validators/auth_password/special_chars.py new file mode 100644 index 0000000..12afb15 --- /dev/null +++ b/ambient_toolbox/validators/auth_password/special_chars.py @@ -0,0 +1,20 @@ +import re + +from django.core.exceptions import ValidationError +from django.utils.translation import gettext as _ + + +class SpecialCharValidator: + """ + The password must contain at least one special character (@#$%!^&*) + """ + + def validate(self, password, user=None): + if not re.findall("[@#$%!^&*]", password): + raise ValidationError( + _('The password has to contain one of the following special characters: "@#$%!^&*"'), + code="password_no_symbol", + ) + + def get_help_text(self): + return _('The password has to contain one of the following special characters: "@#$%!^&*"') diff --git a/docs/features/validators.md b/docs/features/validators.md new file mode 100644 index 0000000..5e0914c --- /dev/null +++ b/docs/features/validators.md @@ -0,0 +1,25 @@ +# Validators + +This package provides additional validators. In the Django ecosystem, you can have two kinds of validators: [Password +validators](https://docs.djangoproject.com/en/4.2/topics/auth/passwords/) and form validators. The first are used in the +settings to add rules for your users passwords. The latter is employed in models and forms to validate user input. + +## Auth password validators + +### Special characters required + +Adding ths validator will require your users to add at least one of the following special characters to their +password: `@#$%!^&*`. + +```python +# Django settings + +# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators +AUTH_PASSWORD_VALIDATORS = [ + ... + { + "NAME": "ambient_toolbox.validators.auth_password.SpecialCharValidator", + }, +] + +``` diff --git a/docs/index.rst b/docs/index.rst index 96b2307..7f7fcd5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,6 +32,7 @@ The package is published at pypi under the following link: `https://pypi.org/pro features/services.md features/tests.md features/utils.rst + features/validators.md features/view-layer.rst features/changelog.md diff --git a/settings.py b/settings.py index 5b55e87..4bc56e2 100644 --- a/settings.py +++ b/settings.py @@ -66,3 +66,7 @@ TIME_ZONE = "UTC" LOCALE_PATHS = [str(BASE_PATH) + "/ambient_toolbox/locale"] + +PASSWORD_HASHERS = [ + "django.contrib.auth.hashers.MD5PasswordHasher", +] diff --git a/tests/validators/__init__.py b/tests/validators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/validators/auth_password/__init__.py b/tests/validators/auth_password/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/validators/auth_password/test_special_chars.py b/tests/validators/auth_password/test_special_chars.py new file mode 100644 index 0000000..eba35bf --- /dev/null +++ b/tests/validators/auth_password/test_special_chars.py @@ -0,0 +1,34 @@ +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.test import TestCase, override_settings + +from ambient_toolbox.validators.auth_password.special_chars import SpecialCharValidator + + +class SpecialCharValidatorTest(TestCase): + def test_validate_happy_path(self): + validator = SpecialCharValidator() + self.assertIsNone(validator.validate("Admin0404!")) + + def test_validate_missing_special_char(self): + validator = SpecialCharValidator() + with self.assertRaisesMessage( + ValidationError, 'The password has to contain one of the following special characters: "@#$%!^&*"' + ): + validator.validate("EasyPassword") + + def test_get_help_text_regular(self): + validator = SpecialCharValidator() + self.assertEqual( + validator.get_help_text(), 'The password has to contain one of the following special characters: "@#$%!^&*"' + ) + + @override_settings(AUTH_PASSWORD_VALIDATORS=["ambient_toolbox.validators.SpecialCharValidator"]) + def test_functional_happy_path(self): + user = User() + self.assertIsNone(user.set_password("Admin0404!")) + + @override_settings(AUTH_PASSWORD_VALIDATORS=["ambient_toolbox.validators.auth_password.SpecialCharValidator"]) + def test_functional_special_char_missing(self): + user = User() + self.assertFalse(user.set_password("EasyPassword"))