models.py 30.3 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 23
# 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.

24 25
from __future__ import unicode_literals

lhark's avatar
lhark committed
26
from django.db import models
27
from django.db.models import Q
28
from django import forms
29 30
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
31
from django.utils.functional import cached_property
32 33 34
from django.template import Context, RequestContext, loader
from django.core.mail import send_mail
from django.core.urlresolvers import reverse
35

36 37 38
from reversion import revisions as reversion
from django.db import transaction

39 40 41
import ldapdb.models
import ldapdb.models.fields

42
from re2o.settings import RIGHTS_LINK, LDAP, GID_RANGES,UID_RANGES
43 44
import re, uuid
import datetime
lhark's avatar
lhark committed
45

46
from django.utils import timezone
47
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
48

49
from django.core.validators import MinLengthValidator
50
from topologie.models import Room
chibrac's avatar
chibrac committed
51
from cotisations.models import Cotisation, Facture, Paiement, Vente
52
from machines.models import Domain, Interface, MachineType, Machine, Nas, MachineType, regen
53
from preferences.models import GeneralOption, AssoOption, OptionalUser, OptionalMachine, MailMessageOption
54

55 56
now = timezone.now()

chirac's avatar
chirac committed
57 58 59 60 61 62 63 64 65
def remove_user_room(room):
    """ Déménage de force l'ancien locataire de la chambre """
    try:
        user = User.objects.get(room=room)
    except User.DoesNotExist:
        return
    user.room = None
    user.save()

66 67

def linux_user_check(login):
chirac's avatar
chirac committed
68
    """ Validation du pseudo pour respecter les contraintes unix"""
69
    UNIX_LOGIN_PATTERN = re.compile("^[a-zA-Z0-9-]*[$]?$")
70 71 72 73 74
    return UNIX_LOGIN_PATTERN.match(login)


def linux_user_validator(login):
    if not linux_user_check(login):
chirac's avatar
chirac committed
75 76 77 78 79
        raise forms.ValidationError(
                ", ce pseudo ('%(label)s') contient des carractères interdits",
                params={'label': login},
        )

80 81
def get_fresh_user_uid():
    uids = list(range(int(min(UID_RANGES['users'])),int(max(UID_RANGES['users']))))
82
    try:
83
        used_uids = list(User.objects.values_list('uid_number', flat=True))
84 85
    except:
        used_uids = []
86 87 88 89 90
    free_uids = [ id for id in uids if id not in used_uids]
    return min(free_uids)

def get_fresh_gid():
    gids = list(range(int(min(GID_RANGES['posix'])),int(max(GID_RANGES['posix']))))
91
    used_gids = list(ListRight.objects.values_list('gid', flat=True))
92 93
    free_gids = [ id for id in gids if id not in used_gids]
    return min(free_gids)
94 95 96 97 98 99

def get_admin_right():
    try:
        admin_right = ListRight.objects.get(listright="admin")
    except ListRight.DoesNotExist:
        admin_right = ListRight(listright="admin")
100
        admin_right.gid = get_fresh_gid()
101 102 103
        admin_right.save()
    return admin_right

104 105
def all_adherent(search_time=now):
    return User.objects.filter(facture__in=Facture.objects.filter(vente__in=Vente.objects.filter(cotisation__in=Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.all().exclude(valid=False))).filter(date_end__gt=search_time)))).distinct()
106

107 108
def all_baned(search_time=now):
    return User.objects.filter(ban__in=Ban.objects.filter(date_end__gt=search_time)).distinct() 
109

110 111
def all_whitelisted(search_time=now):
    return User.objects.filter(whitelist__in=Whitelist.objects.filter(date_end__gt=search_time)).distinct()
112

113 114
def all_has_access(search_time=now):
    return User.objects.filter(Q(state=User.STATE_ACTIVE) & ~Q(ban__in=Ban.objects.filter(date_end__gt=timezone.now())) & (Q(whitelist__in=Whitelist.objects.filter(date_end__gt=timezone.now())) | Q(facture__in=Facture.objects.filter(vente__in=Vente.objects.filter(cotisation__in=Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.all().exclude(valid=False))).filter(date_end__gt=search_time)))))).distinct()
