mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-27 07:02:26 +00:00
Merge branch 'master' into massive_use_bft_tag
This commit is contained in:
commit
2ccf8f4729
34 changed files with 1094 additions and 233 deletions
26
cotisations/migrations/0024_auto_20171015_2033.py
Normal file
26
cotisations/migrations/0024_auto_20171015_2033.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -132,7 +132,7 @@ class Vente(models.Model):
|
|||
name = models.CharField(max_length=255)
|
||||
prix = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
iscotisation = models.BooleanField()
|
||||
duration = models.IntegerField(
|
||||
duration = models.PositiveIntegerField(
|
||||
help_text="Durée exprimée en mois entiers",
|
||||
blank=True,
|
||||
null=True)
|
||||
|
@ -222,7 +222,7 @@ class Article(models.Model):
|
|||
name = models.CharField(max_length=255, unique=True)
|
||||
prix = models.DecimalField(max_digits=5, decimal_places=2)
|
||||
iscotisation = models.BooleanField()
|
||||
duration = models.IntegerField(
|
||||
duration = models.PositiveIntegerField(
|
||||
help_text="Durée exprimée en mois entiers",
|
||||
blank=True,
|
||||
null=True,
|
||||
|
|
|
@ -99,18 +99,18 @@ urlpatterns = [
|
|||
views.index_paiement,
|
||||
name='index-paiement'
|
||||
),
|
||||
url(r'^history/(?P<object>facture)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>facture)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object>article)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>article)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(r'^history/(?P<object>paiement)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>paiement)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object>banque)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>banque)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
|
|
|
@ -533,7 +533,8 @@ def control(request):
|
|||
facture_list = paginator.page(1)
|
||||
except EmptyPage:
|
||||
facture_list = paginator.page(paginator.num.pages)
|
||||
page_query = Facture.objects.order_by('date').reverse().filter(
|
||||
page_query = Facture.objects.order_by('date').select_related('user')\
|
||||
.select_related('paiement').reverse().filter(
|
||||
id__in=[facture.id for facture in facture_list]
|
||||
)
|
||||
controlform = controlform_set(request.POST or None, queryset=page_query)
|
||||
|
@ -603,9 +604,9 @@ def index(request):
|
|||
|
||||
|
||||
@login_required
|
||||
def history(request, object, object_id):
|
||||
def history(request, object_name, object_id):
|
||||
"""Affiche l'historique de chaque objet"""
|
||||
if object == 'facture':
|
||||
if object_name == 'facture':
|
||||
try:
|
||||
object_instance = Facture.objects.get(pk=object_id)
|
||||
except Facture.DoesNotExist:
|
||||
|
@ -616,19 +617,19 @@ def history(request, object, object_id):
|
|||
messages.error(request, "Vous ne pouvez pas afficher l'historique\
|
||||
d'une facture d'un autre user que vous sans droit cableur")
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
elif object == 'paiement' and request.user.has_perms(('cableur',)):
|
||||
elif object_name == 'paiement' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Paiement.objects.get(pk=object_id)
|
||||
except Paiement.DoesNotExist:
|
||||
messages.error(request, "Paiement inexistant")
|
||||
return redirect("/cotisations/")
|
||||
elif object == 'article' and request.user.has_perms(('cableur',)):
|
||||
elif object_name == 'article' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Article.objects.get(pk=object_id)
|
||||
except Article.DoesNotExist:
|
||||
messages.error(request, "Article inexistante")
|
||||
return redirect("/cotisations/")
|
||||
elif object == 'banque' and request.user.has_perms(('cableur',)):
|
||||
elif object_name == 'banque' and request.user.has_perms(('cableur',)):
|
||||
try:
|
||||
object_instance = Banque.objects.get(pk=object_id)
|
||||
except Banque.DoesNotExist:
|
||||
|
|
BIN
docs_utils/re2o-archi.dia
Normal file
BIN
docs_utils/re2o-archi.dia
Normal file
Binary file not shown.
|
@ -292,7 +292,7 @@ def decide_vlan_and_register_switch(nas, nas_type, port_number, mac_address):
|
|||
if not port.room:
|
||||
return (sw_name, u'Chambre inconnue', VLAN_NOK)
|
||||
|
||||
room_user = User.objects.filter(room=Room.objects.filter(name=port.room))
|
||||
room_user = User.objects.filter(room=port.room)
|
||||
if not room_user:
|
||||
return (sw_name, u'Chambre non cotisante', VLAN_NOK)
|
||||
elif not room_user.first().has_access():
|
||||
|
|
|
@ -30,51 +30,67 @@ from .models import IpType, Machine, MachineType, Domain, IpList, Interface
|
|||
from .models import Extension, Mx, Ns, Vlan, Text, Nas, Service, OuverturePort
|
||||
from .models import OuverturePortList
|
||||
|
||||
|
||||
class MachineAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class IpTypeAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MachineTypeAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class VlanAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ExtensionAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class MxAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class NsAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class TextAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class NasAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class IpListAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class OuverturePortAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class OuverturePortListAdmin(VersionAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class InterfaceAdmin(VersionAdmin):
|
||||
list_display = ('machine','type','mac_address','ipv4','details')
|
||||
|
||||
|
||||
class DomainAdmin(VersionAdmin):
|
||||
list_display = ('interface_parent', 'name', 'extension', 'cname')
|
||||
|
||||
|
||||
class ServiceAdmin(VersionAdmin):
|
||||
list_display = ('service_type', 'min_time_regen', 'regular_time_regen')
|
||||
|
||||
|
||||
admin.site.register(Machine, MachineAdmin)
|
||||
admin.site.register(MachineType, MachineTypeAdmin)
|
||||
admin.site.register(IpType, IpTypeAdmin)
|
||||
|
|
|
@ -21,20 +21,43 @@
|
|||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
"""
|
||||
Formulaires d'ajout, edition et suppressions de :
|
||||
- machines
|
||||
- interfaces
|
||||
- domain (noms de machine)
|
||||
- alias (cname)
|
||||
- service (dhcp, dns..)
|
||||
- ns (serveur dns)
|
||||
- mx (serveur mail)
|
||||
- ports ouverts et profils d'ouverture par interface
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from django.forms import ModelForm, Form, ValidationError
|
||||
from django.forms import ModelForm, Form
|
||||
from django import forms
|
||||
from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Text, Ns, Service, Vlan, Nas, IpType, OuverturePortList, OuverturePort
|
||||
from django.db.models import Q
|
||||
from django.core.validators import validate_email
|
||||
|
||||
from users.models import User
|
||||
from .models import (
|
||||
Domain,
|
||||
Machine,
|
||||
Interface,
|
||||
IpList,
|
||||
MachineType,
|
||||
Extension,
|
||||
Mx,
|
||||
Text,
|
||||
Ns,
|
||||
Service,
|
||||
Vlan,
|
||||
Nas,
|
||||
IpType,
|
||||
OuverturePortList,
|
||||
)
|
||||
|
||||
|
||||
class EditMachineForm(ModelForm):
|
||||
"""Formulaire d'édition d'une machine"""
|
||||
class Meta:
|
||||
model = Machine
|
||||
fields = '__all__'
|
||||
|
@ -44,15 +67,22 @@ class EditMachineForm(ModelForm):
|
|||
super(EditMachineForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['name'].label = 'Nom de la machine'
|
||||
|
||||
|
||||
class NewMachineForm(EditMachineForm):
|
||||
"""Creation d'une machine, ne renseigne que le nom"""
|
||||
class Meta(EditMachineForm.Meta):
|
||||
fields = ['name']
|
||||
|
||||
|
||||
class BaseEditMachineForm(EditMachineForm):
|
||||
"""Edition basique, ne permet que de changer le nom et le statut.
|
||||
Réservé aux users sans droits spécifiques"""
|
||||
class Meta(EditMachineForm.Meta):
|
||||
fields = ['name', 'active']
|
||||
|
||||
|
||||
class EditInterfaceForm(ModelForm):
|
||||
"""Edition d'une interface. Edition complète"""
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['machine', 'type', 'ipv4', 'mac_address', 'details']
|
||||
|
@ -64,14 +94,23 @@ class EditInterfaceForm(ModelForm):
|
|||
self.fields['type'].label = 'Type de machine'
|
||||
self.fields['type'].empty_label = "Séléctionner un type de machine"
|
||||
if "ipv4" in self.fields:
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique\
|
||||
de l'ipv4"
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
# Add it's own address
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(
|
||||
interface=self.instance
|
||||
)
|
||||
if "machine" in self.fields:
|
||||
self.fields['machine'].queryset = Machine.objects.all().select_related('user')
|
||||
self.fields['machine'].queryset = Machine.objects.all()\
|
||||
.select_related('user')
|
||||
|
||||
|
||||
class AddInterfaceForm(EditInterfaceForm):
|
||||
"""Ajout d'une interface à une machine. En fonction des droits,
|
||||
affiche ou non l'ensemble des ip disponibles"""
|
||||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'ipv4', 'mac_address', 'details']
|
||||
|
||||
|
@ -80,16 +119,27 @@ class AddInterfaceForm(EditInterfaceForm):
|
|||
super(AddInterfaceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
if not infra:
|
||||
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
self.fields['type'].queryset = MachineType.objects.filter(
|
||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
||||
)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
else:
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
|
||||
|
||||
class NewInterfaceForm(EditInterfaceForm):
|
||||
"""Formulaire light, sans choix de l'ipv4; d'ajout d'une interface"""
|
||||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'mac_address', 'details']
|
||||
|
||||
|
||||
class BaseEditInterfaceForm(EditInterfaceForm):
|
||||
"""Edition basique d'une interface. En fonction des droits,
|
||||
ajoute ou non l'ensemble des ipv4 disponibles (infra)"""
|
||||
class Meta(EditInterfaceForm.Meta):
|
||||
fields = ['type', 'ipv4', 'mac_address', 'details']
|
||||
|
||||
|
@ -98,48 +148,69 @@ class BaseEditInterfaceForm(EditInterfaceForm):
|
|||
super(BaseEditInterfaceForm, self).__init__(*args, **kwargs)
|
||||
self.fields['ipv4'].empty_label = "Assignation automatique de l'ipv4"
|
||||
if not infra:
|
||||
self.fields['type'].queryset = MachineType.objects.filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
self.fields['type'].queryset = MachineType.objects.filter(
|
||||
ip_type__in=IpType.objects.filter(need_infra=False)
|
||||
)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type__in=IpType.objects.filter(need_infra=False))
|
||||
# Add it's own address
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(
|
||||
interface=self.instance
|
||||
)
|
||||
else:
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(interface__isnull=True)
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(interface=self.instance)
|
||||
self.fields['ipv4'].queryset = IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
)
|
||||
self.fields['ipv4'].queryset |= IpList.objects.filter(
|
||||
interface=self.instance
|
||||
)
|
||||
|
||||
|
||||
class AliasForm(ModelForm):
|
||||
"""Ajout d'un alias (et edition), CNAME, contenant nom et extension"""
|
||||
class Meta:
|
||||
model = Domain
|
||||
fields = ['name', 'extension']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
if 'infra' in kwargs:
|
||||
infra = kwargs.pop('infra')
|
||||
super(AliasForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DomainForm(AliasForm):
|
||||
"""Ajout et edition d'un enregistrement de nom, relié à interface"""
|
||||
class Meta(AliasForm.Meta):
|
||||
fields = ['name']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if 'user' in kwargs:
|
||||
user = kwargs.pop('user')
|
||||
nb_machine = kwargs.pop('nb_machine')
|
||||
initial = kwargs.get('initial', {})
|
||||
initial['name'] = user.get_next_domain_name()
|
||||
kwargs['initial'] = initial
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(DomainForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelAliasForm(Form):
|
||||
alias = forms.ModelMultipleChoiceField(queryset=Domain.objects.all(), label="Alias actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs objets alias"""
|
||||
alias = forms.ModelMultipleChoiceField(
|
||||
queryset=Domain.objects.all(),
|
||||
label="Alias actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
interface = kwargs.pop('interface')
|
||||
super(DelAliasForm, self).__init__(*args, **kwargs)
|
||||
self.fields['alias'].queryset = Domain.objects.filter(cname__in=Domain.objects.filter(interface_parent=interface))
|
||||
self.fields['alias'].queryset = Domain.objects.filter(
|
||||
cname__in=Domain.objects.filter(interface_parent=interface)
|
||||
)
|
||||
|
||||
|
||||
class MachineTypeForm(ModelForm):
|
||||
"""Ajout et edition d'un machinetype, relié à un iptype"""
|
||||
class Meta:
|
||||
model = MachineType
|
||||
fields = ['type', 'ip_type']
|
||||
|
@ -150,41 +221,72 @@ class MachineTypeForm(ModelForm):
|
|||
self.fields['type'].label = 'Type de machine à ajouter'
|
||||
self.fields['ip_type'].label = "Type d'ip relié"
|
||||
|
||||
|
||||
class DelMachineTypeForm(Form):
|
||||
machinetypes = forms.ModelMultipleChoiceField(queryset=MachineType.objects.all(), label="Types de machines actuelles", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs machinetype"""
|
||||
machinetypes = forms.ModelMultipleChoiceField(
|
||||
queryset=MachineType.objects.all(),
|
||||
label="Types de machines actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class IpTypeForm(ModelForm):
|
||||
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de
|
||||
stop après creation"""
|
||||
class Meta:
|
||||
model = IpType
|
||||
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'prefix_v6', 'vlan']
|
||||
fields = ['type', 'extension', 'need_infra', 'domaine_ip_start',
|
||||
'domaine_ip_stop', 'prefix_v6', 'vlan', 'ouverture_ports']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(IpTypeForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['type'].label = 'Type ip à ajouter'
|
||||
|
||||
|
||||
class EditIpTypeForm(IpTypeForm):
|
||||
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
|
||||
synchroniser les objets iplist"""
|
||||
class Meta(IpTypeForm.Meta):
|
||||
fields = ['extension','type','need_infra', 'prefix_v6', 'vlan']
|
||||
fields = ['extension', 'type', 'need_infra', 'prefix_v6', 'vlan',
|
||||
'ouverture_ports']
|
||||
|
||||
|
||||
class DelIpTypeForm(Form):
|
||||
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs iptype"""
|
||||
iptypes = forms.ModelMultipleChoiceField(
|
||||
queryset=IpType.objects.all(),
|
||||
label="Types d'ip actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class ExtensionForm(ModelForm):
|
||||
"""Formulaire d'ajout et edition d'une extension"""
|
||||
class Meta:
|
||||
model = Extension
|
||||
fields = ['name', 'need_infra', 'origin']
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(ExtensionForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['name'].label = 'Extension à ajouter'
|
||||
self.fields['origin'].label = 'Enregistrement A origin'
|
||||
self.fields['origin_v6'].label = 'Enregistrement AAAA origin'
|
||||
|
||||
|
||||
class DelExtensionForm(Form):
|
||||
extensions = forms.ModelMultipleChoiceField(queryset=Extension.objects.all(), label="Extensions actuelles", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'une ou plusieurs extensions"""
|
||||
extensions = forms.ModelMultipleChoiceField(
|
||||
queryset=Extension.objects.all(),
|
||||
label="Extensions actuelles",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class MxForm(ModelForm):
|
||||
"""Ajout et edition d'un MX"""
|
||||
class Meta:
|
||||
model = Mx
|
||||
fields = ['zone', 'priority', 'name']
|
||||
|
@ -192,12 +294,24 @@ class MxForm(ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(MxForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['name'].queryset = Domain.objects.exclude(interface_parent=None)
|
||||
self.fields['name'].queryset = Domain.objects.exclude(
|
||||
interface_parent=None
|
||||
).select_related('extension')
|
||||
|
||||
|
||||
class DelMxForm(Form):
|
||||
mx = forms.ModelMultipleChoiceField(queryset=Mx.objects.all(), label="MX actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs MX"""
|
||||
mx = forms.ModelMultipleChoiceField(
|
||||
queryset=Mx.objects.all(),
|
||||
label="MX actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class NsForm(ModelForm):
|
||||
"""Ajout d'un NS pour une zone
|
||||
On exclue les CNAME dans les objets domain (interdit par la rfc)
|
||||
donc on prend uniquemet """
|
||||
class Meta:
|
||||
model = Ns
|
||||
fields = ['zone', 'ns']
|
||||
|
@ -205,12 +319,22 @@ class NsForm(ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(NsForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['ns'].queryset = Domain.objects.exclude(interface_parent=None)
|
||||
self.fields['ns'].queryset = Domain.objects.exclude(
|
||||
interface_parent=None
|
||||
).select_related('extension')
|
||||
|
||||
|
||||
class DelNsForm(Form):
|
||||
ns = forms.ModelMultipleChoiceField(queryset=Ns.objects.all(), label="Enregistrements NS actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppresion d'un ou plusieurs NS"""
|
||||
ns = forms.ModelMultipleChoiceField(
|
||||
queryset=Ns.objects.all(),
|
||||
label="Enregistrements NS actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class TxtForm(ModelForm):
|
||||
"""Ajout d'un txt pour une zone"""
|
||||
class Meta:
|
||||
model = Text
|
||||
fields = '__all__'
|
||||
|
@ -219,10 +343,19 @@ class TxtForm(ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(TxtForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelTxtForm(Form):
|
||||
txt = forms.ModelMultipleChoiceField(queryset=Text.objects.all(), label="Enregistrements Txt actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs TXT"""
|
||||
txt = forms.ModelMultipleChoiceField(
|
||||
queryset=Text.objects.all(),
|
||||
label="Enregistrements Txt actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class NasForm(ModelForm):
|
||||
"""Ajout d'un type de nas (machine d'authentification,
|
||||
swicths, bornes...)"""
|
||||
class Meta:
|
||||
model = Nas
|
||||
fields = '__all__'
|
||||
|
@ -231,10 +364,18 @@ class NasForm(ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(NasForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelNasForm(Form):
|
||||
nas = forms.ModelMultipleChoiceField(queryset=Nas.objects.all(), label="Enregistrements Nas actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs nas"""
|
||||
nas = forms.ModelMultipleChoiceField(
|
||||
queryset=Nas.objects.all(),
|
||||
label="Enregistrements Nas actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class ServiceForm(ModelForm):
|
||||
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
|
||||
class Meta:
|
||||
model = Service
|
||||
fields = '__all__'
|
||||
|
@ -242,6 +383,8 @@ class ServiceForm(ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(ServiceForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['servers'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super(ServiceForm, self).save(commit=False)
|
||||
|
@ -250,10 +393,18 @@ class ServiceForm(ModelForm):
|
|||
instance.process_link(self.cleaned_data.get('servers'))
|
||||
return instance
|
||||
|
||||
|
||||
class DelServiceForm(Form):
|
||||
service = forms.ModelMultipleChoiceField(queryset=Service.objects.all(), label="Services actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs service"""
|
||||
service = forms.ModelMultipleChoiceField(
|
||||
queryset=Service.objects.all(),
|
||||
label="Services actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class VlanForm(ModelForm):
|
||||
"""Ajout d'un vlan : id, nom"""
|
||||
class Meta:
|
||||
model = Vlan
|
||||
fields = '__all__'
|
||||
|
@ -262,24 +413,43 @@ class VlanForm(ModelForm):
|
|||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(VlanForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
|
||||
class DelVlanForm(Form):
|
||||
vlan = forms.ModelMultipleChoiceField(queryset=Vlan.objects.all(), label="Vlan actuels", widget=forms.CheckboxSelectMultiple)
|
||||
"""Suppression d'un ou plusieurs vlans"""
|
||||
vlan = forms.ModelMultipleChoiceField(
|
||||
queryset=Vlan.objects.all(),
|
||||
label="Vlan actuels",
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
|
||||
class EditOuverturePortConfigForm(ModelForm):
|
||||
"""Edition de la liste des profils d'ouverture de ports
|
||||
pour l'interface"""
|
||||
class Meta:
|
||||
model = Interface
|
||||
fields = ['port_lists']
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(EditOuverturePortConfigForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
super(EditOuverturePortConfigForm, self).__init__(
|
||||
*args,
|
||||
prefix=prefix,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
||||
class EditOuverturePortListForm(ModelForm):
|
||||
"""Edition de la liste des ports et profils d'ouverture
|
||||
des ports"""
|
||||
class Meta:
|
||||
model = OuverturePortList
|
||||
fields = '__all__'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(EditOuverturePortListForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
|
||||
super(EditOuverturePortListForm, self).__init__(
|
||||
*args,
|
||||
prefix=prefix,
|
||||
**kwargs
|
||||
)
|
||||
|
|
21
machines/migrations/0060_iptype_ouverture_ports.py
Normal file
21
machines/migrations/0060_iptype_ouverture_ports.py
Normal 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'),
|
||||
),
|
||||
]
|
36
machines/migrations/0061_auto_20171015_2033.py
Normal file
36
machines/migrations/0061_auto_20171015_2033.py
Normal 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)]),
|
||||
),
|
||||
]
|
20
machines/migrations/0062_extension_origin_v6.py
Normal file
20
machines/migrations/0062_extension_origin_v6.py
Normal 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'),
|
||||
),
|
||||
]
|
|
@ -23,45 +23,60 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from datetime import timedelta
|
||||
import re
|
||||
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
|
||||
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save, pre_delete, post_delete
|
||||
from django.db.models.signals import post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
from django.forms import ValidationError
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils import timezone
|
||||
from django.core.validators import MaxValueValidator
|
||||
|
||||
from macaddress.fields import MACAddressField
|
||||
from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress
|
||||
from django.core.validators import MinValueValidator,MaxValueValidator
|
||||
import re
|
||||
from reversion import revisions as reversion
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
class Machine(models.Model):
|
||||
""" Class définissant une machine, object parent user, objets fils interfaces"""
|
||||
""" Class définissant une machine, object parent user, objets fils
|
||||
interfaces"""
|
||||
PRETTY_NAME = "Machine"
|
||||
|
||||
user = models.ForeignKey('users.User', on_delete=models.PROTECT)
|
||||
name = models.CharField(max_length=255, help_text="Optionnel", blank=True, null=True)
|
||||
name = models.CharField(
|
||||
max_length=255,
|
||||
help_text="Optionnel",
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
active = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.user) + ' - ' + str(self.id) + ' - ' + str(self.name)
|
||||
|
||||
|
||||
class MachineType(models.Model):
|
||||
""" Type de machine, relié à un type d'ip, affecté aux interfaces"""
|
||||
PRETTY_NAME = "Type de machine"
|
||||
|
||||
type = models.CharField(max_length=255)
|
||||
ip_type = models.ForeignKey('IpType', on_delete=models.PROTECT, blank=True, null=True)
|
||||
ip_type = models.ForeignKey(
|
||||
'IpType',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
def all_interfaces(self):
|
||||
""" Renvoie toutes les interfaces (cartes réseaux) de type machinetype"""
|
||||
""" Renvoie toutes les interfaces (cartes réseaux) de type
|
||||
machinetype"""
|
||||
return Interface.objects.filter(type=self)
|
||||
|
||||
def __str__(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class IpType(models.Model):
|
||||
""" Type d'ip, définissant un range d'ip, affecté aux machine types"""
|
||||
PRETTY_NAME = "Type d'ip"
|
||||
|
@ -71,8 +86,22 @@ class IpType(models.Model):
|
|||
need_infra = models.BooleanField(default=False)
|
||||
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
|
||||
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
|
||||
prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True)
|
||||
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
|
||||
prefix_v6 = models.GenericIPAddressField(
|
||||
protocol='IPv6',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
vlan = models.ForeignKey(
|
||||
'Vlan',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
ouverture_ports = models.ForeignKey(
|
||||
'OuverturePortList',
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def ip_range(self):
|
||||
|
@ -95,18 +124,22 @@ class IpType(models.Model):
|
|||
|
||||
def free_ip(self):
|
||||
""" Renvoie toutes les ip libres associées au type donné (self)"""
|
||||
return IpList.objects.filter(interface__isnull=True).filter(ip_type=self)
|
||||
return IpList.objects.filter(
|
||||
interface__isnull=True
|
||||
).filter(ip_type=self)
|
||||
|
||||
def gen_ip_range(self):
|
||||
""" Cree les IpList associées au type self. Parcours pédestrement et crée
|
||||
les ip une par une. Si elles existent déjà, met à jour le type associé
|
||||
à l'ip"""
|
||||
""" Cree les IpList associées au type self. Parcours pédestrement et
|
||||
crée les ip une par une. Si elles existent déjà, met à jour le type
|
||||
associé à l'ip"""
|
||||
# Creation du range d'ip dans les objets iplist
|
||||
networks = []
|
||||
for net in self.ip_range.cidrs():
|
||||
networks += net.iter_hosts()
|
||||
ip_obj = [IpList(ip_type=self, ipv4=str(ip)) for ip in networks]
|
||||
listes_ip = IpList.objects.filter(ipv4__in=[str(ip) for ip in networks])
|
||||
listes_ip = IpList.objects.filter(
|
||||
ipv4__in=[str(ip) for ip in networks]
|
||||
)
|
||||
# Si il n'y a pas d'ip, on les crée
|
||||
if not listes_ip:
|
||||
IpList.objects.bulk_create(ip_obj)
|
||||
|
@ -116,9 +149,11 @@ class IpType(models.Model):
|
|||
return
|
||||
|
||||
def del_ip_range(self):
|
||||
""" Methode dépréciée, IpList est en mode cascade et supprimé automatiquement"""
|
||||
""" Methode dépréciée, IpList est en mode cascade et supprimé
|
||||
automatiquement"""
|
||||
if Interface.objects.filter(ipv4__in=self.ip_objects()):
|
||||
raise ValidationError("Une ou plusieurs ip du range sont affectées, impossible de supprimer le range")
|
||||
raise ValidationError("Une ou plusieurs ip du range sont\
|
||||
affectées, impossible de supprimer le range")
|
||||
for ip in self.ip_objects():
|
||||
ip.delete()
|
||||
|
||||
|
@ -132,11 +167,13 @@ class IpType(models.Model):
|
|||
raise ValidationError("Domaine end doit être après start...")
|
||||
# On ne crée pas plus grand qu'un /16
|
||||
if self.ip_range.size > 65536:
|
||||
raise ValidationError("Le range est trop gros, vous ne devez pas créer plus grand qu'un /16")
|
||||
raise ValidationError("Le range est trop gros, vous ne devez\
|
||||
pas créer plus grand qu'un /16")
|
||||
# On check que les / ne se recoupent pas
|
||||
for element in IpType.objects.all().exclude(pk=self.pk):
|
||||
if not self.ip_set.isdisjoint(element.ip_set):
|
||||
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
|
||||
raise ValidationError("Le range indiqué n'est pas disjoint\
|
||||
des ranges existants")
|
||||
# On formate le prefix v6
|
||||
if self.prefix_v6:
|
||||
self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network)
|
||||
|
@ -149,17 +186,20 @@ class IpType(models.Model):
|
|||
def __str__(self):
|
||||
return self.type
|
||||
|
||||
|
||||
class Vlan(models.Model):
|
||||
""" Un vlan : vlan_id et nom"""
|
||||
""" Un vlan : vlan_id et nom
|
||||
On limite le vlan id entre 0 et 4096, comme défini par la norme"""
|
||||
PRETTY_NAME = "Vlans"
|
||||
|
||||
vlan_id = models.IntegerField()
|
||||
vlan_id = models.PositiveIntegerField(validators=[MaxValueValidator(4095)])
|
||||
name = models.CharField(max_length=256)
|
||||
comment = models.CharField(max_length=256, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Nas(models.Model):
|
||||
""" Les nas. Associé à un machine_type.
|
||||
Permet aussi de régler le port_access_mode (802.1X ou mac-address) pour
|
||||
|
@ -173,48 +213,84 @@ class Nas(models.Model):
|
|||
)
|
||||
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
nas_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='nas_type')
|
||||
machine_type = models.ForeignKey('MachineType', on_delete=models.PROTECT, related_name='machinetype_on_nas')
|
||||
port_access_mode = models.CharField(choices=AUTH, default=default_mode, max_length=32)
|
||||
nas_type = models.ForeignKey(
|
||||
'MachineType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='nas_type'
|
||||
)
|
||||
machine_type = models.ForeignKey(
|
||||
'MachineType',
|
||||
on_delete=models.PROTECT,
|
||||
related_name='machinetype_on_nas'
|
||||
)
|
||||
port_access_mode = models.CharField(
|
||||
choices=AUTH,
|
||||
default=default_mode,
|
||||
max_length=32
|
||||
)
|
||||
autocapture_mac = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Extension(models.Model):
|
||||
""" Extension dns type example.org. Précise si tout le monde peut l'utiliser,
|
||||
associé à un origin (ip d'origine)"""
|
||||
""" Extension dns type example.org. Précise si tout le monde peut
|
||||
l'utiliser, associé à un origin (ip d'origine)"""
|
||||
PRETTY_NAME = "Extensions dns"
|
||||
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
need_infra = models.BooleanField(default=False)
|
||||
origin = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
||||
origin = models.OneToOneField(
|
||||
'IpList',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
origin_v6 = models.GenericIPAddressField(
|
||||
protocol='IPv6',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
@cached_property
|
||||
def dns_entry(self):
|
||||
""" Une entrée DNS A"""
|
||||
return "@ IN A " + str(self.origin)
|
||||
""" Une entrée DNS A et AAAA sur origin (zone self)"""
|
||||
entry = ""
|
||||
if self.origin:
|
||||
entry += "@ IN A " + str(self.origin)
|
||||
if self.origin_v6:
|
||||
if entry:
|
||||
entry += "\n"
|
||||
entry += "@ IN AAAA " + str(self.origin_v6)
|
||||
return entry
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Mx(models.Model):
|
||||
""" Entrées des MX. Enregistre la zone (extension) associée et la priorité
|
||||
""" Entrées des MX. Enregistre la zone (extension) associée et la
|
||||
priorité
|
||||
Todo : pouvoir associer un MX à une interface """
|
||||
PRETTY_NAME = "Enregistrements MX"
|
||||
|
||||
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
|
||||
priority = models.IntegerField(unique=True)
|
||||
priority = models.PositiveIntegerField(unique=True)
|
||||
name = models.OneToOneField('Domain', on_delete=models.PROTECT)
|
||||
|
||||
@cached_property
|
||||
def dns_entry(self):
|
||||
"""Renvoie l'entrée DNS complète pour un MX à mettre dans les
|
||||
fichiers de zones"""
|
||||
return "@ IN MX " + str(self.priority) + " " + str(self.name)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.zone) + ' ' + str(self.priority) + ' ' + str(self.name)
|
||||
|
||||
|
||||
class Ns(models.Model):
|
||||
"""Liste des enregistrements name servers par zone considéérée"""
|
||||
PRETTY_NAME = "Enregistrements NS"
|
||||
|
||||
zone = models.ForeignKey('Extension', on_delete=models.PROTECT)
|
||||
|
@ -222,11 +298,13 @@ class Ns(models.Model):
|
|||
|
||||
@cached_property
|
||||
def dns_entry(self):
|
||||
"""Renvoie un enregistrement NS complet pour les filezones"""
|
||||
return "@ IN NS " + str(self.ns)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.zone) + ' ' + str(self.ns)
|
||||
|
||||
|
||||
class Text(models.Model):
|
||||
""" Un enregistrement TXT associé à une extension"""
|
||||
PRETTY_NAME = "Enregistrement text"
|
||||
|
@ -236,22 +314,31 @@ class Text(models.Model):
|
|||
field2 = models.CharField(max_length=255)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.zone) + " : " + str(self.field1) + " " + str(self.field2)
|
||||
return str(self.zone) + " : " + str(self.field1) + " " +\
|
||||
str(self.field2)
|
||||
|
||||
@cached_property
|
||||
def dns_entry(self):
|
||||
"""Renvoie l'enregistrement TXT complet pour le fichier de zone"""
|
||||
return str(self.field1) + " IN TXT " + str(self.field2)
|
||||
|
||||
|
||||
class Interface(models.Model):
|
||||
""" Une interface. Objet clef de l'application machine :
|
||||
- une address mac unique. Possibilité de la rendre unique avec le typemachine
|
||||
- une address mac unique. Possibilité de la rendre unique avec le
|
||||
typemachine
|
||||
- une onetoone vers IpList pour attribution ipv4
|
||||
- le type parent associé au range ip et à l'extension
|
||||
- un objet domain associé contenant son nom
|
||||
- la liste des ports oiuvert"""
|
||||
PRETTY_NAME = "Interface"
|
||||
|
||||
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
||||
ipv4 = models.OneToOneField(
|
||||
'IpList',
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
mac_address = MACAddressField(integer=False, unique=True)
|
||||
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
|
||||
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
|
||||
|
@ -265,12 +352,14 @@ class Interface(models.Model):
|
|||
user = self.machine.user
|
||||
return machine.active and user.has_access()
|
||||
|
||||
|
||||
@cached_property
|
||||
def ipv6_object(self):
|
||||
""" Renvoie un objet type ipv6 à partir du prefix associé à l'iptype parent"""
|
||||
""" Renvoie un objet type ipv6 à partir du prefix associé à
|
||||
l'iptype parent"""
|
||||
if self.type.ip_type.prefix_v6:
|
||||
return EUI(self.mac_address).ipv6(IPNetwork(self.type.ip_type.prefix_v6).network)
|
||||
return EUI(self.mac_address).ipv6(
|
||||
IPNetwork(self.type.ip_type.prefix_v6).network
|
||||
)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -284,7 +373,8 @@ class Interface(models.Model):
|
|||
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
|
||||
|
||||
def filter_macaddress(self):
|
||||
""" Tente un formatage mac_bare, si échoue, lève une erreur de validation"""
|
||||
""" Tente un formatage mac_bare, si échoue, lève une erreur de
|
||||
validation"""
|
||||
try:
|
||||
self.mac_address = str(EUI(self.mac_address))
|
||||
except:
|
||||
|
@ -305,7 +395,8 @@ class Interface(models.Model):
|
|||
if free_ips:
|
||||
self.ipv4 = free_ips[0]
|
||||
else:
|
||||
raise ValidationError("Il n'y a plus d'ip disponibles dans le slash")
|
||||
raise ValidationError("Il n'y a plus d'ip disponibles\
|
||||
dans le slash")
|
||||
return
|
||||
|
||||
def unassign_ipv4(self):
|
||||
|
@ -320,8 +411,10 @@ class Interface(models.Model):
|
|||
def save(self, *args, **kwargs):
|
||||
self.filter_macaddress()
|
||||
# On verifie la cohérence en forçant l'extension par la méthode
|
||||
if self.ipv4:
|
||||
if self.type.ip_type != self.ipv4.ip_type:
|
||||
raise ValidationError("L'ipv4 et le type de la machine ne correspondent pas")
|
||||
raise ValidationError("L'ipv4 et le type de la machine ne\
|
||||
correspondent pas")
|
||||
super(Interface, self).save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
|
@ -340,18 +433,34 @@ class Interface(models.Model):
|
|||
|
||||
def may_have_port_open(self):
|
||||
""" True si l'interface a une ip et une ip publique.
|
||||
Permet de ne pas exporter des ouvertures sur des ip privées (useless)"""
|
||||
Permet de ne pas exporter des ouvertures sur des ip privées
|
||||
(useless)"""
|
||||
return self.ipv4 and not self.has_private_ip()
|
||||
|
||||
|
||||
class Domain(models.Model):
|
||||
""" Objet domain. Enregistrement A et CNAME en même temps : permet de stocker les
|
||||
alias et les nom de machines, suivant si interface_parent ou cname sont remplis"""
|
||||
""" Objet domain. Enregistrement A et CNAME en même temps : permet de
|
||||
stocker les alias et les nom de machines, suivant si interface_parent
|
||||
ou cname sont remplis"""
|
||||
PRETTY_NAME = "Domaine dns"
|
||||
|
||||
interface_parent = models.OneToOneField('Interface', on_delete=models.CASCADE, blank=True, null=True)
|
||||
name = models.CharField(help_text="Obligatoire et unique, ne doit pas comporter de points", max_length=255)
|
||||
interface_parent = models.OneToOneField(
|
||||
'Interface',
|
||||
on_delete=models.CASCADE,
|
||||
blank=True,
|
||||
null=True
|
||||
)
|
||||
name = models.CharField(
|
||||
help_text="Obligatoire et unique, ne doit pas comporter de points",
|
||||
max_length=255
|
||||
)
|
||||
extension = models.ForeignKey('Extension', on_delete=models.PROTECT)
|
||||
cname = models.ForeignKey('self', null=True, blank=True, related_name='related_domain')
|
||||
cname = models.ForeignKey(
|
||||
'self',
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name='related_domain'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
unique_together = (("name", "extension"),)
|
||||
|
@ -370,21 +479,26 @@ class Domain(models.Model):
|
|||
""" Validation :
|
||||
- l'objet est bien soit A soit CNAME
|
||||
- le cname est pas pointé sur lui-même
|
||||
- le nom contient bien les caractères autorisés par la norme dns et moins de 63 caractères au total
|
||||
- le nom contient bien les caractères autorisés par la norme
|
||||
dns et moins de 63 caractères au total
|
||||
- le couple nom/extension est bien unique"""
|
||||
if self.get_extension():
|
||||
self.extension = self.get_extension()
|
||||
""" Validation du nom de domaine, extensions dans type de machine, prefixe pas plus long que 63 caractères """
|
||||
if self.interface_parent and self.cname:
|
||||
raise ValidationError("On ne peut créer à la fois A et CNAME")
|
||||
if self.cname == self:
|
||||
raise ValidationError("On ne peut créer un cname sur lui même")
|
||||
HOSTNAME_LABEL_PATTERN = re.compile("(?!-)[A-Z\d-]+(?<!-)$", re.IGNORECASE)
|
||||
HOSTNAME_LABEL_PATTERN = re.compile(
|
||||
"(?!-)[A-Z\d-]+(?<!-)$",
|
||||
re.IGNORECASE
|
||||
)
|
||||
dns = self.name.lower()
|
||||
if len(dns) > 63:
|
||||
raise ValidationError("Le nom de domaine %s est trop long (maximum de 63 caractères)." % dns)
|
||||
raise ValidationError("Le nom de domaine %s est trop long\
|
||||
(maximum de 63 caractères)." % dns)
|
||||
if not HOSTNAME_LABEL_PATTERN.match(dns):
|
||||
raise ValidationError("Ce nom de domaine %s contient des carractères interdits." % dns)
|
||||
raise ValidationError("Ce nom de domaine %s contient des\
|
||||
carractères interdits." % dns)
|
||||
self.validate_unique()
|
||||
super(Domain, self).clean()
|
||||
|
||||
|
@ -395,7 +509,8 @@ class Domain(models.Model):
|
|||
return str(self.name) + " IN CNAME " + str(self.cname) + "."
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Empèche le save sans extension valide. Force à avoir appellé clean avant"""
|
||||
""" Empèche le save sans extension valide.
|
||||
Force à avoir appellé clean avant"""
|
||||
if not self.get_extension():
|
||||
raise ValidationError("Extension invalide")
|
||||
self.full_clean()
|
||||
|
@ -404,6 +519,7 @@ class Domain(models.Model):
|
|||
def __str__(self):
|
||||
return str(self.name) + str(self.extension)
|
||||
|
||||
|
||||
class IpList(models.Model):
|
||||
PRETTY_NAME = "Addresses ipv4"
|
||||
|
||||
|
@ -412,13 +528,15 @@ class IpList(models.Model):
|
|||
|
||||
@cached_property
|
||||
def need_infra(self):
|
||||
""" Permet de savoir si un user basique peut assigner cette ip ou non"""
|
||||
""" Permet de savoir si un user basique peut assigner cette ip ou
|
||||
non"""
|
||||
return self.ip_type.need_infra
|
||||
|
||||
def clean(self):
|
||||
""" Erreur si l'ip_type est incorrect"""
|
||||
if not str(self.ipv4) in self.ip_type.ip_set_as_str:
|
||||
raise ValidationError("L'ipv4 et le range de l'iptype ne correspondent pas!")
|
||||
raise ValidationError("L'ipv4 et le range de l'iptype ne\
|
||||
correspondent pas!")
|
||||
return
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
@ -428,24 +546,36 @@ class IpList(models.Model):
|
|||
def __str__(self):
|
||||
return self.ipv4
|
||||
|
||||
|
||||
class Service(models.Model):
|
||||
""" Definition d'un service (dhcp, dns, etc)"""
|
||||
service_type = models.CharField(max_length=255, blank=True, unique=True)
|
||||
min_time_regen = models.DurationField(default=timedelta(minutes=1), help_text="Temps minimal avant nouvelle génération du service")
|
||||
regular_time_regen = models.DurationField(default=timedelta(hours=1), help_text="Temps maximal avant nouvelle génération du service")
|
||||
min_time_regen = models.DurationField(
|
||||
default=timedelta(minutes=1),
|
||||
help_text="Temps minimal avant nouvelle génération du service"
|
||||
)
|
||||
regular_time_regen = models.DurationField(
|
||||
default=timedelta(hours=1),
|
||||
help_text="Temps maximal avant nouvelle génération du service"
|
||||
)
|
||||
servers = models.ManyToManyField('Interface', through='Service_link')
|
||||
|
||||
def ask_regen(self):
|
||||
""" Marque à True la demande de régénération pour un service x """
|
||||
Service_link.objects.filter(service=self).exclude(asked_regen=True).update(asked_regen=True)
|
||||
Service_link.objects.filter(service=self).exclude(asked_regen=True)\
|
||||
.update(asked_regen=True)
|
||||
return
|
||||
|
||||
def process_link(self, servers):
|
||||
""" Django ne peut créer lui meme les relations manytomany avec table intermediaire explicite"""
|
||||
for serv in servers.exclude(pk__in=Interface.objects.filter(service=self)):
|
||||
""" Django ne peut créer lui meme les relations manytomany avec table
|
||||
intermediaire explicite"""
|
||||
for serv in servers.exclude(
|
||||
pk__in=Interface.objects.filter(service=self)
|
||||
):
|
||||
link = Service_link(service=self, server=serv)
|
||||
link.save()
|
||||
Service_link.objects.filter(service=self).exclude(server__in=servers).delete()
|
||||
Service_link.objects.filter(service=self).exclude(server__in=servers)\
|
||||
.delete()
|
||||
return
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
@ -454,13 +584,16 @@ class Service(models.Model):
|
|||
def __str__(self):
|
||||
return str(self.service_type)
|
||||
|
||||
|
||||
def regen(service):
|
||||
""" Fonction externe pour régérération d'un service, prend un objet service en arg"""
|
||||
""" Fonction externe pour régérération d'un service, prend un objet service
|
||||
en arg"""
|
||||
obj = Service.objects.filter(service_type=service)
|
||||
if obj:
|
||||
obj[0].ask_regen()
|
||||
return
|
||||
|
||||
|
||||
class Service_link(models.Model):
|
||||
""" Definition du lien entre serveurs et services"""
|
||||
service = models.ForeignKey('Service', on_delete=models.CASCADE)
|
||||
|
@ -475,11 +608,16 @@ class Service_link(models.Model):
|
|||
self.save()
|
||||
|
||||
def need_regen(self):
|
||||
""" Décide si le temps minimal écoulé est suffisant pour provoquer une régénération de service"""
|
||||
if (self.asked_regen and (self.last_regen + self.service.min_time_regen) < timezone.now()) or (self.last_regen + self.service.regular_time_regen) < timezone.now():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
""" Décide si le temps minimal écoulé est suffisant pour provoquer une
|
||||
régénération de service"""
|
||||
return bool(
|
||||
(self.asked_regen and (
|
||||
self.last_regen + self.service.min_time_regen
|
||||
) < timezone.now()
|
||||
) or (
|
||||
self.last_regen + self.service.regular_time_regen
|
||||
) < timezone.now()
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.server) + " " + str(self.service)
|
||||
|
@ -487,22 +625,41 @@ class Service_link(models.Model):
|
|||
|
||||
class OuverturePortList(models.Model):
|
||||
"""Liste des ports ouverts sur une interface."""
|
||||
name = models.CharField(help_text="Nom de la configuration des ports.", max_length=255)
|
||||
name = models.CharField(
|
||||
help_text="Nom de la configuration des ports.",
|
||||
max_length=255
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def tcp_ports_in(self):
|
||||
return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN)
|
||||
"""Renvoie la liste des ports ouverts en TCP IN pour ce profil"""
|
||||
return self.ouvertureport_set.filter(
|
||||
protocole=OuverturePort.TCP,
|
||||
io=OuverturePort.IN
|
||||
)
|
||||
|
||||
def udp_ports_in(self):
|
||||
return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN)
|
||||
"""Renvoie la liste des ports ouverts en UDP IN pour ce profil"""
|
||||
return self.ouvertureport_set.filter(
|
||||
protocole=OuverturePort.UDP,
|
||||
io=OuverturePort.IN
|
||||
)
|
||||
|
||||
def tcp_ports_out(self):
|
||||
return self.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT)
|
||||
"""Renvoie la liste des ports ouverts en TCP OUT pour ce profil"""
|
||||
return self.ouvertureport_set.filter(
|
||||
protocole=OuverturePort.TCP,
|
||||
io=OuverturePort.OUT
|
||||
)
|
||||
|
||||
def udp_ports_out(self):
|
||||
return self.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT)
|
||||
"""Renvoie la liste des ports ouverts en UDP OUT pour ce profil"""
|
||||
return self.ouvertureport_set.filter(
|
||||
protocole=OuverturePort.UDP,
|
||||
io=OuverturePort.OUT
|
||||
)
|
||||
|
||||
|
||||
class OuverturePort(models.Model):
|
||||
|
@ -511,14 +668,19 @@ class OuverturePort(models.Model):
|
|||
|
||||
Les ports de la plage sont compris entre begin et en inclus.
|
||||
Si begin == end alors on ne représente qu'un seul port.
|
||||
|
||||
On limite les ports entre 0 et 65535, tels que défini par la RFC
|
||||
"""
|
||||
TCP = 'T'
|
||||
UDP = 'U'
|
||||
IN = 'I'
|
||||
OUT = 'O'
|
||||
begin = models.IntegerField()
|
||||
end = models.IntegerField()
|
||||
port_list = models.ForeignKey('OuverturePortList', on_delete=models.CASCADE)
|
||||
begin = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
|
||||
end = models.PositiveIntegerField(validators=[MaxValueValidator(65535)])
|
||||
port_list = models.ForeignKey(
|
||||
'OuverturePortList',
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
protocole = models.CharField(
|
||||
max_length=1,
|
||||
choices=(
|
||||
|
@ -542,88 +704,123 @@ class OuverturePort(models.Model):
|
|||
return '-'.join([str(self.begin), str(self.end)])
|
||||
|
||||
def show_port(self):
|
||||
"""Formatage plus joli, alias pour str"""
|
||||
return str(self)
|
||||
|
||||
|
||||
@receiver(post_save, sender=Machine)
|
||||
def machine_post_save(sender, **kwargs):
|
||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
||||
d'une machine"""
|
||||
user = kwargs['instance'].user
|
||||
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
||||
regen('dhcp')
|
||||
regen('mac_ip_list')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Machine)
|
||||
def machine_post_delete(sender, **kwargs):
|
||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
||||
d'une machine"""
|
||||
machine = kwargs['instance']
|
||||
user = machine.user
|
||||
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
||||
regen('dhcp')
|
||||
regen('mac_ip_list')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Interface)
|
||||
def interface_post_save(sender, **kwargs):
|
||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la modification
|
||||
d'une interface"""
|
||||
interface = kwargs['instance']
|
||||
user = interface.machine.user
|
||||
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
||||
if not interface.may_have_port_open() and interface.port_lists.all():
|
||||
interface.port_lists.clear()
|
||||
# Regen services
|
||||
regen('dhcp')
|
||||
regen('mac_ip_list')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Interface)
|
||||
def interface_post_delete(sender, **kwargs):
|
||||
"""Synchronisation ldap et régen parefeu/dhcp lors de la suppression
|
||||
d'une interface"""
|
||||
interface = kwargs['instance']
|
||||
user = interface.machine.user
|
||||
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
||||
|
||||
|
||||
@receiver(post_save, sender=IpType)
|
||||
def iptype_post_save(sender, **kwargs):
|
||||
"""Generation des objets ip après modification d'un range ip"""
|
||||
iptype = kwargs['instance']
|
||||
iptype.gen_ip_range()
|
||||
|
||||
|
||||
@receiver(post_save, sender=MachineType)
|
||||
def machine_post_save(sender, **kwargs):
|
||||
"""Mise à jour des interfaces lorsque changement d'attribution
|
||||
d'une machinetype (changement iptype parent)"""
|
||||
machinetype = kwargs['instance']
|
||||
for interface in machinetype.all_interfaces():
|
||||
interface.update_type()
|
||||
|
||||
|
||||
@receiver(post_save, sender=Domain)
|
||||
def domain_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un domain object"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Domain)
|
||||
def domain_post_delete(sender, **kwargs):
|
||||
"""Regeneration dns après suppression d'un domain object"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Extension)
|
||||
def extension_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'une extension"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Extension)
|
||||
def extension_post_selete(sender, **kwargs):
|
||||
"""Regeneration dns après suppression d'une extension"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Mx)
|
||||
def mx_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un MX"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Mx)
|
||||
def mx_post_delete(sender, **kwargs):
|
||||
"""Regeneration dns après suppresson d'un MX"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Ns)
|
||||
def ns_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un NS"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Ns)
|
||||
def ns_post_delete(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un NS"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_save, sender=Text)
|
||||
def text_post_save(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un TXT"""
|
||||
regen('dns')
|
||||
|
||||
|
||||
@receiver(post_delete, sender=Text)
|
||||
def text_post_delete(sender, **kwargs):
|
||||
"""Regeneration dns après modification d'un TX"""
|
||||
regen('dns')
|
||||
|
|
|
@ -24,20 +24,42 @@
|
|||
#Augustin Lemesle
|
||||
|
||||
from rest_framework import serializers
|
||||
from machines.models import Interface, IpType, Extension, IpList, MachineType, Domain, Text, Mx, Service_link, Ns
|
||||
from machines.models import (
|
||||
Interface,
|
||||
IpType,
|
||||
Extension,
|
||||
IpList,
|
||||
MachineType,
|
||||
Domain,
|
||||
Text,
|
||||
Mx,
|
||||
Service_link,
|
||||
Ns,
|
||||
OuverturePortList,
|
||||
OuverturePort
|
||||
)
|
||||
|
||||
|
||||
class IpTypeField(serializers.RelatedField):
|
||||
"""Serialisation d'une iptype, renvoie son evaluation str"""
|
||||
def to_representation(self, value):
|
||||
return value.type
|
||||
|
||||
|
||||
class IpListSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une iplist, ip_type etant une foreign_key,
|
||||
on evalue sa methode str"""
|
||||
ip_type = IpTypeField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = IpList
|
||||
fields = ('ipv4', 'ip_type')
|
||||
|
||||
|
||||
class InterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une interface, ipv4, domain et extension sont
|
||||
des foreign_key, on les override et on les evalue avec des fonctions
|
||||
get_..."""
|
||||
ipv4 = IpListSerializer(read_only=True)
|
||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||
domain = serializers.SerializerMethodField('get_dns')
|
||||
|
@ -56,7 +78,9 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
|||
def get_macaddress(self, obj):
|
||||
return str(obj.mac_address)
|
||||
|
||||
|
||||
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation complete d'une interface avec l'ipv6 en plus"""
|
||||
ipv4 = IpListSerializer(read_only=True)
|
||||
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||
domain = serializers.SerializerMethodField('get_dns')
|
||||
|
@ -75,24 +99,69 @@ class FullInterfaceSerializer(serializers.ModelSerializer):
|
|||
def get_macaddress(self, obj):
|
||||
return str(obj.mac_address)
|
||||
|
||||
|
||||
class ExtensionNameField(serializers.RelatedField):
|
||||
"""Evaluation str d'un objet extension (.example.org)"""
|
||||
def to_representation(self, value):
|
||||
return value.name
|
||||
|
||||
|
||||
class TypeSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un iptype : extension et la liste des
|
||||
ouvertures de port son evalués en get_... etant des
|
||||
foreign_key ou des relations manytomany"""
|
||||
extension = ExtensionNameField(read_only=True)
|
||||
ouverture_ports_tcp_in = serializers\
|
||||
.SerializerMethodField('get_port_policy_input_tcp')
|
||||
ouverture_ports_tcp_out = serializers\
|
||||
.SerializerMethodField('get_port_policy_output_tcp')
|
||||
ouverture_ports_udp_in = serializers\
|
||||
.SerializerMethodField('get_port_policy_input_udp')
|
||||
ouverture_ports_udp_out = serializers\
|
||||
.SerializerMethodField('get_port_policy_output_udp')
|
||||
|
||||
class Meta:
|
||||
model = IpType
|
||||
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop')
|
||||
fields = ('type', 'extension', 'domaine_ip_start', 'domaine_ip_stop',
|
||||
'ouverture_ports_tcp_in', 'ouverture_ports_tcp_out',
|
||||
'ouverture_ports_udp_in', 'ouverture_ports_udp_out',)
|
||||
|
||||
def get_port_policy(self, obj, protocole, io):
|
||||
if obj.ouverture_ports is None:
|
||||
return []
|
||||
return map(
|
||||
str,
|
||||
obj.ouverture_ports.ouvertureport_set.filter(
|
||||
protocole=protocole
|
||||
).filter(io=io)
|
||||
)
|
||||
|
||||
def get_port_policy_input_tcp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en entrée tcp"""
|
||||
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.IN)
|
||||
|
||||
def get_port_policy_output_tcp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en sortie tcp"""
|
||||
return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.OUT)
|
||||
|
||||
def get_port_policy_input_udp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en entrée udp"""
|
||||
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.IN)
|
||||
|
||||
def get_port_policy_output_udp(self, obj):
|
||||
"""Renvoie la liste des ports ouverts en sortie udp"""
|
||||
return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.OUT)
|
||||
|
||||
|
||||
class ExtensionSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'une extension : origin_ip et la zone sont
|
||||
des foreign_key donc evalués en get_..."""
|
||||
origin = serializers.SerializerMethodField('get_origin_ip')
|
||||
zone_entry = serializers.SerializerMethodField('get_zone_name')
|
||||
|
||||
class Meta:
|
||||
model = Extension
|
||||
fields = ('name', 'origin', 'zone_entry')
|
||||
fields = ('name', 'origin', 'origin_v6', 'zone_entry')
|
||||
|
||||
def get_origin_ip(self, obj):
|
||||
return obj.origin.ipv4
|
||||
|
@ -100,7 +169,10 @@ class ExtensionSerializer(serializers.ModelSerializer):
|
|||
def get_zone_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class MxSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un MX, evaluation du nom, de la zone
|
||||
et du serveur cible, etant des foreign_key"""
|
||||
name = serializers.SerializerMethodField('get_entry_name')
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
mx_entry = serializers.SerializerMethodField('get_mx_name')
|
||||
|
@ -118,7 +190,10 @@ class MxSerializer(serializers.ModelSerializer):
|
|||
def get_mx_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class TextSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un txt : zone cible et l'entrée txt
|
||||
sont evaluées à part"""
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
text_entry = serializers.SerializerMethodField('get_text_name')
|
||||
|
||||
|
@ -132,7 +207,10 @@ class TextSerializer(serializers.ModelSerializer):
|
|||
def get_text_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class NsSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un NS : la zone, l'entrée ns complète et le serveur
|
||||
ns sont évalués à part"""
|
||||
zone = serializers.SerializerMethodField('get_zone_name')
|
||||
ns = serializers.SerializerMethodField('get_domain_name')
|
||||
ns_entry = serializers.SerializerMethodField('get_text_name')
|
||||
|
@ -150,7 +228,10 @@ class NsSerializer(serializers.ModelSerializer):
|
|||
def get_text_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class DomainSerializer(serializers.ModelSerializer):
|
||||
"""Serialisation d'un domain, extension, cname sont des foreign_key,
|
||||
et l'entrée complète, sont évalués à part"""
|
||||
extension = serializers.SerializerMethodField('get_zone_name')
|
||||
cname = serializers.SerializerMethodField('get_alias_name')
|
||||
cname_entry = serializers.SerializerMethodField('get_cname_name')
|
||||
|
@ -168,7 +249,9 @@ class DomainSerializer(serializers.ModelSerializer):
|
|||
def get_cname_name(self, obj):
|
||||
return str(obj.dns_entry)
|
||||
|
||||
|
||||
class ServiceServersSerializer(serializers.ModelSerializer):
|
||||
"""Evaluation d'un Service, et serialisation"""
|
||||
server = serializers.SerializerMethodField('get_server_name')
|
||||
service = serializers.SerializerMethodField('get_service_name')
|
||||
need_regen = serializers.SerializerMethodField('get_regen_status')
|
||||
|
@ -185,3 +268,31 @@ class ServiceServersSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_regen_status(self, obj):
|
||||
return obj.need_regen()
|
||||
|
||||
|
||||
class OuverturePortsSerializer(serializers.Serializer):
|
||||
"""Serialisation de l'ouverture des ports"""
|
||||
ipv4 = serializers.SerializerMethodField()
|
||||
ipv6 = serializers.SerializerMethodField()
|
||||
|
||||
def get_ipv4():
|
||||
return {i.ipv4.ipv4:
|
||||
{
|
||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
||||
}
|
||||
for i in Interface.objects.all() if i.ipv4
|
||||
}
|
||||
|
||||
def get_ipv6():
|
||||
return {i.ipv6:
|
||||
{
|
||||
"tcp_in":[j.tcp_ports_in() for j in i.port_lists.all()],
|
||||
"tcp_out":[j.tcp_ports_out()for j in i.port_lists.all()],
|
||||
"udp_in":[j.udp_ports_in() for j in i.port_lists.all()],
|
||||
"udp_out":[j.udp_ports_out() for j in i.port_lists.all()],
|
||||
}
|
||||
for i in Interface.objects.all() if i.ipv6
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<th>Extension</th>
|
||||
<th>Autorisation infra pour utiliser l'extension</th>
|
||||
<th>Enregistrement A origin</th>
|
||||
<th>Enregistrement AAAA origin</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -36,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ extension.name }}</td>
|
||||
<td>{{ extension.need_infra }}</td>
|
||||
<td>{{ extension.origin }}</td>
|
||||
<td>{{ extension.origin_v6 }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-extension' id=extension.id %}
|
||||
|
|
|
@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<th>Fin</th>
|
||||
<th>Préfixe v6</th>
|
||||
<th>Sur vlan</th>
|
||||
<th>Ouverture ports par défault</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
|
@ -45,6 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<td>{{ type.domaine_ip_stop }}</td>
|
||||
<td>{{ type.prefix_v6 }}</td>
|
||||
<td>{{ type.vlan }}</td>
|
||||
<td>{{ type.ouverture_ports }}</td>
|
||||
<td class="text-right">
|
||||
{% if is_infra %}
|
||||
{% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %}
|
||||
|
|
|
@ -58,7 +58,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% if is_cableur %}
|
||||
<a class="list-group-item list-group-item-info" href="{% url "machines:index-portlist" %}">
|
||||
<i class="glyphicon glyphicon-list"></i>
|
||||
Configuration de ports
|
||||
Ouverture de ports
|
||||
</a>
|
||||
{%endif%}
|
||||
{% endblock %}
|
||||
|
|
|
@ -93,6 +93,7 @@ urlpatterns = [
|
|||
url(r'^rest/text/$', views.text, name='text'),
|
||||
url(r'^rest/zones/$', views.zones, name='zones'),
|
||||
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
|
||||
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),
|
||||
url(r'index_portlist/$', views.index_portlist, name='index-portlist'),
|
||||
url(r'^edit_portlist/(?P<pk>[0-9]+)$', views.edit_portlist, name='edit-portlist'),
|
||||
url(r'^del_portlist/(?P<pk>[0-9]+)$', views.del_portlist, name='del-portlist'),
|
||||
|
|
|
@ -43,18 +43,79 @@ from django.contrib.auth import authenticate, login
|
|||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from rest_framework.renderers import JSONRenderer
|
||||
from machines.serializers import FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
|
||||
from machines.serializers import ( FullInterfaceSerializer,
|
||||
InterfaceSerializer,
|
||||
TypeSerializer,
|
||||
DomainSerializer,
|
||||
TextSerializer,
|
||||
MxSerializer,
|
||||
ExtensionSerializer,
|
||||
ServiceServersSerializer,
|
||||
NsSerializer,
|
||||
OuverturePortsSerializer
|
||||
)
|
||||
from reversion import revisions as reversion
|
||||
from reversion.models import Version
|
||||
|
||||
import re
|
||||
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
|
||||
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, TxtForm, DelTxtForm, MxForm, DelMxForm, VlanForm, DelVlanForm, ServiceForm, DelServiceForm, NasForm, DelNasForm
|
||||
from .forms import (
|
||||
NewMachineForm,
|
||||
EditMachineForm,
|
||||
EditInterfaceForm,
|
||||
AddInterfaceForm,
|
||||
MachineTypeForm,
|
||||
DelMachineTypeForm,
|
||||
ExtensionForm,
|
||||
DelExtensionForm,
|
||||
BaseEditInterfaceForm,
|
||||
BaseEditMachineForm
|
||||
)
|
||||
from .forms import (
|
||||
EditIpTypeForm,
|
||||
IpTypeForm,
|
||||
DelIpTypeForm,
|
||||
DomainForm,
|
||||
AliasForm,
|
||||
DelAliasForm,
|
||||
NsForm,
|
||||
DelNsForm,
|
||||
TxtForm,
|
||||
DelTxtForm,
|
||||
MxForm,
|
||||
DelMxForm,
|
||||
VlanForm,
|
||||
DelVlanForm,
|
||||
ServiceForm,
|
||||
DelServiceForm,
|
||||
NasForm,
|
||||
DelNasForm
|
||||
)
|
||||
from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm
|
||||
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort
|
||||
from .models import (
|
||||
IpType,
|
||||
Machine,
|
||||
Interface,
|
||||
IpList,
|
||||
MachineType,
|
||||
Extension,
|
||||
Mx,
|
||||
Ns,
|
||||
Domain,
|
||||
Service,
|
||||
Service_link,
|
||||
Vlan,
|
||||
Nas,
|
||||
Text,
|
||||
OuverturePortList,
|
||||
OuverturePort
|
||||
)
|
||||
from users.models import User
|
||||
from preferences.models import GeneralOption, OptionalMachine
|
||||
from re2o.utils import all_active_assigned_interfaces, all_has_access
|
||||
from re2o.utils import (
|
||||
all_active_assigned_interfaces,
|
||||
all_has_access,
|
||||
filter_active_interfaces
|
||||
)
|
||||
from re2o.views import form
|
||||
|
||||
def f_type_id( is_type_tt ):
|
||||
|
@ -71,7 +132,8 @@ def generate_ipv4_choices( form ) :
|
|||
choices = '{"":[{key:"",value:"Choisissez d\'abord un type de machine"},'
|
||||
mtype_id = -1
|
||||
|
||||
for ip in f_ipv4.queryset.annotate(mtype_id=F('ip_type__machinetype__id')).order_by('mtype_id', 'id') :
|
||||
for ip in f_ipv4.queryset.annotate(mtype_id=F('ip_type__machinetype__id'))\
|
||||
.order_by('mtype_id', 'id') :
|
||||
if mtype_id != ip.mtype_id :
|
||||
mtype_id = ip.mtype_id
|
||||
used_mtype_id.append(mtype_id)
|
||||
|
@ -140,8 +202,8 @@ def generate_ipv4_mbf_param( form, is_type_tt ):
|
|||
|
||||
@login_required
|
||||
def new_machine(request, userid):
|
||||
""" Fonction de creation d'une machine. Cree l'objet machine, le sous objet interface et l'objet domain
|
||||
à partir de model forms.
|
||||
""" Fonction de creation d'une machine. Cree l'objet machine,
|
||||
le sous objet interface et l'objet domain à partir de model forms.
|
||||
Trop complexe, devrait être simplifié"""
|
||||
try:
|
||||
user = User.objects.get(pk=userid)
|
||||
|
@ -152,15 +214,16 @@ def new_machine(request, userid):
|
|||
max_lambdauser_interfaces = options.max_lambdauser_interfaces
|
||||
if not request.user.has_perms(('cableur',)):
|
||||
if user != request.user:
|
||||
messages.error(request, "Vous ne pouvez pas ajouter une machine à un autre user que vous sans droit")
|
||||
messages.error(
|
||||
request,
|
||||
"Vous ne pouvez pas ajouter une machine à un autre user que vous sans droit")
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
if user.user_interfaces().count() >= max_lambdauser_interfaces:
|
||||
messages.error(request, "Vous avez atteint le maximum d'interfaces autorisées que vous pouvez créer vous même (%s) " % max_lambdauser_interfaces)
|
||||
return redirect("/users/profil/" + str(request.user.id))
|
||||
machine = NewMachineForm(request.POST or None)
|
||||
interface = AddInterfaceForm(request.POST or None, infra=request.user.has_perms(('infra',)))
|
||||
nb_machine = Interface.objects.filter(machine__user=userid).count()
|
||||
domain = DomainForm(request.POST or None, user=user, nb_machine=nb_machine)
|
||||
domain = DomainForm(request.POST or None, user=user)
|
||||
if machine.is_valid() and interface.is_valid():
|
||||
new_machine = machine.save(commit=False)
|
||||
new_machine.user = user
|
||||
|
@ -993,7 +1056,9 @@ def history(request, object, id):
|
|||
@login_required
|
||||
@permission_required('cableur')
|
||||
def index_portlist(request):
|
||||
port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set').prefetch_related('interface_set').order_by('name')
|
||||
port_list = OuverturePortList.objects.prefetch_related('ouvertureport_set')\
|
||||
.prefetch_related('interface_set__domain__extension')\
|
||||
.prefetch_related('interface_set__machine__user').order_by('name')
|
||||
return render(request, "machines/index_portlist.html", {'port_list':port_list})
|
||||
|
||||
@login_required
|
||||
|
@ -1184,6 +1249,34 @@ def service_servers(request):
|
|||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('serveur')
|
||||
def ouverture_ports(request):
|
||||
r = {'ipv4':{}, 'ipv6':{}}
|
||||
for o in OuverturePortList.objects.all().prefetch_related('ouvertureport_set').prefetch_related('interface_set', 'interface_set__ipv4'):
|
||||
pl = {
|
||||
"tcp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.IN))),
|
||||
"tcp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.TCP, io=OuverturePort.OUT))),
|
||||
"udp_in":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.IN))),
|
||||
"udp_out":set(map(str,o.ouvertureport_set.filter(protocole=OuverturePort.UDP, io=OuverturePort.OUT))),
|
||||
}
|
||||
for i in filter_active_interfaces(o.interface_set):
|
||||
if i.may_have_port_open():
|
||||
d = r['ipv4'].get(i.ipv4.ipv4, {})
|
||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
||||
r['ipv4'][i.ipv4.ipv4] = d
|
||||
if i.ipv6_object:
|
||||
d = r['ipv6'].get(i.ipv6, {})
|
||||
d["tcp_in"] = d.get("tcp_in",set()).union(pl["tcp_in"])
|
||||
d["tcp_out"] = d.get("tcp_out",set()).union(pl["tcp_out"])
|
||||
d["udp_in"] = d.get("udp_in",set()).union(pl["udp_in"])
|
||||
d["udp_out"] = d.get("udp_out",set()).union(pl["udp_out"])
|
||||
r['ipv6'][i.ipv6] = d
|
||||
return JSONResponse(r)
|
||||
@csrf_exempt
|
||||
@login_required
|
||||
@permission_required('serveur')
|
||||
def regen_achieved(request):
|
||||
obj = Service_link.objects.filter(service__in=Service.objects.filter(service_type=request.POST['service']), server__in=Interface.objects.filter(domain__in=Domain.objects.filter(name=request.POST['server'])))
|
||||
if obj:
|
||||
|
|
20
preferences/migrations/0021_auto_20171015_1741.py
Normal file
20
preferences/migrations/0021_auto_20171015_1741.py
Normal 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),
|
||||
),
|
||||
]
|
20
preferences/migrations/0022_auto_20171015_1758.py
Normal file
20
preferences/migrations/0022_auto_20171015_1758.py
Normal 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),
|
||||
),
|
||||
]
|
20
preferences/migrations/0023_auto_20171015_2033.py
Normal file
20
preferences/migrations/0023_auto_20171015_2033.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -69,7 +69,7 @@ urlpatterns = [
|
|||
),
|
||||
url(r'^del_services/$', views.del_services, name='del-services'),
|
||||
url(
|
||||
r'^history/(?P<object>service)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>service)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
|
|
|
@ -104,9 +104,9 @@ def all_has_access(search_time=DT_NOW):
|
|||
).distinct()
|
||||
|
||||
|
||||
def all_active_interfaces():
|
||||
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
|
||||
return Interface.objects.filter(
|
||||
def filter_active_interfaces(interface_set):
|
||||
"""Filtre les machines autorisées à sortir sur internet dans une requête"""
|
||||
return interface_set.filter(
|
||||
machine__in=Machine.objects.filter(
|
||||
user__in=all_has_access()
|
||||
).filter(active=True)
|
||||
|
@ -116,6 +116,11 @@ def all_active_interfaces():
|
|||
.distinct()
|
||||
|
||||
|
||||
def all_active_interfaces():
|
||||
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
|
||||
return filter_active_interfaces(Interface.objects)
|
||||
|
||||
|
||||
def all_active_assigned_interfaces():
|
||||
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et
|
||||
disposant de l'accès internet"""
|
||||
|
|
|
@ -82,6 +82,11 @@ class AddPortForm(ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(AddPortForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['machine_interface'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
self.fields['related'].queryset = Port.objects.all()\
|
||||
.select_related('switch__switch_interface__domain__extension')\
|
||||
.order_by('switch', 'port')
|
||||
|
||||
|
||||
class StackForm(ModelForm):
|
||||
|
@ -105,6 +110,8 @@ class EditSwitchForm(ModelForm):
|
|||
def __init__(self, *args, **kwargs):
|
||||
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
|
||||
super(EditSwitchForm, self).__init__(*args, prefix=prefix, **kwargs)
|
||||
self.fields['switch_interface'].queryset = Interface.objects.all()\
|
||||
.select_related('domain__extension')
|
||||
self.fields['location'].label = 'Localisation'
|
||||
self.fields['number'].label = 'Nombre de ports'
|
||||
|
||||
|
|
40
topologie/migrations/0031_auto_20171015_2033.py
Normal file
40
topologie/migrations/0031_auto_20171015_2033.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -52,8 +52,8 @@ class Stack(models.Model):
|
|||
name = models.CharField(max_length=32, blank=True, null=True)
|
||||
stack_id = models.CharField(max_length=32, unique=True)
|
||||
details = models.CharField(max_length=255, blank=True, null=True)
|
||||
member_id_min = models.IntegerField()
|
||||
member_id_max = models.IntegerField()
|
||||
member_id_min = models.PositiveIntegerField()
|
||||
member_id_max = models.PositiveIntegerField()
|
||||
|
||||
def __str__(self):
|
||||
return " ".join([self.name, self.stack_id])
|
||||
|
@ -90,7 +90,7 @@ class Switch(models.Model):
|
|||
on_delete=models.CASCADE
|
||||
)
|
||||
location = models.CharField(max_length=255)
|
||||
number = models.IntegerField()
|
||||
number = models.PositiveIntegerField()
|
||||
details = models.CharField(max_length=255, blank=True)
|
||||
stack = models.ForeignKey(
|
||||
Stack,
|
||||
|
@ -98,7 +98,7 @@ class Switch(models.Model):
|
|||
null=True,
|
||||
on_delete=models.SET_NULL
|
||||
)
|
||||
stack_member_id = models.IntegerField(blank=True, null=True)
|
||||
stack_member_id = models.PositiveIntegerField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ('stack', 'stack_member_id')
|
||||
|
@ -145,8 +145,12 @@ class Port(models.Model):
|
|||
('COMMON', 'COMMON'),
|
||||
)
|
||||
|
||||
switch = models.ForeignKey('Switch', related_name="ports")
|
||||
port = models.IntegerField()
|
||||
switch = models.ForeignKey(
|
||||
'Switch',
|
||||
related_name="ports",
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
port = models.PositiveIntegerField()
|
||||
room = models.ForeignKey(
|
||||
'Room',
|
||||
on_delete=models.PROTECT,
|
||||
|
|
|
@ -52,6 +52,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
<a class="btn btn-info btn-sm" role="button" title="Historique" href="{% url 'topologie:history' 'switch' switch.pk %}">
|
||||
<i class="glyphicon glyphicon-time"></i>
|
||||
</a>
|
||||
{% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.pk %}
|
||||
{% include 'buttons/suppr.html' with href='machines:del-interface' id=switch.switch_interface.id %}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -42,16 +42,16 @@ urlpatterns = [
|
|||
url(r'^switch/(?P<switch_id>[0-9]+)$',
|
||||
views.index_port,
|
||||
name='index-port'),
|
||||
url(r'^history/(?P<object>switch)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>switch)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object>port)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>port)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object>room)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>room)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^history/(?P<object>stack)/(?P<id>[0-9]+)$',
|
||||
url(r'^history/(?P<object_name>stack)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'),
|
||||
url(r'^edit_port/(?P<port_id>[0-9]+)$', views.edit_port, name='edit-port'),
|
||||
|
|
|
@ -50,7 +50,7 @@ from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm
|
|||
from topologie.forms import AddPortForm, EditRoomForm, StackForm
|
||||
from users.views import form
|
||||
|
||||
from machines.forms import AliasForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm
|
||||
from machines.forms import DomainForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm
|
||||
from machines.views import generate_ipv4_mbf_param
|
||||
from preferences.models import AssoOption, GeneralOption
|
||||
|
||||
|
@ -135,7 +135,8 @@ def index_port(request, switch_id):
|
|||
port_list = Port.objects.filter(switch=switch)\
|
||||
.select_related('room')\
|
||||
.select_related('machine_interface__domain__extension')\
|
||||
.select_related('related')\
|
||||
.select_related('machine_interface__machine__user')\
|
||||
.select_related('related__switch__switch_interface__domain__extension')\
|
||||
.select_related('switch')\
|
||||
.order_by('port')
|
||||
return render(request, 'topologie/index_p.html', {
|
||||
|
@ -344,9 +345,8 @@ def new_switch(request):
|
|||
request.POST or None,
|
||||
infra=request.user.has_perms(('infra',))
|
||||
)
|
||||
domain = AliasForm(
|
||||
domain = DomainForm(
|
||||
request.POST or None,
|
||||
infra=request.user.has_perms(('infra',))
|
||||
)
|
||||
if switch.is_valid() and machine.is_valid() and interface.is_valid():
|
||||
options, _created = AssoOption.objects.get_or_create()
|
||||
|
@ -410,9 +410,8 @@ def edit_switch(request, switch_id):
|
|||
request.POST or None,
|
||||
instance=switch.switch_interface
|
||||
)
|
||||
domain_form = AliasForm(
|
||||
domain_form = DomainForm(
|
||||
request.POST or None,
|
||||
infra=request.user.has_perms(('infra',)),
|
||||
instance=switch.switch_interface.domain
|
||||
)
|
||||
if switch_form.is_valid() and machine_form.is_valid()\
|
||||
|
|
|
@ -452,13 +452,14 @@ class RightForm(ModelForm):
|
|||
class DelRightForm(Form):
|
||||
"""Suppression d'un droit d'un user"""
|
||||
rights = forms.ModelMultipleChoiceField(
|
||||
queryset=Right.objects.all(),
|
||||
queryset=Right.objects.select_related('user'),
|
||||
widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
def __init__(self, right, *args, **kwargs):
|
||||
super(DelRightForm, self).__init__(*args, **kwargs)
|
||||
self.fields['rights'].queryset = Right.objects.filter(right=right)
|
||||
self.fields['rights'].queryset = Right.objects.select_related('user')\
|
||||
.select_related('right').filter(right=right)
|
||||
|
||||
|
||||
class BanForm(ModelForm):
|
||||
|
|
37
users/migrations/0056_auto_20171015_2033.py
Normal file
37
users/migrations/0056_auto_20171015_2033.py
Normal 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),
|
||||
),
|
||||
]
|
|
@ -243,8 +243,8 @@ class User(AbstractBaseUser):
|
|||
state = models.IntegerField(choices=STATES, default=STATE_ACTIVE)
|
||||
registered = models.DateTimeField(auto_now_add=True)
|
||||
telephone = models.CharField(max_length=15, blank=True, null=True)
|
||||
uid_number = models.IntegerField(default=auto_uid, unique=True)
|
||||
rezo_rez_uid = models.IntegerField(unique=True, blank=True, null=True)
|
||||
uid_number = models.PositiveIntegerField(default=auto_uid, unique=True)
|
||||
rezo_rez_uid = models.PositiveIntegerField(unique=True, blank=True, null=True)
|
||||
|
||||
USERNAME_FIELD = 'pseudo'
|
||||
REQUIRED_FIELDS = ['name', 'surname', 'email']
|
||||
|
@ -840,7 +840,7 @@ class ListRight(models.Model):
|
|||
que des lettres minuscules"
|
||||
)]
|
||||
)
|
||||
gid = models.IntegerField(unique=True, null=True)
|
||||
gid = models.PositiveIntegerField(unique=True, null=True)
|
||||
details = models.CharField(
|
||||
help_text="Description",
|
||||
max_length=255,
|
||||
|
|
|
@ -34,19 +34,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
<form class="form" method="post">
|
||||
{% csrf_token %}
|
||||
<table class="table table-striped">
|
||||
<table id="delRights_table" class="table table-striped">
|
||||
<tbody>
|
||||
{% for key, values in userform.items %}
|
||||
<tr class="active">
|
||||
<td>
|
||||
<a data-toggle="collapse" href="#collapseRight_{{key}}" aria-expanded="false" aria-controls="collapseRights_{{key}}">
|
||||
<a data-toggle="collapse" href="#collapseRight_{{key}}" aria-expanded="true" aria-controls="collapseRights_{{key}}">
|
||||
<b>{{ key }}</b> ( {{values.rights|length }} users )
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="collapse" id="collapseRight_{{key}}">
|
||||
<div class="collapse in" id="collapseRight_{{key}}">
|
||||
<ul class="list-group" style="margin-bottom: 0px">
|
||||
{% for user in values.rights %}
|
||||
<li class="list-group-item col-xs-6 col-sm-4 col-md-3" style="border: none;">{{ user }}</li>
|
||||
|
@ -58,9 +58,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% bootstrap_button "Modifier" button_type="submit" icon="star" %}
|
||||
{% bootstrap_button "Supprimer" button_type="submit" icon="star" %}
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$("#delRights_table").ready( function () {
|
||||
var rights_div = [{% for k in userform.keys %}$("#collapseRight_{{k}}"), {% endfor %}];
|
||||
for (var i=0 ; i<rights_div.length ; i++) {
|
||||
rights_div[i].collapse('hide');
|
||||
}
|
||||
} );
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<br />
|
||||
|
|
|
@ -88,32 +88,32 @@ urlpatterns = [
|
|||
url(r'^reset_password/$', views.reset_password, name='reset-password'),
|
||||
url(r'^mass_archive/$', views.mass_archive, name='mass-archive'),
|
||||
url(
|
||||
r'^history/(?P<object>user)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>user)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(
|
||||
r'^history/(?P<object>ban)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>ban)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(
|
||||
r'^history/(?P<object>whitelist)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>whitelist)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(
|
||||
r'^history/(?P<object>school)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>school)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(
|
||||
r'^history/(?P<object>listright)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>listright)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
url(
|
||||
r'^history/(?P<object>serviceuser)/(?P<id>[0-9]+)$',
|
||||
r'^history/(?P<object_name>serviceuser)/(?P<object_id>[0-9]+)$',
|
||||
views.history,
|
||||
name='history'
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue