mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-22 11:23:10 +00:00
Merge branch 'master' into ouverture_des_ports
This commit is contained in:
commit
917c2b2b09
12 changed files with 155 additions and 30 deletions
|
@ -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))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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:
|
||||||
|
|
20
topologie/migrations/0029_auto_20171002_0334.py
Normal file
20
topologie/migrations/0029_auto_20171002_0334.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
26
topologie/migrations/0030_auto_20171004_0235.py
Normal file
26
topologie/migrations/0030_auto_20171004_0235.py
Normal 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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -91,23 +91,20 @@ 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()
|
||||||
room = models.ForeignKey('Room', on_delete=models.PROTECT, blank=True, null=True)
|
room = models.ForeignKey('Room', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
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:
|
||||||
|
|
|
@ -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 %}">
|
||||||
|
|
|
@ -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']
|
||||||
|
|
|
@ -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 là 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 = (
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue