Merge branch 'no_ip_whitelist' into 'master'

Rework account forms

See merge request bde/photo21!2
This commit is contained in:
erdnaxe 2021-10-08 20:14:21 +02:00
commit e2c826ec1d
13 changed files with 111 additions and 67 deletions

View file

@ -1,13 +1,31 @@
from django import forms from django import forms
from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
class RegistrationForm(UserCreationForm): class RegistrationForm(UserCreationForm):
email = forms.EmailField(label="Email", widget=forms.TextInput(), required=True) email = forms.EmailField(
first_name = forms.CharField(label="Prénom", widget=forms.TextInput(), required=True) label=_("Email address"),
last_name = forms.CharField(label="Nom", widget=forms.TextInput(), required=True) widget=forms.TextInput(),
required=True,
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: class Meta:
model = User model = User
fields = ["username", "password1", "password2", "email", "first_name", "last_name"] fields = ["username", "password1", "password2", "email"]

View file

@ -1,9 +1,20 @@
{% extends "base.html" %} {% 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 %} {% block content %}
<h1>Création d'utilisateur</h1> <div class="card bg-light">
<form action="" method="post">{% csrf_token %} <h3 class="card-header text-center">
{{ form.as_p }} {% trans "Sign up" %}
<br> </h3>
<input type="submit" value="Envoyer"> <div class="card-body">
</form> <form method="post">{% csrf_token %}
{{ form|crispy }}
<input type="submit" value="Envoyer" class="btn btn-primary mt-4">
</form>
</div>
</div>
{% endblock %} {% endblock %}

View file

@ -2,5 +2,5 @@ from django.urls import path
from .views import signup from .views import signup
urlpatterns = [ urlpatterns = [
path('', signup, name='registration'), path('registration/', signup, name='registration'),
] ]

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,19 +18,41 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\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" msgid "German"
msgstr "" msgstr ""
#: photo21/settings.py:129 #: photo21/settings.py:133
msgid "English" msgid "English"
msgstr "" msgstr ""
#: photo21/settings.py:130 #: photo21/settings.py:134
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: photo21/settings.py:131 #: photo21/settings.py:135
msgid "French" msgid "French"
msgstr "" msgstr ""
@ -90,13 +112,13 @@ msgstr "Galeries"
msgid "Admin" msgid "Admin"
msgstr "" msgstr ""
#: photo21/templates/base.html:47 #: photo21/templates/base.html:51
msgid "Log out" msgid "Log out"
msgstr "" msgstr ""
#: photo21/templates/base.html:53 photo21/templates/registration/login.html:6 #: photo21/templates/base.html:61 photo21/templates/registration/login.html:6
#: photo21/templates/registration/login.html:15 #: photo21/templates/registration/login.html:11
#: photo21/templates/registration/login.html:37 #: photo21/templates/registration/login.html:33
#: photo21/templates/registration/password_reset_complete.html:15 #: photo21/templates/registration/password_reset_complete.html:15
msgid "Log in" msgid "Log in"
msgstr "" msgstr ""
@ -113,24 +135,24 @@ msgstr ""
msgid "Log in again" msgid "Log in again"
msgstr "" msgstr ""
#: photo21/templates/registration/login.html:20 #: photo21/templates/registration/login.html:16
#, python-format #, python-format
msgid "" msgid ""
"You are authenticated as %(username)s, but are not authorized to access this " "You are authenticated as %(username)s, but are not authorized to access this "
"page. Would you like to login to a different account?" "page. Would you like to login to a different account?"
msgstr "" msgstr ""
#: photo21/templates/registration/login.html:29 #: photo21/templates/registration/login.html:25
msgid "" msgid ""
"You must be logged with a staff account with the higher mask to access " "You must be logged with a staff account with the higher mask to access "
"Django Admin." "Django Admin."
msgstr "" msgstr ""
#: photo21/templates/registration/login.html:39 #: photo21/templates/registration/login.html:35
msgid "Forgotten your password or username?" msgid "Forgotten your password or username?"
msgstr "" msgstr ""
#: photo21/templates/registration/login.html:43 #: photo21/templates/registration/login.html:39
msgid "If any problem, please contact the server owners at" msgid "If any problem, please contact the server owners at"
msgstr "En cas de problème, contactez les administrateurs à" msgstr "En cas de problème, contactez les administrateurs à"
@ -220,7 +242,7 @@ msgstr ""
msgid "Published" msgid "Published"
msgstr "" msgstr ""
#: photologue_custom/templates/photologue/gallery_detail.html:34 #: photologue_custom/templates/photologue/gallery_detail.html:41
msgid "Download all gallery" msgid "Download all gallery"
msgstr "Télécharger toute la galerie" msgstr "Télécharger toute la galerie"

View file

@ -1,7 +1,6 @@
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.conf import settings from django.conf import settings
import ipaddress
import re import re
@ -20,30 +19,9 @@ class LoginRequiredMiddleware:
If user is not authenticated and external, redirect to login view If user is not authenticated and external, redirect to login view
before calling the 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): if not self.whitelist_re.match(request.path_info):
return HttpResponseRedirect(settings.LOGIN_URL) return HttpResponseRedirect(settings.LOGIN_URL)
response = self.get_response(request) response = self.get_response(request)
return response 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

