diff --git a/.github/workflows/modoboa.yml b/.github/workflows/modoboa.yml index a411935b0..459502752 100644 --- a/.github/workflows/modoboa.yml +++ b/.github/workflows/modoboa.yml @@ -29,7 +29,7 @@ jobs: environment: test services: postgres: - image: postgres:12 + image: postgres:14 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres @@ -72,7 +72,7 @@ jobs: strategy: matrix: database: ['postgres', 'mysql'] - python-version: [3.9, '3.10', '3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12', '3.13'] fail-fast: false steps: diff --git a/modoboa/admin/tests/test_password_schemes.py b/modoboa/admin/tests/test_password_schemes.py index 57411fbc7..60bc9fa22 100644 --- a/modoboa/admin/tests/test_password_schemes.py +++ b/modoboa/admin/tests/test_password_schemes.py @@ -52,8 +52,5 @@ class PasswordSchemesTestCase(ModoAPITestCase): def test_md5_scheme(self): self._test_scheme("md5", "{MD5}") - def test_crypt(self): - self._test_scheme("crypt", "{CRYPT}") - def test_plain(self): self._test_scheme("plain", "{PLAIN}") diff --git a/modoboa/core/api/v2/tests.py b/modoboa/core/api/v2/tests.py index d1fd83955..3778a0d4b 100644 --- a/modoboa/core/api/v2/tests.py +++ b/modoboa/core/api/v2/tests.py @@ -507,12 +507,6 @@ class NotificationAPITestCase(ModoAPITestCase): data = resp.json() self.assertEqual(len(data), 0) - self.set_global_parameter("password_scheme", "crypt") - resp = self.client.get(url) - self.assertEqual(resp.status_code, 200) - data = resp.json() - self.assertEqual(len(data), 1) - class AuthenticatorData(bytes): diff --git a/modoboa/core/password_hashers/__init__.py b/modoboa/core/password_hashers/__init__.py index b5c1ce167..590f25eef 100644 --- a/modoboa/core/password_hashers/__init__.py +++ b/modoboa/core/password_hashers/__init__.py @@ -9,7 +9,6 @@ from modoboa.core.password_hashers.advanced import ( # NOQA:F401 SSHAHasher, ) from modoboa.core.password_hashers.base import ( # NOQA:F401 - CRYPTHasher, MD5Hasher, PLAINHasher, SHA256Hasher, diff --git a/modoboa/core/password_hashers/base.py b/modoboa/core/password_hashers/base.py index 76314dfa4..dcba941df 100644 --- a/modoboa/core/password_hashers/base.py +++ b/modoboa/core/password_hashers/base.py @@ -5,11 +5,7 @@ Contains weak hashers (the original ones) available with Modoboa. """ import base64 - -import crypt import hashlib -from random import Random -import string from typing import Optional from django.utils.crypto import constant_time_compare @@ -53,7 +49,7 @@ class PasswordHasher(metaclass=MetaHasher): def __init__(self, target="local"): self._target = target - def _encrypt(self, clearvalue: str, salt: Optional[str] = None) -> str: + def _encrypt(self, clearvalue: str, salt: Optional[str] = None) -> str: # NOQA raise NotImplementedError def _b64encode(self, pwhash): @@ -129,26 +125,6 @@ class PLAINHasher(PasswordHasher): return clearvalue -class CRYPTHasher(PasswordHasher): - """ - crypt password hasher. - - Uses python `crypt` standard module. - """ - - _weak = True - deprecated = True - - @property - def scheme(self): - return "{CRYPT}" - - def _encrypt(self, clearvalue: str, salt: Optional[str] = None) -> str: - if salt is None: - salt = "".join(Random().sample(string.ascii_letters + string.digits, 2)) - return crypt.crypt(clearvalue, salt) - - class MD5Hasher(PasswordHasher): """ MD5 password hasher. diff --git a/modoboa/core/tests/test_authentication.py b/modoboa/core/tests/test_authentication.py index 0e45ad31d..9367458b2 100644 --- a/modoboa/core/tests/test_authentication.py +++ b/modoboa/core/tests/test_authentication.py @@ -233,19 +233,11 @@ class AuthenticationTestCase(ModoTestCase): supported_schemes = get_dovecot_schemes()[0] self.assertEqual(supported_schemes, ["{MD5-CRYPT}", "{PLAIN}"]) - def test_deprecated_password_scheme(self): - hasher = get_password_hasher("crypt") - self.assertEqual(hasher.label, "crypt (weak, deprecated)") - def test_is_password_scheme_in_use(self): hasher = get_password_hasher("plain") self.assertTrue( models.User.objects.is_password_scheme_in_use(hasher), ) - hasher = get_password_hasher("crypt") - self.assertFalse( - models.User.objects.is_password_scheme_in_use(hasher), - ) def test_fido_auth_begin(self): url = reverse("core:fido_auth_begin") diff --git a/modoboa/core/tests/test_checks.py b/modoboa/core/tests/test_checks.py index 51d56d9b5..65e1fc8c1 100644 --- a/modoboa/core/tests/test_checks.py +++ b/modoboa/core/tests/test_checks.py @@ -35,10 +35,3 @@ class CheckSessionCookieSecureTest(SimpleModoTestCase): os.environ["OIDC_RSA_PRIVATE_KEY"] = "KEY" msgs = checks.check_rsa_private_key_exists(None) self.assertEqual(len(msgs), 0) - - def test_password_hasher_checks(self): - msgs = checks.check_password_hasher(None) - self.assertEqual(msgs, []) - self.set_global_parameter("password_scheme", "crypt") - msgs = checks.check_password_hasher(None) - self.assertEqual(msgs, [checks.W002]) diff --git a/pyproject.toml b/pyproject.toml index 6cfa37a5a..8f5d719a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,26 +10,25 @@ authors = [ ] description = "Mail hosting made simple" readme = "README.rst" -requires-python = ">=3.8" +requires-python = ">=3.10" classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", - "Framework :: Django :: 4.2", + "Framework :: Django :: 5.2", "Intended Audience :: System Administrators", "License :: OSI Approved :: ISC License (ISCL)", "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", "Topic :: Communications :: Email", "Topic :: Internet :: WWW/HTTP", ] keywords = ["email"] license = {file = "LICENSE"} dependencies = [ - "django>4.0,<5.0", + "django==5.2", "django-phonenumber-field[phonenumbers]==8.1.0", "django-reversion==5.1.0", "django-xforwardedfor-middleware==2.0", @@ -60,7 +59,7 @@ dependencies = [ "ovh", "oath", "redis>=4.2.0rc1", - "rrdtool>=0.1.10", + "rrdtool-bindings>=0.2.0", "qrcode", "aiosmtplib", # PDF credentials diff --git a/tests.py b/tests.py index 5eea344df..3d10f317c 100644 --- a/tests.py +++ b/tests.py @@ -15,7 +15,7 @@ else: class DeployTest(unittest.TestCase): dbtype = DB - dbhost = "localhost" + dbhost = "127.0.0.1" dbport = PORT projname = "modoboa_test" dbuser = DB == "mysql" and "modoboa" or "postgres" @@ -25,20 +25,13 @@ class DeployTest(unittest.TestCase): self.workdir = tempfile.mkdtemp() def test_silent(self): - dburl = "default:%s://%s:%s@%s:%s/%s" % ( - self.dbtype, - self.dbuser, - self.dbpassword, - self.dbhost, - self.dbport, - "modoboa", - ) + dburl = f"default:{self.dbtype}://{self.dbuser}:{self.dbpassword}@{self.dbhost}:{self.dbport}/modoboa" cmd = ( - "modoboa-admin.py deploy --collectstatic " - "--dburl %s --domain %s --admin-username admin %s" - % (dburl, "localhost", self.projname) + f"modoboa-admin.py deploy --collectstatic " + f"--dburl {dburl} --domain localhost --admin-username admin {self.projname}" ) code, output = exec_cmd(cmd, cwd=self.workdir) + print(output) self.assertEqual(code, 0)