mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-12-26 08:53:46 +00:00
Merge branch 'ipv6'
This commit is contained in:
commit
8888d498f2
17 changed files with 491 additions and 14 deletions
|
@ -26,7 +26,9 @@ from __future__ import unicode_literals
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from reversion.admin import VersionAdmin
|
from reversion.admin import VersionAdmin
|
||||||
|
|
||||||
from .models import IpType, Machine, MachineType, Domain, IpList, Interface, Extension, Mx, Ns, Vlan, Text, Nas, Service
|
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):
|
class MachineAdmin(VersionAdmin):
|
||||||
pass
|
pass
|
||||||
|
@ -58,6 +60,12 @@ class NasAdmin(VersionAdmin):
|
||||||
class IpListAdmin(VersionAdmin):
|
class IpListAdmin(VersionAdmin):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class OuverturePortAdmin(VersionAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class OuverturePortListAdmin(VersionAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
class InterfaceAdmin(VersionAdmin):
|
class InterfaceAdmin(VersionAdmin):
|
||||||
list_display = ('machine','type','mac_address','ipv4','details')
|
list_display = ('machine','type','mac_address','ipv4','details')
|
||||||
|
|
||||||
|
@ -80,3 +88,7 @@ admin.site.register(Domain, DomainAdmin)
|
||||||
admin.site.register(Service, ServiceAdmin)
|
admin.site.register(Service, ServiceAdmin)
|
||||||
admin.site.register(Vlan, VlanAdmin)
|
admin.site.register(Vlan, VlanAdmin)
|
||||||
admin.site.register(Nas, NasAdmin)
|
admin.site.register(Nas, NasAdmin)
|
||||||
|
admin.site.register(OuverturePort, OuverturePortAdmin)
|
||||||
|
admin.site.register(OuverturePortList, OuverturePortListAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,9 +22,11 @@
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
from django.forms import ModelForm, Form, ValidationError
|
from django.forms import ModelForm, Form, ValidationError
|
||||||
from django import forms
|
from django import forms
|
||||||
from .models import Domain, Machine, Interface, IpList, MachineType, Extension, Mx, Text, Ns, Service, Vlan, Nas, IpType
|
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.db.models import Q
|
||||||
from django.core.validators import validate_email
|
from django.core.validators import validate_email
|
||||||
|
|
||||||
|
@ -50,7 +52,8 @@ class BaseEditMachineForm(EditMachineForm):
|
||||||
class EditInterfaceForm(ModelForm):
|
class EditInterfaceForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Interface
|
model = Interface
|
||||||
fields = '__all__'
|
# fields = '__all__'
|
||||||
|
exclude = ['port_lists']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(EditInterfaceForm, self).__init__(*args, **kwargs)
|
super(EditInterfaceForm, self).__init__(*args, **kwargs)
|
||||||
|
@ -142,7 +145,7 @@ class DelMachineTypeForm(Form):
|
||||||
class IpTypeForm(ModelForm):
|
class IpTypeForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IpType
|
model = IpType
|
||||||
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'vlan']
|
fields = ['type','extension','need_infra','domaine_ip_start','domaine_ip_stop', 'prefix_v6', 'vlan']
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -151,7 +154,7 @@ class IpTypeForm(ModelForm):
|
||||||
|
|
||||||
class EditIpTypeForm(IpTypeForm):
|
class EditIpTypeForm(IpTypeForm):
|
||||||
class Meta(IpTypeForm.Meta):
|
class Meta(IpTypeForm.Meta):
|
||||||
fields = ['extension','type','need_infra', 'vlan']
|
fields = ['extension','type','need_infra', 'prefix_v6', 'vlan']
|
||||||
|
|
||||||
class DelIpTypeForm(Form):
|
class DelIpTypeForm(Form):
|
||||||
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
|
iptypes = forms.ModelMultipleChoiceField(queryset=IpType.objects.all(), label="Types d'ip actuelles", widget=forms.CheckboxSelectMultiple)
|
||||||
|
@ -232,5 +235,13 @@ class VlanForm(ModelForm):
|
||||||
class DelVlanForm(Form):
|
class DelVlanForm(Form):
|
||||||
vlan = forms.ModelMultipleChoiceField(queryset=Vlan.objects.all(), label="Vlan actuels", widget=forms.CheckboxSelectMultiple)
|
vlan = forms.ModelMultipleChoiceField(queryset=Vlan.objects.all(), label="Vlan actuels", widget=forms.CheckboxSelectMultiple)
|
||||||
|
|
||||||
|
class EditOuverturePortConfigForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = ['port_lists']
|
||||||
|
|
||||||
|
class EditOuverturePortListForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = OuverturePortList
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
43
machines/migrations/0058_auto_20171002_0350.py
Normal file
43
machines/migrations/0058_auto_20171002_0350.py
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-10-02 01:50
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0057_nas_autocapture_mac'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OuverturePort',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('begin', models.IntegerField()),
|
||||||
|
('end', models.IntegerField()),
|
||||||
|
('protocole', models.CharField(choices=[('T', 'TCP'), ('U', 'UDP')], default='T', max_length=1)),
|
||||||
|
('io', models.CharField(choices=[('I', 'IN'), ('O', 'OUT')], default='O', max_length=1)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OuverturePortList',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(help_text='Nom de la configuration des ports.', max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='ouvertureport',
|
||||||
|
name='port_list',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='interface',
|
||||||
|
name='port_lists',
|
||||||
|
field=models.ManyToManyField(blank=True, to='machines.OuverturePortList'),
|
||||||
|
),
|
||||||
|
]
|
20
machines/migrations/0059_iptype_prefix_v6.py
Normal file
20
machines/migrations/0059_iptype_prefix_v6.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-10-02 16:33
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('machines', '0058_auto_20171002_0350'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='iptype',
|
||||||
|
name='prefix_v6',
|
||||||
|
field=models.GenericIPAddressField(blank=True, null=True, protocol='IPv6'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -70,6 +70,7 @@ class IpType(models.Model):
|
||||||
need_infra = models.BooleanField(default=False)
|
need_infra = models.BooleanField(default=False)
|
||||||
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
|
domaine_ip_start = models.GenericIPAddressField(protocol='IPv4')
|
||||||
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
|
domaine_ip_stop = models.GenericIPAddressField(protocol='IPv4')
|
||||||
|
prefix_v6 = models.GenericIPAddressField(protocol='IPv6', null=True, blank=True)
|
||||||
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
|
vlan = models.ForeignKey('Vlan', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
@ -122,6 +123,9 @@ class IpType(models.Model):
|
||||||
for element in IpType.objects.all().exclude(pk=self.pk):
|
for element in IpType.objects.all().exclude(pk=self.pk):
|
||||||
if not self.ip_set.isdisjoint(element.ip_set):
|
if not self.ip_set.isdisjoint(element.ip_set):
|
||||||
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
|
raise ValidationError("Le range indiqué n'est pas disjoint des ranges existants")
|
||||||
|
# On formate le prefix v6
|
||||||
|
if self.prefix_v6:
|
||||||
|
self.prefix_v6 = str(IPNetwork(self.prefix_v6 + '/64').network)
|
||||||
return
|
return
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
@ -218,11 +222,11 @@ class Interface(models.Model):
|
||||||
PRETTY_NAME = "Interface"
|
PRETTY_NAME = "Interface"
|
||||||
|
|
||||||
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
ipv4 = models.OneToOneField('IpList', on_delete=models.PROTECT, blank=True, null=True)
|
||||||
#ipv6 = models.GenericIPAddressField(protocol='IPv6', null=True)
|
|
||||||
mac_address = MACAddressField(integer=False, unique=True)
|
mac_address = MACAddressField(integer=False, unique=True)
|
||||||
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
|
machine = models.ForeignKey('Machine', on_delete=models.CASCADE)
|
||||||
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
|
type = models.ForeignKey('MachineType', on_delete=models.PROTECT)
|
||||||
details = models.CharField(max_length=255, blank=True)
|
details = models.CharField(max_length=255, blank=True)
|
||||||
|
port_lists = models.ManyToManyField('OuverturePortList', blank=True)
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
|
@ -231,6 +235,18 @@ class Interface(models.Model):
|
||||||
user = self.machine.user
|
user = self.machine.user
|
||||||
return machine.active and user.has_access()
|
return machine.active and user.has_access()
|
||||||
|
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def ipv6_object(self):
|
||||||
|
if self.type.ip_type.prefix_v6:
|
||||||
|
return EUI(self.mac_address).ipv6(IPNetwork(self.type.ip_type.prefix_v6).network)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def ipv6(self):
|
||||||
|
return str(self.ipv6_object)
|
||||||
|
|
||||||
def mac_bare(self):
|
def mac_bare(self):
|
||||||
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
|
return str(EUI(self.mac_address, dialect=mac_bare)).lower()
|
||||||
|
|
||||||
|
@ -278,6 +294,15 @@ class Interface(models.Model):
|
||||||
domain = None
|
domain = None
|
||||||
return str(domain)
|
return str(domain)
|
||||||
|
|
||||||
|
def has_private_ip(self):
|
||||||
|
if self.ipv4:
|
||||||
|
return IPAddress(str(self.ipv4)).is_private()
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def may_have_port_open(self):
|
||||||
|
return self.ipv4 and not self.has_private_ip()
|
||||||
|
|
||||||
class Domain(models.Model):
|
class Domain(models.Model):
|
||||||
PRETTY_NAME = "Domaine dns"
|
PRETTY_NAME = "Domaine dns"
|
||||||
|
|
||||||
|
@ -406,6 +431,67 @@ class Service_link(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.server) + " " + str(self.service)
|
return str(self.server) + " " + str(self.service)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def tcp_ports_in(self):
|
||||||
|
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)
|
||||||
|
|
||||||
|
def tcp_ports_out(self):
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class OuverturePort(models.Model):
|
||||||
|
"""
|
||||||
|
Représente un simple port ou une plage de ports.
|
||||||
|
|
||||||
|
Les ports de la plage sont compris entre begin et en inclus.
|
||||||
|
Si begin == end alors on ne représente qu'un seul port.
|
||||||
|
"""
|
||||||
|
TCP = 'T'
|
||||||
|
UDP = 'U'
|
||||||
|
IN = 'I'
|
||||||
|
OUT = 'O'
|
||||||
|
begin = models.IntegerField()
|
||||||
|
end = models.IntegerField()
|
||||||
|
port_list = models.ForeignKey('OuverturePortList', on_delete=models.CASCADE)
|
||||||
|
protocole = models.CharField(
|
||||||
|
max_length=1,
|
||||||
|
choices=(
|
||||||
|
(TCP, 'TCP'),
|
||||||
|
(UDP, 'UDP'),
|
||||||
|
),
|
||||||
|
default=TCP,
|
||||||
|
)
|
||||||
|
io = models.CharField(
|
||||||
|
max_length=1,
|
||||||
|
choices=(
|
||||||
|
(IN, 'IN'),
|
||||||
|
(OUT, 'OUT'),
|
||||||
|
),
|
||||||
|
default=OUT,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
if self.begin == self.end :
|
||||||
|
return str(self.begin)
|
||||||
|
return '-'.join([str(self.begin), str(self.end)])
|
||||||
|
|
||||||
|
def show_port(self):
|
||||||
|
return str(self)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Machine)
|
@receiver(post_save, sender=Machine)
|
||||||
def machine_post_save(sender, **kwargs):
|
def machine_post_save(sender, **kwargs):
|
||||||
user = kwargs['instance'].user
|
user = kwargs['instance'].user
|
||||||
|
@ -426,6 +512,9 @@ def interface_post_save(sender, **kwargs):
|
||||||
interface = kwargs['instance']
|
interface = kwargs['instance']
|
||||||
user = interface.machine.user
|
user = interface.machine.user
|
||||||
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
user.ldap_sync(base=False, access_refresh=False, mac_refresh=True)
|
||||||
|
if not interface.may_have_port_open() and interface.port_lists.all():
|
||||||
|
interface.port_lists.clear()
|
||||||
|
# Regen services
|
||||||
regen('dhcp')
|
regen('dhcp')
|
||||||
regen('mac_ip_list')
|
regen('mac_ip_list')
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,25 @@ class InterfaceSerializer(serializers.ModelSerializer):
|
||||||
def get_macaddress(self, obj):
|
def get_macaddress(self, obj):
|
||||||
return str(obj.mac_address)
|
return str(obj.mac_address)
|
||||||
|
|
||||||
|
class FullInterfaceSerializer(serializers.ModelSerializer):
|
||||||
|
ipv4 = IpListSerializer(read_only=True)
|
||||||
|
mac_address = serializers.SerializerMethodField('get_macaddress')
|
||||||
|
domain = serializers.SerializerMethodField('get_dns')
|
||||||
|
extension = serializers.SerializerMethodField('get_interface_extension')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Interface
|
||||||
|
fields = ('ipv4', 'ipv6', 'mac_address', 'domain', 'extension')
|
||||||
|
|
||||||
|
def get_dns(self, obj):
|
||||||
|
return obj.domain.name
|
||||||
|
|
||||||
|
def get_interface_extension(self, obj):
|
||||||
|
return obj.domain.extension.name
|
||||||
|
|
||||||
|
def get_macaddress(self, obj):
|
||||||
|
return str(obj.mac_address)
|
||||||
|
|
||||||
class ExtensionNameField(serializers.RelatedField):
|
class ExtensionNameField(serializers.RelatedField):
|
||||||
def to_representation(self, value):
|
def to_representation(self, value):
|
||||||
return value.name
|
return value.name
|
||||||
|
|
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<th>Nécessite l'autorisation infra</th>
|
<th>Nécessite l'autorisation infra</th>
|
||||||
<th>Début</th>
|
<th>Début</th>
|
||||||
<th>Fin</th>
|
<th>Fin</th>
|
||||||
|
<th>Préfixe v6</th>
|
||||||
<th>Sur vlan</th>
|
<th>Sur vlan</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
@ -42,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<td>{{ type.need_infra }}</td>
|
<td>{{ type.need_infra }}</td>
|
||||||
<td>{{ type.domaine_ip_start }}</td>
|
<td>{{ type.domaine_ip_start }}</td>
|
||||||
<td>{{ type.domaine_ip_stop }}</td>
|
<td>{{ type.domaine_ip_stop }}</td>
|
||||||
|
<td>{{ type.prefix_v6 }}</td>
|
||||||
<td>{{ type.vlan }}</td>
|
<td>{{ type.vlan }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{% if is_infra %}
|
{% if is_infra %}
|
||||||
|
|
|
@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<th>Nom dns</th>
|
<th>Nom dns</th>
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Mac</th>
|
<th>Mac</th>
|
||||||
<th>Ipv4</th>
|
<th>IP</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
@ -74,7 +74,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
</td>
|
</td>
|
||||||
<td>{{ interface.type }}</td>
|
<td>{{ interface.type }}</td>
|
||||||
<td>{{ interface.mac_address }}</td>
|
<td>{{ interface.mac_address }}</td>
|
||||||
<td>{{ interface.ipv4 }}</td>
|
<td>{{ interface.ipv4 }}
|
||||||
|
{% if ipv6_enabled %}
|
||||||
|
{{ interface.ipv6 }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="dropdown">
|
<div class="dropdown">
|
||||||
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
@ -91,6 +95,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<i class="glyphicon glyphicon-edit"></i> Gerer les alias
|
<i class="glyphicon glyphicon-edit"></i> Gerer les alias
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if interface.may_have_port_open %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'machines:port-config' interface.id%}">
|
||||||
|
<i class="glyphicon glyphicon-edit"></i> Gerer la configuration des ports
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'machines:history' 'interface' interface.id %}">
|
<a href="{% url 'machines:history' 'interface' interface.id %}">
|
||||||
<i class="glyphicon glyphicon-time"></i> Historique
|
<i class="glyphicon glyphicon-time"></i> Historique
|
||||||
|
|
69
machines/templates/machines/edit_portlist.html
Normal file
69
machines/templates/machines/edit_portlist.html
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
{% extends "machines/sidebar.html" %}
|
||||||
|
{% comment %}
|
||||||
|
Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
quelques clics.
|
||||||
|
|
||||||
|
Copyright © 2017 Gabriel Détraz
|
||||||
|
Copyright © 2017 Goulven Kermarec
|
||||||
|
Copyright © 2017 Augustin Lemesle
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
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.
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Création et modification de machines{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% bootstrap_form_errors port_list %}
|
||||||
|
|
||||||
|
|
||||||
|
<form class="form" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form port_list %}
|
||||||
|
{{ ports.management_form }}
|
||||||
|
<div id="formset">
|
||||||
|
{% for form in ports.forms %}
|
||||||
|
<div class="port">
|
||||||
|
<p>
|
||||||
|
{{ form }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<input class="btn btn-primary btn-sm" role="button" value="Ajouter un port" id="add_one">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||||
|
</form>
|
||||||
|
<script type="text/javascript">
|
||||||
|
var template = `{{ports.empty_form}}`;
|
||||||
|
function add_port(){
|
||||||
|
var new_index = document.getElementsByClassName('port').length;
|
||||||
|
document.getElementById('id_form-TOTAL_FORMS').value =
|
||||||
|
parseInt(document.getElementById('id_form-TOTAL_FORMS').value) + 1;
|
||||||
|
var new_port = document.createElement('div');
|
||||||
|
new_port.className = 'port';
|
||||||
|
new_port.innerHTML = template.replace(/__prefix__/g, new_index);
|
||||||
|
document.getElementById('formset').appendChild(new_port);
|
||||||
|
}
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
document.getElementById("add_one").addEventListener("click", add_port, true);});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
57
machines/templates/machines/index_portlist.html
Normal file
57
machines/templates/machines/index_portlist.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{% extends "machines/sidebar.html" %}
|
||||||
|
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block title %}Configuration de ports{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Liste des configurations de ports</h2>
|
||||||
|
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-portlist' %}"><i class="glyphicon glyphicon-plus"></i>Ajouter une configuration</a>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Nom</th>
|
||||||
|
<th>TCP (entrée)</th>
|
||||||
|
<th>TCP (sortie)</th>
|
||||||
|
<th>UDP (entrée)</th>
|
||||||
|
<th>UDP (sortie)</th>
|
||||||
|
<th>Machines</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
{% for pl in port_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{pl.name}}</td>
|
||||||
|
<td>{% for p in pl.tcp_ports_in %}{{p.show_port}}, {%endfor%}</td>
|
||||||
|
<td>{% for p in pl.tcp_ports_out %}{{p.show_port}}, {%endfor%}</td>
|
||||||
|
<td>{% for p in pl.udp_ports_in %}{{p.show_port}}, {%endfor%}</td>
|
||||||
|
<td>{% for p in pl.udp_ports_out %}{{p.show_port}}, {%endfor%}</td>
|
||||||
|
<td>
|
||||||
|
{% if pl.interface_set.all %}
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="btn btn-default dropdown-toggle" type="button" id="editioninterface" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="editioninterface">
|
||||||
|
{% for interface in pl.interface_set.all %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'users:profil' userid=interface.machine.user.id %}">
|
||||||
|
{{ interface }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<td class="text-right">
|
||||||
|
{% include 'buttons/suppr.html' with href='machines:del-portlist' id=pl.id %}
|
||||||
|
{% include 'buttons/edit.html' with href='machines:edit-portlist' id=pl.id %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{%endfor%}
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -55,4 +55,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
Services (dhcp, dns...)
|
Services (dhcp, dns...)
|
||||||
</a>
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% 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
|
||||||
|
</a>
|
||||||
|
{%endif%}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -92,4 +92,10 @@ urlpatterns = [
|
||||||
url(r'^rest/text/$', views.text, name='text'),
|
url(r'^rest/text/$', views.text, name='text'),
|
||||||
url(r'^rest/zones/$', views.zones, name='zones'),
|
url(r'^rest/zones/$', views.zones, name='zones'),
|
||||||
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
|
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
|
||||||
|
url(r'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'),
|
||||||
|
url(r'^add_portlist/$', views.add_portlist, name='add-portlist'),
|
||||||
|
url(r'^port_config/(?P<pk>[0-9]+)$', views.configure_ports, name='port-config'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -35,20 +35,21 @@ from django.template import Context, RequestContext, loader
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.db.models import ProtectedError
|
from django.db.models import ProtectedError
|
||||||
from django.forms import ValidationError
|
from django.forms import ValidationError, modelformset_factory
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
from machines.serializers import InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
|
from machines.serializers import FullInterfaceSerializer, InterfaceSerializer, TypeSerializer, DomainSerializer, TextSerializer, MxSerializer, ExtensionSerializer, ServiceServersSerializer, NsSerializer
|
||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from reversion.models import Version
|
from reversion.models import Version
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
|
from .forms import NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm, MachineTypeForm, DelMachineTypeForm, ExtensionForm, DelExtensionForm, BaseEditInterfaceForm, BaseEditMachineForm
|
||||||
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, TextForm, DelTextForm, MxForm, DelMxForm, VlanForm, DelVlanForm, ServiceForm, DelServiceForm, NasForm, DelNasForm
|
from .forms import EditIpTypeForm, IpTypeForm, DelIpTypeForm, DomainForm, AliasForm, DelAliasForm, NsForm, DelNsForm, TextForm, DelTextForm, MxForm, DelMxForm, VlanForm, DelVlanForm, ServiceForm, DelServiceForm, NasForm, DelNasForm
|
||||||
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text
|
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 users.models import User
|
from users.models import User
|
||||||
from users.models import all_has_access
|
from users.models import all_has_access
|
||||||
from preferences.models import GeneralOption, OptionalMachine
|
from preferences.models import GeneralOption, OptionalMachine
|
||||||
|
@ -912,6 +913,104 @@ def history(request, object, id):
|
||||||
return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance})
|
return render(request, 're2o/history.html', {'reversions': reversions, 'object': object_instance})
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('cableur')
|
||||||
|
def index_portlist(request):
|
||||||
|
port_list = OuverturePortList.objects.all().order_by('name')
|
||||||
|
return render(request, "machines/index_portlist.html", {'port_list':port_list})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def edit_portlist(request, pk):
|
||||||
|
try:
|
||||||
|
port_list_instance = OuverturePortList.objects.get(pk=pk)
|
||||||
|
except OuverturePortList.DoesNotExist:
|
||||||
|
messages.error(request, "Liste de ports inexistante")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
port_list = EditOuverturePortListForm(request.POST or None, instance=port_list_instance)
|
||||||
|
port_formset = modelformset_factory(
|
||||||
|
OuverturePort,
|
||||||
|
fields=('begin','end','protocole','io'),
|
||||||
|
extra=0,
|
||||||
|
can_delete=True,
|
||||||
|
min_num=1,
|
||||||
|
validate_min=True,
|
||||||
|
)(request.POST or None, queryset=port_list_instance.ouvertureport_set.all())
|
||||||
|
if port_list.is_valid() and port_formset.is_valid():
|
||||||
|
pl = port_list.save()
|
||||||
|
instances = port_formset.save(commit=False)
|
||||||
|
for to_delete in port_formset.deleted_objects:
|
||||||
|
to_delete.delete()
|
||||||
|
for port in instances:
|
||||||
|
port.port_list = pl
|
||||||
|
port.save()
|
||||||
|
messages.success(request, "Liste de ports modifiée")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
return form({'port_list' : port_list, 'ports' : port_formset}, 'machines/edit_portlist.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def del_portlist(request, pk):
|
||||||
|
try:
|
||||||
|
port_list_instance = OuverturePortList.objects.get(pk=pk)
|
||||||
|
except OuverturePortList.DoesNotExist:
|
||||||
|
messages.error(request, "Liste de ports inexistante")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
if port_list_instance.interface_set.all():
|
||||||
|
messages.error(request, "Cette liste de ports est utilisée")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
port_list_instance.delete()
|
||||||
|
messages.success(request, "La liste de ports a été supprimée")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('bureau')
|
||||||
|
def add_portlist(request):
|
||||||
|
port_list = EditOuverturePortListForm(request.POST or None)
|
||||||
|
port_formset = modelformset_factory(
|
||||||
|
OuverturePort,
|
||||||
|
fields=('begin','end','protocole','io'),
|
||||||
|
extra=0,
|
||||||
|
can_delete=True,
|
||||||
|
min_num=1,
|
||||||
|
validate_min=True,
|
||||||
|
)(request.POST or None, queryset=OuverturePort.objects.none())
|
||||||
|
if port_list.is_valid() and port_formset.is_valid():
|
||||||
|
pl = port_list.save()
|
||||||
|
instances = port_formset.save(commit=False)
|
||||||
|
for to_delete in port_formset.deleted_objects:
|
||||||
|
to_delete.delete()
|
||||||
|
for port in instances:
|
||||||
|
port.port_list = pl
|
||||||
|
port.save()
|
||||||
|
messages.success(request, "Liste de ports créée")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
return form({'port_list' : port_list, 'ports' : port_formset}, 'machines/edit_portlist.html', request)
|
||||||
|
port_list = EditOuverturePortListForm(request.POST or None)
|
||||||
|
if port_list.is_valid():
|
||||||
|
port_list.save()
|
||||||
|
messages.success(request, "Liste de ports créée")
|
||||||
|
return redirect("/machines/index_portlist/")
|
||||||
|
return form({'machineform' : port_list}, 'machines/machine.html', request)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@permission_required('cableur')
|
||||||
|
def configure_ports(request, pk):
|
||||||
|
try:
|
||||||
|
interface_instance = Interface.objects.get(pk=pk)
|
||||||
|
except Interface.DoesNotExist:
|
||||||
|
messages.error(request, u"Interface inexistante" )
|
||||||
|
return redirect("/machines")
|
||||||
|
if not interface_instance.may_have_port_open():
|
||||||
|
messages.error(request, "L'ip de cette interface n'est pas publique ou non assignée")
|
||||||
|
return redirect("/machines")
|
||||||
|
interface = EditOuverturePortConfigForm(request.POST or None, instance=interface_instance)
|
||||||
|
if interface.is_valid():
|
||||||
|
interface.save()
|
||||||
|
messages.success(request, "Configuration des ports mise à jour.")
|
||||||
|
return redirect("/machines/")
|
||||||
|
return form({'interfaceform' : interface}, 'machines/machine.html', request)
|
||||||
|
|
||||||
""" Framework Rest """
|
""" Framework Rest """
|
||||||
|
|
||||||
class JSONResponse(HttpResponse):
|
class JSONResponse(HttpResponse):
|
||||||
|
@ -928,6 +1027,14 @@ def mac_ip_list(request):
|
||||||
seria = InterfaceSerializer(interfaces, many=True)
|
seria = InterfaceSerializer(interfaces, many=True)
|
||||||
return seria.data
|
return seria.data
|
||||||
|
|
||||||
|
@csrf_exempt
|
||||||
|
@login_required
|
||||||
|
@permission_required('serveur')
|
||||||
|
def full_mac_ip_list(request):
|
||||||
|
interfaces = all_active_assigned_interfaces()
|
||||||
|
seria = FullInterfaceSerializer(interfaces, many=True)
|
||||||
|
return seria.data
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('serveur')
|
@permission_required('serveur')
|
||||||
|
@ -987,7 +1094,7 @@ def mac_ip(request):
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('serveur')
|
@permission_required('serveur')
|
||||||
def mac_ip_dns(request):
|
def mac_ip_dns(request):
|
||||||
seria = mac_ip_list(request)
|
seria = full_mac_ip_list(request)
|
||||||
return JSONResponse(seria)
|
return JSONResponse(seria)
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
|
|
20
preferences/migrations/0020_optionalmachine_ipv6.py
Normal file
20
preferences/migrations/0020_optionalmachine_ipv6.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.10.7 on 2017-10-02 16:14
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('preferences', '0019_remove_optionaltopologie_mac_autocapture'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='optionalmachine',
|
||||||
|
name='ipv6',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
|
@ -45,6 +45,7 @@ class OptionalMachine(models.Model):
|
||||||
password_machine = models.BooleanField(default=False)
|
password_machine = models.BooleanField(default=False)
|
||||||
max_lambdauser_interfaces = models.IntegerField(default=10)
|
max_lambdauser_interfaces = models.IntegerField(default=10)
|
||||||
max_lambdauser_aliases = models.IntegerField(default=10)
|
max_lambdauser_aliases = models.IntegerField(default=10)
|
||||||
|
ipv6 = models.BooleanField(default=False)
|
||||||
|
|
||||||
class OptionalTopologie(models.Model):
|
class OptionalTopologie(models.Model):
|
||||||
PRETTY_NAME = "Options topologie"
|
PRETTY_NAME = "Options topologie"
|
||||||
|
|
|
@ -72,7 +72,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<tr>
|
<tr>
|
||||||
<th>Alias dns autorisé par utilisateur</th>
|
<th>Alias dns autorisé par utilisateur</th>
|
||||||
<td>{{ machineoptions.max_lambdauser_aliases }}</td>
|
<td>{{ machineoptions.max_lambdauser_aliases }}</td>
|
||||||
</tr>
|
<th>Support de l'ipv6</th>
|
||||||
|
<td>{{ machineoptions.ipv6 }}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h4>Préférences topologie</h4>
|
<h4>Préférences topologie</h4>
|
||||||
{% if is_bureau %}
|
{% if is_bureau %}
|
||||||
|
|
|
@ -23,10 +23,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from machines.models import Interface, Machine
|
from machines.models import Interface, Machine
|
||||||
from preferences.models import GeneralOption
|
from preferences.models import GeneralOption, OptionalMachine
|
||||||
|
|
||||||
def context_user(request):
|
def context_user(request):
|
||||||
general_options, created = GeneralOption.objects.get_or_create()
|
general_options, created = GeneralOption.objects.get_or_create()
|
||||||
|
machine_options, created = OptionalMachine.objects.get_or_create()
|
||||||
user = request.user
|
user = request.user
|
||||||
if user.is_authenticated():
|
if user.is_authenticated():
|
||||||
interfaces = user.user_interfaces()
|
interfaces = user.user_interfaces()
|
||||||
|
@ -54,4 +55,5 @@ def context_user(request):
|
||||||
'is_admin' : is_admin,
|
'is_admin' : is_admin,
|
||||||
'interfaces': interfaces,
|
'interfaces': interfaces,
|
||||||
'site_name': general_options.site_name,
|
'site_name': general_options.site_name,
|
||||||
|
'ipv6_enabled' : machine_options.ipv6,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue