Ce serveur Gitlab sera éteint le 30 juin 2020, pensez à migrer vos projets vers les serveurs gitlab-research.centralesupelec.fr et gitlab-student.centralesupelec.fr !

forms.py 19.7 KB
Newer Older
1
# -*- mode: python; coding: utf-8 -*-
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2017  Gabriel Détraz
# Copyright © 2017  Goulven Kermarec
# Copyright © 2017  Augustin Lemesle
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
chirac's avatar
chirac committed
23 24
"""
Definition des forms pour l'application users.
25

chirac's avatar
chirac committed
26 27 28 29 30 31 32
Modification, creation de :
    - un user (informations personnelles)
    - un bannissement
    - le mot de passe d'un user
    - une whiteliste
    - un user de service
"""
33

34
from __future__ import unicode_literals
35 36

from django import forms
37
from django.forms import ModelForm, Form
38
from django.contrib.auth.forms import ReadOnlyPasswordHashField
Gabriel Detraz's avatar
Gabriel Detraz committed
39
from django.core.validators import MinLengthValidator
40
from django.utils import timezone
41
from django.contrib.auth.models import Group, Permission
42

chirac's avatar
chirac committed
43
from preferences.models import OptionalUser
44
from re2o.utils import remove_user_room, get_input_formats_help_text
45 46 47
from re2o.mixins import FormRevMixin
from re2o.field_permissions import FieldPermissionFormMixin

root's avatar
root committed
48 49
from .widgets import DateTimePicker

50 51 52 53 54 55 56 57 58 59 60
from .models import (
    User,
    ServiceUser,
    School,
    ListRight,
    Whitelist,
    ListShell,
    Ban,
    Adherent,
    Club
)
61

62

63
class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
chirac's avatar
chirac committed
64 65 66
    """Formulaire de changement de mot de passe. Verifie que les 2
    nouveaux mots de passe renseignés sont identiques et respectent
    une norme"""
67 68 69 70 71
    selfpasswd = forms.CharField(
        label=u'Saisir le mot de passe existant',
        max_length=255,
        widget=forms.PasswordInput
    )
chirac's avatar
chirac committed
72 73 74 75 76 77 78 79 80 81 82 83
    passwd1 = forms.CharField(
        label=u'Nouveau mot de passe',
        max_length=255,
        validators=[MinLengthValidator(8)],
        widget=forms.PasswordInput
    )
    passwd2 = forms.CharField(
        label=u'Saisir à nouveau le mot de passe',
        max_length=255,
        validators=[MinLengthValidator(8)],
        widget=forms.PasswordInput
    )
84

85 86 87 88
    class Meta:
        model = User
        fields = []

89
    def clean_passwd2(self):
chirac's avatar
chirac committed
90
        """Verifie que passwd1 et 2 sont identiques"""
91 92 93 94
        # Check that the two password entries match
        password1 = self.cleaned_data.get("passwd1")
        password2 = self.cleaned_data.get("passwd2")
        if password1 and password2 and password1 != password2:
95 96 97
            raise forms.ValidationError(
                "Les 2 nouveaux mots de passe sont différents"
            )
98
        return password2
99

100 101
    def clean_selfpasswd(self):
        """Verifie si il y a lieu que le mdp self est correct"""
102
        if not self.instance.check_password(
103 104
                self.cleaned_data.get("selfpasswd")
            ):
105 106 107 108 109 110 111 112 113
            raise forms.ValidationError("Le mot de passe actuel est incorrect")
        return

    def save(self, commit=True):
        """Changement du mot de passe"""
        user = super(PassForm, self).save(commit=False)
        user.set_password(self.cleaned_data.get("passwd1"))
        user.save()

chirac's avatar
chirac committed
114

115
class UserCreationForm(FormRevMixin, forms.ModelForm):
116
    """A form for creating new users. Includes all the required
chirac's avatar
chirac committed
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
    fields, plus a repeated password.

    Formulaire pour la création d'un user. N'est utilisé que pour
    l'admin, lors de la creation d'un user par admin. Inclu tous les
    champs obligatoires"""
    password1 = forms.CharField(
        label='Password',
        widget=forms.PasswordInput,
        validators=[MinLengthValidator(8)],
        max_length=255
    )
    password2 = forms.CharField(
        label='Password confirmation',
        widget=forms.PasswordInput,
        validators=[MinLengthValidator(8)],
        max_length=255
    )
