From c680aa58b2846643327cd1d2b7a0731934657312 Mon Sep 17 00:00:00 2001 From: Nick Sweeting Date: Wed, 2 Sep 2026 04:14:43 -0700 Subject: [PATCH] Honor explicit allauth opt-in --- .github/scripts/discover_test_matrix.py | 8 +++- archivebox/core/admin_site.py | 6 ++- archivebox/core/settings.py | 32 +++++++-------- archivebox/core/urls.py | 19 ++++++--- archivebox/templates/admin/login.html | 42 +++++++++++++++++++- archivebox/tests/conftest.py | 21 +++++++--- archivebox/tests/test_allauth_config.py | 13 +++--- archivebox/tests/test_allauth_integration.py | 11 +++-- 8 files changed, 106 insertions(+), 46 deletions(-) diff --git a/.github/scripts/discover_test_matrix.py b/.github/scripts/discover_test_matrix.py index 67412ae9..7c2201bd 100755 --- a/.github/scripts/discover_test_matrix.py +++ b/.github/scripts/discover_test_matrix.py @@ -20,7 +20,13 @@ def main() -> None: "name": f"main/{path.stem}", "paths": [test_path], "paths_arg": test_path, - "extra": "ldap" if test_path.endswith("/test_auth_ldap.py") else "", + "extra": ( + "ldap" + if test_path.endswith("/test_auth_ldap.py") + else "allauth" + if test_path.endswith("/test_allauth_integration.py") + else "" + ), "count": 1, }, ) diff --git a/archivebox/core/admin_site.py b/archivebox/core/admin_site.py index 665653b8..acc4c3de 100644 --- a/archivebox/core/admin_site.py +++ b/archivebox/core/admin_site.py @@ -11,8 +11,10 @@ from admin_data_views.admin import ( from admin_data_views.admin import ( get_app_list as adv_get_app_list, ) +from django.conf import settings from django.contrib import admin -from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model, login as auth_login +from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model +from django.contrib.auth import login as auth_login from django.contrib.auth.decorators import login_not_required from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.views import LoginView @@ -31,7 +33,6 @@ from archivebox.core.routes_util import is_allowed_archivebox_redirect_url if TYPE_CHECKING: from admin_data_views.typing import AppDict from django.http import HttpRequest - from django.template.response import TemplateResponse from django.urls import URLPattern, URLResolver @@ -114,6 +115,7 @@ class ArchiveBoxAdmin(admin.AdminSite): "app_path": request.get_full_path(), "username": request.user.get_username(), "first_admin_setup": first_admin_setup, + "allauth_enabled": settings.ALLAUTH_ENABLED, } if REDIRECT_FIELD_NAME not in request.GET and REDIRECT_FIELD_NAME not in request.POST: context[REDIRECT_FIELD_NAME] = reverse("admin:index", current_app=self.name) diff --git a/archivebox/core/settings.py b/archivebox/core/settings.py index 7bf84a41..3fbf07a9 100644 --- a/archivebox/core/settings.py +++ b/archivebox/core/settings.py @@ -43,6 +43,7 @@ WSGI_APPLICATION = "archivebox.core.wsgi.application" ASGI_APPLICATION = "archivebox.core.asgi.application" ROOT_URLCONF = "archivebox.core.urls" +LOGIN_URL = "/accounts/login/" LOGOUT_REDIRECT_URL = CONFIG.LOGOUT_REDIRECT_URL PASSWORD_RESET_URL = "/accounts/password_reset/" @@ -151,16 +152,9 @@ try: "email": CONFIG.LDAP_EMAIL_ATTR, } - # Use custom LDAP backend that supports LDAP_CREATE_SUPERUSER - # Include allauth backend first if allauth is installed - try: - import allauth as _allauth_check # noqa: F401 - - _allauth_backend = ["allauth.account.auth_backends.AuthenticationBackend"] - except ImportError: - _allauth_backend = [] - - AUTHENTICATION_BACKENDS = _allauth_backend + [ + # Use custom LDAP backend that supports LDAP_CREATE_SUPERUSER. + # The allauth block below prepends its backend when explicitly enabled. + AUTHENTICATION_BACKENDS = [ "archivebox.ldap.auth.ArchiveBoxLDAPBackend", "django.contrib.auth.backends.RemoteUserBackend", "django.contrib.auth.backends.ModelBackend", @@ -184,10 +178,16 @@ except ImportError: ################################################################################ ### django-allauth Configuration -# Conditionally loaded if django-allauth is installed +# Installing an optional dependency must not silently change authentication. +# ALLAUTH_ENABLED is the single switch for apps, middleware, backend, and routes. ################################################################################ -try: - import allauth # noqa: F401 +ALLAUTH_ENABLED = CONFIG.ALLAUTH_ENABLED + +if ALLAUTH_ENABLED: + try: + import allauth # noqa: F401 + except ImportError as err: + raise ImportError("ALLAUTH_ENABLED=True requires the archivebox[allauth] optional dependency") from err INSTALLED_APPS += [ "archivebox.auth", @@ -213,7 +213,7 @@ try: "allauth.account.middleware.AccountMiddleware", ] - # Prepend allauth backend to the list (only if not already present, e.g., from LDAP block) + # Prepend allauth so its email authentication runs before Django's username backend. _allauth_auth_backend = "allauth.account.auth_backends.AuthenticationBackend" if _allauth_auth_backend not in AUTHENTICATION_BACKENDS: AUTHENTICATION_BACKENDS = [_allauth_auth_backend] + AUTHENTICATION_BACKENDS @@ -239,13 +239,9 @@ try: # e.g. SOCIALACCOUNT_PROVIDERS='{"google": {"APP": {"client_id": "...", "secret": "..."}}}' SOCIALACCOUNT_PROVIDERS = CONFIG.SOCIALACCOUNT_PROVIDERS - LOGIN_URL = "/accounts/login/" LOGIN_REDIRECT_URL = "/admin/" ACCOUNT_LOGOUT_REDIRECT_URL = LOGOUT_REDIRECT_URL or "/" -except ImportError: - pass - ################################################################################ ### Staticfile and Template Settings ################################################################################ diff --git a/archivebox/core/urls.py b/archivebox/core/urls.py index 668d3631..235cead4 100644 --- a/archivebox/core/urls.py +++ b/archivebox/core/urls.py @@ -27,6 +27,17 @@ from archivebox.opencode.views import opencode_proxy_view from archivebox.progressmonitor.views import live_progress_view from archivebox.search.views import public_snapshot_search_stream_view +if settings.ALLAUTH_ENABLED: + account_urlpatterns = [path("accounts/", include("allauth.urls"))] +else: + # Preserve ArchiveBox's built-in auth surface unless allauth was explicitly + # enabled. Optional package installation alone must not change login behavior. + account_urlpatterns = [ + path("accounts/login/", RedirectView.as_view(url="/admin/login/", query_string=True)), + path("accounts/logout/", RedirectView.as_view(url="/admin/logout/", query_string=True)), + path("accounts/", include("django.contrib.auth.urls")), + ] + urlpatterns = [ re_path(r"^static/(?P.*)$", serve_static), path("robots.txt", static.serve, {"document_root": CONSTANTS.STATIC_DIR, "path": "robots.txt"}), @@ -61,12 +72,8 @@ urlpatterns = [ path("admin/core/snapshot/add/", RedirectView.as_view(url="/add/")), path("admin/core/snapshot/replay-auth/", SnapshotReplayAuthView.as_view(), name="snapshot-replay-auth"), path("add/", AddView.as_view(), name="add"), - # ``query_string=True`` preserves the ``?next=…`` param that Django's - # auth/login mixins append, so e.g. ``UserPassesTestMixin`` redirecting - # an unauthenticated ``/add`` visitor to ``/accounts/login/?next=/add/`` - # carries the ``next`` through to ``/admin/login/`` and lands them at - # ``/add/`` after login instead of the admin homepage. - path("accounts/", include("allauth.urls")), + # The disabled-mode login redirect preserves Django's ``?next=…`` query. + *account_urlpatterns, path("progress.json", live_progress_view, name="live_progress"), path("admin/", archivebox_admin.urls), path("api/", include("archivebox.api.urls"), name="api"), diff --git a/archivebox/templates/admin/login.html b/archivebox/templates/admin/login.html index 88414ea7..e1f462bb 100644 --- a/archivebox/templates/admin/login.html +++ b/archivebox/templates/admin/login.html @@ -56,9 +56,11 @@ + {% if allauth_enabled %}

Use a configured identity provider instead

+ {% endif %} -{% else %} +{% elif allauth_enabled %}