diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py index 54c6d4f5b52626bc21e33eb5c6fea8e1c47c54ab..030e56d6da582a522443a26a912be0af37eca091 100644 --- a/aleksis/core/apps.py +++ b/aleksis/core/apps.py @@ -2,6 +2,7 @@ from typing import Any, List, Optional, Tuple import django.apps from django.conf import settings +from django.db import ProgrammingError from django.http import HttpRequest from django.utils.module_loading import autodiscover_modules @@ -51,15 +52,23 @@ class CoreConfig(AppConfig): self._refresh_authentication_backends() - def _refresh_authentication_backends(self): + @classmethod + def _refresh_authentication_backends(cls): """Refresh config list of enabled authentication backends.""" from .preferences import AuthenticationBackends # noqa idx = settings.AUTHENTICATION_BACKENDS.index("django.contrib.auth.backends.ModelBackend") - for backend in get_site_preferences()["auth__backends"]: - settings._wrapped.AUTHENTICATION_BACKENDS.insert(idx, backend) - idx += 1 + try: + # Don't set array directly in order to keep object reference + settings._wrapped.AUTHENTICATION_BACKENDS.clear() + settings._wrapped.AUTHENTICATION_BACKENDS += settings.ORIGINAL_AUTHENTICATION_BACKENDS + + for backend in get_site_preferences()["auth__backends"]: + settings._wrapped.AUTHENTICATION_BACKENDS.insert(idx, backend) + idx += 1 + except ProgrammingError: + pass def preference_updated( self, diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py index cf3d574c3619ed57afa517d8dbf7ed8d11a36881..f0aeb001062b0a1c0fc49d3bf8fcf6f25da67d32 100644 --- a/aleksis/core/preferences.py +++ b/aleksis/core/preferences.py @@ -191,5 +191,7 @@ class AuthenticationBackends(MultipleChoicePreference): section = auth name = "backends" default = None - choices = [(b, b) for b in settings.CUSTOM_AUTHENTICATION_BACKENDS] verbose_name = _("Enabled custom authentication backends") + + def get_choices(self): + return [(b, b) for b in settings.CUSTOM_AUTHENTICATION_BACKENDS] diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index 98c233088c3a4aebd1a4c575efcbbd15b5c2b0df..cf5adee484c91c5bc96ddf76d24f48a65f331e86 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -690,3 +690,5 @@ HEALTH_CHECK = { "DISK_USAGE_MAX": _settings.get("health.disk_usage_max_percent", 90), "MEMORY_MIN": _settings.get("health.memory_min_mb", 500), } + +ORIGINAL_AUTHENTICATION_BACKENDS = AUTHENTICATION_BACKENDS[:] diff --git a/aleksis/core/tests/test_authentication_backends.py b/aleksis/core/tests/test_authentication_backends.py new file mode 100644 index 0000000000000000000000000000000000000000..227f48c1469d97f246132a4bfbef2f5af5f4dbb9 --- /dev/null +++ b/aleksis/core/tests/test_authentication_backends.py @@ -0,0 +1,64 @@ +from typing import Optional + +from django.contrib.auth import authenticate +from django.contrib.auth.backends import BaseBackend +from django.contrib.auth.base_user import AbstractBaseUser +from django.contrib.auth.models import User + +import pytest + +from aleksis.core.apps import CoreConfig +from aleksis.core.util.core_helpers import get_site_preferences + +pytestmark = pytest.mark.django_db + + +class DummyBackend(BaseBackend): + def authenticate( + self, request, username: str, password: str, **kwargs + ) -> Optional[AbstractBaseUser]: + if username == "foo" and password == "baz": + return User.objects.get_or_create(username="foo")[0] + + +backend_name = "aleksis.core.tests.test_authentication_backends.DummyBackend" + + +def test_backends_simple(settings): + + assert not authenticate(username="foo", password="baz") + + assert backend_name not in settings.AUTHENTICATION_BACKENDS + + settings.AUTHENTICATION_BACKENDS.append(backend_name) + assert backend_name in settings.AUTHENTICATION_BACKENDS + + assert authenticate(username="foo", password="baz") + + settings.AUTHENTICATION_BACKENDS.remove(backend_name) + + assert not authenticate(username="foo", password="baz") + + +def test_backends_with_activation(settings): + assert not authenticate(username="foo", password="baz") + + settings.CUSTOM_AUTHENTICATION_BACKENDS.append(backend_name) + + assert backend_name not in get_site_preferences()["auth__backends"] + assert backend_name not in settings.AUTHENTICATION_BACKENDS + assert not authenticate(username="foo", password="baz") + + print(get_site_preferences()["auth__backends"]) + print(get_site_preferences()["auth__backends"].append(backend_name)) + + get_site_preferences()["auth__backends"] = [backend_name] + + assert backend_name in get_site_preferences()["auth__backends"] + assert backend_name in settings.AUTHENTICATION_BACKENDS + assert authenticate(username="foo", password="baz") + + get_site_preferences()["auth__backends"] = [] + + assert backend_name not in get_site_preferences()["auth__backends"] + assert backend_name not in settings.AUTHENTICATION_BACKENDS