8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-26 00:43:46 +00:00

Merge branch 'master' into massive_use_bft_tag

This commit is contained in:
Maël Kervella 2017-10-19 19:47:43 +00:00
commit 3aa740a3b9
34 changed files with 1094 additions and 233 deletions

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 18:33
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0023_auto_20170902_1303'),
]
operations = [
migrations.AlterField(
model_name='article',
name='duration',
field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='vente',
name='duration',
field=models.PositiveIntegerField(blank=True, help_text='Durée exprimée en mois entiers', null=True),
),
]

View file

@ -132,7 +132,7 @@ class Vente(models.Model):
name = models.CharField(max_length=255)
prix = models.DecimalField(max_digits=5, decimal_places=2)
iscotisation = models.BooleanField()
duration = models.IntegerField(
duration = models.PositiveIntegerField(
help_text="Durée exprimée en mois entiers",
blank=True,
null=True)
@ -222,7 +222,7 @@ class Article(models.Model):
name = models.CharField(max_length=255, unique=True)
prix = models.DecimalField(max_digits=5, decimal_places=2)
iscotisation = models.BooleanField()
duration = models.IntegerField(
duration = models.PositiveIntegerField(
help_text="Durée exprimée en mois entiers",
blank=True,
null=True,

View file

@ -99,18 +99,18 @@ urlpatterns = [
views.index_paiement,
name='index-paiement'
),
url(r'^history/(?P<object>facture)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>facture)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(r'^history/(?P<object>article)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>article)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(r'^history/(?P<object>paiement)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>paiement)/(?P<object_id>[0-9]+)$',
views.history,
name='history'),
url(r'^history/(?P<object>banque)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>banque)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),

View file

@ -533,7 +533,8 @@ def control(request):
facture_list = paginator.page(1)
except EmptyPage:
facture_list = paginator.page(paginator.num.pages)
page_query = Facture.objects.order_by('date').reverse().filter(
page_query = Facture.objects.order_by('date').select_related('user')\
.select_related('paiement').reverse().filter(
id__in=[facture.id for facture in facture_list]
)
controlform = controlform_set(request.POST or None, queryset=page_query)
@ -603,9 +604,9 @@ def index(request):
@login_required
def history(request, object, object_id):
def history(request, object_name, object_id):
"""Affiche l'historique de chaque objet"""
if object == 'facture':
if object_name == 'facture':
try:
object_instance = Facture.objects.get(pk=object_id)
except Facture.DoesNotExist:
@ -616,19 +617,19 @@ def history(request, object, object_id):
messages.error(request, "Vous ne pouvez pas afficher l'historique\
d'une facture d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id))
elif object == 'paiement' and request.user.has_perms(('cableur',)):
elif object_name == 'paiement' and request.user.has_perms(('cableur',)):
try:
object_instance = Paiement.objects.get(pk=object_id)
except Paiement.DoesNotExist:
messages.error(request, "Paiement inexistant")
return redirect("/cotisations/")
elif object == 'article' and request.user.has_perms(('cableur',)):
elif object_name == 'article' and request.user.has_perms(('cableur',)):
try:
object_instance = Article.objects.get(pk=object_id)
except Article.DoesNotExist:
messages.error(request, "Article inexistante")
return redirect("/cotisations/")
elif object == 'banque' and request.user.has_perms(('cableur',)):
elif object_name == 'banque' and request.user.has_perms(('cableur',)):
try:
object_instance = Banque.objects.get(pk=object_id)
except Banque.DoesNotExist:

BIN
docs_utils/re2o-archi.dia Normal file

Binary file not shown.

View file

@ -292,7 +292,7 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
if not port.room:
return (sw_name, u'Chambre inconnue', VLAN_NOK)
room_user = User.objects.filter(room=Room.objects.filter(name=port.room))
room_user = User.objects.filter(room=port.room)
if not room_user:
return (sw_name, u'Chambre non cotisante', VLAN_NOK)
elif not room_user.first().has_access():

View file

@ -30,51 +30,67 @@ from .models import IpType, Machine, MachineType, Domain, IpList, Interface
from .models import Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort
from .models import OuverturePortList
class MachineAdmin(VersionAdmin):
pass
class IpTypeAdmin(VersionAdmin):
pass
class MachineTypeAdmin(VersionAdmin):
pass
class VlanAdmin(VersionAdmin):
pass
class ExtensionAdmin(VersionAdmin):
pass
class MxAdmin(VersionAdmin):
pass
class NsAdmin(VersionAdmin):
pass
class TextAdmin(VersionAdmin):
pass
class NasAdmin(VersionAdmin):
pass
class IpListAdmin(VersionAdmin):
pass
class OuverturePortAdmin(VersionAdmin):
pass
class OuverturePortListAdmin(VersionAdmin):
pass
class InterfaceAdmin(VersionAdmin):
list_display = ('machine','type','mac_address','ipv4','details')
class DomainAdmin(VersionAdmin):
list_display = ('interface_parent', 'name', 'extension', 'cname')
class ServiceAdmin(VersionAdmin):
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
admin.site.register(Machine, MachineAdmin)
admin.site.register(MachineType, MachineTypeAdmin)
admin.site.register(IpType, IpTypeAdmin)

View file

@ -21,20 +21,43 @@
# 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.
"""
Formulaires d'ajout, edition et suppressions de :
- machines
- interfaces
- domain (noms de machine)
- alias (cname)
- service (dhcp, dns..)
- ns (serveur dns)
- mx (serveur mail)
- ports ouverts et profils d'ouverture par interface
"""
from __future__ import unicode_literals
import re
from django.forms import ModelForm, Form, ValidationError
from django.forms import ModelForm, Form
from django import forms
from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Text, Ns, Service, Vlan, Nas, IpType, OuverturePortList, OuverturePort
from django.db.models import Q
from django.core.validators import validate_email
from users.models import User
from .models import (
Domain,
Machine,
Interface,
IpList,
MachineType,
Extension,
Mx,
Text,
Ns,
Service,
Vlan,
Nas,
IpType,
OuverturePortList,
)
class EditMachineForm(ModelForm):
"""Formulaire d'édition d'une machine"""
class Meta:
model = Machine
fields = '__all__'
@ -44,15 +67,22 @@ class EditMachineForm(ModelForm):
super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Nom de la machine'
class NewMachineForm(EditMachineForm):
"""Creation d'une machine, ne renseigne que le nom"""
class Meta(EditMachineForm.Meta):
fields = ['name']
class BaseEditMachineForm(EditMachineForm):
"""Edition basique, ne permet que de changer le nom et le statut.
Réservé aux users sans droits spécifiques"""
class Meta(EditMachineForm.Meta):
fields = ['name', 'active']
class EditInterfaceForm(ModelForm):
"""Edition d'une interface. Edition complète"""
class Meta:
model = Interface
fields = ['machine', 'type', 'ipv4', 'mac_address', 'details']
@ -64,14 +94,23 @@ class EditInterfaceForm(ModelForm):
self.fields['type'].label = 'Type de machine'
self.fields['type'].empty_label = "Séléctionner un type de machine"
if "ipv4" in self.fields:
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
self.fields['ipv4'].empty_label = "Assignation automatique\
de l'ipv4"
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
)
# Add it's own address
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
self.fields['ipv4'].queryset |= IpList.objects.filter(
interface=self.instance
)
if "machine" in self.fields:
self.fields['machine'].queryset = Machine.objects.all().select_related('user')
self.fields['machine'].queryset = Machine.objects.all()\
.select_related('user')
class AddInterfaceForm(EditInterfaceForm):
"""Ajout d'une interface à une machine. En fonction des droits,
affiche ou non l'ensemble des ip disponibles"""
class Meta(EditInterfaceForm.Meta):
fields = ['type', 'ipv4', 'mac_address', 'details']
@ -80,16 +119,27 @@ class AddInterfaceForm(EditInterfaceForm):
super(AddInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
if not infra:
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False))
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False))
self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
)
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
else:
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
)
class NewInterfaceForm(EditInterfaceForm):
"""Formulaire light, sans choix de l'ipv4; d'ajout d'une interface"""
class Meta(EditInterfaceForm.Meta):
fields = ['type', 'mac_address', 'details']
class BaseEditInterfaceForm(EditInterfaceForm):
"""Edition basique d'une interface. En fonction des droits,
ajoute ou non l'ensemble des ipv4 disponibles (infra)"""
class Meta(EditInterfaceForm.Meta):
fields = ['type', 'ipv4', 'mac_address', 'details']
@ -98,48 +148,69 @@ class BaseEditInterfaceForm(EditInterfaceForm):
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
if not infra:
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False))
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False))
self.fields['type'].queryset = MachineType.objects.filter(
ip_type__in=IpType.objects.filter(need_infra=False)
)
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
# Add it's own address
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
self.fields['ipv4'].queryset |= IpList.objects.filter(
interface=self.instance
)
else:
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
)
self.fields['ipv4'].queryset |= IpList.objects.filter(
interface=self.instance
)
class AliasForm(ModelForm):
"""Ajout d'un alias (et edition), CNAME, contenant nom et extension"""
class Meta:
model = Domain
fields = ['name', 'extension']
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
if 'infra' in kwargs:
infra = kwargs.pop('infra')
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
class DomainForm(AliasForm):
"""Ajout et edition d'un enregistrement de nom, relié à interface"""
class Meta(AliasForm.Meta):
fields = ['name']
def __init__(self, *args, **kwargs):
if 'user' in kwargs:
user = kwargs.pop('user')
nb_machine = kwargs.pop('nb_machine')
initial = kwargs.get('initial', {})
initial['name'] = user.get_next_domain_name()
kwargs['initial'] = initial
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelAliasForm(Form):
alias = forms.ModelMultipleChoiceField(queryset=Domain.objects.all(), label="Alias actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs objets alias"""
alias = forms.ModelMultipleChoiceField(
queryset=Domain.objects.all(),
label="Alias actuels",
widget=forms.CheckboxSelectMultiple
)
def __init__(self, *args, **kwargs):
interface = kwargs.pop('interface')
super(DelAliasForm, self).__init__(*args, **kwargs)
self.fields['alias'].queryset = Domain.objects.filter(cname__in=Domain.objects.filter(interface_parent=interface))
self.fields['alias'].queryset = Domain.objects.filter(
cname__in=Domain.objects.filter(interface_parent=interface)
)
class MachineTypeForm(ModelForm):
"""Ajout et edition d'un machinetype, relié à un iptype"""
class Meta:
model = MachineType
fields = ['type', 'ip_type']
@ -150,41 +221,72 @@ class MachineTypeForm(ModelForm):
self.fields['type'].label = 'Type de machine à ajouter'
self.fields['ip_type'].label = "Type d'ip relié"
class DelMachineTypeForm(Form):
machinetypes = forms.ModelMultipleChoiceField(queryset=MachineType.objects.all(), label="Types de machines actuelles", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs machinetype"""
machinetypes = forms.ModelMultipleChoiceField(
queryset=MachineType.objects.all(),
label="Types de machines actuelles",
widget=forms.CheckboxSelectMultiple
)
class IpTypeForm(ModelForm):
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de
stop après creation"""
class Meta:
model = IpType
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'prefix_v6', 'vlan']
fields = ['type', 'extension', 'need_infra', 'domaine_ip_start',
'domaine_ip_stop', 'prefix_v6', 'vlan', 'ouverture_ports']
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['type'].label = 'Type ip à ajouter'
class EditIpTypeForm(IpTypeForm):
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
synchroniser les objets iplist"""
class Meta(IpTypeForm.Meta):
fields = ['extension','type','need_infra', 'prefix_v6', 'vlan']
fields = ['extension', 'type', 'need_infra', 'prefix_v6', 'vlan',
'ouverture_ports']
class DelIpTypeForm(Form):
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs iptype"""
iptypes = forms.ModelMultipleChoiceField(
queryset=IpType.objects.all(),
label="Types d'ip actuelles",
widget=forms.CheckboxSelectMultiple
)
class ExtensionForm(ModelForm):
"""Formulaire d'ajout et edition d'une extension"""
class Meta:
model = Extension
fields = ['name', 'need_infra', 'origin']
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Extension à ajouter'
self.fields['origin'].label = 'Enregistrement A origin'
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
class DelExtensionForm(Form):
extensions = forms.ModelMultipleChoiceField(queryset=Extension.objects.all(), label="Extensions actuelles", widget=forms.CheckboxSelectMultiple)
"""Suppression d'une ou plusieurs extensions"""
extensions = forms.ModelMultipleChoiceField(
queryset=Extension.objects.all(),
label="Extensions actuelles",
widget=forms.CheckboxSelectMultiple
)
class MxForm(ModelForm):
"""Ajout et edition d'un MX"""
class Meta:
model = Mx
fields = ['zone', 'priority', 'name']
@ -192,12 +294,24 @@ class MxForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(MxForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].queryset = Domain.objects.exclude(interface_parent=None)
self.fields['name'].queryset = Domain.objects.exclude(
interface_parent=None
).select_related('extension')
class DelMxForm(Form):
mx = forms.ModelMultipleChoiceField(queryset=Mx.objects.all(), label="MX actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs MX"""
mx = forms.ModelMultipleChoiceField(
queryset=Mx.objects.all(),
label="MX actuels",
widget=forms.CheckboxSelectMultiple
)
class NsForm(ModelForm):
"""Ajout d'un NS pour une zone
On exclue les CNAME dans les objets domain (interdit par la rfc)
donc on prend uniquemet """
class Meta:
model = Ns
fields = ['zone', 'ns']
@ -205,12 +319,22 @@ class NsForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NsForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['ns'].queryset = Domain.objects.exclude(interface_parent=None)
self.fields['ns'].queryset = Domain.objects.exclude(
interface_parent=None
).select_related('extension')
class DelNsForm(Form):
ns = forms.ModelMultipleChoiceField(queryset=Ns.objects.all(), label="Enregistrements NS actuels", widget=forms.CheckboxSelectMultiple)
"""Suppresion d'un ou plusieurs NS"""
ns = forms.ModelMultipleChoiceField(
queryset=Ns.objects.all(),
label="Enregistrements NS actuels",
widget=forms.CheckboxSelectMultiple
)
class TxtForm(ModelForm):
"""Ajout d'un txt pour une zone"""
class Meta:
model = Text
fields = '__all__'
@ -219,10 +343,19 @@ class TxtForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelTxtForm(Form):
txt = forms.ModelMultipleChoiceField(queryset=Text.objects.all(), label="Enregistrements Txt actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs TXT"""
txt = forms.ModelMultipleChoiceField(
queryset=Text.objects.all(),
label="Enregistrements Txt actuels",
widget=forms.CheckboxSelectMultiple
)
class NasForm(ModelForm):
"""Ajout d'un type de nas (machine d'authentification,
swicths, bornes...)"""
class Meta:
model = Nas
fields = '__all__'
@ -231,10 +364,18 @@ class NasForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NasForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelNasForm(Form):
nas = forms.ModelMultipleChoiceField(queryset=Nas.objects.all(), label="Enregistrements Nas actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs nas"""
nas = forms.ModelMultipleChoiceField(
queryset=Nas.objects.all(),
label="Enregistrements Nas actuels",
widget=forms.CheckboxSelectMultiple
)
class ServiceForm(ModelForm):
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
class Meta:
model = Service
fields = '__all__'
@ -242,6 +383,8 @@ class ServiceForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['servers'].queryset = Interface.objects.all()\
.select_related('domain__extension')
def save(self, commit=True):
instance = super(ServiceForm, self).save(commit=False)
@ -250,10 +393,18 @@ class ServiceForm(ModelForm):
instance.process_link(self.cleaned_data.get('servers'))
return instance
class DelServiceForm(Form):
service = forms.ModelMultipleChoiceField(queryset=Service.objects.all(), label="Services actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs service"""
service = forms.ModelMultipleChoiceField(
queryset=Service.objects.all(),
label="Services actuels",
widget=forms.CheckboxSelectMultiple
)
class VlanForm(ModelForm):
"""Ajout d'un vlan : id, nom"""
class Meta:
model = Vlan
fields = '__all__'
@ -262,24 +413,43 @@ class VlanForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelVlanForm(Form):
vlan = forms.ModelMultipleChoiceField(queryset=Vlan.objects.all(), label="Vlan actuels", widget=forms.CheckboxSelectMultiple)
"""Suppression d'un ou plusieurs vlans"""
vlan = forms.ModelMultipleChoiceField(
queryset=Vlan.objects.all(),
label="Vlan actuels",
widget=forms.CheckboxSelectMultiple
)
class EditOuverturePortConfigForm(ModelForm):
"""Edition de la liste des profils d'ouverture de ports
pour l'interface"""
class Meta:
model = Interface
fields = ['port_lists']
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditOuverturePortConfigForm, self).__init__(*args, prefix=prefix, **kwargs)
super(EditOuverturePortConfigForm, self).__init__(
*args,
prefix=prefix,
**kwargs
)
class EditOuverturePortListForm(ModelForm):
"""Edition de la liste des ports et profils d'ouverture
des ports"""
class Meta:
model = OuverturePortList
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditOuverturePortListForm, self).__init__(*args, prefix=prefix, **kwargs)
super(EditOuverturePortListForm, self).__init__(
*args,
prefix=prefix,
**kwargs
)

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-03 16:08
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0059_iptype_prefix_v6'),
]
operations = [
migrations.AddField(
model_name='iptype',
name='ouverture_ports',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'),
),
]

View file

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 18:33
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0060_iptype_ouverture_ports'),
]
operations = [
migrations.AlterField(
model_name='mx',
name='priority',
field=models.PositiveIntegerField(unique=True),
),
migrations.AlterField(
model_name='ouvertureport',
name='begin',
field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)]),
),
migrations.AlterField(
model_name='ouvertureport',
name='end',
field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)]),
),
migrations.AlterField(
model_name='vlan',
name='vlan_id',
field=models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(4095)]),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-18 14:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('machines', '0061_auto_20171015_2033'),
]
operations = [
migrations.AddField(
model_name='extension',
name='origin_v6',
field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'),
),
]

