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.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`."
),
)
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", "first_name", "last_name"]
fields = ["username", "password1", "password2", "email"]

View file

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

View file

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

View file

@ -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 <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\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"

View file

@ -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

View file

@ -41,6 +41,7 @@ INSTALLED_APPS = [
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'photologue_custom',
'photologue',
'accounts',
@ -163,8 +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
# 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 %}
<li class="nav-item">
<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" %}
</a>
</li>
{% else %}
<li class="nav-item">
<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" %}
</a>
</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 %}
</ul>
</div>

View file

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

View file

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

View file

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

View file

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

View file

@ -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:

View file

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