From 22e0b7dc2791f8b8d3faf874e1067c67205d5f22 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 11:10:08 +0200 Subject: [PATCH 1/7] Use accounts/ prefix for accounts router --- accounts/urls.py | 2 +- photo21/urls.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/urls.py b/accounts/urls.py index 3e07455..445f6aa 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -2,5 +2,5 @@ from django.urls import path from .views import signup urlpatterns = [ - path('', signup, name='registration'), + path('registration/', signup, name='registration'), ] diff --git a/photo21/urls.py b/photo21/urls.py index 3b4a601..311d17b 100644 --- a/photo21/urls.py +++ b/photo21/urls.py @@ -26,10 +26,10 @@ urlpatterns = [ path('photologue/', include('photologue_custom.urls')), path('photologue/', include('photologue.urls', namespace='photologue')), path('accounts/', include('django.contrib.auth.urls')), + path('accounts/', include('accounts.urls')), path('i18n/', include('django.conf.urls.i18n')), path('admin/', admin.site.urls), path('admin/doc/', include('django.contrib.admindocs.urls')), - path('accounts/registration/', include('accounts.urls')) ] if settings.DEBUG: From 22104d356593c02fe9b02015a5303319abea7537 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 11:10:24 +0200 Subject: [PATCH 2/7] Do not whitelist access by IP range --- photo21/middleware.py | 24 +----------------------- photo21/settings.py | 3 --- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/photo21/middleware.py b/photo21/middleware.py index 88ad41c..29aa5d6 100644 --- a/photo21/middleware.py +++ b/photo21/middleware.py @@ -1,7 +1,6 @@ from django.http import HttpResponseRedirect from django.conf import settings -import ipaddress import re @@ -20,30 +19,9 @@ class LoginRequiredMiddleware: If user is not authenticated and external, redirect to login view before calling the view. """ - if not request.user.is_authenticated and not self.check_ip(request): + if not request.user.is_authenticated: if not self.whitelist_re.match(request.path_info): return HttpResponseRedirect(settings.LOGIN_URL) response = self.get_response(request) return response - - def check_ip(self, request): - """ - Return true if IP is in authorized range - """ - # Get IP address - if 'HTTP_X_REAL_IP' in request.META: - ip = request.META.get('HTTP_X_REAL_IP') - elif 'HTTP_X_FORWARDED_FOR' in request.META: - ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0] - else: - ip = request.META.get('REMOTE_ADDR') - ip = ipaddress.ip_address(ip) - - # Check against ranges - if hasattr(settings, 'LOGIN_EXEMPT_IP_RANGE'): - for ip_range in settings.LOGIN_EXEMPT_IP_RANGE: - net = ipaddress.ip_network(ip_range) - if ip in net: - return True - return False diff --git a/photo21/settings.py b/photo21/settings.py index bda42a0..b5ee4fd 100644 --- a/photo21/settings.py +++ b/photo21/settings.py @@ -165,6 +165,3 @@ SITE_ID = 1 # Photologue PHOTOLOGUE_GALLERY_SAMPLE_SIZE = 1 - -# IP range whitelist -LOGIN_EXEMPT_IP_RANGE = ["185.230.76.0/22", "2a0c:700::/32"] From 3e64f02dea8fc18e27db95267571eca7bbf3838e Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 11:10:37 +0200 Subject: [PATCH 3/7] Add sign up button --- photo21/templates/base.html | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/photo21/templates/base.html b/photo21/templates/base.html index a595efb..d863a89 100644 --- a/photo21/templates/base.html +++ b/photo21/templates/base.html @@ -44,15 +44,32 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if request.user.is_authenticated %} {% else %} + {% endif %} From b9013c4eb38a8e9b00a10c2187844f5f482810be Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 11:21:05 +0200 Subject: [PATCH 4/7] Use Django Crispy forms --- accounts/templates/accounts/registration.html | 23 ++++++++++++++----- photo21/settings.py | 4 ++++ photo21/templates/registration/login.html | 10 +++----- .../registration/password_change_form.html | 4 ++-- .../registration/password_reset_confirm.html | 6 ++--- .../registration/password_reset_form.html | 6 ++--- requirements.txt | 1 + 7 files changed, 33 insertions(+), 21 deletions(-) diff --git a/accounts/templates/accounts/registration.html b/accounts/templates/accounts/registration.html index c62b66e..ba48e03 100644 --- a/accounts/templates/accounts/registration.html +++ b/accounts/templates/accounts/registration.html @@ -1,9 +1,20 @@ {% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-2.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} +{% block title %}{% trans "Sign up" %}{% endblock %} + {% block content %} -

Création d'utilisateur

-
{% csrf_token %} - {{ form.as_p }} -
- -
+
+

+ {% trans "Sign up" %} +

+
+
{% csrf_token %} + {{ form|crispy }} + +
+
+
{% endblock %} \ No newline at end of file diff --git a/photo21/settings.py b/photo21/settings.py index b5ee4fd..b263fd8 100644 --- a/photo21/settings.py +++ b/photo21/settings.py @@ -41,6 +41,7 @@ INSTALLED_APPS = [ 'django.contrib.sites', 'django.contrib.messages', 'django.contrib.staticfiles', + 'crispy_forms', 'photologue_custom', 'photologue', 'accounts', @@ -163,5 +164,8 @@ SESSION_COOKIE_AGE = 60 * 60 * 3 # Use only one Django Sites SITE_ID = 1 +# use Bootstrap forms +CRISPY_TEMPLATE_PACK = 'bootstrap4' + # Photologue PHOTOLOGUE_GALLERY_SAMPLE_SIZE = 1 diff --git a/photo21/templates/registration/login.html b/photo21/templates/registration/login.html index ac23d00..bf5d5f0 100644 --- a/photo21/templates/registration/login.html +++ b/photo21/templates/registration/login.html @@ -2,13 +2,9 @@ {% comment %} SPDX-License-Identifier: GPL-2.0-or-later {% endcomment %} -{% load i18n static %} +{% load i18n crispy_forms_tags %} {% block title %}{% trans "Log in" %}{% endblock %} -{% block extracss %} - -{% endblock %} - {% block content %}

@@ -33,8 +29,8 @@ SPDX-License-Identifier: GPL-2.0-or-later {% endif %}
{% csrf_token %} - {{ form }} - + {{ form|crispy }} + {% trans 'Forgotten your password or username?' %}
diff --git a/photo21/templates/registration/password_change_form.html b/photo21/templates/registration/password_change_form.html index f00698a..d74c762 100644 --- a/photo21/templates/registration/password_change_form.html +++ b/photo21/templates/registration/password_change_form.html @@ -2,7 +2,7 @@ {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} -{% load i18n %} +{% load i18n crispy_forms_tags %} {% block content %}
@@ -12,7 +12,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% csrf_token %}

{% trans "Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly." %}

- {{ form }} + {{ form|crispy }}
diff --git a/photo21/templates/registration/password_reset_confirm.html b/photo21/templates/registration/password_reset_confirm.html index 9719cf6..dd41889 100644 --- a/photo21/templates/registration/password_reset_confirm.html +++ b/photo21/templates/registration/password_reset_confirm.html @@ -2,7 +2,7 @@ {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} -{% load i18n %} +{% load i18n crispy_forms_tags %} {% block content %}
@@ -13,8 +13,8 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if validlink %}

{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}

{% csrf_token %} - {{ form }} - + {{ form|crispy }} +
{% else %}

diff --git a/photo21/templates/registration/password_reset_form.html b/photo21/templates/registration/password_reset_form.html index f419ff7..9a6a15c 100644 --- a/photo21/templates/registration/password_reset_form.html +++ b/photo21/templates/registration/password_reset_form.html @@ -2,7 +2,7 @@ {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} -{% load i18n %} +{% load i18n crispy_forms_tags %} {% block content %}

@@ -14,8 +14,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% csrf_token %} - {{ form }} - + {{ form|crispy }} +
diff --git a/requirements.txt b/requirements.txt index 4d7394d..01d4671 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django~=2.2.20 django-photologue~=3.13 django-taggit~=1.5.1 +django-crispy-forms~=1.7 From 122794a49d6e8b224c5ac6f632f8a313d4bcc3f8 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 11:49:07 +0200 Subject: [PATCH 5/7] Rework account.forms.RegistrationForm --- accounts/forms.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/accounts/forms.py b/accounts/forms.py index 3b34969..c5efdcd 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -1,13 +1,17 @@ from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User +from django.utils.translation import gettext_lazy as _ class RegistrationForm(UserCreationForm): - email = forms.EmailField(label="Email", widget=forms.TextInput(), required=True) - first_name = forms.CharField(label="Prénom", widget=forms.TextInput(), required=True) - last_name = forms.CharField(label="Nom", widget=forms.TextInput(), required=True) + email = forms.EmailField( + label=_("Email address"), + widget=forms.TextInput(), + required=True, + help_text=_("Please enter a valid email address ending with `@crans.org` or `@ens-paris-saclay.fr`."), + ) class Meta: model = User - fields = ["username", "password1", "password2", "email", "first_name", "last_name"] + fields = ["username", "password1", "password2", "email"] From 0391639bed4b94f378d5393d8c78bae75ff450ac Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 12:39:32 +0200 Subject: [PATCH 6/7] Update translations --- photo21/locale/fr/LC_MESSAGES/django.po | 50 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/photo21/locale/fr/LC_MESSAGES/django.po b/photo21/locale/fr/LC_MESSAGES/django.po index d1773ca..c10a3f4 100644 --- a/photo21/locale/fr/LC_MESSAGES/django.po +++ b/photo21/locale/fr/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-09-23 08:18+0000\n" +"POT-Creation-Date: 2021-10-08 11:03+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,19 +18,41 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: photo21/settings.py:128 +#: accounts/forms.py:9 +msgid "Email address" +msgstr "Adresse email" + +#: accounts/forms.py:13 +msgid "" +"Please enter a valid email address ending with `@crans.org` or `@ens-paris-" +"saclay.fr`." +msgstr "" +"Veuillez entrer une adresse email valide finissant par `@crans.org` ou `@ens-" +"paris-saclay.fr`." + +#: accounts/forms.py:25 +msgid "Must end with `@crans.org` or `@ens-paris-saclay.fr`." +msgstr "Doit finir par `@crans.org` ou `@ens-paris-saclay.fr`." + +#: accounts/templates/accounts/registration.html:6 +#: accounts/templates/accounts/registration.html:11 +#: photo21/templates/base.html:70 +msgid "Sign up" +msgstr "Inscription" + +#: photo21/settings.py:132 msgid "German" msgstr "" -#: photo21/settings.py:129 +#: photo21/settings.py:133 msgid "English" msgstr "" -#: photo21/settings.py:130 +#: photo21/settings.py:134 msgid "Spanish" msgstr "" -#: photo21/settings.py:131 +#: photo21/settings.py:135 msgid "French" msgstr "" @@ -90,13 +112,13 @@ msgstr "Galeries" msgid "Admin" msgstr "" -#: photo21/templates/base.html:47 +#: photo21/templates/base.html:51 msgid "Log out" msgstr "" -#: photo21/templates/base.html:53 photo21/templates/registration/login.html:6 -#: photo21/templates/registration/login.html:15 -#: photo21/templates/registration/login.html:37 +#: photo21/templates/base.html:61 photo21/templates/registration/login.html:6 +#: photo21/templates/registration/login.html:11 +#: photo21/templates/registration/login.html:33 #: photo21/templates/registration/password_reset_complete.html:15 msgid "Log in" msgstr "" @@ -113,24 +135,24 @@ msgstr "" msgid "Log in again" msgstr "" -#: photo21/templates/registration/login.html:20 +#: photo21/templates/registration/login.html:16 #, python-format msgid "" "You are authenticated as %(username)s, but are not authorized to access this " "page. Would you like to login to a different account?" msgstr "" -#: photo21/templates/registration/login.html:29 +#: photo21/templates/registration/login.html:25 msgid "" "You must be logged with a staff account with the higher mask to access " "Django Admin." msgstr "" -#: photo21/templates/registration/login.html:39 +#: photo21/templates/registration/login.html:35 msgid "Forgotten your password or username?" msgstr "" -#: photo21/templates/registration/login.html:43 +#: photo21/templates/registration/login.html:39 msgid "If any problem, please contact the server owners at" msgstr "En cas de problème, contactez les administrateurs à" @@ -220,7 +242,7 @@ msgstr "" msgid "Published" msgstr "" -#: photologue_custom/templates/photologue/gallery_detail.html:34 +#: photologue_custom/templates/photologue/gallery_detail.html:41 msgid "Download all gallery" msgstr "Télécharger toute la galerie" From 1853ca96bbfec6eba331a7c555961a5faa06f787 Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Fri, 8 Oct 2021 13:04:06 +0200 Subject: [PATCH 7/7] Check trusted email domains --- accounts/forms.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/accounts/forms.py b/accounts/forms.py index c5efdcd..d11c888 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -9,9 +9,23 @@ class RegistrationForm(UserCreationForm): label=_("Email address"), widget=forms.TextInput(), required=True, - help_text=_("Please enter a valid email address ending with `@crans.org` or `@ens-paris-saclay.fr`."), + help_text=_( + "Please enter a valid email address ending with `@crans.org` or " + "`@ens-paris-saclay.fr`." + ), ) + def clean_email(self): + """ + Check that the email address ends with a trusted domain. + """ + email = self.cleaned_data.get("email") + if not email.endswith("@crans.org") and not email.endswith("@ens-paris-saclay.fr"): + raise forms.ValidationError( + _("Must end with `@crans.org` or `@ens-paris-saclay.fr`.") + ) + return email + class Meta: model = User fields = ["username", "password1", "password2", "email"]