diff options
author | Étienne Loks <etienne.loks@iggdrasil.net> | 2023-04-05 12:33:41 +0200 |
---|---|---|
committer | Étienne Loks <etienne.loks@iggdrasil.net> | 2023-04-05 12:33:41 +0200 |
commit | ea9f65db6850600e21895603102e9a5983f2d2f6 (patch) | |
tree | a3e9ef620a1432f17503d221a0b798c3d7005c07 /ishtar_common | |
parent | ef2b079d276a2e485383ce0e1a187d882ae5c1dc (diff) | |
download | Ishtar-ea9f65db6850600e21895603102e9a5983f2d2f6.tar.bz2 Ishtar-ea9f65db6850600e21895603102e9a5983f2d2f6.zip |
Manage strong password policy (ISHTAR_STRONG_PASSWORD_POLICY) with "Each character type" validator
Diffstat (limited to 'ishtar_common')
-rw-r--r-- | ishtar_common/tests.py | 15 | ||||
-rw-r--r-- | ishtar_common/utils.py | 44 |
2 files changed, 57 insertions, 2 deletions
diff --git a/ishtar_common/tests.py b/ishtar_common/tests.py index e7e665a6f..774fab2a2 100644 --- a/ishtar_common/tests.py +++ b/ishtar_common/tests.py @@ -93,6 +93,7 @@ from ishtar_common.utils import ( rename_and_simplify_media_name, try_fix_file, reverse_coordinates, + EachCharacterTypeValidator, ) from ishtar_common.tasks import launch_export from ishtar_common import utils_secretary @@ -4040,3 +4041,17 @@ class TemplateGenerationTest(TestCase): filtr = doc.get_filter(template, filter_re) for key in expected_keys: self.assertIn(key, filtr) + + +class PasswordValidatorTest(TestCase): + def test_eachcharactertypevalidator(self): + validator = EachCharacterTypeValidator() + self.assertRaises(ValidationError, validator.validate, "") + self.assertRaises(ValidationError, validator.validate, "1") + self.assertRaises(ValidationError, validator.validate, "1a") + self.assertRaises(ValidationError, validator.validate, "1aA") + try: + validator.validate("1aA.") + except ValidationError: + self.fail("Each character class is put, ValidationError should " + "not raise.") diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py index 65b937339..12ab2e646 100644 --- a/ishtar_common/utils.py +++ b/ishtar_common/utils.py @@ -34,6 +34,7 @@ import re import requests from secretary import Renderer as MainSecretaryRenderer, UndefinedSilently import shutil +import string import subprocess import sys import tempfile @@ -51,7 +52,8 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.gis.geos import GEOSGeometry from django.contrib.sessions.backends.db import SessionStore from django.core.cache import cache -from django.core.exceptions import SuspiciousOperation, ObjectDoesNotExist +from django.core.exceptions import SuspiciousOperation, ObjectDoesNotExist, \ + ValidationError from django.core.files import File from django.core.files.storage import FileSystemStorage from django.core.validators import EMPTY_VALUES @@ -2249,4 +2251,42 @@ def reverse_coordinates(wkt): def reverse_list_coordinates(lst): - return list(reversed(lst))
\ No newline at end of file + return list(reversed(lst)) + + +class EachCharacterTypeValidator: + def __init__(self, character_types=None): + if not character_types: + character_types = ( + (_("uppercase letter"), string.ascii_uppercase), + (_("lowercase letter"), string.ascii_lowercase), + (_("number"), [str(i) for i in range(10)]), + (_("punctuation sign"), string.punctuation) + ) + self.character_types = character_types + + def validate(self, password, user=None): + ok = set() + for letter in password: + for idx, character_type in enumerate(self.character_types): + __, character_type = character_type + if idx in ok: + continue + if letter in character_type: + ok.add(idx) + missing = [ + str(character_type[0]) + for idx, character_type in enumerate(self.character_types) + if idx not in ok] + if not missing: + return + msg = str(_("This password must contain one of this character: ")) + ", ".join( + missing) + str(_(".")) + raise ValidationError(msg, code='missing_character_types') + + def get_help_text(self): + return str( + _("Your password must contain each of these characters: ") + ) + ", ".join( + [str(character_type[0]) for character_type in self.character_types] + ) + str(_(".")) |