115 116 117 118 119 120 121

class UserManager(BaseUserManager):
    def _create_user(self, pseudo, name, surname, email, password=None, su=False):
        if not pseudo:
            raise ValueError('Users must have an username')

        if not linux_user_check(pseudo):
122
            raise ValueError('Username shall only contain [a-z0-9-]')
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

        user = self.model(
            pseudo=pseudo,
            name=name,
            surname=surname,
            email=self.normalize_email(email),
        )

        user.set_password(password)
        user.save(using=self._db)
        if su:
            user.make_admin()
        return user

    def create_user(self, pseudo, name, surname, email, password=None):
        """
        Creates and saves a User with the given pseudo, name, surname, email,
        and password.
        """
        return self._create_user(pseudo, name, surname, email, password, False)

    def create_superuser(self, pseudo, name, surname, email, password):
        """
        Creates and saves a superuser with the given pseudo, name, surname,
        email, and password.
        """
        return self._create_user(pseudo, name, surname, email, password, True)


class User(AbstractBaseUser):
153
    PRETTY_NAME = "Utilisateurs"
lhark's avatar
lhark committed
154
    STATE_ACTIVE = 0
chirac's avatar
chirac committed
155 156
    STATE_DISABLED = 1
    STATE_ARCHIVE = 2
lhark's avatar
lhark committed
157
    STATES = (
chirac's avatar
chirac committed
158
            (0, 'STATE_ACTIVE'),
chirac's avatar
chirac committed
159 160
            (1, 'STATE_DISABLED'),
            (2, 'STATE_ARCHIVE'),
lhark's avatar
lhark committed
161 162
            )

chirac's avatar
chirac committed
163
    def auto_uid():
164
        return get_fresh_user_uid()
chirac's avatar
chirac committed
165

lhark's avatar
lhark committed
166 167
    name = models.CharField(max_length=255)
    surname = models.CharField(max_length=255)
168
    pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
lhark's avatar
lhark committed
169
    email = models.EmailField()
root's avatar
root committed
170
    school = models.ForeignKey('School', on_delete=models.PROTECT, null=True, blank=True)
171
    shell = models.ForeignKey('ListShell', on_delete=models.PROTECT, null=True, blank=True)
172
    comment = models.CharField(help_text="Commentaire, promo", max_length=255, blank=True)
173
    room = models.OneToOneField('topologie.Room', on_delete=models.PROTECT, blank=True, null=True)
lhark's avatar
lhark committed
174
    pwd_ntlm = models.CharField(max_length=255)
175
    state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
176
    registered = models.DateTimeField(auto_now_add=True)
Gabriel Detraz's avatar
Gabriel Detraz committed
177
    telephone = models.CharField(max_length=15, blank=True, null=True)
chirac's avatar
chirac committed
178
    uid_number = models.IntegerField(default=auto_uid, unique=True)
root's avatar
root committed
179
    rezo_rez_uid =  models.IntegerField(unique=True, blank=True, null=True)
lhark's avatar
lhark committed
180

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    USERNAME_FIELD = 'pseudo'
    REQUIRED_FIELDS = ['name', 'surname', 'email']

    objects = UserManager()

    @property
    def is_active(self):
        return self.state == self.STATE_ACTIVE

    @property
    def is_staff(self):
        return self.is_admin

    @property
    def is_admin(self):
        try:
            Right.objects.get(user=self, right__listright='admin')
        except Right.DoesNotExist:
            return False
        return True

    @is_admin.setter
    def is_admin(self, value):
        if value and not self.is_admin:
            self.make_admin()
        elif not value and self.is_admin:
            self.un_admin()

    def get_full_name(self):
        return '%s %s' % (self.name, self.surname)

    def get_short_name(self):
        return self.name

215 216
    def has_perms(self, perms, obj=None):
        for perm in perms:
217 218 219 220 221 222
            if perm in RIGHTS_LINK:
                query = Q()
                for right in RIGHTS_LINK[perm]:
                    query = query | Q(right__listright=right)
                if Right.objects.filter(Q(user=self) & query):
                    return True 
