views.py 10.7 KB
Newer Older
Arnaud Levaufre's avatar
Arnaud Levaufre committed
1
import hashlib
2
import uuid
3
from typing import Optional
4
5

from django.conf import settings
Arnaud Levaufre's avatar
Arnaud Levaufre committed
6
from django.contrib import auth
7
8
9
10
11
12
13
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.db import transaction
from django.http import Http404
14
15
from django.http import HttpRequest
from django.http import HttpResponse
16
17
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
Arnaud Levaufre's avatar
Arnaud Levaufre committed
18
from django.shortcuts import redirect
19
from django.shortcuts import render
20
from django.urls import reverse
21
22
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.debug import sensitive_variables
23

Arnaud Levaufre's avatar
Arnaud Levaufre committed
24
from . import forms
Arnaud Levaufre's avatar
Arnaud Levaufre committed
25
from . import models
26
27
28
from bde.models import Contributor
from core.cache import cache_unless
from notifications.shortcuts import notify
Arnaud Levaufre's avatar
Arnaud Levaufre committed
29

30

31
32
@sensitive_variables("password")
@sensitive_post_parameters("password")
33
def login(request: HttpRequest) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
34
35
    context = {}

36
    if request.user.is_authenticated:
37
        return redirect(request.POST.get("next", reverse("news:index")))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
38
    if request.POST:
39
40
        email = request.POST["email"]
        password = request.POST["password"]
41
        user = auth.authenticate(username=email, password=password)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
42
43
        if user:
            auth.login(request, user)
44
            return redirect(request.POST.get("next", reverse("news:index")))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
45
        else:
46
            context["error"] = "Invalid user"
Arnaud Levaufre's avatar
Arnaud Levaufre committed
47

48
49
    context["next"] = request.GET.get("next", reverse("news:index"))
    return render(request, "login.html", context)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
50

51

MOREAU Ulysse's avatar
MOREAU Ulysse committed
52
@login_required
53
def logout(request: HttpRequest) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
54
    auth.logout(request)
55
    return redirect(reverse("news:index"))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
56

57

58
@login_required
59
def profile(request: HttpRequest) -> HttpResponse:
60
61
62
    return redirect(
        reverse("accounts:show", kwargs={"username": request.user.username})
    )
63
64


MOREAU Ulysse's avatar
MOREAU Ulysse committed
65
@login_required
66
def show(request: HttpRequest, username: str) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
67
    try:
68
        user = User.objects.select_related("profile").get(username=username)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
69
    except User.DoesNotExist:
70
        raise Http404(f"User {username} does not exist")
Arnaud Levaufre's avatar
Arnaud Levaufre committed
71
72

    context = {
73
74
        "user": user,
        "display_name": str(user.profile),
Arnaud Levaufre's avatar
Arnaud Levaufre committed
75
    }
76
    return render(request, "accounts/show.html", context)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
77

78

MOREAU Ulysse's avatar
MOREAU Ulysse committed
79
@permission_required("permissions.default_rights")
80
def edit(request: HttpRequest, username: str) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
81
    if request.user.username != username:
82
        return redirect(reverse("accounts:show", kwargs={"username": username}))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
83
84
85
86
87
88

    try:
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        pass

89
    if request.method == "POST":
Arnaud Levaufre's avatar
Arnaud Levaufre committed
90
        userform = forms.UserForm(request.POST, instance=user)
91
        passwordform = auth.forms.PasswordChangeForm(user, request.POST)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
92
        profileform = forms.ProfileForm(request.POST, instance=user.profile)
93
94
95
        profileimgform = forms.ImageProfileForm(
            request.POST, request.FILES, instance=user.profile
        )
Arnaud Levaufre's avatar
Arnaud Levaufre committed
96
        addressform = forms.AddressForm(request.POST, instance=user.profile.address)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
97
98

        error = False
99
        if userform.has_changed() and userform.is_valid():
Arnaud Levaufre's avatar
Arnaud Levaufre committed
100
            userform.save()
101
        elif userform.has_changed():
Arnaud Levaufre's avatar
Arnaud Levaufre committed
102
            error = True
103
104
        else:
            userform = forms.UserForm(instance=user)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
105

106
107
        if passwordform.has_changed() and passwordform.is_valid():
            passwordform.save()
108
        elif passwordform.has_changed():
109
            error = True
110
        else:
111
            passwordform = auth.forms.PasswordChangeForm(user)
112

Arnaud Levaufre's avatar
Arnaud Levaufre committed
113
114
        if profileform.has_changed() and profileform.is_valid():
            profileform.save()
115
        elif profileform.has_changed():
Arnaud Levaufre's avatar
Arnaud Levaufre committed
116
            error = True
117
118
        else:
            profileform = forms.ProfileForm(instance=user.profile)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
119

120
121
122
123
        if profileimgform.has_changed() and profileimgform.is_valid():
            profileimgform.save()
        elif profileimgform.has_changed():
            error = True
124
125
        else:
            profileimgform = forms.ImageProfileForm(instance=user.profile)
126

Arnaud Levaufre's avatar
Arnaud Levaufre committed
127
128
129
130
        if addressform.has_changed() and addressform.is_valid():
            addressform.save()
        elif addressform.has_changed():
            error = True
131
132
133
        else:
            addressform = forms.AddressForm(instance=user.profile.address)

Arnaud Levaufre's avatar
Arnaud Levaufre committed
134
        if error:
135
            context = {
136
137
138
139
140
                "userform": userform.as_p(),
                "passwordform": passwordform.as_p(),
                "profileform": profileform.as_p(),
                "profileimgform": profileimgform.as_p(),
                "addressform": addressform.as_p(),
141
142
            }

143
            return render(request, "accounts/edit.html", context)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
144
        else:
145
            return redirect(reverse("accounts:edit", kwargs={"username": username}))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
146
147
    else:
        userform = forms.UserForm(instance=user)
148
        passwordform = auth.forms.PasswordChangeForm(user)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
149
        profileform = forms.ProfileForm(instance=user.profile)
150
        profileimgform = forms.ImageProfileForm(instance=user.profile)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
151
        addressform = forms.AddressForm(instance=user.profile.address)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
152
153

        context = {
154
155
156
157
158
            "userform": userform.as_p(),
            "passwordform": passwordform.as_p(),
            "profileform": profileform.as_p(),
            "profileimgform": profileimgform.as_p(),
            "addressform": addressform.as_p(),
Arnaud Levaufre's avatar
Arnaud Levaufre committed
159
160
        }

161
        return render(request, "accounts/edit.html", context)
162

Arnaud Levaufre's avatar
Arnaud Levaufre committed
163

164
def get_contrib(user: User) -> Optional[str]:
165
    try:
166
        return user.contribution.current_type
167
168
169
    except Contributor.DoesNotExist:
        return None

Eijebong's avatar
Eijebong committed
170

MOREAU Ulysse's avatar
MOREAU Ulysse committed
171
@permission_required("permissions.default_rights")
172
@cache_unless("members", methods=["OPTIONS"])
173
def members(request: HttpRequest) -> HttpResponse:
174
    if request.method == "OPTIONS":
175
176
        users = [
            {
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
                "id": user.id,
                "display_name": str(user.profile),
                "picture": user.profile.get_picture_url(),
                "profile_url": user.profile.get_url(),
                "first_name": user.first_name,
                "last_name": user.last_name,
                "username": user.username,
                "nickname": user.profile.nickname,
                "contribution": get_contrib(user),
                "email": user.email,
            }
            for user in User.objects.select_related("profile")
            .select_related("contribution")
            .all()
            .only(
                "id",
                "first_name",
                "last_name",
                "username",
                "profile",
                "email",
                "contribution",
            )
200
        ]
201
        return JsonResponse({"users": users})
Arnaud Levaufre's avatar
Arnaud Levaufre committed
202

203
    return render(request, "accounts/list.html", {})
Arnaud Levaufre's avatar
Arnaud Levaufre committed
204

205

MOREAU Ulysse's avatar
MOREAU Ulysse committed
206
@permission_required("permissions.default_rights")
207
def groups(request: HttpRequest) -> HttpResponse:
208
    if request.method == "OPTIONS":
209
210
        groups = [
            {
211
212
213
214
215
                "id": group.id,
                "name": group.name,
                "color": "#%s" % (hashlib.md5(group.name.encode()).hexdigest()[:6]),
            }
            for group in Group.objects.all()
216
        ]
217
218
        return JsonResponse({"groups": groups})
    return redirect("accounts:list")
219

Arnaud Levaufre's avatar
Arnaud Levaufre committed
220

221
def account_request(request: HttpRequest) -> HttpResponse:
222
    if request.user.is_authenticated:
223
        return redirect(reverse("news:index"))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
224

225
    if request.method == "POST":
Arnaud Levaufre's avatar
Arnaud Levaufre committed
226
227
228
        form = forms.UserRequestForm(request.POST)
        if form.is_valid():
            form.save()
Arnaud Levaufre's avatar
Arnaud Levaufre committed
229
            notify(
230
231
232
                "Une demande de création de compte à été déposée.",
                "accounts:list_request",
                groups=Group.objects.filter(name=settings.BDE_GROUP_NAME),
Arnaud Levaufre's avatar
Arnaud Levaufre committed
233
            )
