feat: Use allauth and add passkey support

This commit is contained in:
2025-10-21 00:40:10 +02:00
parent e06efa1539
commit 969339a95f
8 changed files with 59 additions and 22 deletions

View File

@@ -38,7 +38,8 @@ dependencies = [
"celery[redis]", "celery[redis]",
"drf-spectacular[sidecar]", "drf-spectacular[sidecar]",
"django-widget-tweaks", "django-widget-tweaks",
"django-super-deduper" "django-super-deduper",
"django-allauth[mfa]"
] ]
dynamic = ["version", "readme"] dynamic = ["version", "readme"]

View File

@@ -137,6 +137,14 @@ class ModerationActionForm(forms.ModelForm):
fields = ('action', 'public_comment', 'private_comment') fields = ('action', 'public_comment', 'private_comment')
class AddedRegistrationForm(forms.Form):
captcha = forms.CharField(validators=[animal_validator], label=_("Nenne eine bekannte Tierart"), help_text=_(
"Bitte nenne hier eine bekannte Tierart (z.B. ein Tier das an der Leine geführt wird). Das Fragen wir dich um sicherzustellen, dass du kein Roboter bist."))
def signup(self, request, user):
pass
class CustomRegistrationForm(RegistrationForm): class CustomRegistrationForm(RegistrationForm):
class Meta(RegistrationForm.Meta): class Meta(RegistrationForm.Meta):
model = User model = User

View File

@@ -1,7 +1,8 @@
{% extends "fellchensammlung/base.html" %} {% extends "fellchensammlung/base.html" %}
{% load i18n %} {% load i18n %}
{% load account %}
{% block title %}<title>{{ user.get_full_name }}</title>{% endblock %} {% block title %}<title>{% user_display user %}</title>{% endblock %}
{% block content %} {% block content %}
@@ -13,7 +14,7 @@
</div> </div>
<div class="level-right"> <div class="level-right">
<div class="level-item"> <div class="level-item">
<form class="" action="{% url 'logout' %}" method="post"> <form class="" action="{% url 'account_logout' %}" method="post">
{% csrf_token %} {% csrf_token %}
<button class="button" type="submit"> <button class="button" type="submit">
<i aria-hidden="true" class="fas fa-sign-out fa-fw"></i> Logout <i aria-hidden="true" class="fas fa-sign-out fa-fw"></i> Logout
@@ -25,12 +26,12 @@
<div class="block"> <div class="block">
<h2 class="title is-2">{% trans 'Profil verwalten' %}</h2> <h2 class="title is-2">{% trans 'Profil verwalten' %}</h2>
<p><strong>{% translate "E-Mail" %}:</strong> {{ user.email }}</p> <div class="block"><strong>{% translate "E-Mail" %}:</strong> {{ user.email }}</div>
<div class=""> <div class="block">
<p> <a class="button is-warning"
<a class="button is-warning" href="{% url 'password_change' %}">{% translate "Change password" %}</a> href="{% url 'account_change_password' %}">{% translate "Passwort ändern" %}</a>
<a class="button is-info" href="{% url 'user-me-export' %}">{% translate "Daten exportieren" %}</a> <a class="button is-warning" href="{% url 'account_email' %}">{% translate "E-Mail Adresse ändern" %}</a>
</p> <a class="button is-info" href="{% url 'user-me-export' %}">{% translate "Daten exportieren" %}</a>
</div> </div>
</div> </div>

View File

@@ -49,10 +49,10 @@
{% else %} {% else %}
<div class="navbar-item"> <div class="navbar-item">
<div class="buttons"> <div class="buttons">
<a class="button is-primary" href="{% url "django_registration_register" %}"> <a class="button is-primary" href="{% url "account_signup" %}">
<strong>{% translate "Registrieren" %}</strong> <strong>{% translate "Registrieren" %}</strong>
</a> </a>
<a class="button is-light" href="{% url "login" %}"> <a class="button is-light" href="{% url "account_login" %}">
<strong>{% translate "Login" %}</strong> <strong>{% translate "Login" %}</strong>
</a> </a>
</div> </div>

View File

@@ -94,15 +94,6 @@ urlpatterns = [
path("user/notifications/", views.my_notifications, name="user-notifications"), path("user/notifications/", views.my_notifications, name="user-notifications"),
path('user/me/export/', views.export_own_profile, name='user-me-export'), path('user/me/export/', views.export_own_profile, name='user-me-export'),
path('accounts/register/',
registration_views.HTMLMailRegistrationView.as_view(
form_class=CustomRegistrationForm,
email_body_template="fellchensammlung/mail/activation_email.html",
),
name='django_registration_register',
),
path('accounts/', include('django_registration.backends.activation.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('change-language', views.change_language, name="change-language"), path('change-language', views.change_language, name="change-language"),

View File

@@ -130,6 +130,34 @@ ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window
REGISTRATION_OPEN = True REGISTRATION_OPEN = True
REGISTRATION_SALT = "notfellchen" REGISTRATION_SALT = "notfellchen"
AUTHENTICATION_BACKENDS = [
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by email
'allauth.account.auth_backends.AuthenticationBackend',
]
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_EMAIL_VERIFICATION_BY_CODE_ENABLED = True
ACCOUNT_SIGNUP_FIELDS = ['username*', "email*", "password1*", "password2*"]
ACCOUNT_SIGNUP_FORM_CLASS = 'fellchensammlung.forms.AddedRegistrationForm'
MFA_SUPPORTED_TYPES = ["totp",
"webauthn",
"recovery_codes"]
MFA_PASSKEY_LOGIN_ENABLED = True
MFA_PASSKEY_SIGNUP_ENABLED = True
# Optional -- use for local development only: the WebAuthn uses the
#``fido2`` package, and versions up to including version 1.1.3 do not
# regard localhost as a secure origin, which is problematic during
# local development and testing.
MFA_WEBAUTHN_ALLOW_INSECURE_ORIGIN = True
""" SECURITY.TXT """ """ SECURITY.TXT """
SEC_CONTACT = config.get("security", "Contact", fallback="julian-samuel@gebuehr.net") SEC_CONTACT = config.get("security", "Contact", fallback="julian-samuel@gebuehr.net")
SEC_EXPIRES = config.get("security", "Expires", fallback="2028-03-17T07:00:00.000Z") SEC_EXPIRES = config.get("security", "Expires", fallback="2028-03-17T07:00:00.000Z")
@@ -182,7 +210,11 @@ INSTALLED_APPS = [
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
"django.contrib.humanize",
'django.contrib.messages', 'django.contrib.messages',
'allauth',
'allauth.account',
'allauth.mfa',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
"django.contrib.sitemaps", "django.contrib.sitemaps",
'fontawesomefree', 'fontawesomefree',
@@ -193,7 +225,7 @@ INSTALLED_APPS = [
'rest_framework.authtoken', 'rest_framework.authtoken',
'drf_spectacular', 'drf_spectacular',
'drf_spectacular_sidecar', # required for Django collectstatic discovery 'drf_spectacular_sidecar', # required for Django collectstatic discovery
'widget_tweaks' 'widget_tweaks',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@@ -208,6 +240,8 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
# allauth middleware, needs to be after message middleware
"allauth.account.middleware.AccountMiddleware",
] ]
ROOT_URLCONF = 'notfellchen.urls' ROOT_URLCONF = 'notfellchen.urls'
@@ -222,6 +256,7 @@ TEMPLATES = [
'OPTIONS': { 'OPTIONS': {
'context_processors': [ 'context_processors': [
'django.template.context_processors.debug', 'django.template.context_processors.debug',
# Needed for allauth
'django.template.context_processors.request', 'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth', 'django.contrib.auth.context_processors.auth',
'django.template.context_processors.media', 'django.template.context_processors.media',

View File

@@ -23,6 +23,7 @@ from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('accounts/', include('allauth.urls')),
] ]
urlpatterns += i18n_patterns( urlpatterns += i18n_patterns(

View File

@@ -47,7 +47,7 @@
<div class="block"> <div class="block">
<a class="button is-warning" href="{% url 'password_reset' %}">{% translate "Passwort vergessen?" %}</a> <a class="button is-warning" href="{% url 'password_reset' %}">{% translate "Passwort vergessen?" %}</a>
<a class="button is-link" <a class="button is-link"
href="{% url 'django_registration_register' %}">{% translate "Registrieren" %}</a> href="{% url 'account_signup' %}">{% translate "Registrieren" %}</a>
</div> </div>
</div> </div>
{% endif %} {% endif %}