223 224 225 226
            try:
                Right.objects.get(user=self, right__listright=perm)
            except Right.DoesNotExist:
                return False
227 228
        return True

229 230 231
    def has_perm(self, perm, obj=None):
        return True

232 233

    def has_right(self, right):
234 235 236 237 238 239
        try:
            list_right = ListRight.objects.get(listright=right)
        except:
            list_right = ListRight(listright=right, gid=get_fresh_gid())
            list_right.save()
        return Right.objects.filter(user=self).filter(right=list_right).exists()
240 241 242

    @cached_property
    def is_bureau(self):
243
        return self.has_right('bureau')
244 245 246

    @cached_property
    def is_bofh(self):
247
        return self.has_right('bofh')
248 249 250 251 252 253 254

    @cached_property
    def is_cableur(self):
        return self.has_right('cableur') or self.has_right('bureau') or self.has_right('infra') or self.has_right('bofh')

    @cached_property
    def is_trez(self):
255
        return self.has_right('trésorier')
256 257 258

    @cached_property
    def is_infra(self):
259
        return self.has_right('infra')
260

261
    def end_adhesion(self):
262
        date_max = Cotisation.objects.filter(vente__in=Vente.objects.filter(facture__in=Facture.objects.filter(user=self).exclude(valid=False))).aggregate(models.Max('date_end'))['date_end__max']
263 264 265
        return date_max

    def is_adherent(self):
Gabriel Detraz's avatar
Gabriel Detraz committed
266
        end = self.end_adhesion()
267 268 269 270 271 272 273
        if not end:
            return False
        elif end < timezone.now():
            return False
        else:
            return True

274
    @cached_property
275 276
    def end_ban(self):
        """ Renvoie la date de fin de ban d'un user, False sinon """
277
        date_max = Ban.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
278 279
        return date_max

280
    @cached_property
281
    def end_whitelist(self):
282
        """ Renvoie la date de fin de whitelist d'un user, False sinon """
283
        date_max = Whitelist.objects.filter(user=self).aggregate(models.Max('date_end'))['date_end__max']
284 285
        return date_max

286
    @cached_property
287 288
    def is_ban(self):
        """ Renvoie si un user est banni ou non """
289
        end = self.end_ban
290 291 292 293 294 295 296
        if not end:
            return False
        elif end < timezone.now():
            return False
        else:
            return True

297
    @cached_property
298 299
    def is_whitelisted(self):
        """ Renvoie si un user est whitelisté ou non """
300
        end = self.end_whitelist
301 302 303 304 305 306 307 308 309 310
        if not end:
            return False
        elif end < timezone.now():
            return False
        else:
            return True

    def has_access(self):
        """ Renvoie si un utilisateur a accès à internet """
        return self.state == User.STATE_ACTIVE \
Gabriel Detraz's avatar
Gabriel Detraz committed
311
            and not self.is_ban and (self.is_adherent() or self.is_whitelisted)
312

313 314
    def end_access(self):
        """ Renvoie la date de fin normale d'accès (adhésion ou whiteliste)"""
Gabriel Detraz's avatar
Gabriel Detraz committed
315
        if not self.end_adhesion():
316
            if not self.end_whitelist:
317 318
                return None
            else:
319
                return self.end_whitelist
320
        else:
321
            if not self.end_whitelist:
Gabriel Detraz's avatar
Gabriel Detraz committed
322
                return self.end_adhesion()
323
            else:        
Gabriel Detraz's avatar
Gabriel Detraz committed
324
                return max(self.end_adhesion(), self.end_whitelist)
325

chibrac's avatar
chibrac committed
326 327 328 329 330 331 332 333 334 335 336 337
    @cached_property
    def solde(self):
        options, created = OptionalUser.objects.get_or_create()
        user_solde = options.user_solde
        if user_solde:
            solde_object, created=Paiement.objects.get_or_create(moyen='Solde')
            somme_debit = Vente.objects.filter(facture__in=Facture.objects.filter(user=self, paiement=solde_object)).aggregate(total=models.Sum(models.F('prix')*models.F('number'), output_field=models.FloatField()))['total'] or 0
            somme_credit =Vente.objects.filter(facture__in=Facture.objects.filter(user=self), name="solde").aggregate(total=models.Sum(models.F('prix')*models.F('number'), output_field=models.FloatField()))['total'] or 0
            return somme_credit - somme_debit
        else:
            return 0

338
    def user_interfaces(self):
339
        return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=True))
340

341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    def assign_ips(self):
        """ Assign une ipv4 aux machines d'un user """
        interfaces = self.user_interfaces()
        for interface in interfaces:
            if not interface.ipv4:
                with transaction.atomic(), reversion.create_revision():
                    interface.assign_ipv4()
                    reversion.set_comment("Assignation ipv4")
                    interface.save()

    def unassign_ips(self):
        interfaces = self.user_interfaces()
        for interface in interfaces:
            with transaction.atomic(), reversion.create_revision():
                interface.unassign_ipv4()
                reversion.set_comment("Désassignation ipv4")
                interface.save()

    def archive(self):
        self.unassign_ips()
        self.state = User.STATE_ARCHIVE 

    def unarchive(self):
        self.assign_ips()
        self.state = User.STATE_ACTIVE

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
    def has_module_perms(self, app_label):
        # Simplest version again
        return True

    def make_admin(self):
        """ Make User admin """
        user_admin_right = Right(user=self, right=get_admin_right())
        user_admin_right.save()

    def un_admin(self):
        try:
            user_right = Right.objects.get(user=self,right=get_admin_right())
        except Right.DoesNotExist:
            return
        user_right.delete()

383
    def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True):
chirac's avatar
chirac committed
384
        self.refresh_from_db()
385
        try:
root's avatar
root committed
386
            user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
387
        except LdapUser.DoesNotExist:
root's avatar
root committed
388
            user_ldap = LdapUser(uidNumber=self.uid_number)
389
        if base:
chirac's avatar
chirac committed
390
            user_ldap.name = self.pseudo
391
            user_ldap.sn = self.pseudo
Gabriel Detraz's avatar
Gabriel Detraz committed
392
            user_ldap.dialupAccess = str(self.has_access())
393 394 395
            user_ldap.home_directory = '/home/' + self.pseudo
            user_ldap.mail = self.email
            user_ldap.given_name = str(self.surname).lower() + '_' + str(self.name).lower()[:3]
396
            user_ldap.gid = LDAP['user_gid']
397
            user_ldap.user_password = self.password[:6] + self.password[7:]
Gabriel Detraz's avatar
Gabriel Detraz committed
398
            user_ldap.sambat_nt_password = self.pwd_ntlm.upper()
chirac's avatar
chirac committed
399
            if self.shell:
chibrac's avatar
chibrac committed
400 401 402 403 404
                user_ldap.login_shell = self.shell.shell
            if self.state == self.STATE_DISABLED:
                user_ldap.shadowexpire = str(0)
            else:
                user_ldap.shadowexpire = None
405
        if access_refresh:
Gabriel Detraz's avatar
Gabriel Detraz committed
406
            user_ldap.dialupAccess = str(self.has_access())
407
        if mac_refresh:
408
            user_ldap.macs = [inter.mac_bare() for inter in Interface.objects.filter(machine__in=Machine.objects.filter(user=self))]
409 410 411 412 413 414 415 416 417
        user_ldap.save()

    def ldap_del(self):
        try:
            user_ldap = LdapUser.objects.get(name=self.pseudo)
            user_ldap.delete()
        except LdapUser.DoesNotExist:
            pass

418 419 420
    def notif_inscription(self):
        """ Prend en argument un objet user, envoie un mail de bienvenue """
        t = loader.get_template('users/email_welcome')
421 422
        assooptions, created = AssoOption.objects.get_or_create()
        mailmessageoptions, created = MailMessageOption.objects.get_or_create()
423 424 425
        general_options, created = GeneralOption.objects.get_or_create()
        c = Context({
            'nom': str(self.name) + ' ' + str(self.surname),
426 427 428 429
            'asso_name': assooptions.name,
            'asso_email': assooptions.contact,
            'welcome_mail_fr' : mailmessageoptions.welcome_mail_fr,
            'welcome_mail_en' : mailmessageoptions.welcome_mail_en,
430 431
            'pseudo':self.pseudo,
        })