134 135
    is_admin = forms.BooleanField(label='is admin')

136
    def __init__(self, *args, **kwargs):
137
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
138 139
        super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs)

140
    class Meta:
141
        model = Adherent
142
        fields = ('pseudo', 'surname', 'email')
143 144

    def clean_password2(self):
chirac's avatar
chirac committed
145
        """Verifie que password1 et 2 sont identiques"""
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        user.save()
        user.is_admin = self.cleaned_data.get("is_admin")
        return user

chirac's avatar
chirac committed
161

162
class ServiceUserCreationForm(FormRevMixin, forms.ModelForm):
chirac's avatar
chirac committed
163
    """A form for creating new users. Includes all the required
chirac's avatar
chirac committed
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
    fields, plus a repeated password.

    Formulaire pour la creation de nouveaux serviceusers.
    Requiert seulement un mot de passe; et un pseudo"""
    password1 = forms.CharField(
        label='Password',
        widget=forms.PasswordInput,
        min_length=8,
        max_length=255
    )
    password2 = forms.CharField(
        label='Password confirmation',
        widget=forms.PasswordInput,
        min_length=8,
        max_length=255
    )
chirac's avatar
chirac committed
180

181
    def __init__(self, *args, **kwargs):
182
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
chirac's avatar
chirac committed
183 184 185 186 187
        super(ServiceUserCreationForm, self).__init__(
            *args,
            prefix=prefix,
            **kwargs
        )
188

chirac's avatar
chirac committed
189 190 191 192 193
    class Meta:
        model = ServiceUser
        fields = ('pseudo',)

    def clean_password2(self):
chirac's avatar
chirac committed
194
        """Verifie que password1 et 2 sont indentiques"""