View file

@ -23,45 +23,60 @@
from __future__ import unicode_literals
from datetime import timedelta
import re
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
from django.db import models
from django.db.models.signals import post_save, pre_delete, post_delete
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.forms import ValidationError
from django.utils.functional import cached_property
from django.utils import timezone
from django.core.validators import MaxValueValidator
from macaddress.fields import MACAddressField
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
from django.core.validators import MinValueValidator,MaxValueValidator
import re
from reversion import revisions as reversion
from datetime import timedelta
class Machine(models.Model):
""" Class définissant une machine, object parent user, objets fils interfaces"""
""" Class définissant une machine, object parent user, objets fils
interfaces"""
PRETTY_NAME = "Machine"
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
name = models.CharField(max_length=255, help_text="Optionnel", blank=True, null=True)
name = models.CharField(
max_length=255,
help_text="Optionnel",
blank=True,
null=True
)
active = models.BooleanField(default=True)
def __str__(self):
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
class MachineType(models.Model):
""" Type de machine, relié à un type d'ip, affecté aux interfaces"""
PRETTY_NAME = "Type de machine"
type = models.CharField(max_length=255)
ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT, blank=True, null=True)
ip_type = models.ForeignKey(
'IpType',
on_delete=models.PROTECT,
blank=True,
null=True
)
def all_interfaces(self):
""" Renvoie toutes les interfaces (cartes réseaux) de type machinetype"""
""" Renvoie toutes les interfaces (cartes réseaux) de type
machinetype"""
return Interface.objects.filter(type=self)
def __str__(self):
return self.type
class IpType(models.Model):
""" Type d'ip, définissant un range d'ip, affecté aux machine types"""
PRETTY_NAME = "Type d'ip"
@ -71,8 +86,22 @@ class IpType(models.Model):
need_infra = models.BooleanField(default=False)
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True)
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
prefix_v6 = models.GenericIPAddressField(
protocol='IPv6',
null=True,
blank=True
)
vlan = models.ForeignKey(
'Vlan',
on_delete=models.PROTECT,
blank=True,
null=True
)
ouverture_ports = models.ForeignKey(
'OuverturePortList',
blank=True,
null=True
)
@cached_property
def ip_range(self):
@ -95,18 +124,22 @@ class IpType(models.Model):
def free_ip(self):
""" Renvoie toutes les ip libres associées au type donné (self)"""
return IpList.objects.filter(interface__isnull=True).filter(ip_type=self)
return IpList.objects.filter(
interface__isnull=True
).filter(ip_type=self)
def gen_ip_range(self):
""" Cree les IpList associées au type self. Parcours pédestrement et crée
les ip une par une. Si elles existent déjà, met à jour le type associé
à l'ip"""
""" Cree les IpList associées au type self. Parcours pédestrement et
crée les ip une par une. Si elles existent déjà, met à jour le type
associé à l'ip"""
# Creation du range d'ip dans les objets iplist
networks = []
for net in self.ip_range.cidrs():
networks += net.iter_hosts()
ip_obj = [IpList(ip_type=self, ipv4=str(ip)) for ip in networks]
listes_ip = IpList.objects.filter(ipv4__in=[str(ip) for ip in networks])
listes_ip = IpList.objects.filter(
ipv4__in=[str(ip) for ip in networks]
)
# Si il n'y a pas d'ip, on les crée
if not listes_ip:
IpList.objects.bulk_create(ip_obj)
@ -116,9 +149,11 @@ class IpType(models.Model):
return
def del_ip_range(self):
""" Methode dépréciée, IpList est en mode cascade et supprimé automatiquement"""
""" Methode dépréciée, IpList est en mode cascade et supprimé
automatiquement"""
if Interface.objects.filter(ipv4__in=self.ip_objects()):
raise ValidationError("Une ou plusieurs ip du range sont affectées, impossible de supprimer le range")
raise ValidationError("Une ou plusieurs ip du range sont\
affectées, impossible de supprimer le range")
for ip in self.ip_objects():
ip.delete()
@ -132,11 +167,13 @@ class IpType(models.Model):
raise ValidationError("Domaine end doit être après start...")
# On ne crée pas plus grand qu'un /16
if self.ip_range.size > 65536:
raise ValidationError("Le range est trop gros, vous ne devez pas créer plus grand qu'un /16")
raise ValidationError("Le range est trop gros, vous ne devez\
pas créer plus grand qu'un /16")
# On check que les / ne se recoupent pas
for element in IpType.objects.all().exclude(pk=self.pk):
if not self.ip_set.isdisjoint(element.ip_set):
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
raise ValidationError("Le range indiqué n'est pas disjoint\
des ranges existants")
# On formate le prefix v6
if self.prefix_v6:
self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network)
@ -149,17 +186,20 @@ class IpType(models.Model):
def __str__(self):
return self.type
class Vlan(models.Model):
""" Un vlan : vlan_id et nom"""
""" Un vlan : vlan_id et nom
On limite le vlan id entre 0 et 4096, comme défini par la norme"""
PRETTY_NAME = "Vlans"
vlan_id = models.IntegerField()
vlan_id = models.PositiveIntegerField(validators=[MaxValueValidator(4095)])
name = models.CharField(max_length=256)
comment = models.CharField(max_length=256, blank=True)
def __str__(self):
return self.name
class Nas(models.Model):
""" Les nas. Associé à un machine_type.
Permet aussi de régler le port_access_mode (802.1X ou mac-address) pour
@ -173,48 +213,84 @@ class Nas(models.Model):
)
name = models.CharField(max_length=255, unique=True)
nas_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='nas_type')
machine_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='machinetype_on_nas')
port_access_mode = models.CharField(choices=AUTH, default=default_mode, max_length=32)
nas_type = models.ForeignKey(
'MachineType',
on_delete=models.PROTECT,
related_name='nas_type'
)
machine_type = models.ForeignKey(
'MachineType',
on_delete=models.PROTECT,
related_name='machinetype_on_nas'
)
port_access_mode = models.CharField(
choices=AUTH,
default=default_mode,
max_length=32
)
autocapture_mac = models.BooleanField(default=False)
def __str__(self):
return self.name
class Extension(models.Model):
""" Extension dns type example.org. Précise si tout le monde peut l'utiliser,
associé à un origin (ip d'origine)"""
""" Extension dns type example.org. Précise si tout le monde peut
l'utiliser, associé à un origin (ip d'origine)"""
PRETTY_NAME = "Extensions dns"
name = models.CharField(max_length=255, unique=True)
need_infra = models.BooleanField(default=False)
origin = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
origin = models.OneToOneField(
'IpList',
on_delete=models.PROTECT,
blank=True,
null=True
)
origin_v6 = models.GenericIPAddressField(
protocol='IPv6',
null=True,
blank=True
)
@cached_property
def dns_entry(self):
""" Une entrée DNS A"""
return "@ IN A " + str(self.origin)
""" Une entrée DNS A et AAAA sur origin (zone self)"""
entry = ""
if self.origin:
entry += "@ IN A " + str(self.origin)
if self.origin_v6:
if entry:
entry += "\n"
entry += "@ IN AAAA " + str(self.origin_v6)
return entry
def __str__(self):
return self.name
class Mx(models.Model):
""" Entrées des MX. Enregistre la zone (extension) associée et la priorité
""" Entrées des MX. Enregistre la zone (extension) associée et la
priorité
Todo : pouvoir associer un MX à une interface """
PRETTY_NAME = "Enregistrements MX"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
priority = models.IntegerField(unique=True)
priority = models.PositiveIntegerField(unique=True)
name = models.OneToOneField('Domain', on_delete=models.PROTECT)
@cached_property
def dns_entry(self):
"""Renvoie l'entrée DNS complète pour un MX à mettre dans les
fichiers de zones"""
return "@ IN MX " + str(self.priority) + " " + str(self.name)
def __str__(self):
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
class Ns(models.Model):
"""Liste des enregistrements name servers par zone considéérée"""
PRETTY_NAME = "Enregistrements NS"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
@ -222,11 +298,13 @@ class Ns(models.Model):
@cached_property
def dns_entry(self):
"""Renvoie un enregistrement NS complet pour les filezones"""
return "@ IN NS " + str(self.ns)
def __str__(self):
return str(self.zone) + ' ' + str(self.ns)
class Text(models.Model):
""" Un enregistrement TXT associé à une extension"""
PRETTY_NAME = "Enregistrement text"
@ -236,22 +314,31 @@ class Text(models.Model):
field2 = models.CharField(max_length=255)
def __str__(self):
return str(self.zone) + " : " + str(self.field1) + " " + str(self.field2)
return str(self.zone) + " : " + str(self.field1) + " " +\
str(self.field2)
@cached_property
def dns_entry(self):
"""Renvoie l'enregistrement TXT complet pour le fichier de zone"""
return str(self.field1) + " IN TXT " + str(self.field2)
class Interface(models.Model):
""" Une interface. Objet clef de l'application machine :
- une address mac unique. Possibilité de la rendre unique avec le typemachine
- une address mac unique. Possibilité de la rendre unique avec le
typemachine
- une onetoone vers IpList pour attribution ipv4
- le type parent associé au range ip et à l'extension
- un objet domain associé contenant son nom
- la liste des ports oiuvert"""
PRETTY_NAME = "Interface"
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
ipv4 = models.OneToOneField(
'IpList',
on_delete=models.PROTECT,
blank=True,
null=True
)
mac_address = MACAddressField(integer=False, unique=True)
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
@ -265,12 +352,14 @@ class Interface(models.Model):
user = self.machine.user
return machine.active and user.has_access()
@cached_property
def ipv6_object(self):
""" Renvoie un objet type ipv6 à partir du prefix associé à l'iptype parent"""
""" Renvoie un objet type ipv6 à partir du prefix associé à
l'iptype parent"""
if self.type.ip_type.prefix_v6:
return EUI(self.mac_address).ipv6(IPNetwork(self.type.ip_type.prefix_v6).network)
return EUI(self.mac_address).ipv6(
IPNetwork(self.type.ip_type.prefix_v6).network
)
else:
return None
@ -284,7 +373,8 @@ class Interface(models.Model):
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
def filter_macaddress(self):
""" Tente un formatage mac_bare, si échoue, lève une erreur de validation"""
""" Tente un formatage mac_bare, si échoue, lève une erreur de
validation"""
try:
self.mac_address = str(EUI(self.mac_address))
except:
@ -305,7 +395,8 @@ class Interface(models.Model):
if free_ips:
self.ipv4 = free_ips[0]
else:
raise ValidationError("Il n'y a plus d'ip disponibles dans le slash")
raise ValidationError("Il n'y a plus d'ip disponibles\
dans le slash")
return
def unassign_ipv4(self):
@ -320,8 +411,10 @@ class Interface(models.Model):
def save(self, *args, **kwargs):
self.filter_macaddress()
# On verifie la cohérence en forçant l'extension par la méthode
if self.ipv4:
if self.type.ip_type != self.ipv4.ip_type:
raise ValidationError("L'ipv4 et le type de la machine ne correspondent pas")
raise ValidationError("L'ipv4 et le type de la machine ne\
correspondent pas")
super(Interface, self).save(*args, **kwargs)
def __str__(self):
@ -340,18 +433,34 @@ class Interface(models.Model):
def may_have_port_open(self):
""" True si l'interface a une ip et une ip publique.
Permet de ne pas exporter des ouvertures sur des ip privées (useless)"""
Permet de ne pas exporter des ouvertures sur des ip privées
(useless)"""
return self.ipv4 and not self.has_private_ip()
class Domain(models.Model):
""" Objet domain. Enregistrement A et CNAME en même temps : permet de stocker les
alias et les nom de machines, suivant si interface_parent ou cname sont remplis"""
""" Objet domain. Enregistrement A et CNAME en même temps : permet de
stocker les alias et les nom de machines, suivant si interface_parent
ou cname sont remplis"""
PRETTY_NAME = "Domaine dns"
interface_parent = models.OneToOneField('Interface', on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(help_text="Obligatoire et unique, ne doit pas comporter de points", max_length=255)
interface_parent = models.OneToOneField(
'Interface',
on_delete=models.CASCADE,
blank=True,
null=True
)
name = models.CharField(
help_text="Obligatoire et unique, ne doit pas comporter de points",
max_length=255
)
extension = models.ForeignKey('Extension', on_delete=models.PROTECT)
cname = models.ForeignKey('self', null=True, blank=True, related_name='related_domain')
cname = models.ForeignKey(
'self',
null=True,
blank=True,
related_name='related_domain'
)
class Meta:
unique_together = (("name", "extension"),)
@ -370,21 +479,26 @@ class Domain(models.Model):
""" Validation :
- l'objet est bien soit A soit CNAME
- le cname est pas pointé sur lui-même
- le nom contient bien les caractères autorisés par la norme dns et moins de 63 caractères au total
- le nom contient bien les caractères autorisés par la norme
dns et moins de 63 caractères au total
- le couple nom/extension est bien unique"""
if self.get_extension():
self.extension = self.get_extension()
""" Validation du nom de domaine, extensions dans type de machine, prefixe pas plus long que 63 caractères """
if self.interface_parent and self.cname:
raise ValidationError("On ne peut créer à la fois A et CNAME")
if self.cname == self:
raise ValidationError("On ne peut créer un cname sur lui même")
HOSTNAME_LABEL_PATTERN = re.compile("(?!-)[A-Z\d-]+(?<!-)$", re.IGNORECASE)
HOSTNAME_LABEL_PATTERN = re.compile(
"(?!-)[A-Z\d-]+(?<!-)$",
re.IGNORECASE
)
dns = self.name.lower()
if len(dns) > 63:
raise ValidationError("Le nom de domaine %s est trop long (maximum de 63 caractères)." % dns)
raise ValidationError("Le nom de domaine %s est trop long\
(maximum de 63 caractères)." % dns)
if not HOSTNAME_LABEL_PATTERN.match(dns):
raise ValidationError("Ce nom de domaine %s contient des carractères interdits." % dns)
raise ValidationError("Ce nom de domaine %s contient des\
carractères interdits." % dns)
self.validate_unique()
super(Domain, self).clean()
@ -395,7 +509,8 @@ class Domain(models.Model):
return str(self.name) + " IN CNAME " + str(self.cname) + "."
def save(self, *args, **kwargs):
""" Empèche le save sans extension valide. Force à avoir appellé clean avant"""
""" Empèche le save sans extension valide.
Force à avoir appellé clean avant"""
if not self.get_extension():
raise ValidationError("Extension invalide")
self.full_clean()
@ -404,6 +519,7 @@ class Domain(models.Model):
def __str__(self):
return str(self.name) + str(self.extension)
class IpList(models.Model):
PRETTY_NAME = "Addresses ipv4"
@ -412,13 +528,15 @@ class IpList(models.Model):
@cached_property
def need_infra(self):
""" Permet de savoir si un user basique peut assigner cette ip ou non"""
""" Permet de savoir si un user basique peut assigner cette ip ou
non"""
return self.ip_type.need_infra
def clean(self):
""" Erreur si l'ip_type est incorrect"""
if not str(self.ipv4) in self.ip_type.ip_set_as_str:
raise ValidationError("L'ipv4 et le range de l'iptype ne correspondent pas!")
raise ValidationError("L'ipv4 et le range de l'iptype ne\
correspondent pas!")
return
def save(self, *args, **kwargs):
@ -428,24 +546,36 @@ class IpList(models.Model):
def __str__(self):
return self.ipv4
class Service(models.Model):
""" Definition d'un service (dhcp, dns, etc)"""
service_type = models.CharField(max_length=255, blank=True, unique=True)
min_time_regen = models.DurationField(default=timedelta(minutes=1), help_text="Temps minimal avant nouvelle génération du service")
regular_time_regen = models.DurationField(default=timedelta(hours=1), help_text="Temps maximal avant nouvelle génération du service")
min_time_regen = models.DurationField(
default=timedelta(minutes=1),
help_text="Temps minimal avant nouvelle génération du service"
)
regular_time_regen = models.DurationField(
default=timedelta(hours=1),
help_text="Temps maximal avant nouvelle génération du service"
)
servers = models.ManyToManyField('Interface', through='Service_link')
def ask_regen(self):
""" Marque à True la demande de régénération pour un service x """
Service_link.objects.filter(service=self).exclude(asked_regen=True).update(asked_regen=True)
Service_link.objects.filter(service=self).exclude(asked_regen=True)\
.update(asked_regen=True)
return
def process_link(self, servers):
""" Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite"""
for serv in servers.exclude(pk__in=Interface.objects.filter(service=self)):
""" Django ne peut créer lui meme les relations manytomany avec table
intermediaire explicite"""
for serv in servers.exclude(
pk__in=Interface.objects.filter(service=self)
):
link = Service_link(service=self, server=serv)
link.save()
Service_link.objects.filter(service=self).exclude(server__in=servers).delete()
Service_link.objects.filter(service=self).exclude(server__in=servers)\
.delete()
return
def save(self, *args, **kwargs):
@ -454,13 +584,16 @@ class Service(models.Model):
def __str__(self):
return str(self.service_type)
def regen(service):
""" Fonction externe pour régérération d'un service, prend un objet service en arg"""
""" Fonction externe pour régérération d'un service, prend un objet service
en arg"""
obj = Service.objects.filter(service_type=service)
if obj:
obj[0].ask_regen()
return
class Service_link(models.Model):
""" Definition du lien entre serveurs et services"""
service = models.ForeignKey('Service', on_delete=models.CASCADE)
@ -475,11 +608,16 @@ class Service_link(models.Model):
self.save()
def need_regen(self):
""" Décide si le temps minimal écoulé est suffisant pour provoquer une régénération de service"""
if (self.asked_regen and (self.last_regen + self.service.min_time_regen) < timezone.now()) or (self.last_regen + self.service.regular_time_regen) < timezone.now():
return True
else:
return False
""" Décide si le temps minimal écoulé est suffisant pour provoquer une
régénération de service"""
return bool(
(self.asked_regen and (
self.last_regen + self.service.min_time_regen
) < timezone.now()
) or (
self.last_regen + self.service.regular_time_regen
) < timezone.now()
)
def __str__(self):
return str(self.server) + " " + str(self.service)
@ -487,22 +625,41 @@ class Service_link(models.Model):
class OuverturePortList(models.Model):
"""Liste des ports ouverts sur une interface."""
name = models.CharField(help_text="Nom de la configuration des ports.", max_length=255)
name = models.CharField(
help_text="Nom de la configuration des ports.",
max_length=255
)
def __str__(self):
return self.name
def tcp_ports_in(self):
return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN)
"""Renvoie la liste des ports ouverts en TCP IN pour ce profil"""
return self.ouvertureport_set.filter(
protocole=OuverturePort.TCP,
io=OuverturePort.IN
)
def udp_ports_in(self):
return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN)
"""Renvoie la liste des ports ouverts en UDP IN pour ce profil"""
return self.ouvertureport_set.filter(
protocole=OuverturePort.UDP,
io=OuverturePort.IN
)
def tcp_ports_out(self):
return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT)
"""Renvoie la liste des ports ouverts en TCP OUT pour ce profil"""
return self.ouvertureport_set.filter(
protocole=OuverturePort.TCP,
io=OuverturePort.OUT
)
def udp_ports_out(self):
return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT)
"""Renvoie la liste des ports ouverts en UDP OUT pour ce profil"""
return self.ouvertureport_set.filter(
protocole=OuverturePort.UDP,
io=OuverturePort.OUT
)
class OuverturePort(models.Model):
@ -511,14 +668,19 @@ class OuverturePort(models.Model):
Les ports de la plage sont compris entre begin et en inclus.
Si begin == end alors on ne représente qu'un seul port.
On limite les ports entre 0 et 65535, tels que défini par la RFC
"""
TCP = 'T'
UDP = 'U'
IN = 'I'
OUT = 'O'
begin = models.IntegerField()
end = models.IntegerField()
port_list = models.ForeignKey('OuverturePortList', on_delete=models.CASCADE)
begin = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
end = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
port_list = models.ForeignKey(
'OuverturePortList',
on_delete=models.CASCADE
)
protocole = models.CharField(
max_length=1,
choices=(
@ -542,88 +704,123 @@ class OuverturePort(models.Model):
return '-'.join([str(self.begin), str(self.end)])
def show_port(self):
"""Formatage plus joli, alias pour str"""
return str(self)
@receiver(post_save, sender=Machine)
def machine_post_save(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
d'une machine"""
user = kwargs['instance'].user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
regen('dhcp')
regen('mac_ip_list')
@receiver(post_delete, sender=Machine)
def machine_post_delete(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
d'une machine"""
machine = kwargs['instance']
user = machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
regen('dhcp')
regen('mac_ip_list')
@receiver(post_save, sender=Interface)
def interface_post_save(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
d'une interface"""
interface = kwargs['instance']
user = interface.machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
if not interface.may_have_port_open() and interface.port_lists.all():
interface.port_lists.clear()
# Regen services
regen('dhcp')
regen('mac_ip_list')
@receiver(post_delete, sender=Interface)
def interface_post_delete(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
d'une interface"""
interface = kwargs['instance']
user = interface.machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
@receiver(post_save, sender=IpType)
def iptype_post_save(sender, **kwargs):
"""Generation des objets ip après modification d'un range ip"""
iptype = kwargs['instance']
iptype.gen_ip_range()
@receiver(post_save, sender=MachineType)
def machine_post_save(sender, **kwargs):
"""Mise à jour des interfaces lorsque changement d'attribution
d'une machinetype (changement iptype parent)"""
machinetype = kwargs['instance']
for interface in machinetype.all_interfaces():
interface.update_type()
@receiver(post_save, sender=Domain)
def domain_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un domain object"""
regen('dns')
@receiver(post_delete, sender=Domain)
def domain_post_delete(sender, **kwargs):
"""Regeneration dns après suppression d'un domain object"""
regen('dns')
@receiver(post_save, sender=Extension)
def extension_post_save(sender, **kwargs):
"""Regeneration dns après modification d'une extension"""
regen('dns')
@receiver(post_delete, sender=Extension)
def extension_post_selete(sender, **kwargs):
"""Regeneration dns après suppression d'une extension"""
regen('dns')
@receiver(post_save, sender=Mx)
def mx_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un MX"""
regen('dns')
@receiver(post_delete, sender=Mx)
def mx_post_delete(sender, **kwargs):
"""Regeneration dns après suppresson d'un MX"""
regen('dns')
@receiver(post_save, sender=Ns)
def ns_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un NS"""
regen('dns')
@receiver(post_delete, sender=Ns)
def ns_post_delete(sender, **kwargs):
"""Regeneration dns après modification d'un NS"""
regen('dns')
@receiver(post_save, sender=Text)
def text_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un TXT"""
regen('dns')
@receiver(post_delete, sender=Text)
def text_post_delete(sender, **kwargs):
"""Regeneration dns après modification d'un TX"""
regen('dns')

View file

@ -24,20 +24,42 @@
#Augustin Lemesle
from rest_framework import serializers
from machines.models import Interface, IpType, Extension, IpList, MachineType, Domain, Text, Mx, Service_link, Ns
from machines.models import (
Interface,
IpType,
Extension,
IpList,
MachineType,
Domain,
Text,
Mx,
Service_link,
Ns,
OuverturePortList,
OuverturePort
)
class IpTypeField(serializers.RelatedField):
"""Serialisation d'une iptype, renvoie son evaluation str"""
def to_representation(self, value):
return value.type
class IpListSerializer(serializers.ModelSerializer):
"""Serialisation d'une iplist, ip_type etant une foreign_key,
on evalue sa methode str"""
ip_type = IpTypeField(read_only=True)
class Meta:
model = IpList
fields = ('ipv4', 'ip_type')
class InterfaceSerializer(serializers.ModelSerializer):
"""Serialisation d'une interface, ipv4, domain et extension sont
des foreign_key, on les override et on les evalue avec des fonctions
get_..."""
ipv4 = IpListSerializer(read_only=True)
mac_address = serializers.SerializerMethodField('get_macaddress')
domain = serializers.SerializerMethodField('get_dns')
@ -56,7 +78,9 @@ class InterfaceSerializer(serializers.ModelSerializer):
def get_macaddress(self, obj):
return str(obj.mac_address)
class FullInterfaceSerializer(serializers.ModelSerializer):
"""Serialisation complete d'une interface avec l'ipv6 en plus"""
ipv4 = IpListSerializer(read_only=True)
mac_address = serializers.SerializerMethodField('get_macaddress')
domain = serializers.SerializerMethodField('get_dns')
@ -75,24 +99,69 @@ class FullInterfaceSerializer(serializers.ModelSerializer):
def get_macaddress(self, obj):
return str(obj.mac_address)
class ExtensionNameField(serializers.RelatedField):
"""Evaluation str d'un objet extension (.example.org)"""
def to_representation(self, value):
return value.name
class TypeSerializer(serializers.ModelSerializer):
"""Serialisation d'un iptype : extension et la liste des
ouvertures de port son evalués en get_... etant des
foreign_key ou des relations manytomany"""
extension = ExtensionNameField(read_only=True)
ouverture_ports_tcp_in = serializers\
.SerializerMethodField('get_port_policy_input_tcp')
ouverture_ports_tcp_out = serializers\
.SerializerMethodField('get_port_policy_output_tcp')
ouverture_ports_udp_in = serializers\
.SerializerMethodField('get_port_policy_input_udp')
ouverture_ports_udp_out = serializers\
.SerializerMethodField('get_port_policy_output_udp')
class Meta:
model = IpType
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop')
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop',
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
def get_port_policy(self, obj, protocole, io):
if obj.ouverture_ports is None:
return []
return map(
str,
obj.ouverture_ports.ouvertureport_set.filter(
protocole=protocole
).filter(io=io)
)
def get_port_policy_input_tcp(self, obj):
"""Renvoie la liste des ports ouverts en entrée tcp"""
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.IN)
def get_port_policy_output_tcp(self, obj):
"""Renvoie la liste des ports ouverts en sortie tcp"""
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.OUT)
def get_port_policy_input_udp(self, obj):
"""Renvoie la liste des ports ouverts en entrée udp"""
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.IN)
def get_port_policy_output_udp(self, obj):
"""Renvoie la liste des ports ouverts en sortie udp"""
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.OUT)
class ExtensionSerializer(serializers.ModelSerializer):
"""Serialisation d'une extension : origin_ip et la zone sont
des foreign_key donc evalués en get_..."""
origin = serializers.SerializerMethodField('get_origin_ip')
zone_entry = serializers.SerializerMethodField('get_zone_name')
class Meta:
model = Extension
fields = ('name', 'origin', 'zone_entry')
fields = ('name', 'origin', 'origin_v6', 'zone_entry')
def get_origin_ip(self, obj):
return obj.origin.ipv4
@ -100,7 +169,10 @@ class ExtensionSerializer(serializers.ModelSerializer):
def get_zone_name(self, obj):
return str(obj.dns_entry)
class MxSerializer(serializers.ModelSerializer):
"""Serialisation d'un MX, evaluation du nom, de la zone
et du serveur cible, etant des foreign_key"""
name = serializers.SerializerMethodField('get_entry_name')
zone = serializers.SerializerMethodField('get_zone_name')
mx_entry = serializers.SerializerMethodField('get_mx_name')
@ -118,7 +190,10 @@ class MxSerializer(serializers.ModelSerializer):
def get_mx_name(self, obj):
return str(obj.dns_entry)
class TextSerializer(serializers.ModelSerializer):
"""Serialisation d'un txt : zone cible et l'entrée txt
sont evaluées à part"""
zone = serializers.SerializerMethodField('get_zone_name')
text_entry = serializers.SerializerMethodField('get_text_name')
@ -132,7 +207,10 @@ class TextSerializer(serializers.ModelSerializer):
def get_text_name(self, obj):
return str(obj.dns_entry)
class NsSerializer(serializers.ModelSerializer):
"""Serialisation d'un NS : la zone, l'entrée ns complète et le serveur
ns sont évalués à part"""
zone = serializers.SerializerMethodField('get_zone_name')
ns = serializers.SerializerMethodField('get_domain_name')
ns_entry = serializers.SerializerMethodField('get_text_name')
@ -150,7 +228,10 @@ class NsSerializer(serializers.ModelSerializer):
def get_text_name(self, obj):
return str(obj.dns_entry)
class DomainSerializer(serializers.ModelSerializer):
"""Serialisation d'un domain, extension, cname sont des foreign_key,
et l'entrée complète, sont évalués à part"""
extension = serializers.SerializerMethodField('get_zone_name')
cname = serializers.SerializerMethodField('get_alias_name')
cname_entry = serializers.SerializerMethodField('get_cname_name')
@ -168,7 +249,9 @@ class DomainSerializer(serializers.ModelSerializer):
def get_cname_name(self, obj):
return str(obj.dns_entry)
class ServiceServersSerializer(serializers.ModelSerializer):
"""Evaluation d'un Service, et serialisation"""
server = serializers.SerializerMethodField('get_server_name')
service = serializers.SerializerMethodField('get_service_name')
need_regen = serializers.SerializerMethodField('get_regen_status')
@ -185,3 +268,31 @@ class ServiceServersSerializer(serializers.ModelSerializer):
def get_regen_status(self, obj):
return obj.need_regen()
class OuverturePortsSerializer(serializers.Serializer):
"""Serialisation de l'ouverture des ports"""
ipv4 = serializers.SerializerMethodField()
ipv6 = serializers.SerializerMethodField()
def get_ipv4():
return {i.ipv4.ipv4:
{
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
}
for i in Interface.objects.all() if i.ipv4
}
def get_ipv6():
return {i.ipv6:
{
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
}
for i in Interface.objects.all() if i.ipv6
}

View file

@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Extension</th>
<th>Autorisation infra pour utiliser l'extension</th>
<th>Enregistrement A origin</th>
<th>Enregistrement AAAA origin</th>
<th></th>
</tr>
</thead>
@ -36,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ extension.name }}</td>
<td>{{ extension.need_infra }}</td>
<td>{{ extension.origin }}</td>
<td>{{ extension.origin_v6 }}</td>
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}

View file

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<th>Fin</th>
<th>Préfixe v6</th>
<th>Sur vlan</th>
<th>Ouverture ports par défault</th>
<th></th>
<th></th>
</tr>
@ -45,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ type.domaine_ip_stop }}</td>
<td>{{ type.prefix_v6 }}</td>
<td>{{ type.vlan }}</td>
<td>{{ type.ouverture_ports }}</td>
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %}

View file

@ -58,7 +58,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if is_cableur %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
<i class="glyphicon glyphicon-list"></i>
Configuration de ports
Ouverture de ports
</a>
{%endif%}
{% endblock %}

View file

@ -93,6 +93,7 @@ urlpatterns = [
url(r'^rest/text/$', views.text, name='text'),
url(r'^rest/zones/$', views.zones, name='zones'),
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
url(r'^edit_portlist/(?P<pk>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
url(r'^del_portlist/(?P<pk>[0-9]+)$', views.del_portlist, name='del-portlist'),

View file

@ -43,18 +43,79 @@ from django.contrib.auth import authenticate, login
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from machines.serializers import FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
from machines.serializers import ( FullInterfaceSerializer,
InterfaceSerializer,
TypeSerializer,
DomainSerializer,
TextSerializer,
MxSerializer,
ExtensionSerializer,
ServiceServersSerializer,
NsSerializer,
OuverturePortsSerializer
)
from reversion import revisions as reversion
from reversion.models import Version
import re
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, TxtForm, DelTxtForm, MxForm, DelMxForm, VlanForm, DelVlanForm, ServiceForm, DelServiceForm, NasForm, DelNasForm
from .forms import (
NewMachineForm,
EditMachineForm,
EditInterfaceForm,
AddInterfaceForm,
MachineTypeForm,
DelMachineTypeForm,
ExtensionForm,
DelExtensionForm,
BaseEditInterfaceForm,
BaseEditMachineForm
)
from .forms import (
EditIpTypeForm,
IpTypeForm,
DelIpTypeForm,
DomainForm,
AliasForm,
DelAliasForm,
NsForm,
DelNsForm,
TxtForm,
DelTxtForm,
MxForm,
DelMxForm,
VlanForm,
DelVlanForm,
ServiceForm,
DelServiceForm,
NasForm,
DelNasForm
)
from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort
from .models import (
IpType,
Machine,
Interface,
IpList,
MachineType,
Extension,
Mx,
Ns,
Domain,
Service,
Service_link,
Vlan,
Nas,
Text,
OuverturePortList,
OuverturePort
)
from users.models import User
from preferences.models import GeneralOption, OptionalMachine
from re2o.utils import all_active_assigned_interfaces, all_has_access
from re2o.utils import (
all_active_assigned_interfaces,
all_has_access,
filter_active_interfaces
)
from re2o.views import form
def f_type_id( is_type_tt ):
@ -71,7 +132,8 @@ def generate_ipv4_choices( form ) :
choices = '{"":[{key:"",value:"Choisissez d\'abord un type de machine"},'
mtype_id = -1
for ip in f_ipv4.queryset.annotate(mtype_id=F('ip_type__machinetype__id')).order_by('mtype_id', 'id') :
for ip in f_ipv4.queryset.annotate(mtype_id=F('ip_type__machinetype__id'))\
.order_by('mtype_id', 'id') :
if mtype_id != ip.mtype_id :
mtype_id = ip.mtype_id
used_mtype_id.append(mtype_id)
@ -140,8 +202,8 @@ def generate_ipv4_mbf_param( form, is_type_tt ):
@login_required
def new_machine(request, userid):
""" Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain
à partir de model forms.
""" Fonction de creation d'une machine. Cree l'objet machine,
le sous objet interface et l'objet domain à partir de model forms.
Trop complexe, devrait être simplifié"""
try:
user = User.objects.get(pk=userid)
@ -152,15 +214,16 @@ def new_machine(request, userid):
max_lambdauser_interfaces = options.max_lambdauser_interfaces
if not request.user.has_perms(('cableur',)):
if user != request.user:
messages.error(request, "Vous ne pouvez pas ajouter une machine à un autre user que vous sans droit")
messages.error(
request,
"Vous ne pouvez pas ajouter une machine à un autre user que vous sans droit")
return redirect("/users/profil/" + str(request.user.id))
if user.user_interfaces().count() >= max_lambdauser_interfaces:
messages.error(request, "Vous avez atteint le maximum d'interfaces autorisées que vous pouvez créer vous même (%s) " % max_lambdauser_interfaces)
return redirect("/users/profil/" + str(request.user.id))
machine = NewMachineForm(request.POST or None)
interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
nb_machine = Interface.objects.filter(machine__user=userid).count()
domain = DomainForm(request.POST or None, user=user, nb_machine=nb_machine)
domain = DomainForm(request.POST or None, user=user)
if machine.is_valid() and interface.is_valid():
new_machine = machine.save(commit=False)
new_machine.user = user
@ -993,7 +1056,9 @@ def history(request, object, id):
@login_required
@permission_required('cableur')
def index_portlist(request):
port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set').prefetch_related('interface_set').order_by('name')
port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set')\
.prefetch_related('interface_set__domain__extension')\
.prefetch_related('interface_set__machine__user').order_by('name')
return render(request, "machines/index_portlist.html", {'port_list':port_list})
@login_required
@ -1184,6 +1249,34 @@ def service_servers(request):
@csrf_exempt
@login_required
@permission_required('serveur')
def ouverture_ports(request):
r = {'ipv4':{}, 'ipv6':{}}
for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'):
pl = {
"tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))),
"tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))),
"udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))),
"udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))),
}
for i in filter_active_interfaces(o.interface_set):
if i.may_have_port_open():
d = r['ipv4'].get(i.ipv4.ipv4, {})
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
r['ipv4'][i.ipv4.ipv4] = d
if i.ipv6_object:
d = r['ipv6'].get(i.ipv6, {})
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
r['ipv6'][i.ipv6] = d
return JSONResponse(r)
@csrf_exempt
@login_required
@permission_required('serveur')
def regen_achieved(request):
obj = Service_link.objects.filter(service__in=Service.objects.filter(service_type=request.POST['service']), server__in=Interface.objects.filter(domain__in=Domain.objects.filter(name=request.POST['server'])))
if obj:

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 15:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0020_optionalmachine_ipv6'),
]
operations = [
migrations.AlterField(
model_name='optionaltopologie',
name='radius_general_policy',
field=models.CharField(choices=[('MACHINE', 'Sur le vlan de la plage ip machine'), ('DEFINED', 'Prédéfini dans "Vlan où placer les machines après acceptation RADIUS"')], default='DEFINED', max_length=32),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 15:58
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0021_auto_20171015_1741'),
]
operations = [
migrations.AlterField(
model_name='optionaltopologie',
name='radius_general_policy',
field=models.CharField(choices=[('MACHINE', 'Sur le vlan de la plage ip machine'), ('DEFINED', 'Prédéfini dans "Vlan où placer les machines après acceptation RADIUS"')], default='DEFINED', max_length=32),
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 18:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('preferences', '0022_auto_20171015_1758'),
]
operations = [
migrations.AlterField(
model_name='optionaltopologie',
name='radius_general_policy',
field=models.CharField(choices=[('MACHINE', 'Sur le vlan de la plage ip machine'), ('DEFINED', 'Prédéfini dans "Vlan où placer les machines après acceptation RADIUS"')], default='DEFINED', max_length=32),
),
]

View file

@ -69,7 +69,7 @@ urlpatterns = [
),
url(r'^del_services/$', views.del_services, name='del-services'),
url(
r'^history/(?P<object>service)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>service)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),

View file

@ -104,9 +104,9 @@ def all_has_access(search_time=DT_NOW):
).distinct()
def all_active_interfaces():
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
return Interface.objects.filter(
def filter_active_interfaces(interface_set):
"""Filtre les machines autorisées à sortir sur internet dans une requête"""
return interface_set.filter(
machine__in=Machine.objects.filter(
user__in=all_has_access()
).filter(active=True)
@ -116,6 +116,11 @@ def all_active_interfaces():
.distinct()
def all_active_interfaces():
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
return filter_active_interfaces(Interface.objects)
def all_active_assigned_interfaces():
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et
disposant de l'accès internet"""

View file

@ -82,6 +82,11 @@ class AddPortForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['machine_interface'].queryset = Interface.objects.all()\
.select_related('domain__extension')
self.fields['related'].queryset = Port.objects.all()\
.select_related('switch__switch_interface__domain__extension')\
.order_by('switch', 'port')
class StackForm(ModelForm):
@ -105,6 +110,8 @@ class EditSwitchForm(ModelForm):
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['switch_interface'].queryset = Interface.objects.all()\
.select_related('domain__extension')
self.fields['location'].label = 'Localisation'
self.fields['number'].label = 'Nombre de ports'

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 18:33
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('topologie', '0030_auto_20171004_0235'),
]
operations = [
migrations.AlterField(
model_name='port',
name='port',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='stack',
name='member_id_max',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='stack',
name='member_id_min',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='switch',
name='number',
field=models.PositiveIntegerField(),
),
migrations.AlterField(
model_name='switch',
name='stack_member_id',
field=models.PositiveIntegerField(blank=True, null=True),
),
]

View file

@ -52,8 +52,8 @@ class Stack(models.Model):
name = models.CharField(max_length=32, blank=True, null=True)
stack_id = models.CharField(max_length=32, unique=True)
details = models.CharField(max_length=255, blank=True, null=True)
member_id_min = models.IntegerField()
member_id_max = models.IntegerField()
member_id_min = models.PositiveIntegerField()
member_id_max = models.PositiveIntegerField()
def __str__(self):
return " ".join([self.name, self.stack_id])
@ -90,7 +90,7 @@ class Switch(models.Model):
on_delete=models.CASCADE
)
location = models.CharField(max_length=255)
number = models.IntegerField()
number = models.PositiveIntegerField()
details = models.CharField(max_length=255, blank=True)
stack = models.ForeignKey(
Stack,
@ -98,7 +98,7 @@ class Switch(models.Model):
null=True,
on_delete=models.SET_NULL
)
stack_member_id = models.IntegerField(blank=True, null=True)
stack_member_id = models.PositiveIntegerField(blank=True, null=True)
class Meta:
unique_together = ('stack', 'stack_member_id')
@ -145,8 +145,12 @@ class Port(models.Model):
('COMMON', 'COMMON'),
)
switch = models.ForeignKey('Switch', related_name="ports")
port = models.IntegerField()
switch = models.ForeignKey(
'Switch',
related_name="ports",
on_delete=models.CASCADE
)
port = models.PositiveIntegerField()
room = models.ForeignKey(
'Room',
on_delete=models.PROTECT,

View file

@ -52,6 +52,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switch' switch.pk %}">
<i class="glyphicon glyphicon-time"></i>
</a>
{% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.pk %}
{% include 'buttons/suppr.html' with href='machines:del-interface' id=switch.switch_interface.id %}
</td>
</tr>
{% endfor %}

View file

@ -42,16 +42,16 @@ urlpatterns = [
url(r'^switch/(?P<switch_id>[0-9]+)$',
views.index_port,
name='index-port'),
url(r'^history/(?P<object>switch)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>switch)/(?P<object_id>[0-9]+)$',
views.history,
name='history'),
url(r'^history/(?P<object>port)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>port)/(?P<object_id>[0-9]+)$',
views.history,
name='history'),
url(r'^history/(?P<object>room)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>room)/(?P<object_id>[0-9]+)$',
views.history,
name='history'),
url(r'^history/(?P<object>stack)/(?P<id>[0-9]+)$',
url(r'^history/(?P<object_name>stack)/(?P<object_id>[0-9]+)$',
views.history,
name='history'),
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'),

View file

@ -50,7 +50,7 @@ from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm
from topologie.forms import AddPortForm, EditRoomForm, StackForm
from users.views import form
from machines.forms import AliasForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm
from machines.forms import DomainForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm
from machines.views import generate_ipv4_mbf_param
from preferences.models import AssoOption, GeneralOption
@ -135,7 +135,8 @@ def index_port(request, switch_id):
port_list = Port.objects.filter(switch=switch)\
.select_related('room')\
.select_related('machine_interface__domain__extension')\
.select_related('related')\
.select_related('machine_interface__machine__user')\
.select_related('related__switch__switch_interface__domain__extension')\
.select_related('switch')\
.order_by('port')
return render(request, 'topologie/index_p.html', {
@ -344,9 +345,8 @@ def new_switch(request):
request.POST or None,
infra=request.user.has_perms(('infra',))
)
domain = AliasForm(
domain = DomainForm(
request.POST or None,
infra=request.user.has_perms(('infra',))
)
if switch.is_valid() and machine.is_valid() and interface.is_valid():
options, _created = AssoOption.objects.get_or_create()
@ -410,9 +410,8 @@ def edit_switch(request, switch_id):
request.POST or None,
instance=switch.switch_interface
)
domain_form = AliasForm(
domain_form = DomainForm(
request.POST or None,
infra=request.user.has_perms(('infra',)),
instance=switch.switch_interface.domain
)
if switch_form.is_valid() and machine_form.is_valid()\

View file

@ -452,13 +452,14 @@ class RightForm(ModelForm):
class DelRightForm(Form):
"""Suppression d'un droit d'un user"""
rights = forms.ModelMultipleChoiceField(
queryset=Right.objects.all(),
queryset=Right.objects.select_related('user'),
widget=forms.CheckboxSelectMultiple
)
def __init__(self, right, *args, **kwargs):
super(DelRightForm, self).__init__(*args, **kwargs)
self.fields['rights'].queryset = Right.objects.filter(right=right)
self.fields['rights'].queryset = Right.objects.select_related('user')\
.select_related('right').filter(right=right)
class BanForm(ModelForm):

View file

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-10-15 18:33
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import users.models
class Migration(migrations.Migration):
dependencies = [
('users', '0055_auto_20171003_0556'),
]
operations = [
migrations.AlterField(
model_name='listright',
name='gid',
field=models.PositiveIntegerField(null=True, unique=True),
),
migrations.AlterField(
model_name='listright',
name='listright',
field=models.CharField(max_length=255, unique=True, validators=[django.core.validators.RegexValidator('^[a-z]+$', message='Les groupes unix ne peuvent contenir que des lettres minuscules')]),
),
migrations.AlterField(
model_name='user',
name='rezo_rez_uid',
field=models.PositiveIntegerField(blank=True, null=True, unique=True),
),
migrations.AlterField(
model_name='user',
name='uid_number',
field=models.PositiveIntegerField(default=users.models.User.auto_uid, unique=True),
),
]

View file

@ -243,8 +243,8 @@ class User(AbstractBaseUser):
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
registered = models.DateTimeField(auto_now_add=True)
telephone = models.CharField(max_length=15, blank=True, null=True)
uid_number = models.IntegerField(default=auto_uid, unique=True)
rezo_rez_uid = models.IntegerField(unique=True, blank=True, null=True)
uid_number = models.PositiveIntegerField(default=auto_uid, unique=True)
rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True)
USERNAME_FIELD = 'pseudo'
REQUIRED_FIELDS = ['name', 'surname', 'email']
@ -840,7 +840,7 @@ class ListRight(models.Model):
que des lettres minuscules"
)]
)
gid = models.IntegerField(unique=True, null=True)
gid = models.PositiveIntegerField(unique=True, null=True)
details = models.CharField(
help_text="Description",
max_length=255,

View file

@ -34,19 +34,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<form class="form" method="post">
{% csrf_token %}
<table class="table table-striped">
<table id="delRights_table" class="table table-striped">
<tbody>
{% for key, values in userform.items %}
<tr class="active">
<td>
<a data-toggle="collapse" href="#collapseRight_{{key}}" aria-expanded="false" aria-controls="collapseRights_{{key}}">
<a data-toggle="collapse" href="#collapseRight_{{key}}" aria-expanded="true" aria-controls="collapseRights_{{key}}">
<b>{{ key }}</b> ( {{values.rights|length }} users )
</a>
</td>
</tr>
<tr>
<td>
<div class="collapse" id="collapseRight_{{key}}">
<div class="collapse in" id="collapseRight_{{key}}">
<ul class="list-group" style="margin-bottom: 0px">
{% for user in values.rights %}
<li class="list-group-item col-xs-6 col-sm-4 col-md-3" style="border: none;">{{ user }}</li>
@ -58,9 +58,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% endfor %}
</tbody>
</table>
{% bootstrap_button "Modifier" button_type="submit" icon="star" %}
{% bootstrap_button "Supprimer" button_type="submit" icon="star" %}
</form>
<script>
$("#delRights_table").ready( function () {
var rights_div = [{% for k in userform.keys %}$("#collapseRight_{{k}}"), {% endfor %}];
for (var i=0 ; i<rights_div.length ; i++) {
rights_div[i].collapse('hide');
}
} );
</script>
<br />
<br />
<br />

View file

@ -88,32 +88,32 @@ urlpatterns = [
url(r'^reset_password/$', views.reset_password, name='reset-password'),
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
url(
r'^history/(?P<object>user)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>user)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>ban)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>ban)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>whitelist)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>school)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>school)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>listright)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>listright)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),
url(
r'^history/(?P<object>serviceuser)/(?P<id>[0-9]+)$',
r'^history/(?P<object_name>serviceuser)/(?P<object_id>[0-9]+)$',
views.history,
name='history'
),