432
        send_mail('Bienvenue au %(name)s / Welcome to %(name)s' % {'name': assooptions.name }, '',
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
        general_options.email_from, [self.email], html_message=t.render(c))
        return

    def reset_passwd_mail(self, request):
        """ Prend en argument un request, envoie un mail de réinitialisation de mot de pass """
        req = Request()
        req.type = Request.PASSWD
        req.user = self
        req.save()
        t = loader.get_template('users/email_passwd_request')
        options, created = AssoOption.objects.get_or_create()
        general_options, created = GeneralOption.objects.get_or_create()
        c = {
            'name': str(req.user.name) + ' ' + str(req.user.surname),
            'asso': options.name,
            'asso_mail': options.contact,
            'site_name': general_options.site_name,
            'url': request.build_absolute_uri(
            reverse('users:process', kwargs={'token': req.token})),
            'expire_in': str(general_options.req_expire_hrs) + ' heures',
            }
        send_mail('Changement de mot de passe du %(name)s / Password renewal for %(name)s' % {'name': options.name }, t.render(c),
        general_options.email_from, [req.user.email], fail_silently=False)
        return

458
    def autoregister_machine(self, mac_address, nas_ip):
459 460
        all_machines = self.all_machines()
        options, created = OptionalMachine.objects.get_or_create() 
461
        if all_machines.count() > options.max_lambdauser_interfaces:
462
            return False, "Maximum de machines enregistrees atteinte"
463 464 465 466
        nas_object = Nas.objects.filter(nas_type__in=MachineType.objects.filter(ip_type=nas_ip.ip_type))
        if not nas_object:
            return False, "Re2o ne sait pas à quel machinetype affecter cette machine"
        machine_type_cible = nas_object.first().machine_type
467 468 469 470 471
        try:
            machine_parent = Machine()
            machine_parent.user = self
            interface_cible = Interface()
            interface_cible.mac_address = mac_address
472
            interface_cible.type = machine_type_cible
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
            interface_cible.clean()
            machine_parent.clean()
            domain = Domain()
            domain.name = self.pseudo.replace('_','-').lower() + str(all_machines.count())
            with transaction.atomic(), reversion.create_revision():
                machine_parent.save()
                interface_cible.machine = machine_parent
                interface_cible.save()
                domain.interface_parent = interface_cible
                domain.clean()
                domain.save()
                reversion.set_comment("Autocapture radius")
        except Exception as e:
            return False, e
        return True, "Ok"

    def all_machines(self):
490
        return Interface.objects.filter(machine__in=Machine.objects.filter(user=self))
491

492
    def __str__(self):
493
        return self.pseudo
lhark's avatar
lhark committed
494

495 496
@receiver(post_save, sender=User)
def user_post_save(sender, **kwargs):
497
    is_created = kwargs['created']
498
    user = kwargs['instance']
499 500
    if is_created:
        user.notif_inscription()
501
    user.ldap_sync(base=True, access_refresh=True, mac_refresh=False)
502 503 504 505

@receiver(post_delete, sender=User)
def user_post_delete(sender, **kwargs):
    user = kwargs['instance']
506
    user.ldap_del()
507

chirac's avatar
chirac committed
508
class ServiceUser(AbstractBaseUser):
509 510 511 512 513 514 515
    readonly = 'readonly'
    ACCESS = (
            ('auth', 'auth'),
            ('readonly', 'readonly'),
            ('usermgmt', 'usermgmt'),
            )

516
    PRETTY_NAME = "Utilisateurs de service"
chirac's avatar
chirac committed
517 518

    pseudo = models.CharField(max_length=32, unique=True, help_text="Doit contenir uniquement des lettres, chiffres, ou tirets", validators=[linux_user_validator])
519
    access_group = models.CharField(choices=ACCESS, default=readonly, max_length=32)
520
    comment = models.CharField(help_text="Commentaire", max_length=255, blank=True)
chirac's avatar
chirac committed
521 522 523 524 525 526 527 528 529 530

    USERNAME_FIELD = 'pseudo'
 
    objects = UserManager()

    def ldap_sync(self):
        try:
            user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
        except LdapServiceUser.DoesNotExist:
            user_ldap = LdapServiceUser(name=self.pseudo)
531
        user_ldap.user_password = self.password[:6] + self.password[7:]
chirac's avatar
chirac committed
532
        user_ldap.save()
533
        self.serviceuser_group_sync()
chirac's avatar
chirac committed
534 535 536 537 538 539 540

    def ldap_del(self):
        try:
            user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
            user_ldap.delete()
        except LdapUser.DoesNotExist:
            pass
541 542 543 544 545 546 547
        self.serviceuser_group_sync()

    def serviceuser_group_sync(self):
        try:
            group = LdapServiceUserGroup.objects.get(name=self.access_group)
        except:
            group = LdapServiceUserGroup(name=self.access_group)
548
        group.members = list(LdapServiceUser.objects.filter(name__in=[user.pseudo for user in ServiceUser.objects.filter(access_group=self.access_group)]).values_list('dn', flat=True))
549
        group.save()
chirac's avatar
chirac committed
550 551 552 553 554 555 556

    def __str__(self):
        return self.pseudo

@receiver(post_save, sender=ServiceUser)
def service_user_post_save(sender, **kwargs):
    service_user = kwargs['instance']
557
    service_user.ldap_sync()
chirac's avatar
chirac committed
558 559 560 561

@receiver(post_delete, sender=ServiceUser)
def service_user_post_delete(sender, **kwargs):
    service_user = kwargs['instance']
562
    service_user.ldap_del()
chirac's avatar
chirac committed
563

564
class Right(models.Model):
565 566
    PRETTY_NAME = "Droits affectés à des users"

567
    user = models.ForeignKey('User', on_delete=models.PROTECT)
568
    right = models.ForeignKey('ListRight', on_delete=models.PROTECT)
569

570 571 572
    class Meta:
        unique_together = ("user", "right")

573
    def __str__(self):
574
        return str(self.user)
575

576 577 578
@receiver(post_save, sender=Right)
def right_post_save(sender, **kwargs):
    right = kwargs['instance'].right
579
    right.ldap_sync()
580 581 582 583

@receiver(post_delete, sender=Right)
def right_post_delete(sender, **kwargs):
    right = kwargs['instance'].right
584
    right.ldap_sync()
585

lhark's avatar
lhark committed
586
class School(models.Model):
587 588
    PRETTY_NAME = "Etablissements enregistrés"

lhark's avatar
lhark committed
589 590
    name = models.CharField(max_length=255)

591 592 593
    def __str__(self):
        return self.name

594

595
class ListRight(models.Model):
596 597
    PRETTY_NAME = "Liste des droits existants"

598
    listright = models.CharField(max_length=255, unique=True)
599
    gid = models.IntegerField(unique=True, null=True)
600
    details = models.CharField(help_text="Description", max_length=255, blank=True)
601 602 603 604

    def __str__(self):
        return self.listright

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
    def ldap_sync(self):
        try:
            group_ldap = LdapUserGroup.objects.get(gid=self.gid)
        except LdapUserGroup.DoesNotExist:
            group_ldap = LdapUserGroup(gid=self.gid)
        group_ldap.name = self.listright
        group_ldap.members = [right.user.pseudo for right in Right.objects.filter(right=self)]
        group_ldap.save()

    def ldap_del(self):
        try:
            group_ldap = LdapUserGroup.objects.get(gid=self.gid)
            group_ldap.delete()
        except LdapUserGroup.DoesNotExist:
            pass

@receiver(post_save, sender=ListRight)
def listright_post_save(sender, **kwargs):
    right = kwargs['instance']
624
    right.ldap_sync()
625 626 627 628

@receiver(post_delete, sender=ListRight)
def listright_post_delete(sender, **kwargs):
    right = kwargs['instance']
629
    right.ldap_del()
630 631

class ListShell(models.Model):
632 633
    PRETTY_NAME = "Liste des shells disponibles"

634 635 636 637
    shell = models.CharField(max_length=255, unique=True)

    def __str__(self):
        return self.shell
