mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Merge pull request #3275 from modoboa/fix/beta3_review
Fixes after review of beta3
This commit is contained in:
+26
-17
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
# Import these to force registration of checks
|
||||
from . import settings_checks # NOQA:F401
|
||||
@@ -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}"
|
||||
}}
|
||||
"""
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user