8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-26 06:32:26 +00:00

Merge branch 'master' into ouverture_des_ports

This commit is contained in:
root 2017-10-04 22:05:09 +02:00
commit 917c2b2b09
12 changed files with 155 additions and 30 deletions

View file

@ -257,12 +257,15 @@ def check_user_machine_and_register(nas_type, username, mac_address):
return (True, u'Access Ok, Capture de la mac...', user.pwd_ntlm) return (True, u'Access Ok, Capture de la mac...', user.pwd_ntlm)
else: else:
return (False, u'Erreur dans le register mac %s' % reason, '') return (False, u'Erreur dans le register mac %s' % reason, '')
else:
return (False, u'Machine inconnue', '')
else: else:
return (False, u"Machine inconnue", '') return (False, u"Machine inconnue", '')
def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address): def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
# Get port from switch and port number # Get port from switch and port number
extra_log = ""
if not nas: if not nas:
return ('?', u'Nas inconnu', VLAN_OK) return ('?', u'Nas inconnu', VLAN_OK)
@ -273,9 +276,15 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
return (sw_name, u'Port inconnu', VLAN_OK) return (sw_name, u'Port inconnu', VLAN_OK)
port = port.first() port = port.first()
# Si un vlan a été précisé, on l'utilise pour VLAN_OK
if port.vlan_force:
DECISION_VLAN = int(port.vlan_force.vlan_id)
extra_log = u"Force sur vlan " + str(DECISION_VLAN)
else:
DECISION_VLAN = VLAN_OK
if port.radius == 'NO': if port.radius == 'NO':
return (sw_name, u"Pas d'authentification sur ce port", VLAN_OK) return (sw_name, u"Pas d'authentification sur ce port" + extra_log, DECISION_VLAN)
if port.radius == 'BLOQ': if port.radius == 'BLOQ':
return (sw_name, u'Port desactive', VLAN_NOK) return (sw_name, u'Port desactive', VLAN_NOK)
@ -309,16 +318,12 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
else: else:
result, reason = room_user.first().autoregister_machine(mac_address, nas_type) result, reason = room_user.first().autoregister_machine(mac_address, nas_type)
if result: if result:
return (sw_name, u'Access Ok, Capture de la mac...', VLAN_OK) return (sw_name, u'Access Ok, Capture de la mac...' + extra_log, DECISION_VLAN)
else: else:
return (sw_name, u'Erreur dans le register mac %s' % reason + unicode(mac_address), VLAN_NOK) return (sw_name, u'Erreur dans le register mac %s' % reason + unicode(mac_address), VLAN_NOK)
elif not interface.first().is_active: elif not interface.first().is_active:
return (sw_name, u'Machine non active / adherent non cotisant', VLAN_NOK) return (sw_name, u'Machine non active / adherent non cotisant', VLAN_NOK)
else: else:
return (sw_name, u'Machine OK', VLAN_OK) return (sw_name, u'Machine OK' + extra_log, DECISION_VLAN)
# On gere bien tous les autres états possibles, il ne reste que le VLAN en dur
return (sw_name, u'VLAN impose', int(port.radius))

View file

@ -96,13 +96,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<i class="glyphicon glyphicon-edit"></i> Gerer les alias <i class="glyphicon glyphicon-edit"></i> Gerer les alias
</a> </a>
</li> </li>
{% if interface.may_have_port_open %}
<li> <li>
<a href="{% url 'machines:port-config' interface.id%}"> <a href="{% url 'machines:port-config' interface.id%}">
<i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports <i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports
</a> </a>
</li> </li>
{% endif %}
<li> <li>
<a href="{% url 'machines:history' 'interface' interface.id %}"> <a href="{% url 'machines:history' 'interface' interface.id %}">
<i class="glyphicon glyphicon-time"></i> Historique <i class="glyphicon glyphicon-time"></i> Historique

View file