638

chirac's avatar
chirac committed
639
class Ban(models.Model):
640 641
    PRETTY_NAME = "Liste des bannissements"

Gabriel Detraz's avatar
Gabriel Detraz committed
642 643 644 645 646 647 648 649 650
    STATE_HARD = 0
    STATE_SOFT = 1
    STATE_BRIDAGE = 2
    STATES = (
            (0, 'HARD (aucun accès)'),
            (1, 'SOFT (accès local seulement)'),
            (2, 'BRIDAGE (bridage du débit)'),
            )

chirac's avatar
chirac committed
651 652
    user = models.ForeignKey('User', on_delete=models.PROTECT)
    raison = models.CharField(max_length=255)
653
    date_start = models.DateTimeField(auto_now_add=True)
654
    date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
Gabriel Detraz's avatar
Gabriel Detraz committed
655
    state = models.IntegerField(choices=STATES, default=STATE_HARD) 
chirac's avatar
chirac committed
656

657 658 659 660 661 662 663 664 665 666 667 668 669 670 671
    def notif_ban(self):
        """ Prend en argument un objet ban, envoie un mail de notification """
        general_options, created = GeneralOption.objects.get_or_create()
        t = loader.get_template('users/email_ban_notif')
        options, created = AssoOption.objects.get_or_create()
        c = Context({
            'name': str(self.user.name) + ' ' + str(self.user.surname),
            'raison': self.raison,
            'date_end': self.date_end,
            'asso_name' : options.name,
        })
        send_mail('Deconnexion disciplinaire', t.render(c),
        general_options.email_from, [self.user.email], fail_silently=False)
        return

chirac's avatar
chirac committed
672 673 674
    def __str__(self):
        return str(self.user) + ' ' + str(self.raison)

675 676 677
@receiver(post_save, sender=Ban)
def ban_post_save(sender, **kwargs):
    ban = kwargs['instance']
678
    is_created = kwargs['created']
679 680
    user = ban.user
    user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
681 682
    if is_created:
        ban.notif_ban()
683 684 685 686 687
        regen('dhcp')
        regen('mac_ip_list')
    if user.has_access():
        regen('dhcp')
        regen('mac_ip_list')
688 689 690 691 692

@receiver(post_delete, sender=Ban)
def ban_post_delete(sender, **kwargs):
    user = kwargs['instance'].user
    user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
693 694
    regen('dhcp')
    regen('mac_ip_list')
695

chirac's avatar
chirac committed
696
class Whitelist(models.Model):
697 698
    PRETTY_NAME = "Liste des accès gracieux"

chirac's avatar
chirac committed
699 700 701
    user = models.ForeignKey('User', on_delete=models.PROTECT)
    raison = models.CharField(max_length=255)
    date_start = models.DateTimeField(auto_now_add=True)
702
    date_end = models.DateTimeField(help_text='%d/%m/%y %H:%M:%S')
chirac's avatar
chirac committed
703 704 705 706

    def __str__(self):
        return str(self.user) + ' ' + str(self.raison)

707 708 709 710 711
@receiver(post_save, sender=Whitelist)
def whitelist_post_save(sender, **kwargs):
    whitelist = kwargs['instance']
    user = whitelist.user
    user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
712 713 714 715 716 717 718
    is_created = kwargs['created']
    if is_created:
        regen('dhcp')
        regen('mac_ip_list')
    if user.has_access():
        regen('dhcp')
        regen('mac_ip_list')
719 720 721 722 723

@receiver(post_delete, sender=Whitelist)
def whitelist_post_delete(sender, **kwargs):
    user = kwargs['instance'].user
    user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
724 725
    regen('dhcp')
    regen('mac_ip_list')
726

727 728 729 730 731 732 733 734 735 736 737 738 739 740 741
class Request(models.Model):
    PASSWD = 'PW'
    EMAIL = 'EM'
    TYPE_CHOICES = (
        (PASSWD, 'Mot de passe'),
        (EMAIL, 'Email'),
    )
    type = models.CharField(max_length=2, choices=TYPE_CHOICES)
    token = models.CharField(max_length=32)
    user = models.ForeignKey('User', on_delete=models.PROTECT)
    created_at = models.DateTimeField(auto_now_add=True, editable=False)
    expires_at = models.DateTimeField()

    def save(self):
        if not self.expires_at:
