From 1c981f389adcf7006d9bcd99754f0ec2b7d2f767 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:06:35 +0200 Subject: [PATCH 01/14] Added checks for Dovecot and Radicale oauth2 app --- modoboa/core/checks.py | 48 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/modoboa/core/checks.py b/modoboa/core/checks.py index ca61a40af..f8ea41d4b 100644 --- a/modoboa/core/checks.py +++ b/modoboa/core/checks.py @@ -1,8 +1,10 @@ from django.db.utils import ProgrammingError, OperationalError -from django.core.checks import register, Info, Warning +from django.core.checks import register, Info, Warning, Error from django.conf import settings from django.utils.translation import gettext as _ +from oauth2_provider.models import get_application_model + from modoboa.core.utils import ( check_for_deprecated_password_schemes, generate_rsa_private_key, @@ -15,14 +17,38 @@ W001 = Warning( "twice due to clock change)." ), hint=_("Set `USE_TZ = True` in settings.py"), - id="modoboa.W001", + id="modoboa.core.W001", ) W002 = Warning( _("The password scheme you are using is deprecated."), hint=_("Upgrade your password scheme to a stronger one"), - id="modoboa.W002", + id="modoboa.core.W002", ) +E001 = Error( + _( + "Oauth2 application for dovecot has not been found." + " Webmail and some other features won't be available." + ), + hint=_( + "Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/dovecot.html#dovecot-oauth2" + ), + id="modoboa.core.E001", +) + +E002 = Error( + _( + "Oauth2 application for radicale has not been found." + " Calendar and some other features won't be available." + ), + id="modoboa.core.E002", +) + + +def oauth_app_exists(app_name): + oauth_application = get_application_model().objects.filter(name=app_name) + return oauth_application.exists() + @register(deploy=True) def check_use_tz_enabled(app_configs, **kwargs): @@ -59,3 +85,19 @@ def check_password_hasher(app_configs, **kwargs): if hasher: msgs.append(W002) return msgs + + +@register(deploy=True) +def check_dovecot_oauth_app(*args, **kwargs): + error = [] + if not oauth_app_exists("Dovecot"): + error.append(E001) + return error + + +@register(deploy=True) +def check_radicale_oauth_app(*args, **kwargs): + error = [] + if not oauth_app_exists("Radicale"): + error.append(E002) + return error From b933407d16c1122bf3be4b1086128f4cfb006d11 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:06:53 +0200 Subject: [PATCH 02/14] Force drf to be compatible with django 5.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7b37e13e4..fb2af69d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ "django-rename-app==0.1.7", "django-environ", "dj-database-url", - "djangorestframework>=3.14,<3.17", + "djangorestframework>=3.16,<3.17", "drf-spectacular", "uritemplate", "django-oauth-toolkit", From 537ef287126f810f0f6dd02a1d9ae8f51a6bfc6e Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:07:05 +0200 Subject: [PATCH 03/14] Updated doc --- doc/upgrade.rst | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/doc/upgrade.rst b/doc/upgrade.rst index 9adc118f7..17e21c6f1 100644 --- a/doc/upgrade.rst +++ b/doc/upgrade.rst @@ -85,24 +85,6 @@ If some error appears, you may try this: > git reset --hard origin/master - -New-admin interface -******************* - -.. note:: - You don't need to perform these actions if you use the installer to upgrade your instance. - -New admin interface won't update by itself. Run the following commands to update it: - -.. sourcecode:: bash - - > sudo -u -i bash - > cd - > cp frontend/config.json ./ - > rm -rf frontend - > cp -R /lib/pythonX.X/site-packages/modoboa/frontend_dist/ frontend/ - > cp ./config.json frontend/config.json - Extensions ********** @@ -195,8 +177,9 @@ Required changes to :file:`settings.py` - ``modoboa_contacts`` -> ``modoboa.contacts`` - ``modoboa_radicale`` -> ``modoboa.calendars`` - ``modoboa_webmail`` -> ``modoboa.webmail`` + - ``modoboa_rspamd`` -> ``modoboa.rspamd`` -- Remove configuration blocks related to ``modoboa_contacts`` and ``modoboa_radicale`` plugins +- Remove configuration blocks related to ``modoboa_contacts`` and ``modoboa_radicale`` plugins (these should be at the end of the file) - Remove ``AjaxLoginRedirect`` from ``MIDDLEWARE`` variable: @@ -217,11 +200,11 @@ Required changes to :file:`settings.py` 'modoboa.lib.middleware.RequestCatcherMiddleware', ) -- Remove ``NEW_ADMIN_URL`` variable +- Remove ``NEW_ADMIN_URL`` variable if found - Remove ``new_admin_url`` and ``top_notifications`` context processors -- Remove ``ckeditor`` related apps from ``INSTALLED_APPS`` variable +- Remove ``ckeditor`` related apps from ``INSTALLED_APPS`` variable and ``CKEDITOR_*`` settings - Remove ``STATICFILE_DIRS`` variable From e861807bdc05b73e4172ecc494342b412a05358c Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:26:54 +0200 Subject: [PATCH 04/14] Added tests for new checks --- modoboa/core/tests/test_checks.py | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/modoboa/core/tests/test_checks.py b/modoboa/core/tests/test_checks.py index 65e1fc8c1..31f2a5a59 100644 --- a/modoboa/core/tests/test_checks.py +++ b/modoboa/core/tests/test_checks.py @@ -1,15 +1,18 @@ import os import shutil import tempfile +import uuid +from django.core.management import call_command from django.test.utils import override_settings +from oauth2_provider.models import get_application_model + from modoboa.core import checks from modoboa.lib.tests import SimpleModoTestCase class CheckSessionCookieSecureTest(SimpleModoTestCase): - def setUp(self): self.workdir = tempfile.mkdtemp() @@ -35,3 +38,37 @@ 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_dovecot_oauth_app_exists(self): + get_application_model().objects.filter(name="Dovecot").delete() + msgs = checks.check_dovecot_oauth_app() + self.assertEqual(len(msgs), 1) + client_secret = str(uuid.uuid4()) + call_command( + "createapplication", + "--name=Dovecot", + "--skip-authorization", + "--client-id=dovecot", + f"--client-secret={client_secret}", + "confidential", + "client-credentials", + ) + msgs = checks.check_dovecot_oauth_app() + self.assertEqual(len(msgs), 0) + + def test_redicale_oauth_app_exists(self): + get_application_model().objects.filter(name="Radicale").delete() + msgs = checks.check_dovecot_oauth_app() + self.assertEqual(len(msgs), 1) + client_secret = str(uuid.uuid4()) + call_command( + "createapplication", + "--name=Radicale", + "--skip-authorization", + "--client-id=radicale", + f"--client-secret={client_secret}", + "confidential", + "client-credentials", + ) + msgs = checks.check_dovecot_oauth_app() + self.assertEqual(len(msgs), 0) From 7ade9bc3a206b7a5b971ea315573bc9b6fd05875 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 17:25:28 +0200 Subject: [PATCH 05/14] Updated favicon --- modoboa/static/css/favicon.ico | Bin 0 -> 10127 bytes modoboa/static/css/logo-icon.png | Bin 550 -> 0 bytes modoboa/templates/layout_simple.html | 2 +- modoboa/templates/registration/base.html | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 modoboa/static/css/favicon.ico delete mode 100644 modoboa/static/css/logo-icon.png diff --git a/modoboa/static/css/favicon.ico b/modoboa/static/css/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..985be6b035613a73a23e7c8fbe7f5ded40f8d7fd GIT binary patch literal 10127 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 ZJBglrZg&ir=q+`ChKjCo-80*${{W0w6BGad literal 0 HcmV?d00001 diff --git a/modoboa/static/css/logo-icon.png b/modoboa/static/css/logo-icon.png deleted file mode 100644 index 859251fe0fcdbdf20de5040a802825ce977c1a24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 550 zcmV+>0@?kEP)R4zO-SE_shK7L#K^bOYL`2|4i#%^^+d&Zo zk&sSy=pcw4>ZZxwY}*bA=G8Pha*v|J@tT}?%W zfngW`WV2bOr`~d}ruX2F47l3KjU|BXjcOQ7B-6|9cn5Kyc>qH_^Pxk?Y5HvP6 z@NQ%F=Lf9UupKV(UBp_bq(9L5mb75XrJ44nVXyC#?3BIa!Q*#eDs))^YbWW z`NsDFJ}oTJ+SbPAhK=hul>Tj?q2ZJq>dv3z`|8@kUyxGLkZ9U&=waVr86b=ZH79G4 oQU(Hs$Hqtd{^O2@kK`J+0Q~)|WKOu)r~m)}07*qoM6N<$g3M6(EC2ui diff --git a/modoboa/templates/layout_simple.html b/modoboa/templates/layout_simple.html index f0378811e..4a9ebc701 100644 --- a/modoboa/templates/layout_simple.html +++ b/modoboa/templates/layout_simple.html @@ -6,7 +6,7 @@ {% block pagetitle %}{% endblock %} - + diff --git a/modoboa/templates/registration/base.html b/modoboa/templates/registration/base.html index 12feed341..bc41ca789 100644 --- a/modoboa/templates/registration/base.html +++ b/modoboa/templates/registration/base.html @@ -6,7 +6,7 @@ {% trans "Welcome to Modoboa" %} - + {% block extra_css %}{% endblock %} From f49ce46ec304a7a2e2bf98e8d329548c13c0d7f9 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:41:25 +0200 Subject: [PATCH 06/14] Fixed test --- modoboa/core/tests/test_checks.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modoboa/core/tests/test_checks.py b/modoboa/core/tests/test_checks.py index 31f2a5a59..d2fe5df1e 100644 --- a/modoboa/core/tests/test_checks.py +++ b/modoboa/core/tests/test_checks.py @@ -56,9 +56,9 @@ class CheckSessionCookieSecureTest(SimpleModoTestCase): msgs = checks.check_dovecot_oauth_app() self.assertEqual(len(msgs), 0) - def test_redicale_oauth_app_exists(self): + def test_radicale_oauth_app_exists(self): get_application_model().objects.filter(name="Radicale").delete() - msgs = checks.check_dovecot_oauth_app() + msgs = checks.check_radicale_oauth_app() self.assertEqual(len(msgs), 1) client_secret = str(uuid.uuid4()) call_command( @@ -70,5 +70,5 @@ class CheckSessionCookieSecureTest(SimpleModoTestCase): "confidential", "client-credentials", ) - msgs = checks.check_dovecot_oauth_app() + msgs = checks.check_radicale_oauth_app() self.assertEqual(len(msgs), 0) From c50915d43ccc139e0bb126f74ab91b4d038df541 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 22:12:37 +0200 Subject: [PATCH 07/14] Start of support for multiple allowed_hosts --- .../management/commands/load_initial_data.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index deda33932..351cb7179 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -106,15 +106,17 @@ class Command(BaseCommand): app_model = get_application_model() allowed_host = getattr(settings, "ALLOWED_HOSTS", None) - if allowed_host is not None: - allowed_host = allowed_host[0] - else: + if allowed_host is None: allowed_host = input("What will be the hostname used to access Modoboa? ") if not allowed_host: allowed_host = "localhost" frontend_application = app_model.objects.filter(name="modoboa_frontend") - base_uri = f"https://{allowed_host}" - redirect_uri = f"{base_uri}/login/logged" + # TODO : improve support for multiple allowed_host for frontend + base_uris_list = [f"https://{host}" for host in allowed_host] + base_uris = " ".join(base_uris_list) + base_uri = base_uris_list[0] + redirect_uris = " ".join([f"{uri}/login/logged" for uri in base_uris_list]) + redirect_uri = redirect_uris.split(" ")[0] client_id = "" if not frontend_application.exists(): if options["dev"]: @@ -126,18 +128,18 @@ class Command(BaseCommand): call_command( "createapplication", "--algorithm=RS256", - f"--redirect-uris={redirect_uri}", + f"--redirect-uris={redirect_uris}", "--name=modoboa_frontend", f"--client-id={client_id}", - f"--post-logout-redirect-uris={base_uri}", + f"--post-logout-redirect-uris={base_uris}", "--skip-authorization", "public", "authorization-code", ) else: app = frontend_application.first() - app.redirect_uris = redirect_uri - app.post_logout_redirect_urls = base_uri + app.redirect_uris = redirect_uris + app.post_logout_redirect_urls = base_uris app.save() client_id = app.client_id @@ -150,9 +152,11 @@ class Command(BaseCommand): os.makedirs(frontend_target_dir, exist_ok=True) for entry in os.scandir(base_frontend_dir): if entry.name != "config.json": - os.symlink(f"{base_frontend_dir}/{entry.name}", - f"{frontend_target_dir}/{entry.name}", - target_is_directory=entry.is_dir()) + os.symlink( + f"{base_frontend_dir}/{entry.name}", + f"{frontend_target_dir}/{entry.name}", + target_is_directory=entry.is_dir(), + ) with open(f"{frontend_target_dir}/config.json", "w") as fp: fp.write( f"""{{ From 6514c060f31ce2e0fe59bdd57a707271670ae2f2 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 22:18:27 +0200 Subject: [PATCH 08/14] Fixed post logout redirect not updating Of course it was a typo --- modoboa/core/management/commands/load_initial_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index 351cb7179..1aeb55b2a 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -139,7 +139,7 @@ class Command(BaseCommand): else: app = frontend_application.first() app.redirect_uris = redirect_uris - app.post_logout_redirect_urls = base_uris + app.post_logout_redirect_uris = base_uris app.save() client_id = app.client_id From bf5f9840be96c46fa923e28af4718bc6e0d150d5 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Fri, 18 Jul 2025 09:58:49 +0200 Subject: [PATCH 09/14] Added some instructions for uwsgi --- doc/upgrade.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/upgrade.rst b/doc/upgrade.rst index 17e21c6f1..8f79e2a2f 100644 --- a/doc/upgrade.rst +++ b/doc/upgrade.rst @@ -219,6 +219,8 @@ Post update - Remove ``autoreply`` service definition from postfix`s :file:`master.cf` file if you were using it +- Make sure :file:`/etc/uwsgi/sites-enabled/` contains `max-requests=5000` and that `process` is superior or equal to 4. Don't forget to restart uwsgi service after the configuration change. + - Then proceed with regular upgrade instructions Version 2.3.3 From aaee15bb1b58661d1a538cc6ad0485b641f1ee0f Mon Sep 17 00:00:00 2001 From: Adrien P Date: Thu, 17 Jul 2025 18:40:36 +0200 Subject: [PATCH 10/14] Added oauth 2 generation explaination for radicale --- doc/installation.rst | 1 + doc/manual_installation/radicale.rst | 38 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 doc/manual_installation/radicale.rst diff --git a/doc/installation.rst b/doc/installation.rst index 15b07506e..aa8a3c877 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -72,6 +72,7 @@ specific part, here are the steps you must follow: manual_installation/dovecot manual_installation/postfix manual_installation/opendkim + manual_installation/radicale Extensions ---------- diff --git a/doc/manual_installation/radicale.rst b/doc/manual_installation/radicale.rst new file mode 100644 index 000000000..abe6191fc --- /dev/null +++ b/doc/manual_installation/radicale.rst @@ -0,0 +1,38 @@ +.. _radicale: + +####### +Radicale +####### + +This page is currently being written. + +.. _radicale_oauth2: + +OAuth 2 authentication +====================== + +You first need to register Radicale as an authorized consumer of the +OAuth2 authentication service provided by Modoboa. To do so, create an +application with the following commands: + +.. sourcecode:: bash + + > cd + > python manage.py createapplication --name=Radicale --skip-authorization --client-id=radicale confidential client-credentials + +On success, you should see an output similar to:: + + New application Radicale created successfully. + client_secret: XXXX + +To enable OAuth2 authentication in Radicale, edit the :file:`/etc/radicale/config` +file and update the `type` in `[auth]` to:: + + type = radicale_modoboa_auth_oauth2 + +Then, add the following line in `[auth]`:: + + oauth2_introspection_endpoint = https://radical:@/api/o/introspect/ + + +Replace ```` with the value you obtained earlier. From 6b84f03e24aded7fef185010982d7ad4cd71d62d Mon Sep 17 00:00:00 2001 From: Adrien P Date: Fri, 18 Jul 2025 10:04:54 +0200 Subject: [PATCH 11/14] Added radicale link --- doc/upgrade.rst | 6 ++++++ modoboa/core/checks.py | 2 ++ 2 files changed, 8 insertions(+) diff --git a/doc/upgrade.rst b/doc/upgrade.rst index 8f79e2a2f..a73fbeb03 100644 --- a/doc/upgrade.rst +++ b/doc/upgrade.rst @@ -208,6 +208,12 @@ Required changes to :file:`settings.py` - Remove ``STATICFILE_DIRS`` variable +Radicale configuration changes +----------------------------- + +You must enable oauth2 authentication in Radicale to make the calendard +and contact work. Follow this :ref:`guide `. + Post update ----------- diff --git a/modoboa/core/checks.py b/modoboa/core/checks.py index f8ea41d4b..81673e6ff 100644 --- a/modoboa/core/checks.py +++ b/modoboa/core/checks.py @@ -41,6 +41,8 @@ E002 = Error( "Oauth2 application for radicale has not been found." " Calendar and some other features won't be available." ), + hint=_( + "Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-oauth2") id="modoboa.core.E002", ) From bf14603381cb3647f26d7b6c45539bc1039e6001 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Fri, 18 Jul 2025 10:07:21 +0200 Subject: [PATCH 12/14] allowed_host should be a list --- modoboa/core/management/commands/load_initial_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/modoboa/core/management/commands/load_initial_data.py b/modoboa/core/management/commands/load_initial_data.py index 1aeb55b2a..be4e73a82 100644 --- a/modoboa/core/management/commands/load_initial_data.py +++ b/modoboa/core/management/commands/load_initial_data.py @@ -110,6 +110,7 @@ class Command(BaseCommand): allowed_host = input("What will be the hostname used to access Modoboa? ") if not allowed_host: allowed_host = "localhost" + allowed_host = [allowed_host] frontend_application = app_model.objects.filter(name="modoboa_frontend") # TODO : improve support for multiple allowed_host for frontend base_uris_list = [f"https://{host}" for host in allowed_host] From f00bcd1c9063609ce8d067f08e85ca553ca0f649 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Fri, 18 Jul 2025 10:11:06 +0200 Subject: [PATCH 13/14] Fixed typo --- modoboa/core/checks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modoboa/core/checks.py b/modoboa/core/checks.py index 81673e6ff..c12101719 100644 --- a/modoboa/core/checks.py +++ b/modoboa/core/checks.py @@ -42,7 +42,8 @@ E002 = Error( " Calendar and some other features won't be available." ), hint=_( - "Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-oauth2") + "Visit : https://modoboa.readthedocs.io/en/latest/manual_installation/radicale.html#radicale-oauth2" + ), id="modoboa.core.E002", ) From 3c0388fe7d3be62fe1a0f5e6fa5564d33d52cc85 Mon Sep 17 00:00:00 2001 From: Adrien P Date: Fri, 18 Jul 2025 12:04:45 +0200 Subject: [PATCH 14/14] Updated explaination for radicale --- doc/manual_installation/radicale.rst | 19 +++++++++++++++++++ doc/upgrade.rst | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/doc/manual_installation/radicale.rst b/doc/manual_installation/radicale.rst index abe6191fc..cc53aa94f 100644 --- a/doc/manual_installation/radicale.rst +++ b/doc/manual_installation/radicale.rst @@ -11,6 +11,25 @@ This page is currently being written. OAuth 2 authentication ====================== +.. note:: + + These steps are automatic if you use an up-to-date modoboa-installer to install/upgrade. + +Installing radicale oauth plugin +-------------------------------- + +You need to install ``radicale-modoboa-auth-oauth2`` python package inside radicale venv. +If you used the installer you can use the following commands: + +.. sourcecode:: bash + + > sudo -u radicale -i bash + > cd ~ + > env/bin/pip install radicale-modoboa-auth-oauth2 + +Oauth application creation +-------------------------- + You first need to register Radicale as an authorized consumer of the OAuth2 authentication service provided by Modoboa. To do so, create an application with the following commands: diff --git a/doc/upgrade.rst b/doc/upgrade.rst index a73fbeb03..7b9009e3c 100644 --- a/doc/upgrade.rst +++ b/doc/upgrade.rst @@ -211,8 +211,8 @@ Required changes to :file:`settings.py` Radicale configuration changes ----------------------------- -You must enable oauth2 authentication in Radicale to make the calendard -and contact work. Follow this :ref:`guide `. +You must enable oauth2 authentication in Radicale to make the calendar +and contact feature work. Follow this :ref:`guide `. Post update -----------