@ -1002,8 +1002,7 @@ def configure_ports(request, pk):
messages.error(request, u"Interface inexistante" ) messages.error(request, u"Interface inexistante" )
return redirect("/machines") return redirect("/machines")
if not interface_instance.may_have_port_open(): if not interface_instance.may_have_port_open():
messages.error(request, "L'ip de cette interface n'est pas publique ou non assignée") messages.error(request, "Attention, l'ipv4 n'est pas publique, l'ouverture n'aura pas d'effet en v4")
return redirect("/machines")
interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance) interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance)
if interface.is_valid(): if interface.is_valid():
interface.save() interface.save()

View file

@ -29,16 +29,16 @@ from reversion.admin import VersionAdmin
from .models import Port, Room, Switch, Stack from .models import Port, Room, Switch, Stack
class StackAdmin(VersionAdmin): class StackAdmin(VersionAdmin):
list_display = ('name', 'stack_id', 'details') pass
class SwitchAdmin(VersionAdmin): class SwitchAdmin(VersionAdmin):
list_display = ('switch_interface','location','number','details') pass
class PortAdmin(VersionAdmin): class PortAdmin(VersionAdmin):
list_display = ('switch', 'port','room','machine_interface','radius','details') pass
class RoomAdmin(VersionAdmin): class RoomAdmin(VersionAdmin):
list_display = ('name','details') pass
admin.site.register(Port, PortAdmin) admin.site.register(Port, PortAdmin)
admin.site.register(Room, RoomAdmin) admin.site.register(Room, RoomAdmin)

View file

@ -33,7 +33,7 @@ class PortForm(ModelForm):
class EditPortForm(ModelForm): class EditPortForm(ModelForm):
class Meta(PortForm.Meta): class Meta(PortForm.Meta):
fields = ['room', 'related', 'machine_interface', 'radius', 'details'] fields = ['room', 'related', 'machine_interface', 'radius', 'vlan_force', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(EditPortForm, self).__init__(*args, **kwargs) super(EditPortForm, self).__init__(*args, **kwargs)
@ -42,7 +42,7 @@ class EditPortForm(ModelForm):
class AddPortForm(ModelForm): class AddPortForm(ModelForm):
class Meta(PortForm.Meta): class Meta(PortForm.Meta):
fields = ['port', 'room', 'machine_interface', 'related', 'radius', 'details'] fields = ['port', 'room', 'machine_interface', 'related', 'radius', 'vlan_force', 'details']
class StackForm(ModelForm): class StackForm(ModelForm):
class Meta: class Meta:

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-02 01:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0028_auto_20170913_1503'),
]
operations = [
migrations.AlterField(
model_name='port',
name='radius',
field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON'), ('3', '3'), ('7', '7'), ('8', '8'), ('13', '13'), ('20', '20'), ('42', '42'), ('69', '69')], default='NO', max_length=32),
),
]

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-04 00:35
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('topologie', '0029_auto_20171002_0334'),
]
operations = [
migrations.AddField(
model_name='port',
name='vlan_force',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='machines.Vlan'),
),
migrations.AlterField(
model_name='port',
name='radius',
field=models.CharField(choices=[('NO', 'NO'), ('STRICT', 'STRICT'), ('BLOQ', 'BLOQ'), ('COMMON', 'COMMON')], default='NO', max_length=32),
),
]

View file