742
            options, created = GeneralOption.objects.get_or_create()
743
            self.expires_at = timezone.now() \
744
                + datetime.timedelta(hours=options.req_expire_hrs)
745 746 747 748
        if not self.token:
            self.token = str(uuid.uuid4()).replace('-', '')  # remove hyphens
        super(Request, self).save()

749 750 751 752 753
class LdapUser(ldapdb.models.Model):
    """
    Class for representing an LDAP user entry.
    """
    # LDAP meta-data
754
    base_dn = LDAP['base_user_dn']
chibrac's avatar
chibrac committed
755
    object_classes = ['inetOrgPerson','top','posixAccount','sambaSamAccount','radiusprofile', 'shadowAccount']
756 757 758 759 760 761 762

    # attributes
    gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
    name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
    uid = ldapdb.models.fields.CharField(db_column='uid', max_length=200)
    uidNumber = ldapdb.models.fields.IntegerField(db_column='uidNumber', unique=True)
    sn = ldapdb.models.fields.CharField(db_column='sn', max_length=200)
chirac's avatar
chirac committed
763
    login_shell = ldapdb.models.fields.CharField(db_column='loginShell', max_length=200, blank=True, null=True)
764 765 766
    mail = ldapdb.models.fields.CharField(db_column='mail', max_length=200) 
    given_name = ldapdb.models.fields.CharField(db_column='givenName', max_length=200)
    home_directory = ldapdb.models.fields.CharField(db_column='homeDirectory', max_length=200)
767
    display_name = ldapdb.models.fields.CharField(db_column='displayName', max_length=200, blank=True, null=True)
768 769 770 771 772
    dialupAccess = ldapdb.models.fields.CharField(db_column='dialupAccess')
    sambaSID = ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)
    user_password = ldapdb.models.fields.CharField(db_column='userPassword', max_length=200, blank=True, null=True)
    sambat_nt_password = ldapdb.models.fields.CharField(db_column='sambaNTPassword', max_length=200, blank=True, null=True)
    macs = ldapdb.models.fields.ListField(db_column='radiusCallingStationId', max_length=200, blank=True, null=True)
chibrac's avatar
chibrac committed
773
    shadowexpire = ldapdb.models.fields.CharField(db_column='shadowExpire', blank=True, null=True)
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791

    def __str__(self):
        return self.name

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.sn = self.name
        self.uid = self.name
        self.sambaSID = self.uidNumber
        super(LdapUser, self).save(*args, **kwargs)

class LdapUserGroup(ldapdb.models.Model):
    """
    Class for representing an LDAP user entry.
    """
    # LDAP meta-data
792
    base_dn = LDAP['base_usergroup_dn']
793 794 795 796 797 798 799 800 801 802
    object_classes = ['posixGroup']

    # attributes
    gid = ldapdb.models.fields.IntegerField(db_column='gidNumber')
    members = ldapdb.models.fields.ListField(db_column='memberUid', blank=True)
    name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)

    def __str__(self):
        return self.name

chirac's avatar
chirac committed
803 804 805 806 807 808 809 810 811 812 813 814
class LdapServiceUser(ldapdb.models.Model):
    """
    Class for representing an LDAP userservice entry.
    """
    # LDAP meta-data
    base_dn = LDAP['base_userservice_dn']
    object_classes = ['applicationProcess','simpleSecurityObject']

    # attributes
    name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
    user_password = ldapdb.models.fields.CharField(db_column='userPassword', max_length=200, blank=True, null=True)

815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
    def __str__(self):
        return self.name

class LdapServiceUserGroup(ldapdb.models.Model):
    """
    Class for representing an LDAP userservice entry.
    """
    # LDAP meta-data
    base_dn = LDAP['base_userservicegroup_dn']
    object_classes = ['groupOfNames']

    # attributes
    name = ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True)
    members = ldapdb.models.fields.ListField(db_column='member', blank=True)

    def __str__(self):
        return self.name