View file

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.sites', 'django.contrib.sites',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'crispy_forms',
'photologue_custom', 'photologue_custom',
'photologue', 'photologue',
'accounts', 'accounts',
@ -163,8 +164,8 @@ SESSION_COOKIE_AGE = 60 * 60 * 3
# Use only one Django Sites # Use only one Django Sites
SITE_ID = 1 SITE_ID = 1
# use Bootstrap forms
CRISPY_TEMPLATE_PACK = 'bootstrap4'
# Photologue # Photologue
PHOTOLOGUE_GALLERY_SAMPLE_SIZE = 1 PHOTOLOGUE_GALLERY_SAMPLE_SIZE = 1
# IP range whitelist
LOGIN_EXEMPT_IP_RANGE = ["185.230.76.0/22", "2a0c:700::/32"]

View file

@ -44,15 +44,32 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if request.user.is_authenticated %} {% if request.user.is_authenticated %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}"> <a class="nav-link" href="{% url 'logout' %}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M10 12.5a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-9a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v2a.5.5 0 0 0 1 0v-2A1.5 1.5 0 0 0 9.5 2h-8A1.5 1.5 0 0 0 0 3.5v9A1.5 1.5 0 0 0 1.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-2a.5.5 0 0 0-1 0v2z"/>
<path fill-rule="evenodd" d="M15.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 0 0-.708.708L14.293 7.5H5.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z"/>
</svg>
{% trans "Log out" %} {% trans "Log out" %}
</a> </a>
</li> </li>
{% else %} {% else %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{% url 'login' %}"> <a class="nav-link" href="{% url 'login' %}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-in-right" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M6 3.5a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 .5.5v9a.5.5 0 0 1-.5.5h-8a.5.5 0 0 1-.5-.5v-2a.5.5 0 0 0-1 0v2A1.5 1.5 0 0 0 6.5 14h8a1.5 1.5 0 0 0 1.5-1.5v-9A1.5 1.5 0 0 0 14.5 2h-8A1.5 1.5 0 0 0 5 3.5v2a.5.5 0 0 0 1 0v-2z"/>
<path fill-rule="evenodd" d="M11.854 8.354a.5.5 0 0 0 0-.708l-3-3a.5.5 0 1 0-.708.708L10.293 7.5H1.5a.5.5 0 0 0 0 1h8.793l-2.147 2.146a.5.5 0 0 0 .708.708l3-3z"/>
</svg>
{% trans "Log in" %} {% trans "Log in" %}
</a> </a>
</li> </li>
<li class="nav-item">
<a class="nav-link" href="{% url 'registration' %}">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-plus" viewBox="0 0 16 16">
<path d="M6 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm2-3a2 2 0 1 1-4 0 2 2 0 0 1 4 0zm4 8c0 1-1 1-1 1H1s-1 0-1-1 1-4 6-4 6 3 6 4zm-1-.004c-.001-.246-.154-.986-.832-1.664C9.516 10.68 8.289 10 6 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664h10z"/>
<path fill-rule="evenodd" d="M13.5 5a.5.5 0 0 1 .5.5V7h1.5a.5.5 0 0 1 0 1H14v1.5a.5.5 0 0 1-1 0V8h-1.5a.5.5 0 0 1 0-1H13V5.5a.5.5 0 0 1 .5-.5z"/>
</svg>
{% trans "Sign up" %}
</a>
</li>
{% endif %} {% endif %}
</ul> </ul>
</div> </div>

View file

@ -2,13 +2,9 @@
{% comment %} {% comment %}
SPDX-License-Identifier: GPL-2.0-or-later SPDX-License-Identifier: GPL-2.0-or-later
{% endcomment %} {% endcomment %}
{% load i18n static %} {% load i18n crispy_forms_tags %}
{% block title %}{% trans "Log in" %}{% endblock %} {% block title %}{% trans "Log in" %}{% endblock %}
{% block extracss %}
<link rel="stylesheet" href="{% static "registration/css/login.css" %}">
{% endblock %}
{% block content %} {% block content %}
<div class="card bg-light mx-auto" style="max-width: 35rem;"> <div class="card bg-light mx-auto" style="max-width: 35rem;">
<h3 class="card-header text-center"> <h3 class="card-header text-center">
@ -33,8 +29,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
{% endif %} {% endif %}
<form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %} <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
{{ form }} {{ form|crispy }}
<input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary btn-block btn-lg"> <input type="submit" value="{% trans 'Log in' %}" class="btn btn-primary btn-lg mt-2">
<a href="{% url 'password_reset' %}" <a href="{% url 'password_reset' %}"
class="badge bg-light text-dark">{% trans 'Forgotten your password or username?' %}</a> class="badge bg-light text-dark">{% trans 'Forgotten your password or username?' %}</a>
</form> </form>

