From 320411fd1829110461bd9e62e334445bf709e222 Mon Sep 17 00:00:00 2001 From: krek0 Date: Sat, 2 May 2026 14:18:02 +0200 Subject: [PATCH] Make database, oauth, smtp server, mail verificaiton configurable in .env --- .env.example | 36 ++++++++++ .../__init__.py | 2 + .../provider.py | 12 ++-- {allauth_note_kfet => allauth_oauth}/urls.py | 4 +- {allauth_note_kfet => allauth_oauth}/views.py | 12 ++-- photo21/settings.py | 71 ++++++++++++++----- requirements.txt | 1 + tox.ini | 2 +- 8 files changed, 109 insertions(+), 31 deletions(-) rename {allauth_note_kfet => allauth_oauth}/__init__.py (69%) rename {allauth_note_kfet => allauth_oauth}/provider.py (83%) rename {allauth_note_kfet => allauth_oauth}/urls.py (70%) rename {allauth_note_kfet => allauth_oauth}/views.py (77%) diff --git a/.env.example b/.env.example index 145b31e..9fa2ff9 100644 --- a/.env.example +++ b/.env.example @@ -14,3 +14,39 @@ ADMINS=admin:photos-admin@lists.crans.org # Email address used as sender for server emails SERVER_EMAIL=photos@crans.org + +# Email verification: 'mandatory', 'optional', or 'none' +EMAIL_VERIFICATION=mandatory + +# Mail server settings +SMTP_HOST=localhost +SMTP_PORT=25 +#SMTP_USER= +#SMTP_PASSWORD= +SMTP_USE_TLS=False + +# OAuth2 settings +# Enable OAuth2 login +OAUTH_ENABLED=False +# Disable normal username/password login (requires OAUTH_ENABLED=True) +OAUTH_ONLY=False +# OAuth2 server base URL (e.g. auth.example.com) +#OAUTH_SERVER_URL= +# OAuth2 app credentials +#OAUTH_CLIENT_ID= +#OAUTH_CLIENT_SECRET= +# Button appearance on the login page +#OAUTH_BUTTON_TEXT=Login with OAuth +#OAUTH_BUTTON_IMAGE= +# Space-separated OAuth2 scopes +#OAUTH_SCOPE=openid profile email + +# Database engine: 'sqlite' or 'postgres' +DB_ENGINE=sqlite + +# PostgreSQL settings (only used when DB_ENGINE=postgres) +#DB_NAME=photo21 +#DB_USER=photo21 +#DB_PASSWORD= +#DB_HOST=localhost +#DB_PORT=5432 diff --git a/allauth_note_kfet/__init__.py b/allauth_oauth/__init__.py similarity index 69% rename from allauth_note_kfet/__init__.py rename to allauth_oauth/__init__.py index 6c9e378..eae4948 100644 --- a/allauth_note_kfet/__init__.py +++ b/allauth_oauth/__init__.py @@ -1,3 +1,5 @@ # This file is part of photo21 # Copyright (C) 2022 Amicale des élèves de l'ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later + +default_app_config = "allauth_oauth.apps.AllauthOAuthConfig" diff --git a/allauth_note_kfet/provider.py b/allauth_oauth/provider.py similarity index 83% rename from allauth_note_kfet/provider.py rename to allauth_oauth/provider.py index d2ce6a5..6a6430c 100644 --- a/allauth_note_kfet/provider.py +++ b/allauth_oauth/provider.py @@ -7,15 +7,15 @@ from allauth.socialaccount.providers.base import ProviderAccount from allauth.socialaccount.providers.oauth2.provider import OAuth2Provider -class NoteKfetAccount(ProviderAccount): +class OAuthAccount(ProviderAccount): def to_str(self): return self.account.extra_data.get("username") -class NoteKfetProvider(OAuth2Provider): - id = "notekfet" - name = "Note Kfet" - account_class = NoteKfetAccount +class OAuthProvider(OAuth2Provider): + id = "oauth" + name = "OAuth" + account_class = OAuthAccount def extract_uid(self, data): return str(data["username"]) @@ -39,4 +39,4 @@ class NoteKfetProvider(OAuth2Provider): return ret -provider_classes = [NoteKfetProvider] +provider_classes = [OAuthProvider] diff --git a/allauth_note_kfet/urls.py b/allauth_oauth/urls.py similarity index 70% rename from allauth_note_kfet/urls.py rename to allauth_oauth/urls.py index e1bf561..190eac3 100644 --- a/allauth_note_kfet/urls.py +++ b/allauth_oauth/urls.py @@ -4,6 +4,6 @@ from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns -from .provider import NoteKfetProvider +from .provider import OAuthProvider -urlpatterns = default_urlpatterns(NoteKfetProvider) +urlpatterns = default_urlpatterns(OAuthProvider) diff --git a/allauth_note_kfet/views.py b/allauth_oauth/views.py similarity index 77% rename from allauth_note_kfet/views.py rename to allauth_oauth/views.py index 86a16ee..7a568a5 100644 --- a/allauth_note_kfet/views.py +++ b/allauth_oauth/views.py @@ -10,11 +10,11 @@ from allauth.socialaccount.providers.oauth2.views import ( OAuth2LoginView, ) -from .provider import NoteKfetProvider +from .provider import OAuthProvider -class NoteKfetOAuth2Adapter(OAuth2Adapter): - provider_id = NoteKfetProvider.id +class OAuthAdapter(OAuth2Adapter): + provider_id = OAuthProvider.id def complete_login(self, request, app, token, **kwargs): headers = { @@ -31,7 +31,7 @@ class NoteKfetOAuth2Adapter(OAuth2Adapter): @property def domain(self): - return self.settings.get("DOMAIN", "note.crans.org") + return self.settings.get("DOMAIN", "") @property def access_token_url(self): @@ -46,5 +46,5 @@ class NoteKfetOAuth2Adapter(OAuth2Adapter): return f"https://{self.domain}/api/me/" -oauth2_login = OAuth2LoginView.adapter_view(NoteKfetOAuth2Adapter) -oauth2_callback = OAuth2CallbackView.adapter_view(NoteKfetOAuth2Adapter) +oauth2_login = OAuth2LoginView.adapter_view(OAuthAdapter) +oauth2_callback = OAuth2CallbackView.adapter_view(OAuthAdapter) diff --git a/photo21/settings.py b/photo21/settings.py index fb36c54..a80e727 100644 --- a/photo21/settings.py +++ b/photo21/settings.py @@ -56,6 +56,15 @@ SECURE_HSTS_PRELOAD = True # Application definition +OAUTH_ENABLED = config("OAUTH_ENABLED", default=False, cast=bool) +OAUTH_ONLY = config("OAUTH_ONLY", default=False, cast=bool) +OAUTH_CLIENT_ID = config("OAUTH_CLIENT_ID", default="") +OAUTH_CLIENT_SECRET = config("OAUTH_CLIENT_SECRET", default="") +OAUTH_SERVER_URL = config("OAUTH_SERVER_URL", default="") +OAUTH_BUTTON_TEXT = config("OAUTH_BUTTON_TEXT", default="Login with OAuth") +OAUTH_BUTTON_IMAGE = config("OAUTH_BUTTON_IMAGE", default="") +OAUTH_SCOPE = config("OAUTH_SCOPE", default="openid profile email", cast=Csv(delimiter=" ")) + INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.admindocs", @@ -69,12 +78,14 @@ INSTALLED_APPS = [ "allauth", "allauth.account", "allauth.socialaccount", - "allauth_note_kfet", "crispy_forms", "photologue", "photo21", ] +if OAUTH_ENABLED: + INSTALLED_APPS += ["allauth_oauth"] + if DEBUG: INSTALLED_APPS += ["debug_toolbar",] # For debug and optimisations @@ -125,15 +136,31 @@ WSGI_APPLICATION = "photo21.wsgi.application" # Database # https://docs.djangoproject.com/en/2.2/ref/settings/#databases -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.path.join(BASE_DIR, "db.sqlite3"), - "OPTIONS": { - "timeout": 10, - }, +_db_engine = config("DB_ENGINE", default="sqlite").strip().lower() + +if _db_engine == "postgres": + DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": config("DB_NAME", default="photo21"), + "USER": config("DB_USER", default="photo21"), + "PASSWORD": config("DB_PASSWORD", default=""), + "HOST": config("DB_HOST", default="localhost"), + "PORT": config("DB_PORT", default="5432"), + } } -} +elif _db_engine == "sqlite": + DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.path.join(BASE_DIR, "db.sqlite3"), + "OPTIONS": { + "timeout": 10, + }, + } + } +else: + raise ValueError(f"Unknown DB_ENGINE '{_db_engine}'. Must be 'sqlite' or 'postgres'.") CACHES = { "default": { @@ -221,6 +248,11 @@ if DEBUG: SERVER_EMAIL = config("SERVER_EMAIL", default="photos@crans.org") DEFAULT_FROM_EMAIL = f"Serveur photos <{SERVER_EMAIL}>" EMAIL_SUBJECT_PREFIX = "[Serveur photos] " +EMAIL_HOST = config("SMTP_HOST", default="localhost") +EMAIL_PORT = config("SMTP_PORT", default=25, cast=int) +EMAIL_HOST_USER = config("SMTP_USER", default="") +EMAIL_HOST_PASSWORD = config("SMTP_PASSWORD", default="") +EMAIL_USE_TLS = config("SMTP_USE_TLS", default=False, cast=bool) # After login redirect user to transfer page LOGIN_REDIRECT_URL = "/" @@ -240,16 +272,23 @@ MESSAGE_TAGS = { # Allauth configuration ## For the django =< 5.0 ACCOUNT_EMAIL_REQUIRED = True # ACCOUNT_SIGNUP_FIELDS = ['email*', 'username*', 'password1*', 'password2*'] ## For the django =< 5.0 -ACCOUNT_EMAIL_VERIFICATION = "mandatory" +ACCOUNT_EMAIL_VERIFICATION = config("EMAIL_VERIFICATION", default="mandatory") ACCOUNT_AUTHENTICATION_METHOD = "username_email" # ACCOUNT_LOGIN_METHODS = {'username', 'email'} ACCOUNT_FORMS = {"signup": "photo21.forms.CustomSignupForm"} -SOCIALACCOUNT_PROVIDERS = { - "notekfet": { - # Fetch user profile - "SCOPE": ["1_1"], - }, -} + +if OAUTH_ENABLED: + SOCIALACCOUNT_ONLY = OAUTH_ONLY + SOCIALACCOUNT_PROVIDERS = { + "oauth": { + "SCOPE": OAUTH_SCOPE, + "DOMAIN": OAUTH_SERVER_URL, + "APP": { + "client_id": OAUTH_CLIENT_ID, + "secret": OAUTH_CLIENT_SECRET, + }, + }, + } # Use Bootstrap forms CRISPY_TEMPLATE_PACK = "bootstrap4" diff --git a/requirements.txt b/requirements.txt index 31eeff0..c0d5114 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ Pillow>=6.0.0 django-debug-toolbar>=3.2.0 python-decouple>=3.6 whitenoise>=6.0 +psycopg2>=2.9 diff --git a/tox.ini b/tox.ini index cc05d77..55bb7f6 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,7 @@ deps = pep8-naming pyflakes commands = - flake8 allauth_note_kfet photo21 photologue + flake8 allauth_oauth photo21 photologue [flake8] ignore = W503, I100, I101