Merge pull request #3275 from modoboa/fix/beta3_review

Fixes after review of beta3
This commit is contained in:
Antoine Nguyen
2024-07-19 10:43:27 +02:00
committed by GitHub
7 changed files with 116 additions and 51 deletions
+26 -17
View File
@@ -28,6 +28,13 @@ use them:
> sudo -u <modoboa_user> -i bash
> source <virtuenv_path>/bin/activate
.. _post_upgrade_commands:
.. note::
Check the :ref:`specific_upgrade_instructions` before running the following commands.
Then, run the following commands:
.. sourcecode:: bash
@@ -37,6 +44,7 @@ Then, run the following commands:
> python manage.py migrate
> python manage.py collectstatic
> python manage.py check --deploy
> python manage.py load_initial_data
Once done, check if the version you are installing requires
:ref:`specific_upgrade_instructions`.
@@ -146,8 +154,8 @@ Version 2.3.0
Pre update
----------
Before your start upgrading modoboa, run the following commands from
@TODO : Update this with SQL changes, clarify "before you start upgrading modoboa"
Before you start upgrading modoboa, run the following commands from
your virtual environment:
.. sourcecode:: bash
@@ -259,7 +267,21 @@ by default.
'modoboa.lib.middleware.RequestCatcherMiddleware',
)
- Add the following content after ``MEDIA_ROOT``:
- Add ``'oauth2_provider.contrib.rest_framework.OAuth2Authentication'``
at the top of the list of ``DEFAULT_AUTHENTICATION_CLASSES``
(inside ``REST_FRAMEWORK`` section):
.. sourcecode:: python
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
- Run the commands indicated :ref:`in the first section <post_upgrade_commands>`
then add the following content after ``MEDIA_ROOT``:
.. sourcecode:: python
@@ -282,19 +304,6 @@ by default.
# If CORS fail, you might want to try to set it to True
#CORS_ORIGIN_ALLOW_ALL = False
- Add ``'oauth2_provider.contrib.rest_framework.OAuth2Authentication'``
at the top of the list of ``DEFAULT_AUTHENTICATION_CLASSES``
(inside ``REST_FRAMEWORK`` section):
.. sourcecode:: python
'DEFAULT_AUTHENTICATION_CLASSES': (
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
SORBS DNS
---------
@@ -983,7 +992,7 @@ This release introduces an optional LDAP synchronization process. If
you want to use it, please follow the :ref:`dedicated procedure <ldap_sync>`.
Version 1.13.1
=======
==============
Upgrade postfix maps files as follows:
@@ -1,7 +1,10 @@
from django.core.checks import register, Info, Warning
from django.conf import settings
from django.core.checks import Warning, register
from django.utils.translation import gettext as _
from modoboa.core.utils import generate_rsa_private_key
W001 = Warning(
_(
"You have USE_TZ set to False, this may result in issues during "
@@ -24,3 +27,14 @@ def check_use_tz_enabled(app_configs, **kwargs):
if not settings.USE_TZ:
errors.append(W001)
return errors
@register(deploy=True)
def check_rsa_private_key_exists(app_configs, **kwargs):
"""
Ensure an RSA private key exists to enable OIDC.
"""
msgs = []
if generate_rsa_private_key(settings.BASE_DIR):
msgs.append(Info("An RSA private key has been generated for OIDC."))
return msgs
-2
View File
@@ -1,2 +0,0 @@
# Import these to force registration of checks
from . import settings_checks # NOQA:F401
+2 -19
View File
@@ -9,8 +9,6 @@ import string
import subprocess
import sys
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
import dj_database_url
import django
@@ -19,6 +17,7 @@ from django.template import Context, Template
from django.utils.encoding import smart_str
from modoboa.core.commands import Command
from modoboa.core.utils import generate_rsa_private_key
from modoboa.lib.api_client import ModoAPIClient
from modoboa.lib.sysutils import exec_cmd
@@ -199,22 +198,6 @@ class DeployCommand(Command):
extra_settings.append(extension[1])
return extra_settings
def generate_rsa_private_key(self, storage_path: str):
"""Generate RSA private key for OIDC support."""
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
)
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
content = bytes(pem)
content = content.replace(b"\n", b"\\n")
with open(f"{storage_path}/.env", "wb") as fp:
fp.write(b'OIDC_RSA_PRIVATE_KEY="' + content + b'"\n')
def handle(self, parsed_args):
django.setup()
management.call_command("startproject", parsed_args.name, verbosity=False)
@@ -285,7 +268,7 @@ class DeployCommand(Command):
)
with open("%s/settings.py" % path, "w") as fp:
fp.write(tpl)
self.generate_rsa_private_key(parsed_args.name)
generate_rsa_private_key(parsed_args.name)
shutil.copyfile("%s/urls.py.tpl" % self._templates_dir, "%s/urls.py" % path)
os.mkdir("%s/media" % parsed_args.name)
@@ -112,12 +112,17 @@ class Command(BaseCommand):
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")
frontend_application_qs = app_model.objects.all()
frontend_application = None
for app in frontend_application_qs:
if app.name == "Modoboa frontend":
frontend_application = app
break
frontend_path = getattr(settings, "NEW_ADMIN_URL", "new-admin")
base_uri = f"https://{allowed_host}/{frontend_path}"
redirect_uri = f"{base_uri}/login/logged"
client_id = ""
if not frontend_application.exists():
if frontend_application is None:
if options["dev"]:
base_uri = "https://localhost:3000/"
redirect_uri = "https://localhost:3000/login/logged"
@@ -136,13 +141,14 @@ class Command(BaseCommand):
"authorization-code",
)
else:
client_id = frontend_application.first().client_id
client_id = frontend_application.client_id
base_frontend_dir = os.path.join(
os.path.dirname(__file__), "../../frontend_dist/"
os.path.dirname(__file__), "../../../frontend_dist/"
)
frontend_target_dir = "{}/frontend".format(options["name"])
frontend_target_dir = "{}/frontend".format(settings.BASE_DIR)
if os.path.exists(base_frontend_dir):
shutil.rmtree(frontend_target_dir, ignore_errors=True)
shutil.copytree(base_frontend_dir, frontend_target_dir)
with open(f"{frontend_target_dir}/config.json", "w") as fp:
fp.write(
@@ -150,7 +156,7 @@ class Command(BaseCommand):
"API_BASE_URL": "https://{allowed_host}/api/v2",
"OAUTH_AUTHORITY_URL": "https://{allowed_host}/api/o",
"OAUTH_CLIENT_ID": "{client_id}",
"OAUTH_REDIRECT_URI": "{redirect_uri}"
"OAUTH_REDIRECT_URI": "{redirect_uri}",
"OAUTH_POST_REDIRECT_URI": "{base_uri}"
}}
"""
+24 -5
View File
@@ -1,18 +1,37 @@
import os
import shutil
import tempfile
from django.test import SimpleTestCase
from django.test.utils import override_settings
from ..checks import settings_checks
from modoboa.core import checks
class CheckSessionCookieSecureTest(SimpleTestCase):
def setUp(self):
self.workdir = tempfile.mkdtemp()
def tearDown(self):
shutil.rmtree(self.workdir)
@override_settings(USE_TZ=False)
def test_use_tz_false(self):
"""If USE_TZ is off provide one warning."""
self.assertEqual(
settings_checks.check_use_tz_enabled(None), [settings_checks.W001]
)
self.assertEqual(checks.check_use_tz_enabled(None), [checks.W001])
@override_settings(USE_TZ=True)
def test_use_tz_true(self):
"""If USE_TZ is on, there's no warning about it."""
self.assertEqual(settings_checks.check_use_tz_enabled(None), [])
self.assertEqual(checks.check_use_tz_enabled(None), [])
def test_rsa_private_key_exists(self):
with override_settings(BASE_DIR=self.workdir):
os.environ["OIDC_RSA_PRIVATE_KEY"] = "NONE"
msgs = checks.check_rsa_private_key_exists(None)
self.assertEqual(len(msgs), 1)
self.assertTrue(os.path.exists(f"{self.workdir}/.env"))
os.environ["OIDC_RSA_PRIVATE_KEY"] = "KEY"
msgs = checks.check_rsa_private_key_exists(None)
self.assertEqual(len(msgs), 0)
+37 -1
View File
@@ -1,12 +1,16 @@
"""Utility functions."""
from pkg_resources import parse_version
import os
import environ
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from django.utils.translation import gettext as _
from modoboa.core.extensions import exts_pool
from modoboa.lib.api_client import ModoAPIClient
from . import models
def parse_map_file(path):
@@ -23,6 +27,8 @@ def parse_map_file(path):
def check_for_updates():
"""Check if a new version of Modoboa is available."""
from . import models
local_config = models.LocalConfig.objects.first()
client = ModoAPIClient()
extensions = exts_pool.list_all()
@@ -49,3 +55,33 @@ def check_for_updates():
update_avail = True
break
return update_avail, extensions
def generate_rsa_private_key(storage_path: str) -> bool:
"""Generate RSA private key for OIDC support.
:return: False if the key was not generated
and True if it was."""
env_path = os.path.join(storage_path, ".env")
env = environ.Env(OIDC_RSA_PRIVATE_KEY=(str, "NONE"))
environ.Env.read_env(env_path)
if env("OIDC_RSA_PRIVATE_KEY") != "NONE":
return False
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
)
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
content = bytes(pem)
content = content.replace(b"\n", b"\\n")
with open(env_path, "wb") as fp:
fp.write(b'OIDC_RSA_PRIVATE_KEY="' + content + b'"\n')
return True