@ -91,16 +91,12 @@ class Switch(models.Model):
class Port(models.Model): class Port(models.Model):
PRETTY_NAME = "Port de switch" PRETTY_NAME = "Port de switch"
STATES_BASE = ( STATES = (
('NO', 'NO'), ('NO', 'NO'),
('STRICT', 'STRICT'), ('STRICT', 'STRICT'),
('BLOQ', 'BLOQ'), ('BLOQ', 'BLOQ'),
('COMMON', 'COMMON'), ('COMMON', 'COMMON'),
) )
try:
STATES = STATES_BASE + tuple([(str(id), str(id)) for id in list(Vlan.objects.values_list('vlan_id', flat=True).order_by('vlan_id'))])
except:
STATES = STATES_BASE
switch = models.ForeignKey('Switch', related_name="ports") switch = models.ForeignKey('Switch', related_name="ports")
port = models.IntegerField() port = models.IntegerField()
@ -108,6 +104,7 @@ class Port(models.Model):
machine_interface = models.ForeignKey('machines.Interface', on_delete=models.SET_NULL, blank=True, null=True) machine_interface = models.ForeignKey('machines.Interface', on_delete=models.SET_NULL, blank=True, null=True)
related = models.OneToOneField('self', null=True, blank=True, related_name='related_port') related = models.OneToOneField('self', null=True, blank=True, related_name='related_port')
radius = models.CharField(max_length=32, choices=STATES, default='NO') radius = models.CharField(max_length=32, choices=STATES, default='NO')
vlan_force = models.ForeignKey('machines.Vlan', on_delete=models.SET_NULL, blank=True, null=True)
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
class Meta: class Meta:

View file

@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Interface machine</th> <th>Interface machine</th>
<th>Related</th> <th>Related</th>
<th>Radius</th> <th>Radius</th>
<th>Vlan forcé</th>
<th>Détails</th> <th>Détails</th>
<th></th> <th></th>
</tr> </tr>
@ -53,6 +54,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endif %} {% endif %}
</td> </td>
<td>{{ port.radius }}</td> <td>{{ port.radius }}</td>
<td>{% if not port.vlan_force %} Aucun{%else %}{{ port.vlan_force }}{% endif %}</td>
<td>{{ port.details }}</td> <td>{{ port.details }}</td>
<td class="text-right"> <td class="text-right">
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'port' port.pk %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'port' port.pk %}">

View file

@ -207,6 +207,7 @@ class EditInfoForm(BaseInfoForm):
] ]
class InfoForm(EditInfoForm): class InfoForm(EditInfoForm):
""" Utile pour forcer un déménagement quand il y a déjà un user en place"""
force = forms.BooleanField(label="Forcer le déménagement ?", initial=False, required=False) force = forms.BooleanField(label="Forcer le déménagement ?", initial=False, required=False)
def clean_force(self): def clean_force(self):
@ -215,15 +216,18 @@ class InfoForm(EditInfoForm):
return return
class UserForm(InfoForm): class UserForm(InfoForm):
""" Model form general"""
class Meta(InfoForm.Meta): class Meta(InfoForm.Meta):
fields = '__all__' fields = '__all__'
class PasswordForm(ModelForm): class PasswordForm(ModelForm):
""" Formulaire de changement brut de mot de passe. Ne pas utiliser sans traitement"""
class Meta: class Meta:
model = User model = User
fields = ['password', 'pwd_ntlm'] fields = ['password', 'pwd_ntlm']
class ServiceUserForm(ModelForm): class ServiceUserForm(ModelForm):
""" Modification d'un service user"""
password = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, required=False) password = forms.CharField(label=u'Nouveau mot de passe', max_length=255, validators=[MinLengthValidator(8)], widget=forms.PasswordInput, required=False)
class Meta: class Meta:
@ -235,6 +239,7 @@ class EditServiceUserForm(ServiceUserForm):
fields = ['access_group','comment'] fields = ['access_group','comment']
class StateForm(ModelForm): class StateForm(ModelForm):
""" Changement de l'état d'un user"""
class Meta: class Meta:
model = User model = User
fields = ['state'] fields = ['state']

View file

@ -56,6 +56,9 @@ from preferences.models import GeneralOption, AssoOption, OptionalUser, Optional
now = timezone.now() now = timezone.now()
#### Utilitaires généraux
def remove_user_room(room): def remove_user_room(room):
""" Déménage de force l'ancien locataire de la chambre """ """ Déménage de force l'ancien locataire de la chambre """
try: try:
@ -73,6 +76,8 @@ def linux_user_check(login):
def linux_user_validator(login): def linux_user_validator(login):
""" Retourne une erreur de validation si le login ne respecte
pas les contraintes unix (maj, min, chiffres ou tiret)"""
if not linux_user_check(login): if not linux_user_check(login):
raise forms.ValidationError( raise forms.ValidationError(
", ce pseudo ('%(label)s') contient des carractères interdits", ", ce pseudo ('%(label)s') contient des carractères interdits",
@ -80,6 +85,7 @@ def linux_user_validator(login):
) )
def get_fresh_user_uid(): def get_fresh_user_uid():
""" Renvoie le plus petit uid non pris. Fonction très paresseuse """
uids = list(range(int(min(UID_RANGES['users'])),int(max(UID_RANGES['users'])))) uids = list(range(int(min(UID_RANGES['users'])),int(max(UID_RANGES['users']))))
try: try:
used_uids = list(User.objects.values_list('uid_number', flat=True)) used_uids = list(User.objects.values_list('uid_number', flat=True))
@ -89,12 +95,15 @@ def get_fresh_user_uid():
return min(free_uids) return min(free_uids)
def get_fresh_gid(): def get_fresh_gid():
""" Renvoie le plus petit gid libre """
gids = list(range(int(min(GID_RANGES['posix'])),int(max(GID_RANGES['posix'])))) gids = list(range(int(min(GID_RANGES['posix'])),int(max(GID_RANGES['posix']))))
used_gids = list(ListRight.objects.values_list('gid', flat=True)) used_gids = list(ListRight.objects.values_list('gid', flat=True))
free_gids = [ id for id in gids if id not in used_gids] free_gids = [ id for id in gids if id not in used_gids]
return min(free_gids) return min(free_gids)
def get_admin_right(): def get_admin_right():
""" Renvoie l'instance droit admin. La crée si elle n'existe pas
Lui attribue un gid libre"""
try: try:
admin_right = ListRight.objects.get(listright="admin") admin_right = ListRight.objects.get(listright="admin")
except ListRight.DoesNotExist: except ListRight.DoesNotExist:
@ -104,15 +113,20 @@ def get_admin_right():
return admin_right return admin_right
def all_adherent(search_time=now): def all_adherent(search_time=now):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est qu'une seule requete sql
Inspecte les factures de l'user et ses cotisation, regarde si elles sont posterieur à now (end_time)"""
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() 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()
def all_baned(search_time=now): def all_baned(search_time=now):
""" Fonction renvoyant tous les users bannis """
return User.objects.filter(ban__in=Ban.objects.filter(date_end__gt=search_time)).distinct() return User.objects.filter(ban__in=Ban.objects.filter(date_end__gt=search_time)).distinct()
def all_whitelisted(search_time=now): def all_whitelisted(search_time=now):
""" Fonction renvoyant tous les users whitelistes """
return User.objects.filter(whitelist__in=Whitelist.objects.filter(date_end__gt=search_time)).distinct() return User.objects.filter(whitelist__in=Whitelist.objects.filter(date_end__gt=search_time)).distinct()
def all_has_access(search_time=now): def all_has_access(search_time=now):
""" Renvoie tous les users beneficiant d'une connexion : user adherent ou whiteliste et non banni """
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() 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()
class UserManager(BaseUserManager): class UserManager(BaseUserManager):
@ -152,6 +166,9 @@ class UserManager(BaseUserManager):
class User(AbstractBaseUser): class User(AbstractBaseUser):
""" Definition de l'utilisateur de base.
Champs principaux : name, surnname, pseudo, email, room, password
Herite du django BaseUser et du système d'auth django"""
PRETTY_NAME = "Utilisateurs" PRETTY_NAME = "Utilisateurs"
STATE_ACTIVE = 0 STATE_ACTIVE = 0
STATE_DISABLED = 1 STATE_DISABLED = 1
@ -187,14 +204,17 @@ class User(AbstractBaseUser):
@property @property
def is_active(self): def is_active(self):
""" Renvoie si l'user est à l'état actif"""
return self.state == self.STATE_ACTIVE return self.state == self.STATE_ACTIVE
@property @property
def is_staff(self): def is_staff(self):
""" Fonction de base django, renvoie si l'user est admin"""
return self.is_admin return self.is_admin
@property @property
def is_admin(self): def is_admin(self):
""" Renvoie si l'user est admin"""
try: try:
Right.objects.get(user=self, right__listright='admin') Right.objects.get(user=self, right__listright='admin')
except Right.DoesNotExist: except Right.DoesNotExist:
@ -203,18 +223,24 @@ class User(AbstractBaseUser):
@is_admin.setter @is_admin.setter
def is_admin(self, value): def is_admin(self, value):
""" Change la valeur de admin à true ou false suivant la valeur de value"""
if value and not self.is_admin: if value and not self.is_admin:
self.make_admin() self.make_admin()
elif not value and self.is_admin: elif not value and self.is_admin:
self.un_admin() self.un_admin()
def get_full_name(self): def get_full_name(self):
""" Renvoie le nom complet de l'user formaté nom/prénom"""
return '%s %s' % (self.name, self.surname) return '%s %s' % (self.name, self.surname)
def get_short_name(self): def get_short_name(self):
""" Renvoie seulement le nom"""
return self.name return self.name
def has_perms(self, perms, obj=None): def has_perms(self, perms, obj=None):
""" Renvoie true si l'user dispose de la permission.
Prend en argument une liste de permissions.
TODO : Arranger cette fonction"""
for perm in perms: for perm in perms:
if perm in RIGHTS_LINK: if perm in RIGHTS_LINK:
query = Q() query = Q()
@ -233,6 +259,7 @@ class User(AbstractBaseUser):
def has_right(self, right): def has_right(self, right):
""" Renvoie si un user a un right donné. Crée le right si il n'existe pas"""
try: try:
list_right = ListRight.objects.get(listright=right) list_right = ListRight.objects.get(listright=right)
except: except:
@ -242,29 +269,38 @@ class User(AbstractBaseUser):
@cached_property @cached_property
def is_bureau(self): def is_bureau(self):
""" True si user a les droits bureau """
return self.has_right('bureau') return self.has_right('bureau')
@cached_property @cached_property
def is_bofh(self): def is_bofh(self):
""" True si l'user a les droits bofh"""
return self.has_right('bofh') return self.has_right('bofh')
@cached_property @cached_property
def is_cableur(self): def is_cableur(self):
""" True si l'user a les droits cableur
(également true si bureau, infra ou bofh)"""
return self.has_right('cableur') or self.has_right('bureau') or self.has_right('infra') or self.has_right('bofh') return self.has_right('cableur') or self.has_right('bureau') or self.has_right('infra') or self.has_right('bofh')
@cached_property @cached_property
def is_trez(self): def is_trez(self):
""" Renvoie true si droits trésorier pour l'user"""
return self.has_right('tresorier') return self.has_right('tresorier')
@cached_property @cached_property
def is_infra(self): def is_infra(self):
""" True si a les droits infra"""
return self.has_right('infra') return self.has_right('infra')
def end_adhesion(self): def end_adhesion(self):
""" Renvoie la date de fin d'adhésion d'un user. Examine les objets
cotisation"""
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'] 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']
return date_max return date_max
def is_adherent(self): def is_adherent(self):
""" Renvoie True si l'user est adhérent : si self.end_adhesion()>now"""
end = self.end_adhesion() end = self.end_adhesion()
if not end: if not end:
return False return False
@ -327,6 +363,8 @@ class User(AbstractBaseUser):
@cached_property @cached_property
def solde(self): def solde(self):
""" Renvoie le solde d'un user. Vérifie que l'option solde est activé, retourne 0 sinon.
Somme les crédits de solde et retire les débit payés par solde"""
options, created = OptionalUser.objects.get_or_create() options, created = OptionalUser.objects.get_or_create()
user_solde = options.user_solde user_solde = options.user_solde
if user_solde: if user_solde:
@ -337,8 +375,10 @@ class User(AbstractBaseUser):
else: else:
return 0 return 0
def user_interfaces(self): def user_interfaces(self, active=True):
return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=True)) """ Renvoie toutes les interfaces dont les machines appartiennent à self
Par defaut ne prend que les interfaces actives"""
return Interface.objects.filter(machine__in=Machine.objects.filter(user=self, active=active))
def assign_ips(self): def assign_ips(self):
""" Assign une ipv4 aux machines d'un user """ """ Assign une ipv4 aux machines d'un user """
@ -351,6 +391,7 @@ class User(AbstractBaseUser):
interface.save() interface.save()
def unassign_ips(self): def unassign_ips(self):
""" Désassigne les ipv4 aux machines de l'user"""
interfaces = self.user_interfaces() interfaces = self.user_interfaces()
for interface in interfaces: for interface in interfaces:
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -359,10 +400,12 @@ class User(AbstractBaseUser):
interface.save() interface.save()
def archive(self): def archive(self):
""" Archive l'user : appelle unassign_ips() puis passe state à ARCHIVE"""
self.unassign_ips() self.unassign_ips()
self.state = User.STATE_ARCHIVE self.state = User.STATE_ARCHIVE
def unarchive(self): def unarchive(self):
""" Désarchive l'user : réassigne ses ip et le passe en state ACTIVE"""
self.assign_ips() self.assign_ips()
self.state = User.STATE_ACTIVE self.state = User.STATE_ACTIVE
@ -383,6 +426,10 @@ class User(AbstractBaseUser):
user_right.delete() user_right.delete()
def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True): def ldap_sync(self, base=True, access_refresh=True, mac_refresh=True):
""" Synchronisation du ldap. Synchronise dans le ldap les attributs de self
Options : base : synchronise tous les attributs de base - nom, prenom, mail, password, shell, home
access_refresh : synchronise le dialup_access notant si l'user a accès aux services
mac_refresh : synchronise les machines de l'user"""
self.refresh_from_db() self.refresh_from_db()
try: try:
user_ldap = LdapUser.objects.get(uidNumber=self.uid_number) user_ldap = LdapUser.objects.get(uidNumber=self.uid_number)
@ -411,6 +458,7 @@ class User(AbstractBaseUser):
user_ldap.save() user_ldap.save()
def ldap_del(self): def ldap_del(self):
""" Supprime la version ldap de l'user"""
try: try:
user_ldap = LdapUser.objects.get(name=self.pseudo) user_ldap = LdapUser.objects.get(name=self.pseudo)
user_ldap.delete() user_ldap.delete()
@ -458,9 +506,11 @@ class User(AbstractBaseUser):
return return
def autoregister_machine(self, mac_address, nas_type): def autoregister_machine(self, mac_address, nas_type):
all_machines = self.all_machines() """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue
sur le compte de l'user"""
all_interfaces = self.user_interfaces(active=False)
options, created = OptionalMachine.objects.get_or_create() options, created = OptionalMachine.objects.get_or_create()
if all_machines.count() > options.max_lambdauser_interfaces: if all_interfaces.count() > options.max_lambdauser_interfaces:
return False, "Maximum de machines enregistrees atteinte" return False, "Maximum de machines enregistrees atteinte"
if not nas_type: if not nas_type:
return False, "Re2o ne sait pas à quel machinetype affecter cette machine" return False, "Re2o ne sait pas à quel machinetype affecter cette machine"
@ -487,10 +537,9 @@ class User(AbstractBaseUser):
return False, e return False, e
return True, "Ok" return True, "Ok"
def all_machines(self):
return Interface.objects.filter(machine__in=Machine.objects.filter(user=self))
def set_user_password(self, password): def set_user_password(self, password):
""" A utiliser de préférence, set le password en hash courrant et
dans la version ntlm"""
self.set_password(password) self.set_password(password)
self.pwd_ntlm = hashNT(password) self.pwd_ntlm = hashNT(password)
return return
@ -517,6 +566,8 @@ class User(AbstractBaseUser):
@receiver(post_save, sender=User) @receiver(post_save, sender=User)
def user_post_save(sender, **kwargs): def user_post_save(sender, **kwargs):
""" Synchronisation post_save : envoie le mail de bienvenue si creation
Synchronise le ldap"""
is_created = kwargs['created'] is_created = kwargs['created']
user = kwargs['instance'] user = kwargs['instance']
if is_created: if is_created:
@ -531,6 +582,7 @@ def user_post_delete(sender, **kwargs):
regen('mailing') regen('mailing')
class ServiceUser(AbstractBaseUser): class ServiceUser(AbstractBaseUser):
""" Classe des users daemons, règle leurs accès au ldap"""
readonly = 'readonly' readonly = 'readonly'
ACCESS = ( ACCESS = (
('auth', 'auth'), ('auth', 'auth'),
@ -549,6 +601,7 @@ class ServiceUser(AbstractBaseUser):
objects = UserManager() objects = UserManager()
def ldap_sync(self): def ldap_sync(self):
""" Synchronisation du ServiceUser dans sa version ldap"""
try: try:
user_ldap = LdapServiceUser.objects.get(name=self.pseudo) user_ldap = LdapServiceUser.objects.get(name=self.pseudo)
except LdapServiceUser.DoesNotExist: except LdapServiceUser.DoesNotExist:
@ -578,15 +631,19 @@ class ServiceUser(AbstractBaseUser):
@receiver(post_save, sender=ServiceUser) @receiver(post_save, sender=ServiceUser)
def service_user_post_save(sender, **kwargs): def service_user_post_save(sender, **kwargs):
""" Synchronise un service user ldap après modification django"""
service_user = kwargs['instance'] service_user = kwargs['instance']
service_user.ldap_sync() service_user.ldap_sync()
@receiver(post_delete, sender=ServiceUser) @receiver(post_delete, sender=ServiceUser)
def service_user_post_delete(sender, **kwargs): def service_user_post_delete(sender, **kwargs):
""" Supprime un service user ldap après suppression django"""
service_user = kwargs['instance'] service_user = kwargs['instance']
service_user.ldap_del() service_user.ldap_del()
class Right(models.Model): class Right(models.Model):
""" Couple droit/user. Peut-être aurait-on mieux fait ici d'utiliser un manytomany
Ceci dit le résultat aurait été le même avec une table intermediaire"""
PRETTY_NAME = "Droits affectés à des users" PRETTY_NAME = "Droits affectés à des users"
user = models.ForeignKey('User', on_delete=models.PROTECT) user = models.ForeignKey('User', on_delete=models.PROTECT)
@ -600,15 +657,18 @@ class Right(models.Model):
@receiver(post_save, sender=Right) @receiver(post_save, sender=Right)
def right_post_save(sender, **kwargs): def right_post_save(sender, **kwargs):
""" Synchronise les users ldap groups avec les groupes de droits"""
right = kwargs['instance'].right right = kwargs['instance'].right
right.ldap_sync() right.ldap_sync()
@receiver(post_delete, sender=Right) @receiver(post_delete, sender=Right)
def right_post_delete(sender, **kwargs): def right_post_delete(sender, **kwargs):
""" Supprime l'user du groupe"""
right = kwargs['instance'].right right = kwargs['instance'].right
right.ldap_sync() right.ldap_sync()
class School(models.Model): class School(models.Model):
""" Etablissement d'enseignement"""
PRETTY_NAME = "Etablissements enregistrés" PRETTY_NAME = "Etablissements enregistrés"
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
@ -618,6 +678,9 @@ class School(models.Model):
class ListRight(models.Model): class ListRight(models.Model):
""" Ensemble des droits existants. Chaque droit crée un groupe ldap synchronisé, avec gid.
Permet de gérer facilement les accès serveurs et autres
La clef de recherche est le gid, pour cette raison il n'est plus modifiable après creation"""
PRETTY_NAME = "Liste des droits existants" PRETTY_NAME = "Liste des droits existants"
listright = models.CharField(max_length=255, unique=True, validators=[RegexValidator('^[a-z]+$', message="Les groupes unix ne peuvent contenir que des lettres minuscules")]) listright = models.CharField(max_length=255, unique=True, validators=[RegexValidator('^[a-z]+$', message="Les groupes unix ne peuvent contenir que des lettres minuscules")])
@ -645,6 +708,7 @@ class ListRight(models.Model):
@receiver(post_save, sender=ListRight) @receiver(post_save, sender=ListRight)
def listright_post_save(sender, **kwargs): def listright_post_save(sender, **kwargs):
""" Synchronise le droit ldap quand il est modifié"""
right = kwargs['instance'] right = kwargs['instance']
right.ldap_sync() right.ldap_sync()
@ -662,6 +726,8 @@ class ListShell(models.Model):
return self.shell return self.shell
class Ban(models.Model): class Ban(models.Model):
""" Bannissement. Actuellement a un effet tout ou rien.
Gagnerait à être granulaire"""
PRETTY_NAME = "Liste des bannissements" PRETTY_NAME = "Liste des bannissements"
STATE_HARD = 0 STATE_HARD = 0
@ -702,6 +768,7 @@ class Ban(models.Model):
@receiver(post_save, sender=Ban) @receiver(post_save, sender=Ban)
def ban_post_save(sender, **kwargs): def ban_post_save(sender, **kwargs):
""" Regeneration de tous les services après modification d'un ban"""
ban = kwargs['instance'] ban = kwargs['instance']
is_created = kwargs['created'] is_created = kwargs['created']
user = ban.user user = ban.user
@ -717,6 +784,7 @@ def ban_post_save(sender, **kwargs):
@receiver(post_delete, sender=Ban) @receiver(post_delete, sender=Ban)
def ban_post_delete(sender, **kwargs): def ban_post_delete(sender, **kwargs):
""" Regen de tous les services après suppression d'un ban"""
user = kwargs['instance'].user user = kwargs['instance'].user
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) user.ldap_sync(base=False, access_refresh=True, mac_refresh=False)
regen('mailing') regen('mailing')
@ -760,6 +828,9 @@ def whitelist_post_delete(sender, **kwargs):
regen('mac_ip_list') regen('mac_ip_list')
class Request(models.Model): class Request(models.Model):
""" Objet request, générant une url unique de validation.
Utilisé par exemple pour la generation du mot de passe et
sa réinitialisation"""
PASSWD = 'PW' PASSWD = 'PW'
EMAIL = 'EM' EMAIL = 'EM'
TYPE_CHOICES = ( TYPE_CHOICES = (

View file

@ -716,6 +716,8 @@ class JSONResponse(HttpResponse):
@login_required @login_required
@permission_required('serveur') @permission_required('serveur')
def mailing(request): def mailing(request):
""" Fonction de serialisation des addresses mail de tous les users
Pour generation de ml all users"""
mails = all_has_access().values('email').distinct() mails = all_has_access().values('email').distinct()
seria = MailSerializer(mails, many=True) seria = MailSerializer(mails, many=True)
return JSONResponse(seria.data) return JSONResponse(seria.data)