From 71b3c2adece411c7eed2c802183f065eb3ba9071 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Wed, 15 Jul 2020 17:16:23 +0200
Subject: [PATCH] Make custom authentication backends configurable via site
 preferences

---
 aleksis/core/apps.py                           | 18 +++++++++++++++++-
 aleksis/core/preferences.py                    | 17 ++++++++++++++++-
 aleksis/core/settings.py                       |  7 ++++---
 .../partials/alternative_login_options.html    |  2 +-
 aleksis/core/util/core_helpers.py              |  3 +++
 5 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/aleksis/core/apps.py b/aleksis/core/apps.py
index aeda60932..54c6d4f5b 100644
--- a/aleksis/core/apps.py
+++ b/aleksis/core/apps.py
@@ -1,6 +1,7 @@
 from typing import Any, List, Optional, Tuple
 
 import django.apps
+from django.conf import settings
 from django.http import HttpRequest
 from django.utils.module_loading import autodiscover_modules
 
@@ -12,7 +13,7 @@ from .registries import (
     site_preferences_registry,
 )
 from .util.apps import AppConfig
-from .util.core_helpers import has_person
+from .util.core_helpers import get_site_preferences, has_person, lazy_preference
 from .util.sass_helpers import clean_scss
 
 
@@ -48,6 +49,18 @@ class CoreConfig(AppConfig):
         preference_models.register(personpreferencemodel, person_preferences_registry)
         preference_models.register(grouppreferencemodel, group_preferences_registry)
 
+        self._refresh_authentication_backends()
+
+    def _refresh_authentication_backends(self):
+        """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
+
     def preference_updated(
         self,
         sender: Any,
@@ -57,6 +70,9 @@ class CoreConfig(AppConfig):
         new_value: Optional[Any] = None,
         **kwargs,
     ) -> None:
+        if section == "auth" and name == "backends":
+            self._refresh_authentication_backends()
+
         if section == "theme":
             if name in ("primary", "secondary"):
                 clean_scss()
diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py
index 6693aff1f..cf3d574c3 100644
--- a/aleksis/core/preferences.py
+++ b/aleksis/core/preferences.py
@@ -3,7 +3,12 @@ from django.forms import EmailField, ImageField, URLField
 from django.utils.translation import gettext_lazy as _
 
 from dynamic_preferences.preferences import Section
-from dynamic_preferences.types import ChoicePreference, FilePreference, StringPreference
+from dynamic_preferences.types import (
+    ChoicePreference,
+    FilePreference,
+    MultipleChoicePreference,
+    StringPreference,
+)
 
 from .models import Person
 from .registries import person_preferences_registry, site_preferences_registry
@@ -16,6 +21,7 @@ mail = Section("mail")
 notification = Section("notification")
 footer = Section("footer")
 account = Section("account")
+auth = Section("auth", verbose_name=_("Authentication"))
 
 
 @site_preferences_registry.register
@@ -178,3 +184,12 @@ class SchoolNameOfficial(StringPreference):
     default = ""
     required = False
     verbose_name = _("Official name of the school, e.g. as given by supervisory authority")
+
+
+@site_preferences_registry.register
+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")
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index 8eff3ef21..98c233088 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -282,13 +282,14 @@ if _settings.get("ldap.uri", None):
                 "is_superuser"
             ]
 
-merge_app_settings("AUTHENTICATION_BACKENDS", AUTHENTICATION_BACKENDS)
+CUSTOM_AUTHENTICATION_BACKENDS = []
+merge_app_settings("AUTHENTICATION_BACKENDS", CUSTOM_AUTHENTICATION_BACKENDS)
 
 # Add ModelBckend last so all other backends get a chance
 # to verify passwords first
 AUTHENTICATION_BACKENDS.append("django.contrib.auth.backends.ModelBackend")
 
-# Structure of items: URL name, icon name, button title
+# Structure of items: backend, URL name, icon name, button title
 ALTERNATIVE_LOGIN_VIEWS = []
 merge_app_settings("ALTERNATIVE_LOGIN_VIEWS", ALTERNATIVE_LOGIN_VIEWS, True)
 
@@ -386,7 +387,7 @@ TEMPLATED_EMAIL_BACKEND = "templated_email.backends.vanilla_django"
 TEMPLATED_EMAIL_AUTO_PLAIN = True
 
 
-TEMPLATE_VISIBLE_SETTINGS = ["ADMINS", "DEBUG", "ALTERNATIVE_LOGIN_VIEWS"]
+TEMPLATE_VISIBLE_SETTINGS = ["ADMINS", "DEBUG"]
 
 DYNAMIC_PREFERENCES = {
     "REGISTRY_MODULE": "preferences",
diff --git a/aleksis/core/templates/core/partials/alternative_login_options.html b/aleksis/core/templates/core/partials/alternative_login_options.html
index f9198e5a8..24d36e9ef 100644
--- a/aleksis/core/templates/core/partials/alternative_login_options.html
+++ b/aleksis/core/templates/core/partials/alternative_login_options.html
@@ -1,6 +1,6 @@
 {% if ALTERNATIVE_LOGIN_VIEWS %}
   <p>
-    {% for url, icon, text in ALTERNATIVE_LOGIN_VIEWS %}
+    {% for backend, url, icon, text in ALTERNATIVE_LOGIN_VIEWS %}
       <a class="btn-large waves-effect waves-light primary" href="{% url url %}">
         <i class="material-icons left">{{ icon }}</i>
         {{ text }}
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index ba28f6629..8302d51db 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -323,6 +323,9 @@ def custom_information_processor(request: HttpRequest) -> dict:
 
     return {
         "FOOTER_MENU": CustomMenu.get_default("footer"),
+        "ALTERNATIVE_LOGIN_VIEWS": [
+            a for a in settings.ALTERNATIVE_LOGIN_VIEWS if a[0] in settings.AUTHENTICATION_BACKENDS
+        ],
     }
 
 
-- 
GitLab