chirac's avatar
chirac committed
195 196 197 198 199 200 201 202 203 204 205 206 207
        # Check that the two password entries match
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        if password1 and password2 and password1 != password2:
            raise forms.ValidationError("Passwords don't match")
        return password2

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(ServiceUserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        user.save()
        return user
208

chirac's avatar
chirac committed
209

210
class UserChangeForm(FormRevMixin, forms.ModelForm):
211 212 213
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
chirac's avatar
chirac committed
214 215

    Formulaire pour la modification d'un user coté admin
216 217 218 219 220
    """
    password = ReadOnlyPasswordHashField()
    is_admin = forms.BooleanField(label='is admin', required=False)

    class Meta:
221
        model = Adherent
222
        fields = ('pseudo', 'password', 'surname', 'email')
223 224

    def __init__(self, *args, **kwargs):
225
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
226
        super(UserChangeForm, self).__init__(*args, prefix=prefix, **kwargs)
227 228 229 230
        print("User is admin : %s" % kwargs['instance'].is_admin)
        self.initial['is_admin'] = kwargs['instance'].is_admin

    def clean_password(self):
chirac's avatar
chirac committed
231
        """Dummy fun"""
232 233 234 235 236 237 238 239 240 241 242 243
        # Regardless of what the user provides, return the initial value.
        # This is done here, rather than on the field, because the
        # field does not have access to the initial value
        return self.initial["password"]

    def save(self, commit=True):
        # Save the provided password in hashed format
        user = super(UserChangeForm, self).save(commit=False)
        user.is_admin = self.cleaned_data.get("is_admin")
        if commit:
            user.save()
        return user
chirac's avatar
chirac committed
244

chirac's avatar
chirac committed
245

246
class ServiceUserChangeForm(FormRevMixin, forms.ModelForm):
chirac's avatar
chirac committed
247 248 249
    """A form for updating users. Includes all the fields on
    the user, but replaces the password field with admin's
    password hash display field.
chirac's avatar
chirac committed
250 251

    Formulaire pour l'edition des service users coté admin
chirac's avatar
chirac committed
252 253 254
    """
    password = ReadOnlyPasswordHashField()

255
    def __init__(self, *args, **kwargs):
256
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
chirac's avatar
chirac committed
257 258 259 260 261
        super(ServiceUserChangeForm, self).__init__(
            *args,
            prefix=prefix,
            **kwargs
        )
262

chirac's avatar
chirac committed
263 264 265 266 267
    class Meta:
        model = ServiceUser
        fields = ('pseudo',)

    def clean_password(self):
chirac's avatar
chirac committed
268
        """Dummy fun"""
chirac's avatar
chirac committed
269 270
        return self.initial["password"]

chirac's avatar
chirac committed
271

chirac's avatar
chirac committed
272
class ResetPasswordForm(forms.Form):
chirac's avatar
chirac committed
273 274
    """Formulaire de demande de reinitialisation de mot de passe,
    mdp oublié"""
chirac's avatar
chirac committed
275 276
    pseudo = forms.CharField(label=u'Pseudo', max_length=255)
    email = forms.EmailField(max_length=255)
277

chirac's avatar
chirac committed
278

279
class MassArchiveForm(forms.Form):
chirac's avatar
chirac committed
280 281 282
    """Formulaire d'archivage des users inactif. Prend en argument
    du formulaire la date de depart avant laquelle archiver les
    users"""
283 284 285
    date = forms.DateTimeField(help_text='%d/%m/%y')

    def clean(self):
chirac's avatar
chirac committed
286
        cleaned_data = super(MassArchiveForm, self).clean()
287 288
        date = cleaned_data.get("date")
        if date:
289
            if date > timezone.now():
chirac's avatar
chirac committed
290 291 292
                raise forms.ValidationError("Impossible d'archiver des\
                utilisateurs dont la fin d'accès se situe dans le futur !")

293

294
class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
chirac's avatar
chirac committed
295 296 297
    """Formulaire de base d'edition d'un user. Formulaire de base, utilisé
    pour l'edition de self par self ou un cableur. On formate les champs
    avec des label plus jolis"""
298
    def __init__(self, *args, **kwargs):
299
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
300
        super(AdherentForm, self).__init__(*args, prefix=prefix, **kwargs)
301 302 303 304 305 306 307 308 309
        self.fields['name'].label = 'Prénom'
        self.fields['surname'].label = 'Nom'
        self.fields['school'].label = 'Établissement'
        self.fields['comment'].label = 'Commentaire'
        self.fields['room'].label = 'Chambre'
        self.fields['room'].empty_label = "Pas de chambre"
        self.fields['school'].empty_label = "Séléctionner un établissement"

    class Meta:
310
        model = Adherent
311 312 313 314 315 316 317 318
        fields = [
            'name',
            'surname',
            'pseudo',
            'email',
            'school',
            'comment',
            'room',
319
            'shell',
320 321 322 323
            'telephone',
        ]

    def clean_telephone(self):
chirac's avatar
chirac committed
324 325
        """Verifie que le tel est présent si 'option est validée
        dans preferences"""
326
        telephone = self.cleaned_data['telephone']
327
        if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'):
chirac's avatar
chirac committed
328 329 330
            raise forms.ValidationError(
                "Un numéro de téléphone valide est requis"
            )
331 332
        return telephone

333 334 335 336 337 338 339 340 341 342
    force = forms.BooleanField(
        label="Forcer le déménagement ?",
        initial=False,
        required=False
    )

    def clean_force(self):
        """On supprime l'ancien user de la chambre si et seulement si la
        case est cochée"""
        if self.cleaned_data.get('force', False):
343
            remove_user_room(self.cleaned_data.get('room'))
344
        return
chirac's avatar
chirac committed
345

346

347
class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
348 349 350 351 352
    """Formulaire de base d'edition d'un user. Formulaire de base, utilisé
    pour l'edition de self par self ou un cableur. On formate les champs
    avec des label plus jolis"""
    def __init__(self, *args, **kwargs):
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
353
        super(ClubForm, self).__init__(*args, prefix=prefix, **kwargs)
354 355 356
        self.fields['surname'].label = 'Nom'
        self.fields['school'].label = 'Établissement'
        self.fields['comment'].label = 'Commentaire'
357
        self.fields['room'].label = 'Local'
358 359
        self.fields['room'].empty_label = "Pas de chambre"
        self.fields['school'].empty_label = "Séléctionner un établissement"
360
        self.fields['mailing'].label = 'Utiliser une mailing'
361 362 363 364 365 366 367 368 369 370 371

    class Meta:
        model = Club
        fields = [
            'surname',
            'pseudo',
            'email',
            'school',
            'comment',
            'room',
            'telephone',
372
            'shell',
373
            'mailing'
374 375 376 377 378 379
        ]

    def clean_telephone(self):
        """Verifie que le tel est présent si 'option est validée
        dans preferences"""
        telephone = self.cleaned_data['telephone']
380
        if not telephone and OptionalUser.get_cached_value('is_tel_mandatory'):
381 382 383 384 385 386
            raise forms.ValidationError(
                "Un numéro de téléphone valide est requis"
            )
        return telephone


387
class ClubAdminandMembersForm(FormRevMixin, ModelForm):
388 389 390 391 392 393 394 395
    """Permet d'éditer la liste des membres et des administrateurs
    d'un club"""
    class Meta:
        model = Club
        fields = ['administrators', 'members']

    def __init__(self, *args, **kwargs):
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
396 397 398 399 400
        super(ClubAdminandMembersForm, self).__init__(
            *args,
            prefix=prefix,
            **kwargs
        )
401 402


403
class PasswordForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
404 405
    """ Formulaire de changement brut de mot de passe.
    Ne pas utiliser sans traitement"""
406 407 408 409
    class Meta:
        model = User
        fields = ['password', 'pwd_ntlm']

410
    def __init__(self, *args, **kwargs):
411
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
412 413
        super(PasswordForm, self).__init__(*args, prefix=prefix, **kwargs)

chirac's avatar
chirac committed
414

415
class ServiceUserForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
416
    """ Modification d'un service user"""
chirac's avatar
chirac committed
417 418 419 420 421 422 423
    password = forms.CharField(
        label=u'Nouveau mot de passe',
        max_length=255,
        validators=[MinLengthValidator(8)],
        widget=forms.PasswordInput,
        required=False
    )
424 425 426

    class Meta:
        model = ServiceUser
427
        fields = ('pseudo', 'access_group','comment')
428

429
    def __init__(self, *args, **kwargs):
430
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
431 432
        super(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs)

433 434 435 436 437 438 439
    def save(self, commit=True):
        """Changement du mot de passe"""
        user = super(ServiceUserForm, self).save(commit=False)
        if self.cleaned_data['password']:
            user.set_password(self.cleaned_data.get("password"))
        user.save()

chirac's avatar
chirac committed
440

441
class EditServiceUserForm(ServiceUserForm):
chirac's avatar
chirac committed
442 443
    """Formulaire d'edition de base d'un service user. Ne permet
    d'editer que son group d'acl et son commentaire"""
444
    class Meta(ServiceUserForm.Meta):
chirac's avatar
chirac committed
445 446
        fields = ['access_group', 'comment']

447

448
class StateForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
449
    """ Changement de l'état d'un user"""
450 451 452 453
    class Meta:
        model = User
        fields = ['state']

454
    def __init__(self, *args, **kwargs):
455
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
456 457
        super(StateForm, self).__init__(*args, prefix=prefix, **kwargs)

458

Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
459
class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
460 461 462 463 464 465 466 467 468
    """ Gestion des groupes d'un user"""
    groups = forms.ModelMultipleChoiceField(
        Group.objects.all(),
        widget=forms.CheckboxSelectMultiple,
        required=False
    )

    class Meta:
        model = User
469
        fields = ['is_superuser', 'groups']
470 471 472 473

    def __init__(self, *args, **kwargs):
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
        super(GroupForm, self).__init__(*args, prefix=prefix, **kwargs)
Levy--Falk Hugo's avatar
Levy--Falk Hugo committed
474 475
        if 'is_superuser' in self.fields:
            self.fields['is_superuser'].label = "Superuser"
476 477


478
class SchoolForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
479
    """Edition, creation d'un école"""
480 481 482 483 484
    class Meta:
        model = School
        fields = ['name']

    def __init__(self, *args, **kwargs):
485
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
486
        super(SchoolForm, self).__init__(*args, prefix=prefix, **kwargs)
487 488
        self.fields['name'].label = 'Établissement'

chirac's avatar
chirac committed
489

490
class ShellForm(FormRevMixin, ModelForm):
491 492 493 494 495 496 497 498 499 500 501
    """Edition, creation d'un école"""
    class Meta:
        model = ListShell
        fields = ['shell']

    def __init__(self, *args, **kwargs):
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
        super(ShellForm, self).__init__(*args, prefix=prefix, **kwargs)
        self.fields['shell'].label = 'Nom du shell'


502
class ListRightForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
503 504
    """Edition, d'un groupe , équivalent à un droit
    Ne peremet pas d'editer le gid, car il sert de primary key"""
505
    permissions = forms.ModelMultipleChoiceField(
506
        Permission.objects.all().select_related('content_type'),
507 508 509 510
        widget=forms.CheckboxSelectMultiple,
        required=False
    )

511 512
    class Meta:
        model = ListRight
513
        fields = ['name', 'unix_name', 'critical', 'permissions', 'details']
514 515

    def __init__(self, *args, **kwargs):
516
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
517
        super(ListRightForm, self).__init__(*args, prefix=prefix, **kwargs)
518
        self.fields['unix_name'].label = 'Nom du droit/groupe'
519

chirac's avatar
chirac committed
520

521
class NewListRightForm(ListRightForm):
chirac's avatar
chirac committed
522
    """Ajout d'un groupe/list de droit """
523 524 525 526 527
    class Meta(ListRightForm.Meta):
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(NewListRightForm, self).__init__(*args, **kwargs)
chirac's avatar
chirac committed
528 529 530
        self.fields['gid'].label = 'Gid, attention, cet attribut ne doit\
        pas être modifié après création'

531

532
class DelListRightForm(Form):
chirac's avatar
chirac committed
533 534
    """Suppression d'un ou plusieurs groupes"""
    listrights = forms.ModelMultipleChoiceField(
535
        queryset=ListRight.objects.none(),
chirac's avatar
chirac committed
536 537 538 539
        label="Droits actuels",
        widget=forms.CheckboxSelectMultiple
    )

540 541 542 543
    def __init__(self, *args, **kwargs):
        instances = kwargs.pop('instances', None)
        super(DelListRightForm, self).__init__(*args, **kwargs)
        if instances:
544
            self.fields['listrights'].queryset = instances
545
        else:
546
            self.fields['listrights'].queryset = ListRight.objects.all()
547

548

549
class DelSchoolForm(Form):
chirac's avatar
chirac committed
550 551
    """Suppression d'une ou plusieurs écoles"""
    schools = forms.ModelMultipleChoiceField(
552
        queryset=School.objects.none(),
chirac's avatar
chirac committed
553 554 555 556
        label="Etablissements actuels",
        widget=forms.CheckboxSelectMultiple
    )

557
    def __init__(self, *args, **kwargs):
558
        instances = kwargs.pop('instances', None)
559
        super(DelSchoolForm, self).__init__(*args, **kwargs)
560 561 562 563
        if instances:
            self.fields['schools'].queryset = instances
        else:
            self.fields['schools'].queryset = School.objects.all()
564

565

566
class BanForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
567
    """Creation, edition d'un objet bannissement"""
568
    def __init__(self, *args, **kwargs):
569
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
570
        super(BanForm, self).__init__(*args, prefix=prefix, **kwargs)
571
        self.fields['date_end'].label = 'Date de fin'
572
        self.fields['date_end'].localize = False
573 574 575 576

    class Meta:
        model = Ban
        exclude = ['user']
577
        widgets = {'date_end':DateTimePicker}
578 579


580
class WhitelistForm(FormRevMixin, ModelForm):
chirac's avatar
chirac committed
581
    """Creation, edition d'un objet whitelist"""
582
    def __init__(self, *args, **kwargs):
583
        prefix = kwargs.pop('prefix', self.Meta.model.__name__)
584
        super(WhitelistForm, self).__init__(*args, prefix=prefix, **kwargs)
585
        self.fields['date_end'].label = 'Date de fin'
root's avatar
root committed
586
        self.fields['date_end'].localize = False
587 588 589 590

    class Meta:
        model = Whitelist
        exclude = ['user']
root's avatar
root committed
591
        widgets = {'date_end':DateTimePicker}