Skip to content

Commit c1b7c9d

Browse files
authored
Merge pull request #981 from MTES-MCT/feature/2fa-django
Django 2FA
2 parents 3f52389 + ec05b1a commit c1b7c9d

File tree

6 files changed

+649
-473
lines changed

6 files changed

+649
-473
lines changed

Pipfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,6 @@ setuptools = "*"
7171
py7zr = "*"
7272
dependency-injector = "*"
7373
django-webpack-loader = "*"
74+
django-two-factor-auth = "*"
75+
qrcode = "*"
76+
phonenumbers = "*"

Pipfile.lock

Lines changed: 511 additions & 470 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/settings.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@
8484
"corsheaders",
8585
"fancy_cache",
8686
"webpack_loader",
87+
"two_factor",
88+
"django_otp",
89+
"django_otp.plugins.otp_totp",
90+
"django_otp.plugins.otp_static",
91+
"qrcode",
8792
]
8893

8994
# upper app should not communicate with lower ones
@@ -115,6 +120,7 @@
115120
"django.middleware.common.CommonMiddleware",
116121
"django.middleware.csrf.CsrfViewMiddleware",
117122
"django.contrib.auth.middleware.AuthenticationMiddleware",
123+
"django_otp.middleware.OTPMiddleware",
118124
"django.contrib.messages.middleware.MessageMiddleware",
119125
"django.middleware.clickjacking.XFrameOptionsMiddleware",
120126
"simple_history.middleware.HistoryRequestMiddleware",
@@ -183,7 +189,7 @@
183189
# indicate the new User model to Django
184190
AUTH_USER_MODEL = "users.User"
185191
LOGIN_REDIRECT_URL = "project:list"
186-
LOGIN_URL = "users:signin"
192+
LOGIN_URL = "two_factor:login"
187193

188194

189195
# Internationalization

config/urls.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,18 @@
1818
from django.conf.urls.static import static
1919
from django.contrib import admin
2020
from django.urls import include, path
21+
from two_factor.admin import AdminSiteOTPRequired
22+
from two_factor.urls import urlpatterns as tf_urls
2123

2224
from config.views import EnvironmentView
2325

26+
admin.site.__class__ = AdminSiteOTPRequired
2427
admin.site.site_header = f"Mon Diagnostic Artificialisation v{settings.OFFICIAL_VERSION}"
2528

26-
2729
urlpatterns = [
28-
path("", include("home.urls")),
30+
path("", include(tf_urls)),
2931
path("admin/", admin.site.urls),
32+
path("", include("home.urls")),
3033
path("users/", include("users.urls")),
3134
path("public/", include("public_data.urls")),
3235
path("project/", include("project.urls")),

templates/two_factor/_base.html

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{% load static %}
2+
3+
<!DOCTYPE html>
4+
<html lang="fr">
5+
<head>
6+
<meta charset="utf-8" />
7+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
8+
9+
<title>Authentification 2FA</title>
10+
11+
<style>
12+
body {
13+
font-family: 'Marianne', sans-serif;
14+
margin: 0;
15+
padding: 0;
16+
display: flex;
17+
justify-content: center;
18+
align-items: center;
19+
min-height: 100vh;
20+
background-color: #f7f7f7;
21+
}
22+
23+
.container {
24+
background-color: white;
25+
padding: 40px 30px;
26+
border-radius: 12px;
27+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
28+
width: 100%;
29+
max-width: 400px;
30+
}
31+
32+
.container:hover {
33+
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.2);
34+
}
35+
36+
h1 {
37+
font-size: 2rem;
38+
text-align: center;
39+
color: #333;
40+
font-weight: 600;
41+
}
42+
43+
p {
44+
margin-bottom: 2.5rem;
45+
text-align: center;
46+
font-size: 0.95em;
47+
}
48+
49+
form {
50+
display: flex;
51+
flex-direction: column;
52+
gap: 20px;
53+
}
54+
55+
form label {
56+
font-size: 0.9em;
57+
font-weight: 500;
58+
color: #333;
59+
display: block;
60+
text-align: left;
61+
margin-right: 1.5rem;
62+
}
63+
64+
form input {
65+
padding: 12px;
66+
margin: 0;
67+
border: 1px solid #ddd;
68+
border-radius: 8px;
69+
font-size: 16px;
70+
background-color: #fafafa;
71+
width: 100%;
72+
box-sizing: border-box;
73+
transition: border-color 0.3s ease, background-color 0.3s ease;
74+
}
75+
76+
form input:focus {
77+
outline: none;
78+
border-color: #007bff;
79+
background-color: #ffffff;
80+
}
81+
82+
form button {
83+
background: linear-gradient(90deg, rgba(2,0,36,1) 0%, rgba(118,57,246,1) 0%, rgba(0,101,203,1) 100%);
84+
color: white;
85+
padding: 12px 18px;
86+
border: none;
87+
border-radius: 8px;
88+
cursor: pointer;
89+
font-size: 16px;
90+
font-weight: 500;
91+
transition: opacity 0.3s;
92+
width: 100%;
93+
}
94+
95+
form button:hover {
96+
opacity: 0.8;
97+
}
98+
99+
.message {
100+
margin-top: 20px;
101+
text-align: center;
102+
font-size: 14px;
103+
color: #555;
104+
}
105+
</style>
106+
</head>
107+
<body>
108+
{% block content_wrapper %}
109+
<div class="container">
110+
{% block content %}{% endblock %}
111+
</div>
112+
{% endblock %}
113+
</body>
114+
</html>

users/views.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ def get_context_breadcrumbs(self):
2424
{"href": reverse_lazy("users:signin"), "title": "Connexion"},
2525
]
2626

27+
def get_success_url(self):
28+
"""Redirige les admins vers le 2FA et les autres vers /project/"""
29+
user = self.request.user
30+
if user.is_authenticated:
31+
if user.is_staff or user.is_superuser:
32+
return reverse_lazy("two_factor:login")
33+
return reverse_lazy("project:list")
34+
return super().get_success_url()
35+
2736

2837
class SignoutView(RedirectView):
2938
url = reverse_lazy("users:signin")

0 commit comments

Comments
 (0)