feat: add min_len, max_len, forbidden_char for password option

This commit is contained in:
egarette@silique.fr 2024-11-05 08:55:53 +01:00
parent c3bb590415
commit a3228fbf30
9 changed files with 85 additions and 4 deletions

View file

@ -293,6 +293,9 @@ Unix options
* - PasswordOption * - PasswordOption
- Simple string with no other restriction: - Simple string with no other restriction:
- -
- min_len: minimum length autorise for a password
- max_len: maximum length autorise for a passwword
- forbidden_char: list of forbidden characters for a password
* - FilenameOption * - FilenameOption
- For this option, only lowercase and uppercas ASCII character, "-", ".", "_", "~", and "/" are allowed. - For this option, only lowercase and uppercas ASCII character, "-", ".", "_", "~", and "/" are allowed.

View file

@ -2,7 +2,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Tiramisu\n" "Project-Id-Version: Tiramisu\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-30 13:15+0100\n" "POT-Creation-Date: 2024-11-05 08:49+0100\n"
"PO-Revision-Date: \n" "PO-Revision-Date: \n"
"Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n" "Last-Translator: Emmanuel Garette <egarette@cadoles.com>\n"
"Language-Team: Tiramisu's team <egarette@cadoles.com>\n" "Language-Team: Tiramisu's team <egarette@cadoles.com>\n"
@ -762,6 +762,18 @@ msgstr "ne peut changer group_type si déjà spécifié (ancien {0}, nouveau {1}
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "group_type : {0} non autorisé" msgstr "group_type : {0} non autorisé"
#: tiramisu/option/passwordoption.py:49
msgid "at least {0} characters are required"
msgstr "au moins {0} caractères sont requis"
#: tiramisu/option/passwordoption.py:52
msgid "maximum {0} characters required"
msgstr "un maximum de {0} caractères sont autorisés"
#: tiramisu/option/passwordoption.py:57
msgid "must not have the characters {0}"
msgstr "ne doit pas contenir les caractères {0}"
#: tiramisu/option/permissionsoption.py:52 #: tiramisu/option/permissionsoption.py:52
msgid "only 3 or 4 octal digits are allowed" msgid "only 3 or 4 octal digits are allowed"
msgstr "seulement 3 ou 4 chiffres octal sont autorisées" msgstr "seulement 3 ou 4 chiffres octal sont autorisées"

View file

@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-10-30 13:15+0100\n" "POT-Creation-Date: 2024-11-05 08:52+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -668,6 +668,18 @@ msgstr ""
msgid "group_type: {0} not allowed" msgid "group_type: {0} not allowed"
msgstr "" msgstr ""
#: tiramisu/option/passwordoption.py:49
msgid "at least {0} characters are required"
msgstr ""
#: tiramisu/option/passwordoption.py:52
msgid "maximum {0} characters required"
msgstr ""
#: tiramisu/option/passwordoption.py:57
msgid "must not have the characters {0}"
msgstr ""
#: tiramisu/option/permissionsoption.py:52 #: tiramisu/option/permissionsoption.py:52
msgid "only 3 or 4 octal digits are allowed" msgid "only 3 or 4 octal digits are allowed"
msgstr "" msgstr ""

View file

@ -9,6 +9,7 @@ authors = [{name = "Emmanuel Garette", email = "gnunux@gnunux.info"}]
readme = "README.md" readme = "README.md"
description = "an options controller tool" description = "an options controller tool"
requires-python = ">=3.8" requires-python = ">=3.8"
license = {file = "LICENSE"}
classifiers = [ classifiers = [
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python", "Programming Language :: Python",

View file

@ -158,13 +158,38 @@ def test_with_many_subgroups(config_type):
def test_password_option(config_type): def test_password_option(config_type):
o = PasswordOption('o', '') o = PasswordOption('o', '')
od1 = OptionDescription('d', '', [o]) o1 = PasswordOption('o1', '', min_len=4)
o2 = PasswordOption('o2', '', max_len=4)
o3 = PasswordOption('o3', '', forbidden_char=['p'])
od1 = OptionDescription('d', '', [o, o1, o2, o3])
cfg = Config(od1) cfg = Config(od1)
cfg = get_config(cfg, config_type) cfg = get_config(cfg, config_type)
cfg.option('o').value.set('a_valid_password') cfg.option('o').value.set('a_valid_password')
with pytest.raises(ValueError): with pytest.raises(ValueError):
cfg.option('o').value.set(1) cfg.option('o').value.set(1)
#
assert cfg.option('o1').value.get() is None
with pytest.raises(ValueError):
cfg.option('o1').value.set("1")
with pytest.raises(ValueError):
cfg.option('o1').value.set("12")
with pytest.raises(ValueError):
cfg.option('o1').value.set("123")
cfg.option('o1').value.set("1234")
cfg.option('o1').value.set("12345")
#
assert cfg.option('o2').value.get() is None
with pytest.raises(ValueError):
cfg.option('o2').value.set("12345")
cfg.option('o2').value.set("1")
cfg.option('o2').value.set("12")
cfg.option('o2').value.set("123")
cfg.option('o2').value.set("1234")
#
with pytest.raises(ValueError):
cfg.option('o3').value.set("password")
cfg.option('o3').value.set("assword")
# assert not list_sessions() # assert not list_sessions()

View file

@ -21,6 +21,6 @@
from gettext import translation from gettext import translation
from pathlib import Path from pathlib import Path
t = translation('tiramisu', str(Path(__file__).parent / 'locale'), fallback=True) t = translation("tiramisu", str(Path(__file__).parent / "locale"), fallback=True)
_ = t.gettext _ = t.gettext

View file

@ -22,6 +22,7 @@
""" """
from ..i18n import _ from ..i18n import _
from ..error import display_list
from .stroption import StrOption from .stroption import StrOption
@ -30,3 +31,30 @@ class PasswordOption(StrOption):
__slots__ = tuple() __slots__ = tuple()
_type = "password" _type = "password"
def __init__(self, *args, min_len=None, max_len=None, forbidden_char=[], **kwargs):
extra = {}
if min_len is not None:
extra["min_len"] = min_len
if max_len is not None:
extra["max_len"] = max_len
if forbidden_char:
extra["forbidden_char"] = set(forbidden_char)
super().__init__(*args, extra=extra, **kwargs)
def validate(self, value: str) -> None:
super().validate(value)
min_len = self.impl_get_extra("min_len")
if min_len and len(value) < min_len:
raise ValueError(_("at least {0} characters are required").format(min_len))
max_len = self.impl_get_extra("max_len")
if max_len and len(value) > max_len:
raise ValueError(_("maximum {0} characters required").format(max_len))
if self.impl_get_extra("forbidden_char"):
forbidden_char = set(value) & self.impl_get_extra("forbidden_char")
if forbidden_char:
raise ValueError(
_("must not have the characters {0}").format(
display_list(list(forbidden_char), add_quote=True)
)
)