234
            return redirect(reverse("accounts:confirmation"))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
235
236
237
    else:
        form = forms.UserRequestForm()

238
239
    context = {"form": form}
    return render(request, "accounts/request.html", context)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
240
241


242
@permission_required("accounts.manage_account_request")
243
def list_request(request: HttpRequest, error: Optional[str] = None) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
244
    context = {
245
246
        "requests": models.UserRequest.objects.all(),
        "error": error,
Arnaud Levaufre's avatar
Arnaud Levaufre committed
247
248
    }

249
    return render(request, "accounts/list-request.html", context)
Arnaud Levaufre's avatar
Arnaud Levaufre committed
250
251


Eijebong's avatar
Eijebong committed
252
ACCEPT_MAIL_TPL = """Bonjour {first_name} {last_name},
Arnaud Levaufre's avatar
Arnaud Levaufre committed
253
254
255
256
257
258
259
260

Votre compte enib.net à été créé.
Vous pouvez dés maintenant vous connecter avec votre adresse email et le mot de
passe suivant:

{password}

Pour garantir la sécurité de votre compte nous vous conseillons
Eijebong's avatar
Eijebong committed
261
fortement de le changer dès votre première connexion.
Arnaud Levaufre's avatar
Arnaud Levaufre committed
262
263
264
"""


Eijebong's avatar
Eijebong committed
265
REJECT_MAIL_TPL = """Bonjour {first_name} {last_name},
Arnaud Levaufre's avatar
Arnaud Levaufre committed
266
267
268
269

Votre demande de création de compte sur enib.net à été rejetée.
"""

Eijebong's avatar
Eijebong committed
270

271
@sensitive_variables("password")
272
@permission_required("accounts.manage_account_request")
273
def accept_request(request: HttpRequest, rid: int) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
274
    user_request = get_object_or_404(models.UserRequest, id=rid)
275
276
277
    username = (
        "%s_%s" % (user_request.first_name[0], user_request.last_name[:6])
    ).lower()
Arnaud Levaufre's avatar
Arnaud Levaufre committed
278

279
    if User.objects.filter(email=user_request.email).count() != 0:
280
        return redirect("accounts:list_request", error="email")
281
    if User.objects.filter(username=username).count() != 0:
282
        return redirect("accounts:list_request", error="username")
283

Arnaud Levaufre's avatar
Arnaud Levaufre committed
284
285
    with transaction.atomic():
        user = User.objects.create(
286
            username=username,
Arnaud Levaufre's avatar
Arnaud Levaufre committed
287
288
289
290
291
292
293
294
295
296
297
            email=user_request.email,
            first_name=user_request.first_name,
            last_name=user_request.last_name,
        )

        password = str(uuid.uuid4())
        user.set_password(password)
        user.save()
        user_request.delete()

        send_mail(
298
            "Création de votre compte enib.net",
Arnaud Levaufre's avatar
Arnaud Levaufre committed
299
300
301
302
303
            ACCEPT_MAIL_TPL.format(
                first_name=user_request.first_name,
                last_name=user_request.last_name,
                password=password,
            ),
304
            "bde@enib.fr",
Arnaud Levaufre's avatar
Arnaud Levaufre committed
305
            [user.email],
306
            fail_silently=False,
Arnaud Levaufre's avatar
Arnaud Levaufre committed
307
308
        )

309
    return redirect(reverse("accounts:list_request"))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
310
311


312
@permission_required("accounts.manage_account_request")
313
def reject_request(request: HttpRequest, rid: int) -> HttpResponse:
Arnaud Levaufre's avatar
Arnaud Levaufre committed
314
315
316
317
    user_request = get_object_or_404(models.UserRequest, id=rid)
    user_request.delete()

    send_mail(
318
        "Rejet de votre demande de création de compte enib.net",
Arnaud Levaufre's avatar
Arnaud Levaufre committed
319
320
321
322
        REJECT_MAIL_TPL.format(
            first_name=user_request.first_name,
            last_name=user_request.last_name,
        ),
323
        "bde@enib.fr",
Arnaud Levaufre's avatar
Arnaud Levaufre committed
324
        [user_request.email],
325
        fail_silently=False,
Arnaud Levaufre's avatar
Arnaud Levaufre committed
326
    )
327
    return redirect(reverse("accounts:list_request"))
Arnaud Levaufre's avatar
Arnaud Levaufre committed
328

329

330
def confirmation_request(request: HttpRequest) -> HttpResponse:
331
    return render(request, "accounts/confirmation.html")