summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
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
commitea9f65db6850600e21895603102e9a5983f2d2f6 (patch)
treea3e9ef620a1432f17503d221a0b798c3d7005c07 /ishtar_common
parentef2b079d276a2e485383ce0e1a187d882ae5c1dc (diff)
downloadIshtar-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.py15
-rw-r--r--ishtar_common/utils.py44
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(_("."))