View file

@ -2,7 +2,7 @@
{% comment %} {% comment %}
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %} {% endcomment %}
{% load i18n %} {% load i18n crispy_forms_tags %}
{% block content %} {% block content %}
<div class="card bg-light"> <div class="card bg-light">
@ -12,7 +12,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
<div class="card-body"> <div class="card-body">
<form method="post">{% csrf_token %} <form method="post">{% csrf_token %}
<p>{% 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." %}</p> <p>{% 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." %}</p>
{{ form }} {{ form|crispy }}
<input class="btn btn-primary" type="submit" value="{% trans 'Change my password' %}"> <input class="btn btn-primary" type="submit" value="{% trans 'Change my password' %}">
</form> </form>
</div> </div>

View file

@ -2,7 +2,7 @@
{% comment %} {% comment %}
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %} {% endcomment %}
{% load i18n %} {% load i18n crispy_forms_tags %}
{% block content %} {% block content %}
<div class="card bg-light"> <div class="card bg-light">
@ -13,8 +13,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% if validlink %} {% if validlink %}
<p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p> <p>{% trans "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
<form method="post">{% csrf_token %} <form method="post">{% csrf_token %}
{{ form }} {{ form|crispy }}
<input class="btn btn-primary" type="submit" value="{% trans 'Change my password' %}"> <input class="btn btn-primary mt-4" type="submit" value="{% trans 'Change my password' %}">
</form> </form>
{% else %} {% else %}
<p> <p>

View file

@ -2,7 +2,7 @@
{% comment %} {% comment %}
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
{% endcomment %} {% endcomment %}
{% load i18n %} {% load i18n crispy_forms_tags %}
{% block content %} {% block content %}
<div class="card bg-light"> <div class="card bg-light">
@ -14,8 +14,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form }} {{ form|crispy }}
<input class="btn btn-primary" type="submit" value="{% trans 'Reset my password' %}"> <input class="btn btn-primary mt-4" type="submit" value="{% trans 'Reset my password' %}">
</form> </form>
</div> </div>
</div> </div>

View file

@ -26,10 +26,10 @@ urlpatterns = [
path('photologue/', include('photologue_custom.urls')), path('photologue/', include('photologue_custom.urls')),
path('photologue/', include('photologue.urls', namespace='photologue')), path('photologue/', include('photologue.urls', namespace='photologue')),
path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('django.contrib.auth.urls')),
path('accounts/', include('accounts.urls')),
path('i18n/', include('django.conf.urls.i18n')), path('i18n/', include('django.conf.urls.i18n')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('admin/doc/', include('django.contrib.admindocs.urls')), path('admin/doc/', include('django.contrib.admindocs.urls')),
path('accounts/registration/', include('accounts.urls'))
] ]
if settings.DEBUG: if settings.DEBUG:

View file

@ -1,3 +1,4 @@
Django~=2.2.20 Django~=2.2.20
django-photologue~=3.13 django-photologue~=3.13
django-taggit~=1.5.1 django-taggit~=1.5.1
django-crispy-forms~=1.7