8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-23 20:03:11 +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 2ccf8f4729
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) name = models.CharField(max_length=255)
prix = models.DecimalField(max_digits=5, decimal_places=2) prix = models.DecimalField(max_digits=5, decimal_places=2)
iscotisation = models.BooleanField() iscotisation = models.BooleanField()
duration = models.IntegerField( duration = models.PositiveIntegerField(
help_text="Durée exprimée en mois entiers", help_text="Durée exprimée en mois entiers",
blank=True, blank=True,
null=True) null=True)
@ -222,7 +222,7 @@ class Article(models.Model):
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
prix = models.DecimalField(max_digits=5, decimal_places=2) prix = models.DecimalField(max_digits=5, decimal_places=2)
iscotisation = models.BooleanField() iscotisation = models.BooleanField()
duration = models.IntegerField( duration = models.PositiveIntegerField(
help_text="Durée exprimée en mois entiers", help_text="Durée exprimée en mois entiers",
blank=True, blank=True,
null=True, null=True,

View file

@ -99,18 +99,18 @@ urlpatterns = [
views.index_paiement, views.index_paiement,
name='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, views.history,
name='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, views.history,
name='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, views.history,
name='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, views.history,
name='history' name='history'
), ),

View file

@ -533,7 +533,8 @@ def control(request):
facture_list = paginator.page(1) facture_list = paginator.page(1)
except EmptyPage: except EmptyPage:
facture_list = paginator.page(paginator.num.pages) 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] id__in=[facture.id for facture in facture_list]
) )
controlform = controlform_set(request.POST or None, queryset=page_query) controlform = controlform_set(request.POST or None, queryset=page_query)
@ -603,9 +604,9 @@ def index(request):
@login_required @login_required
def history(request, object, object_id): def history(request, object_name, object_id):
"""Affiche l'historique de chaque objet""" """Affiche l'historique de chaque objet"""
if object == 'facture': if object_name == 'facture':
try: try:
object_instance = Facture.objects.get(pk=object_id) object_instance = Facture.objects.get(pk=object_id)
except Facture.DoesNotExist: except Facture.DoesNotExist:
@ -616,19 +617,19 @@ def history(request, object, object_id):
messages.error(request, "Vous ne pouvez pas afficher l'historique\ messages.error(request, "Vous ne pouvez pas afficher l'historique\
d'une facture d'un autre user que vous sans droit cableur") d'une facture d'un autre user que vous sans droit cableur")
return redirect("/users/profil/" + str(request.user.id)) 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: try:
object_instance = Paiement.objects.get(pk=object_id) object_instance = Paiement.objects.get(pk=object_id)
except Paiement.DoesNotExist: except Paiement.DoesNotExist:
messages.error(request, "Paiement inexistant") messages.error(request, "Paiement inexistant")
return redirect("/cotisations/") return redirect("/cotisations/")
elif object == 'article' and request.user.has_perms(('cableur',)): elif object_name == 'article' and request.user.has_perms(('cableur',)):
try: try:
object_instance = Article.objects.get(pk=object_id) object_instance = Article.objects.get(pk=object_id)
except Article.DoesNotExist: except Article.DoesNotExist:
messages.error(request, "Article inexistante") messages.error(request, "Article inexistante")
return redirect("/cotisations/") return redirect("/cotisations/")
elif object == 'banque' and request.user.has_perms(('cableur',)): elif object_name == 'banque' and request.user.has_perms(('cableur',)):
try: try:
object_instance = Banque.objects.get(pk=object_id) object_instance = Banque.objects.get(pk=object_id)
except Banque.DoesNotExist: 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: if not port.room:
return (sw_name, u'Chambre inconnue', VLAN_NOK) 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: if not room_user:
return (sw_name, u'Chambre non cotisante', VLAN_NOK) return (sw_name, u'Chambre non cotisante', VLAN_NOK)
elif not room_user.first().has_access(): 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 Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort
from .models import OuverturePortList from .models import OuverturePortList
class MachineAdmin(VersionAdmin): class MachineAdmin(VersionAdmin):
pass pass
class IpTypeAdmin(VersionAdmin): class IpTypeAdmin(VersionAdmin):
pass pass
class MachineTypeAdmin(VersionAdmin): class MachineTypeAdmin(VersionAdmin):
pass pass
class VlanAdmin(VersionAdmin): class VlanAdmin(VersionAdmin):
pass pass
class ExtensionAdmin(VersionAdmin): class ExtensionAdmin(VersionAdmin):
pass pass
class MxAdmin(VersionAdmin): class MxAdmin(VersionAdmin):
pass pass
class NsAdmin(VersionAdmin): class NsAdmin(VersionAdmin):
pass pass
class TextAdmin(VersionAdmin): class TextAdmin(VersionAdmin):
pass pass
class NasAdmin(VersionAdmin): class NasAdmin(VersionAdmin):
pass pass
class IpListAdmin(VersionAdmin): class IpListAdmin(VersionAdmin):
pass pass
class OuverturePortAdmin(VersionAdmin): class OuverturePortAdmin(VersionAdmin):
pass pass
class OuverturePortListAdmin(VersionAdmin): class OuverturePortListAdmin(VersionAdmin):
pass pass
class InterfaceAdmin(VersionAdmin): class InterfaceAdmin(VersionAdmin):
list_display = ('machine','type','mac_address','ipv4','details') list_display = ('machine','type','mac_address','ipv4','details')
class DomainAdmin(VersionAdmin): class DomainAdmin(VersionAdmin):
list_display = ('interface_parent', 'name', 'extension', 'cname') list_display = ('interface_parent', 'name', 'extension', 'cname')
class ServiceAdmin(VersionAdmin): class ServiceAdmin(VersionAdmin):
list_display = ('service_type', 'min_time_regen', 'regular_time_regen') list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
admin.site.register(Machine, MachineAdmin) admin.site.register(Machine, MachineAdmin)
admin.site.register(MachineType, MachineTypeAdmin) admin.site.register(MachineType, MachineTypeAdmin)
admin.site.register(IpType, IpTypeAdmin) admin.site.register(IpType, IpTypeAdmin)

View file

@ -21,20 +21,43 @@
# You should have received a copy of the GNU General Public License along # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 from __future__ import unicode_literals
import re from django.forms import ModelForm, Form
from django.forms import ModelForm, Form, ValidationError
from django import forms 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): class EditMachineForm(ModelForm):
"""Formulaire d'édition d'une machine"""
class Meta: class Meta:
model = Machine model = Machine
fields = '__all__' fields = '__all__'
@ -44,15 +67,22 @@ class EditMachineForm(ModelForm):
super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs) super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Nom de la machine' self.fields['name'].label = 'Nom de la machine'
class NewMachineForm(EditMachineForm): class NewMachineForm(EditMachineForm):
"""Creation d'une machine, ne renseigne que le nom"""
class Meta(EditMachineForm.Meta): class Meta(EditMachineForm.Meta):
fields = ['name'] fields = ['name']
class BaseEditMachineForm(EditMachineForm): 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): class Meta(EditMachineForm.Meta):
fields = ['name','active'] fields = ['name', 'active']
class EditInterfaceForm(ModelForm): class EditInterfaceForm(ModelForm):
"""Edition d'une interface. Edition complète"""
class Meta: class Meta:
model = Interface model = Interface
fields = ['machine', 'type', 'ipv4', 'mac_address', 'details'] fields = ['machine', 'type', 'ipv4', 'mac_address', 'details']
@ -64,85 +94,126 @@ class EditInterfaceForm(ModelForm):
self.fields['type'].label = 'Type de machine' self.fields['type'].label = 'Type de machine'
self.fields['type'].empty_label = "Séléctionner un type de machine" self.fields['type'].empty_label = "Séléctionner un type de machine"
if "ipv4" in self.fields: if "ipv4" in self.fields:
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" self.fields['ipv4'].empty_label = "Assignation automatique\
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True) de l'ipv4"
self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
)
# Add it's own address # 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: 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): 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): class Meta(EditInterfaceForm.Meta):
fields = ['type','ipv4','mac_address','details'] fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
infra = kwargs.pop('infra') infra = kwargs.pop('infra')
super(AddInterfaceForm, self).__init__(*args, **kwargs) super(AddInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
if not infra: if not infra:
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)) self.fields['type'].queryset = MachineType.objects.filter(
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False)) 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: else:
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True) self.fields['ipv4'].queryset = IpList.objects.filter(
interface__isnull=True
)
class NewInterfaceForm(EditInterfaceForm): class NewInterfaceForm(EditInterfaceForm):
"""Formulaire light, sans choix de l'ipv4; d'ajout d'une interface"""
class Meta(EditInterfaceForm.Meta): class Meta(EditInterfaceForm.Meta):
fields = ['type','mac_address','details'] fields = ['type', 'mac_address', 'details']
class BaseEditInterfaceForm(EditInterfaceForm): 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): class Meta(EditInterfaceForm.Meta):
fields = ['type','ipv4','mac_address','details'] fields = ['type', 'ipv4', 'mac_address', 'details']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
infra = kwargs.pop('infra') infra = kwargs.pop('infra')
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs) super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4" self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
if not infra: if not infra:
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False)) self.fields['type'].queryset = MachineType.objects.filter(
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False)) 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 # 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: else:
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True) self.fields['ipv4'].queryset = IpList.objects.filter(
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance) interface__isnull=True
)
self.fields['ipv4'].queryset |= IpList.objects.filter(
interface=self.instance
)
class AliasForm(ModelForm): class AliasForm(ModelForm):
"""Ajout d'un alias (et edition), CNAME, contenant nom et extension"""
class Meta: class Meta:
model = Domain model = Domain
fields = ['name','extension'] fields = ['name', 'extension']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
if 'infra' in kwargs:
infra = kwargs.pop('infra')
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs) super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
class DomainForm(AliasForm): class DomainForm(AliasForm):
"""Ajout et edition d'un enregistrement de nom, relié à interface"""
class Meta(AliasForm.Meta): class Meta(AliasForm.Meta):
fields = ['name'] fields = ['name']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
if 'user' in kwargs: if 'user' in kwargs:
user = kwargs.pop('user') user = kwargs.pop('user')
nb_machine = kwargs.pop('nb_machine')
initial = kwargs.get('initial', {}) initial = kwargs.get('initial', {})
initial['name'] = user.get_next_domain_name() initial['name'] = user.get_next_domain_name()
kwargs['initial'] = initial kwargs['initial'] = initial
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs) super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelAliasForm(Form): 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): def __init__(self, *args, **kwargs):
interface = kwargs.pop('interface') interface = kwargs.pop('interface')
super(DelAliasForm, self).__init__(*args, **kwargs) 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): class MachineTypeForm(ModelForm):
"""Ajout et edition d'un machinetype, relié à un iptype"""
class Meta: class Meta:
model = MachineType model = MachineType
fields = ['type','ip_type'] fields = ['type', 'ip_type']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
@ -150,41 +221,72 @@ class MachineTypeForm(ModelForm):
self.fields['type'].label = 'Type de machine à ajouter' self.fields['type'].label = 'Type de machine à ajouter'
self.fields['ip_type'].label = "Type d'ip relié" self.fields['ip_type'].label = "Type d'ip relié"
class DelMachineTypeForm(Form): 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): 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: class Meta:
model = IpType 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): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs) super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['type'].label = 'Type ip à ajouter' self.fields['type'].label = 'Type ip à ajouter'
class EditIpTypeForm(IpTypeForm): class EditIpTypeForm(IpTypeForm):
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
synchroniser les objets iplist"""
class Meta(IpTypeForm.Meta): 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): 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): class ExtensionForm(ModelForm):
"""Formulaire d'ajout et edition d'une extension"""
class Meta: class Meta:
model = Extension model = Extension
fields = ['name', 'need_infra', 'origin'] fields = '__all__'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs) super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['name'].label = 'Extension à ajouter' self.fields['name'].label = 'Extension à ajouter'
self.fields['origin'].label = 'Enregistrement A origin' self.fields['origin'].label = 'Enregistrement A origin'
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
class DelExtensionForm(Form): 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): class MxForm(ModelForm):
"""Ajout et edition d'un MX"""
class Meta: class Meta:
model = Mx model = Mx
fields = ['zone', 'priority', 'name'] fields = ['zone', 'priority', 'name']
@ -192,12 +294,24 @@ class MxForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(MxForm, self).__init__(*args, prefix=prefix, **kwargs) 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): 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): 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: class Meta:
model = Ns model = Ns
fields = ['zone', 'ns'] fields = ['zone', 'ns']
@ -205,12 +319,22 @@ class NsForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NsForm, self).__init__(*args, prefix=prefix, **kwargs) 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): 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): class TxtForm(ModelForm):
"""Ajout d'un txt pour une zone"""
class Meta: class Meta:
model = Text model = Text
fields = '__all__' fields = '__all__'
@ -219,10 +343,19 @@ class TxtForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs) super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelTxtForm(Form): 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): class NasForm(ModelForm):
"""Ajout d'un type de nas (machine d'authentification,
swicths, bornes...)"""
class Meta: class Meta:
model = Nas model = Nas
fields = '__all__' fields = '__all__'
@ -231,10 +364,18 @@ class NasForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(NasForm, self).__init__(*args, prefix=prefix, **kwargs) super(NasForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelNasForm(Form): 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): class ServiceForm(ModelForm):
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
class Meta: class Meta:
model = Service model = Service
fields = '__all__' fields = '__all__'
@ -242,6 +383,8 @@ class ServiceForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs) super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
self.fields['servers'].queryset = Interface.objects.all()\
.select_related('domain__extension')
def save(self, commit=True): def save(self, commit=True):
instance = super(ServiceForm, self).save(commit=False) instance = super(ServiceForm, self).save(commit=False)
@ -250,10 +393,18 @@ class ServiceForm(ModelForm):
instance.process_link(self.cleaned_data.get('servers')) instance.process_link(self.cleaned_data.get('servers'))
return instance return instance
class DelServiceForm(Form): 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): class VlanForm(ModelForm):
"""Ajout d'un vlan : id, nom"""
class Meta: class Meta:
model = Vlan model = Vlan
fields = '__all__' fields = '__all__'
@ -262,24 +413,43 @@ class VlanForm(ModelForm):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs) super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelVlanForm(Form): 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): class EditOuverturePortConfigForm(ModelForm):
"""Edition de la liste des profils d'ouverture de ports
pour l'interface"""
class Meta: class Meta:
model = Interface model = Interface
fields = ['port_lists'] fields = ['port_lists']
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) 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): class EditOuverturePortListForm(ModelForm):
"""Edition de la liste des ports et profils d'ouverture
des ports"""
class Meta: class Meta:
model = OuverturePortList model = OuverturePortList
fields = '__all__' fields = '__all__'
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) 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 __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 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.dispatch import receiver
from django.forms import ValidationError from django.forms import ValidationError
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils import timezone from django.utils import timezone
from django.core.validators import MaxValueValidator
from macaddress.fields import MACAddressField 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 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" PRETTY_NAME = "Machine"
user = models.ForeignKey('users.User', on_delete=models.PROTECT) 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) active = models.BooleanField(default=True)
def __str__(self): def __str__(self):
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name) return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
class MachineType(models.Model): class MachineType(models.Model):
""" Type de machine, relié à un type d'ip, affecté aux interfaces""" """ Type de machine, relié à un type d'ip, affecté aux interfaces"""
PRETTY_NAME = "Type de machine" PRETTY_NAME = "Type de machine"
type = models.CharField(max_length=255) 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): 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) return Interface.objects.filter(type=self)
def __str__(self): def __str__(self):
return self.type return self.type
class IpType(models.Model): class IpType(models.Model):
""" Type d'ip, définissant un range d'ip, affecté aux machine types""" """ Type d'ip, définissant un range d'ip, affecté aux machine types"""
PRETTY_NAME = "Type d'ip" PRETTY_NAME = "Type d'ip"
@ -71,8 +86,22 @@ class IpType(models.Model):
need_infra = models.BooleanField(default=False) need_infra = models.BooleanField(default=False)
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4') domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4') domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True) prefix_v6 = models.GenericIPAddressField(
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True) 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 @cached_property
def ip_range(self): def ip_range(self):
@ -95,18 +124,22 @@ class IpType(models.Model):
def free_ip(self): def free_ip(self):
""" Renvoie toutes les ip libres associées au type donné (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): def gen_ip_range(self):
""" Cree les IpList associées au type self. Parcours pédestrement et crée """ Cree les IpList associées au type self. Parcours pédestrement et
les ip une par une. Si elles existent déjà, met à jour le type associé crée les ip une par une. Si elles existent déjà, met à jour le type
à l'ip""" associé à l'ip"""
# Creation du range d'ip dans les objets iplist # Creation du range d'ip dans les objets iplist
networks = [] networks = []
for net in self.ip_range.cidrs(): for net in self.ip_range.cidrs():
networks += net.iter_hosts() networks += net.iter_hosts()
ip_obj = [IpList(ip_type=self, ipv4=str(ip)) for ip in networks] 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 # Si il n'y a pas d'ip, on les crée
if not listes_ip: if not listes_ip:
IpList.objects.bulk_create(ip_obj) IpList.objects.bulk_create(ip_obj)
@ -116,9 +149,11 @@ class IpType(models.Model):
return return
def del_ip_range(self): 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()): 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(): for ip in self.ip_objects():
ip.delete() ip.delete()
@ -132,11 +167,13 @@ class IpType(models.Model):
raise ValidationError("Domaine end doit être après start...") raise ValidationError("Domaine end doit être après start...")
# On ne crée pas plus grand qu'un /16 # On ne crée pas plus grand qu'un /16
if self.ip_range.size > 65536: 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 # On check que les / ne se recoupent pas
for element in IpType.objects.all().exclude(pk=self.pk): for element in IpType.objects.all().exclude(pk=self.pk):
if not self.ip_set.isdisjoint(element.ip_set): 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 # On formate le prefix v6
if self.prefix_v6: if self.prefix_v6:
self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network) self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network)
@ -149,17 +186,20 @@ class IpType(models.Model):
def __str__(self): def __str__(self):
return self.type return self.type
class Vlan(models.Model): 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" PRETTY_NAME = "Vlans"
vlan_id = models.IntegerField() vlan_id = models.PositiveIntegerField(validators=[MaxValueValidator(4095)])
name = models.CharField(max_length=256) name = models.CharField(max_length=256)
comment = models.CharField(max_length=256, blank=True) comment = models.CharField(max_length=256, blank=True)
def __str__(self): def __str__(self):
return self.name return self.name
class Nas(models.Model): class Nas(models.Model):
""" Les nas. Associé à un machine_type. """ Les nas. Associé à un machine_type.
Permet aussi de régler le port_access_mode (802.1X ou mac-address) pour 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) name = models.CharField(max_length=255, unique=True)
nas_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='nas_type') nas_type = models.ForeignKey(
machine_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='machinetype_on_nas') 'MachineType',
port_access_mode = models.CharField(choices=AUTH, default=default_mode, max_length=32) 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) autocapture_mac = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return self.name return self.name
class Extension(models.Model): class Extension(models.Model):
""" Extension dns type example.org. Précise si tout le monde peut l'utiliser, """ Extension dns type example.org. Précise si tout le monde peut
associé à un origin (ip d'origine)""" l'utiliser, associé à un origin (ip d'origine)"""
PRETTY_NAME = "Extensions dns" PRETTY_NAME = "Extensions dns"
name = models.CharField(max_length=255, unique=True) name = models.CharField(max_length=255, unique=True)
need_infra = models.BooleanField(default=False) 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 @cached_property
def dns_entry(self): def dns_entry(self):
""" Une entrée DNS A""" """ Une entrée DNS A et AAAA sur origin (zone self)"""
return "@ IN A " + str(self.origin) 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): def __str__(self):
return self.name return self.name
class Mx(models.Model): 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 """ Todo : pouvoir associer un MX à une interface """
PRETTY_NAME = "Enregistrements MX" PRETTY_NAME = "Enregistrements MX"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT) 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) name = models.OneToOneField('Domain', on_delete=models.PROTECT)
@cached_property @cached_property
def dns_entry(self): 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) return "@ IN MX " + str(self.priority) + " " + str(self.name)
def __str__(self): def __str__(self):
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name) return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
class Ns(models.Model): class Ns(models.Model):
"""Liste des enregistrements name servers par zone considéérée"""
PRETTY_NAME = "Enregistrements NS" PRETTY_NAME = "Enregistrements NS"
zone = models.ForeignKey('Extension', on_delete=models.PROTECT) zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
@ -222,11 +298,13 @@ class Ns(models.Model):
@cached_property @cached_property
def dns_entry(self): def dns_entry(self):
"""Renvoie un enregistrement NS complet pour les filezones"""
return "@ IN NS " + str(self.ns) return "@ IN NS " + str(self.ns)
def __str__(self): def __str__(self):
return str(self.zone) + ' ' + str(self.ns) return str(self.zone) + ' ' + str(self.ns)
class Text(models.Model): class Text(models.Model):
""" Un enregistrement TXT associé à une extension""" """ Un enregistrement TXT associé à une extension"""
PRETTY_NAME = "Enregistrement text" PRETTY_NAME = "Enregistrement text"
@ -236,22 +314,31 @@ class Text(models.Model):
field2 = models.CharField(max_length=255) field2 = models.CharField(max_length=255)
def __str__(self): 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 @cached_property
def dns_entry(self): def dns_entry(self):
"""Renvoie l'enregistrement TXT complet pour le fichier de zone"""
return str(self.field1) + " IN TXT " + str(self.field2) return str(self.field1) + " IN TXT " + str(self.field2)
class Interface(models.Model): class Interface(models.Model):
""" Une interface. Objet clef de l'application machine : """ 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 - une onetoone vers IpList pour attribution ipv4
- le type parent associé au range ip et à l'extension - le type parent associé au range ip et à l'extension
- un objet domain associé contenant son nom - un objet domain associé contenant son nom
- la liste des ports oiuvert""" - la liste des ports oiuvert"""
PRETTY_NAME = "Interface" 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) mac_address = MACAddressField(integer=False, unique=True)
machine = models.ForeignKey('Machine', on_delete=models.CASCADE) machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
type = models.ForeignKey('MachineType', on_delete=models.PROTECT) type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
@ -265,12 +352,14 @@ class Interface(models.Model):
user = self.machine.user user = self.machine.user
return machine.active and user.has_access() return machine.active and user.has_access()
@cached_property @cached_property
def ipv6_object(self): 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: 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: else:
return None return None
@ -284,10 +373,11 @@ class Interface(models.Model):
return str(EUI(self.mac_address, dialect=mac_bare)).lower() return str(EUI(self.mac_address, dialect=mac_bare)).lower()
def filter_macaddress(self): 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: try:
self.mac_address = str(EUI(self.mac_address)) self.mac_address = str(EUI(self.mac_address))
except : except:
raise ValidationError("La mac donnée est invalide") raise ValidationError("La mac donnée est invalide")
def clean(self, *args, **kwargs): def clean(self, *args, **kwargs):
@ -305,7 +395,8 @@ class Interface(models.Model):
if free_ips: if free_ips:
self.ipv4 = free_ips[0] self.ipv4 = free_ips[0]
else: 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 return
def unassign_ipv4(self): def unassign_ipv4(self):
@ -320,8 +411,10 @@ class Interface(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
self.filter_macaddress() self.filter_macaddress()
# On verifie la cohérence en forçant l'extension par la méthode # 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: 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) super(Interface, self).save(*args, **kwargs)
def __str__(self): def __str__(self):
@ -340,18 +433,34 @@ class Interface(models.Model):
def may_have_port_open(self): def may_have_port_open(self):
""" True si l'interface a une ip et une ip publique. """ 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() return self.ipv4 and not self.has_private_ip()
class Domain(models.Model): class Domain(models.Model):
""" Objet domain. Enregistrement A et CNAME en même temps : permet de stocker les """ Objet domain. Enregistrement A et CNAME en même temps : permet de
alias et les nom de machines, suivant si interface_parent ou cname sont remplis""" stocker les alias et les nom de machines, suivant si interface_parent
ou cname sont remplis"""
PRETTY_NAME = "Domaine dns" PRETTY_NAME = "Domaine dns"
interface_parent = models.OneToOneField('Interface', on_delete=models.CASCADE, blank=True, null=True) interface_parent = models.OneToOneField(
name = models.CharField(help_text="Obligatoire et unique, ne doit pas comporter de points", max_length=255) '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) 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: class Meta:
unique_together = (("name", "extension"),) unique_together = (("name", "extension"),)
@ -361,7 +470,7 @@ class Domain(models.Model):
Retourne l'extension propre si c'est un cname, renvoie None sinon""" Retourne l'extension propre si c'est un cname, renvoie None sinon"""
if self.interface_parent: if self.interface_parent:
return self.interface_parent.type.ip_type.extension return self.interface_parent.type.ip_type.extension
elif hasattr(self,'extension'): elif hasattr(self, 'extension'):
return self.extension return self.extension
else: else:
return None return None
@ -370,21 +479,26 @@ class Domain(models.Model):
""" Validation : """ Validation :
- l'objet est bien soit A soit CNAME - l'objet est bien soit A soit CNAME
- le cname est pas pointé sur lui-même - 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""" - le couple nom/extension est bien unique"""
if self.get_extension(): if self.get_extension():
self.extension=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: if self.interface_parent and self.cname:
raise ValidationError("On ne peut créer à la fois A et CNAME") raise ValidationError("On ne peut créer à la fois A et CNAME")
if self.cname==self: if self.cname == self:
raise ValidationError("On ne peut créer un cname sur lui même") 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() dns = self.name.lower()
if len(dns) > 63: 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): 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() self.validate_unique()
super(Domain, self).clean() super(Domain, self).clean()
@ -395,7 +509,8 @@ class Domain(models.Model):
return str(self.name) + " IN CNAME " + str(self.cname) + "." return str(self.name) + " IN CNAME " + str(self.cname) + "."
def save(self, *args, **kwargs): 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(): if not self.get_extension():
raise ValidationError("Extension invalide") raise ValidationError("Extension invalide")
self.full_clean() self.full_clean()
@ -404,6 +519,7 @@ class Domain(models.Model):
def __str__(self): def __str__(self):
return str(self.name) + str(self.extension) return str(self.name) + str(self.extension)
class IpList(models.Model): class IpList(models.Model):
PRETTY_NAME = "Addresses ipv4" PRETTY_NAME = "Addresses ipv4"
@ -412,13 +528,15 @@ class IpList(models.Model):
@cached_property @cached_property
def need_infra(self): 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 return self.ip_type.need_infra
def clean(self): def clean(self):
""" Erreur si l'ip_type est incorrect""" """ Erreur si l'ip_type est incorrect"""
if not str(self.ipv4) in self.ip_type.ip_set_as_str: 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 return
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -428,24 +546,36 @@ class IpList(models.Model):
def __str__(self): def __str__(self):
return self.ipv4 return self.ipv4
class Service(models.Model): class Service(models.Model):
""" Definition d'un service (dhcp, dns, etc)""" """ Definition d'un service (dhcp, dns, etc)"""
service_type = models.CharField(max_length=255, blank=True, unique=True) 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") min_time_regen = models.DurationField(
regular_time_regen = models.DurationField(default=timedelta(hours=1), help_text="Temps maximal avant nouvelle génération du service") 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') servers = models.ManyToManyField('Interface', through='Service_link')
def ask_regen(self): def ask_regen(self):
""" Marque à True la demande de régénération pour un service x """ """ 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 return
def process_link(self, servers): def process_link(self, servers):
""" Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite""" """ Django ne peut créer lui meme les relations manytomany avec table
for serv in servers.exclude(pk__in=Interface.objects.filter(service=self)): intermediaire explicite"""
for serv in servers.exclude(
pk__in=Interface.objects.filter(service=self)
):
link = Service_link(service=self, server=serv) link = Service_link(service=self, server=serv)
link.save() 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 return
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
@ -454,13 +584,16 @@ class Service(models.Model):
def __str__(self): def __str__(self):
return str(self.service_type) return str(self.service_type)
def regen(service): 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) obj = Service.objects.filter(service_type=service)
if obj: if obj:
obj[0].ask_regen() obj[0].ask_regen()
return return
class Service_link(models.Model): class Service_link(models.Model):
""" Definition du lien entre serveurs et services""" """ Definition du lien entre serveurs et services"""
service = models.ForeignKey('Service', on_delete=models.CASCADE) service = models.ForeignKey('Service', on_delete=models.CASCADE)
@ -475,11 +608,16 @@ class Service_link(models.Model):
self.save() self.save()
def need_regen(self): def need_regen(self):
""" Décide si le temps minimal écoulé est suffisant pour provoquer une régénération de service""" """ Décide si le temps minimal écoulé est suffisant pour provoquer une
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(): régénération de service"""
return True return bool(
else: (self.asked_regen and (
return False self.last_regen + self.service.min_time_regen
) < timezone.now()
) or (
self.last_regen + self.service.regular_time_regen
) < timezone.now()
)
def __str__(self): def __str__(self):
return str(self.server) + " " + str(self.service) return str(self.server) + " " + str(self.service)
@ -487,22 +625,41 @@ class Service_link(models.Model):
class OuverturePortList(models.Model): class OuverturePortList(models.Model):
"""Liste des ports ouverts sur une interface.""" """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): def __str__(self):
return self.name return self.name
def tcp_ports_in(self): 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): 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): 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): 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): class OuverturePort(models.Model):
@ -511,14 +668,19 @@ class OuverturePort(models.Model):
Les ports de la plage sont compris entre begin et en inclus. Les ports de la plage sont compris entre begin et en inclus.
Si begin == end alors on ne représente qu'un seul port. 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' TCP = 'T'
UDP = 'U' UDP = 'U'
IN = 'I' IN = 'I'
OUT = 'O' OUT = 'O'
begin = models.IntegerField() begin = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
end = models.IntegerField() end = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
port_list = models.ForeignKey('OuverturePortList', on_delete=models.CASCADE) port_list = models.ForeignKey(
'OuverturePortList',
on_delete=models.CASCADE
)
protocole = models.CharField( protocole = models.CharField(
max_length=1, max_length=1,
choices=( choices=(
@ -537,93 +699,128 @@ class OuverturePort(models.Model):
) )
def __str__(self): def __str__(self):
if self.begin == self.end : if self.begin == self.end:
return str(self.begin) return str(self.begin)
return '-'.join([str(self.begin), str(self.end)]) return '-'.join([str(self.begin), str(self.end)])
def show_port(self): def show_port(self):
"""Formatage plus joli, alias pour str"""
return str(self) return str(self)
@receiver(post_save, sender=Machine) @receiver(post_save, sender=Machine)
def machine_post_save(sender, **kwargs): 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 = kwargs['instance'].user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
regen('dhcp') regen('dhcp')
regen('mac_ip_list') regen('mac_ip_list')
@receiver(post_delete, sender=Machine) @receiver(post_delete, sender=Machine)
def machine_post_delete(sender, **kwargs): def machine_post_delete(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
d'une machine"""
machine = kwargs['instance'] machine = kwargs['instance']
user = machine.user user = machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
regen('dhcp') regen('dhcp')
regen('mac_ip_list') regen('mac_ip_list')
@receiver(post_save, sender=Interface) @receiver(post_save, sender=Interface)
def interface_post_save(sender, **kwargs): def interface_post_save(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
d'une interface"""
interface = kwargs['instance'] interface = kwargs['instance']
user = interface.machine.user user = interface.machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) 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 services
regen('dhcp') regen('dhcp')
regen('mac_ip_list') regen('mac_ip_list')
@receiver(post_delete, sender=Interface) @receiver(post_delete, sender=Interface)
def interface_post_delete(sender, **kwargs): def interface_post_delete(sender, **kwargs):
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
d'une interface"""
interface = kwargs['instance'] interface = kwargs['instance']
user = interface.machine.user user = interface.machine.user
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
@receiver(post_save, sender=IpType) @receiver(post_save, sender=IpType)
def iptype_post_save(sender, **kwargs): def iptype_post_save(sender, **kwargs):
"""Generation des objets ip après modification d'un range ip"""
iptype = kwargs['instance'] iptype = kwargs['instance']
iptype.gen_ip_range() iptype.gen_ip_range()
@receiver(post_save, sender=MachineType) @receiver(post_save, sender=MachineType)
def machine_post_save(sender, **kwargs): def machine_post_save(sender, **kwargs):
"""Mise à jour des interfaces lorsque changement d'attribution
d'une machinetype (changement iptype parent)"""
machinetype = kwargs['instance'] machinetype = kwargs['instance']
for interface in machinetype.all_interfaces(): for interface in machinetype.all_interfaces():
interface.update_type() interface.update_type()
@receiver(post_save, sender=Domain) @receiver(post_save, sender=Domain)
def domain_post_save(sender, **kwargs): def domain_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un domain object"""
regen('dns') regen('dns')
@receiver(post_delete, sender=Domain) @receiver(post_delete, sender=Domain)
def domain_post_delete(sender, **kwargs): def domain_post_delete(sender, **kwargs):
"""Regeneration dns après suppression d'un domain object"""
regen('dns') regen('dns')
@receiver(post_save, sender=Extension) @receiver(post_save, sender=Extension)
def extension_post_save(sender, **kwargs): def extension_post_save(sender, **kwargs):
"""Regeneration dns après modification d'une extension"""
regen('dns') regen('dns')
@receiver(post_delete, sender=Extension) @receiver(post_delete, sender=Extension)
def extension_post_selete(sender, **kwargs): def extension_post_selete(sender, **kwargs):
"""Regeneration dns après suppression d'une extension"""
regen('dns') regen('dns')
@receiver(post_save, sender=Mx) @receiver(post_save, sender=Mx)
def mx_post_save(sender, **kwargs): def mx_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un MX"""
regen('dns') regen('dns')
@receiver(post_delete, sender=Mx) @receiver(post_delete, sender=Mx)
def mx_post_delete(sender, **kwargs): def mx_post_delete(sender, **kwargs):
"""Regeneration dns après suppresson d'un MX"""
regen('dns') regen('dns')
@receiver(post_save, sender=Ns) @receiver(post_save, sender=Ns)
def ns_post_save(sender, **kwargs): def ns_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un NS"""
regen('dns') regen('dns')
@receiver(post_delete, sender=Ns) @receiver(post_delete, sender=Ns)
def ns_post_delete(sender, **kwargs): def ns_post_delete(sender, **kwargs):
"""Regeneration dns après modification d'un NS"""
regen('dns') regen('dns')
@receiver(post_save, sender=Text) @receiver(post_save, sender=Text)
def text_post_save(sender, **kwargs): def text_post_save(sender, **kwargs):
"""Regeneration dns après modification d'un TXT"""
regen('dns') regen('dns')
@receiver(post_delete, sender=Text) @receiver(post_delete, sender=Text)
def text_post_delete(sender, **kwargs): def text_post_delete(sender, **kwargs):
"""Regeneration dns après modification d'un TX"""
regen('dns') regen('dns')

View file

@ -24,20 +24,42 @@
#Augustin Lemesle #Augustin Lemesle
from rest_framework import serializers 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): class IpTypeField(serializers.RelatedField):
"""Serialisation d'une iptype, renvoie son evaluation str"""
def to_representation(self, value): def to_representation(self, value):
return value.type return value.type
class IpListSerializer(serializers.ModelSerializer): 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) ip_type = IpTypeField(read_only=True)
class Meta: class Meta:
model = IpList model = IpList
fields = ('ipv4', 'ip_type') fields = ('ipv4', 'ip_type')
class InterfaceSerializer(serializers.ModelSerializer): 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) ipv4 = IpListSerializer(read_only=True)
mac_address = serializers.SerializerMethodField('get_macaddress') mac_address = serializers.SerializerMethodField('get_macaddress')
domain = serializers.SerializerMethodField('get_dns') domain = serializers.SerializerMethodField('get_dns')
@ -56,7 +78,9 @@ class InterfaceSerializer(serializers.ModelSerializer):
def get_macaddress(self, obj): def get_macaddress(self, obj):
return str(obj.mac_address) return str(obj.mac_address)
class FullInterfaceSerializer(serializers.ModelSerializer): class FullInterfaceSerializer(serializers.ModelSerializer):
"""Serialisation complete d'une interface avec l'ipv6 en plus"""
ipv4 = IpListSerializer(read_only=True) ipv4 = IpListSerializer(read_only=True)
mac_address = serializers.SerializerMethodField('get_macaddress') mac_address = serializers.SerializerMethodField('get_macaddress')
domain = serializers.SerializerMethodField('get_dns') domain = serializers.SerializerMethodField('get_dns')
@ -75,24 +99,69 @@ class FullInterfaceSerializer(serializers.ModelSerializer):
def get_macaddress(self, obj): def get_macaddress(self, obj):
return str(obj.mac_address) return str(obj.mac_address)
class ExtensionNameField(serializers.RelatedField): class ExtensionNameField(serializers.RelatedField):
"""Evaluation str d'un objet extension (.example.org)"""
def to_representation(self, value): def to_representation(self, value):
return value.name return value.name
class TypeSerializer(serializers.ModelSerializer): 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) 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: class Meta:
model = IpType 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): 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') origin = serializers.SerializerMethodField('get_origin_ip')
zone_entry = serializers.SerializerMethodField('get_zone_name') zone_entry = serializers.SerializerMethodField('get_zone_name')
class Meta: class Meta:
model = Extension model = Extension
fields = ('name', 'origin', 'zone_entry') fields = ('name', 'origin', 'origin_v6', 'zone_entry')
def get_origin_ip(self, obj): def get_origin_ip(self, obj):
return obj.origin.ipv4 return obj.origin.ipv4
@ -100,7 +169,10 @@ class ExtensionSerializer(serializers.ModelSerializer):
def get_zone_name(self, obj): def get_zone_name(self, obj):
return str(obj.dns_entry) return str(obj.dns_entry)
class MxSerializer(serializers.ModelSerializer): 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') name = serializers.SerializerMethodField('get_entry_name')
zone = serializers.SerializerMethodField('get_zone_name') zone = serializers.SerializerMethodField('get_zone_name')
mx_entry = serializers.SerializerMethodField('get_mx_name') mx_entry = serializers.SerializerMethodField('get_mx_name')
@ -118,13 +190,16 @@ class MxSerializer(serializers.ModelSerializer):
def get_mx_name(self, obj): def get_mx_name(self, obj):
return str(obj.dns_entry) return str(obj.dns_entry)
class TextSerializer(serializers.ModelSerializer): 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') zone = serializers.SerializerMethodField('get_zone_name')
text_entry = serializers.SerializerMethodField('get_text_name') text_entry = serializers.SerializerMethodField('get_text_name')
class Meta: class Meta:
model = Text model = Text
fields = ('zone','text_entry','field1', 'field2') fields = ('zone', 'text_entry', 'field1', 'field2')
def get_zone_name(self, obj): def get_zone_name(self, obj):
return str(obj.zone.name) return str(obj.zone.name)
@ -132,7 +207,10 @@ class TextSerializer(serializers.ModelSerializer):
def get_text_name(self, obj): def get_text_name(self, obj):
return str(obj.dns_entry) return str(obj.dns_entry)
class NsSerializer(serializers.ModelSerializer): 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') zone = serializers.SerializerMethodField('get_zone_name')
ns = serializers.SerializerMethodField('get_domain_name') ns = serializers.SerializerMethodField('get_domain_name')
ns_entry = serializers.SerializerMethodField('get_text_name') ns_entry = serializers.SerializerMethodField('get_text_name')
@ -150,7 +228,10 @@ class NsSerializer(serializers.ModelSerializer):
def get_text_name(self, obj): def get_text_name(self, obj):
return str(obj.dns_entry) return str(obj.dns_entry)
class DomainSerializer(serializers.ModelSerializer): 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') extension = serializers.SerializerMethodField('get_zone_name')
cname = serializers.SerializerMethodField('get_alias_name') cname = serializers.SerializerMethodField('get_alias_name')
cname_entry = serializers.SerializerMethodField('get_cname_name') cname_entry = serializers.SerializerMethodField('get_cname_name')
@ -168,7 +249,9 @@ class DomainSerializer(serializers.ModelSerializer):
def get_cname_name(self, obj): def get_cname_name(self, obj):
return str(obj.dns_entry) return str(obj.dns_entry)
class ServiceServersSerializer(serializers.ModelSerializer): class ServiceServersSerializer(serializers.ModelSerializer):
"""Evaluation d'un Service, et serialisation"""
server = serializers.SerializerMethodField('get_server_name') server = serializers.SerializerMethodField('get_server_name')
service = serializers.SerializerMethodField('get_service_name') service = serializers.SerializerMethodField('get_service_name')
need_regen = serializers.SerializerMethodField('get_regen_status') need_regen = serializers.SerializerMethodField('get_regen_status')
@ -185,3 +268,31 @@ class ServiceServersSerializer(serializers.ModelSerializer):
def get_regen_status(self, obj): def get_regen_status(self, obj):
return obj.need_regen() 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>Extension</th>
<th>Autorisation infra pour utiliser l'extension</th> <th>Autorisation infra pour utiliser l'extension</th>
<th>Enregistrement A origin</th> <th>Enregistrement A origin</th>
<th>Enregistrement AAAA origin</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -36,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ extension.name }}</td> <td>{{ extension.name }}</td>
<td>{{ extension.need_infra }}</td> <td>{{ extension.need_infra }}</td>
<td>{{ extension.origin }}</td> <td>{{ extension.origin }}</td>
<td>{{ extension.origin_v6 }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %} {% 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>Fin</th>
<th>Préfixe v6</th> <th>Préfixe v6</th>
<th>Sur vlan</th> <th>Sur vlan</th>
<th>Ouverture ports par défault</th>
<th></th> <th></th>
<th></th> <th></th>
</tr> </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.domaine_ip_stop }}</td>
<td>{{ type.prefix_v6 }}</td> <td>{{ type.prefix_v6 }}</td>
<td>{{ type.vlan }}</td> <td>{{ type.vlan }}</td>
<td>{{ type.ouverture_ports }}</td>
<td class="text-right"> <td class="text-right">
{% if is_infra %} {% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %} {% 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 %} {% if is_cableur %}
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}"> <a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
<i class="glyphicon glyphicon-list"></i> <i class="glyphicon glyphicon-list"></i>
Configuration de ports Ouverture de ports
</a> </a>
{%endif%} {%endif%}
{% endblock %} {% endblock %}

View file

@ -93,6 +93,7 @@ urlpatterns = [
url(r'^rest/text/$', views.text, name='text'), url(r'^rest/text/$', views.text, name='text'),
url(r'^rest/zones/$', views.zones, name='zones'), url(r'^rest/zones/$', views.zones, name='zones'),
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'), 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'index_portlist/$', views.index_portlist, name='index-portlist'),
url(r'^edit_portlist/(?P<pk>[0-9]+)$', views.edit_portlist, name='edit-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'), 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 django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer 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 import revisions as reversion
from reversion.models import Version from reversion.models import Version
import re import re
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm from .forms import (
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, TxtForm, DelTxtForm, MxForm, DelMxForm, VlanForm, DelVlanForm, ServiceForm, DelServiceForm, NasForm, DelNasForm 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 .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 users.models import User
from preferences.models import GeneralOption, OptionalMachine 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 from re2o.views import form
def f_type_id( is_type_tt ): 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"},' choices = '{"":[{key:"",value:"Choisissez d\'abord un type de machine"},'
mtype_id = -1 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 : if mtype_id != ip.mtype_id :
mtype_id = ip.mtype_id mtype_id = ip.mtype_id
used_mtype_id.append(mtype_id) used_mtype_id.append(mtype_id)
@ -140,8 +202,8 @@ def generate_ipv4_mbf_param( form, is_type_tt ):
@login_required @login_required
def new_machine(request, userid): def new_machine(request, userid):
""" Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain """ Fonction de creation d'une machine. Cree l'objet machine,
à partir de model forms. le sous objet interface et l'objet domain à partir de model forms.
Trop complexe, devrait être simplifié""" Trop complexe, devrait être simplifié"""
try: try:
user = User.objects.get(pk=userid) user = User.objects.get(pk=userid)
@ -152,15 +214,16 @@ def new_machine(request, userid):
max_lambdauser_interfaces = options.max_lambdauser_interfaces max_lambdauser_interfaces = options.max_lambdauser_interfaces
if not request.user.has_perms(('cableur',)): if not request.user.has_perms(('cableur',)):
if user != request.user: 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)) return redirect("/users/profil/" + str(request.user.id))
if user.user_interfaces().count() >= max_lambdauser_interfaces: 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) 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)) return redirect("/users/profil/" + str(request.user.id))
machine = NewMachineForm(request.POST or None) machine = NewMachineForm(request.POST or None)
interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',))) 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)
domain = DomainForm(request.POST or None, user=user, nb_machine=nb_machine)
if machine.is_valid() and interface.is_valid(): if machine.is_valid() and interface.is_valid():
new_machine = machine.save(commit=False) new_machine = machine.save(commit=False)
new_machine.user = user new_machine.user = user
@ -993,7 +1056,9 @@ def history(request, object, id):
@login_required @login_required
@permission_required('cableur') @permission_required('cableur')
def index_portlist(request): 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}) return render(request, "machines/index_portlist.html", {'port_list':port_list})
@login_required @login_required
@ -1184,6 +1249,34 @@ def service_servers(request):
@csrf_exempt @csrf_exempt
@login_required @login_required
@permission_required('serveur') @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): 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']))) 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: 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'^del_services/$', views.del_services, name='del-services'),
url( url(
r'^history/(?P<object>service)/(?P<id>[0-9]+)$', r'^history/(?P<object_name>service)/(?P<object_id>[0-9]+)$',
views.history, views.history,
name='history' name='history'
), ),

View file

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

View file

@ -82,6 +82,11 @@ class AddPortForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs) 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): class StackForm(ModelForm):
@ -105,6 +110,8 @@ class EditSwitchForm(ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__) prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(EditSwitchForm, self).__init__(*args, prefix=prefix, **kwargs) 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['location'].label = 'Localisation'
self.fields['number'].label = 'Nombre de ports' 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) name = models.CharField(max_length=32, blank=True, null=True)
stack_id = models.CharField(max_length=32, unique=True) stack_id = models.CharField(max_length=32, unique=True)
details = models.CharField(max_length=255, blank=True, null=True) details = models.CharField(max_length=255, blank=True, null=True)
member_id_min = models.IntegerField() member_id_min = models.PositiveIntegerField()
member_id_max = models.IntegerField() member_id_max = models.PositiveIntegerField()
def __str__(self): def __str__(self):
return " ".join([self.name, self.stack_id]) return " ".join([self.name, self.stack_id])
@ -90,7 +90,7 @@ class Switch(models.Model):
on_delete=models.CASCADE on_delete=models.CASCADE
) )
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
number = models.IntegerField() number = models.PositiveIntegerField()
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
stack = models.ForeignKey( stack = models.ForeignKey(
Stack, Stack,
@ -98,7 +98,7 @@ class Switch(models.Model):
null=True, null=True,
on_delete=models.SET_NULL 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: class Meta:
unique_together = ('stack', 'stack_member_id') unique_together = ('stack', 'stack_member_id')
@ -145,8 +145,12 @@ class Port(models.Model):
('COMMON', 'COMMON'), ('COMMON', 'COMMON'),
) )
switch = models.ForeignKey('Switch', related_name="ports") switch = models.ForeignKey(
port = models.IntegerField() 'Switch',
related_name="ports",
on_delete=models.CASCADE
)
port = models.PositiveIntegerField()
room = models.ForeignKey( room = models.ForeignKey(
'Room', 'Room',
on_delete=models.PROTECT, 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 %}"> <a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switch' switch.pk %}">
<i class="glyphicon glyphicon-time"></i> <i class="glyphicon glyphicon-time"></i>
</a> </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> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -42,16 +42,16 @@ urlpatterns = [
url(r'^switch/(?P<switch_id>[0-9]+)$', url(r'^switch/(?P<switch_id>[0-9]+)$',
views.index_port, views.index_port,
name='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, views.history,
name='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, views.history,
name='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, views.history,
name='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, views.history,
name='history'), name='history'),
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'), 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 topologie.forms import AddPortForm, EditRoomForm, StackForm
from users.views import form 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 machines.views import generate_ipv4_mbf_param
from preferences.models import AssoOption, GeneralOption from preferences.models import AssoOption, GeneralOption
@ -135,7 +135,8 @@ def index_port(request, switch_id):
port_list = Port.objects.filter(switch=switch)\ port_list = Port.objects.filter(switch=switch)\
.select_related('room')\ .select_related('room')\
.select_related('machine_interface__domain__extension')\ .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')\ .select_related('switch')\
.order_by('port') .order_by('port')
return render(request, 'topologie/index_p.html', { return render(request, 'topologie/index_p.html', {
@ -344,9 +345,8 @@ def new_switch(request):
request.POST or None, request.POST or None,
infra=request.user.has_perms(('infra',)) infra=request.user.has_perms(('infra',))
) )
domain = AliasForm( domain = DomainForm(
request.POST or None, request.POST or None,
infra=request.user.has_perms(('infra',))
) )
if switch.is_valid() and machine.is_valid() and interface.is_valid(): if switch.is_valid() and machine.is_valid() and interface.is_valid():
options, _created = AssoOption.objects.get_or_create() options, _created = AssoOption.objects.get_or_create()
@ -410,9 +410,8 @@ def edit_switch(request, switch_id):
request.POST or None, request.POST or None,
instance=switch.switch_interface instance=switch.switch_interface
) )
domain_form = AliasForm( domain_form = DomainForm(
request.POST or None, request.POST or None,
infra=request.user.has_perms(('infra',)),
instance=switch.switch_interface.domain instance=switch.switch_interface.domain
) )
if switch_form.is_valid() and machine_form.is_valid()\ if switch_form.is_valid() and machine_form.is_valid()\

View file

@ -452,13 +452,14 @@ class RightForm(ModelForm):
class DelRightForm(Form): class DelRightForm(Form):
"""Suppression d'un droit d'un user""" """Suppression d'un droit d'un user"""
rights = forms.ModelMultipleChoiceField( rights = forms.ModelMultipleChoiceField(
queryset=Right.objects.all(), queryset=Right.objects.select_related('user'),
widget=forms.CheckboxSelectMultiple widget=forms.CheckboxSelectMultiple
) )
def __init__(self, right, *args, **kwargs): def __init__(self, right, *args, **kwargs):
super(DelRightForm, self).__init__(*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): 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) state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
registered = models.DateTimeField(auto_now_add=True) registered = models.DateTimeField(auto_now_add=True)
telephone = models.CharField(max_length=15, blank=True, null=True) telephone = models.CharField(max_length=15, blank=True, null=True)
uid_number = models.IntegerField(default=auto_uid, unique=True) uid_number = models.PositiveIntegerField(default=auto_uid, unique=True)
rezo_rez_uid = models.IntegerField(unique=True, blank=True, null=True) rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True)
USERNAME_FIELD = 'pseudo' USERNAME_FIELD = 'pseudo'
REQUIRED_FIELDS = ['name', 'surname', 'email'] REQUIRED_FIELDS = ['name', 'surname', 'email']
@ -840,7 +840,7 @@ class ListRight(models.Model):
que des lettres minuscules" que des lettres minuscules"
)] )]
) )
gid = models.IntegerField(unique=True, null=True) gid = models.PositiveIntegerField(unique=True, null=True)
details = models.CharField( details = models.CharField(
help_text="Description", help_text="Description",
max_length=255, 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"> <form class="form" method="post">
{% csrf_token %} {% csrf_token %}
<table class="table table-striped"> <table id="delRights_table" class="table table-striped">
<tbody> <tbody>
{% for key, values in userform.items %} {% for key, values in userform.items %}
<tr class="active"> <tr class="active">
<td> <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 ) <b>{{ key }}</b> ( {{values.rights|length }} users )
</a> </a>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>
<div class="collapse" id="collapseRight_{{key}}"> <div class="collapse in" id="collapseRight_{{key}}">
<ul class="list-group" style="margin-bottom: 0px"> <ul class="list-group" style="margin-bottom: 0px">
{% for user in values.rights %} {% for user in values.rights %}
<li class="list-group-item col-xs-6 col-sm-4 col-md-3" style="border: none;">{{ user }}</li> <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 %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% bootstrap_button "Modifier" button_type="submit" icon="star" %} {% bootstrap_button "Supprimer" button_type="submit" icon="star" %}
</form> </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 /> <br />
<br /> <br />

View file

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