8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-12-26 00:43:46 +00:00
This commit is contained in:
Gabriel Detraz 2017-11-16 02:33:57 +01:00 committed by root
parent 2c08510e3d
commit 055249d1cd
11 changed files with 351 additions and 8 deletions

View file

@ -27,8 +27,19 @@ from django.contrib import admin
from reversion.admin import VersionAdmin from reversion.admin import VersionAdmin
from .models import IpType, Machine, MachineType, Domain, IpList, Interface from .models import IpType, Machine, MachineType, Domain, IpList, Interface
from .models import Extension, SOA, Mx, Ns, Vlan, Txt, Nas, Service from .models import (
from .models import OuverturePort, OuverturePortList Extension,
SOA,
Mx,
Ns,
Vlan,
Txt,
Srv,
Nas,
Service,
OuverturePort,
OuverturePortList
)
class MachineAdmin(VersionAdmin): class MachineAdmin(VersionAdmin):
@ -67,6 +78,10 @@ class TxtAdmin(VersionAdmin):
pass pass
class SrvAdmin(VersionAdmin):
pass
class NasAdmin(VersionAdmin): class NasAdmin(VersionAdmin):
pass pass
@ -103,6 +118,7 @@ admin.site.register(SOA, SOAAdmin)
admin.site.register(Mx, MxAdmin) admin.site.register(Mx, MxAdmin)
admin.site.register(Ns, NsAdmin) admin.site.register(Ns, NsAdmin)
admin.site.register(Txt, TxtAdmin) admin.site.register(Txt, TxtAdmin)
admin.site.register(Srv, SrvAdmin)
admin.site.register(IpList, IpListAdmin) admin.site.register(IpList, IpListAdmin)
admin.site.register(Interface, InterfaceAdmin) admin.site.register(Interface, InterfaceAdmin)
admin.site.register(Domain, DomainAdmin) admin.site.register(Domain, DomainAdmin)

View file

@ -51,6 +51,7 @@ from .models import (
Ns, Ns,
Service, Service,
Vlan, Vlan,
Srv,
Nas, Nas,
IpType, IpType,
OuverturePortList, OuverturePortList,
@ -381,6 +382,26 @@ class DelTxtForm(Form):
) )
class SrvForm(ModelForm):
"""Ajout d'un srv pour une zone"""
class Meta:
model = Srv
fields = '__all__'
def __init__(self, *args, **kwargs):
prefix = kwargs.pop('prefix', self.Meta.model.__name__)
super(SrvForm, self).__init__(*args, prefix=prefix, **kwargs)
class DelSrvForm(Form):
"""Suppression d'un ou plusieurs Srv"""
srv = forms.ModelMultipleChoiceField(
queryset=Srv.objects.all(),
label="Enregistrements Srv actuels",
widget=forms.CheckboxSelectMultiple
)
class NasForm(ModelForm): class NasForm(ModelForm):
"""Ajout d'un type de nas (machine d'authentification, """Ajout d'un type de nas (machine d'authentification,
swicths, bornes...)""" swicths, bornes...)"""

View file

@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-11-16 00:10
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0065_auto_20171115_1514'),
]
operations = [
migrations.CreateModel(
name='Srv',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('service', models.CharField(max_length=31)),
('protocole', models.CharField(choices=[('TCP', 'TCP'), ('UDP', 'UDP')], default='TCP', max_length=3)),
('ttl', models.PositiveIntegerField(default=172800, help_text='Time To Live')),
('priority', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])),
('weight', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])),
('port', models.PositiveIntegerField(validators=[django.core.validators.MaxValueValidator(65535)])),
('extension', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Extension')),
('target', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='machines.Domain')),
],
),
]

View file

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-11-16 00:52
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('machines', '0066_srv'),
]
operations = [
migrations.AlterField(
model_name='srv',
name='port',
field=models.PositiveIntegerField(help_text='Port (tcp/udp)', validators=[django.core.validators.MaxValueValidator(65535)]),
),
migrations.AlterField(
model_name='srv',
name='priority',
field=models.PositiveIntegerField(help_text="La priorité du serveur cible (valeur entière non négative, plus elle est faible, plus ce serveur sera utilisé s'il est disponible)", validators=[django.core.validators.MaxValueValidator(65535)]),
),
migrations.AlterField(
model_name='srv',
name='target',
field=models.ForeignKey(help_text='Serveur cible', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'),
),
migrations.AlterField(
model_name='srv',
name='weight',
field=models.PositiveIntegerField(help_text='Poids relatif pour les enregistrements de même priorité (valeur entière de 0 à 65535)', validators=[django.core.validators.MaxValueValidator(65535)]),
),
]

View file

@ -311,18 +311,24 @@ class Extension(models.Model):
l'utiliser, associé à un origin (ip d'origine)""" l'utiliser, associé à un origin (ip d'origine)"""
PRETTY_NAME = "Extensions dns" PRETTY_NAME = "Extensions dns"
name = models.CharField(max_length=255, unique=True) name = models.CharField(
max_length=255,
unique=True,
help_text="Nom de la zone, doit commencer par un point (.example.org)"
)
need_infra = models.BooleanField(default=False) need_infra = models.BooleanField(default=False)
origin = models.OneToOneField( origin = models.OneToOneField(
'IpList', 'IpList',
on_delete=models.PROTECT, on_delete=models.PROTECT,
blank=True, blank=True,
null=True null=True,
help_text="Enregistrement A associé à la zone"
) )
origin_v6 = models.GenericIPAddressField( origin_v6 = models.GenericIPAddressField(
protocol='IPv6', protocol='IPv6',
null=True, null=True,
blank=True blank=True,
help_text="Enregistremen AAAA associé à la zone"
) )
soa = models.ForeignKey( soa = models.ForeignKey(
'SOA', 'SOA',
@ -345,6 +351,11 @@ class Extension(models.Model):
def __str__(self): def __str__(self):
return self.name return self.name
def clean(self):
if self.name and self.name[0] != '.':
raise ValidationError("Une extension doit commencer par un point")
super(Extension, self).clean(*args, **kwargs)
class Mx(models.Model): class Mx(models.Model):
""" Entrées des MX. Enregistre la zone (extension) associée et la """ Entrées des MX. Enregistre la zone (extension) associée et la
@ -400,6 +411,61 @@ class Txt(models.Model):
return str(self.field1).ljust(15) + " IN TXT " + str(self.field2) return str(self.field1).ljust(15) + " IN TXT " + str(self.field2)
class Srv(models.Model):
PRETTY_NAME = "Enregistrement Srv"
TCP = 'TCP'
UDP = 'UDP'
service = models.CharField(max_length=31)
protocole = models.CharField(
max_length=3,
choices=(
(TCP, 'TCP'),
(UDP, 'UDP'),
),
default=TCP,
)
extension = models.ForeignKey('Extension', on_delete=models.PROTECT)
ttl = models.PositiveIntegerField(
default=172800, # 2 days
help_text='Time To Live'
)
priority = models.PositiveIntegerField(
validators=[MaxValueValidator(65535)],
help_text="La priorité du serveur cible (valeur entière non négative,\
plus elle est faible, plus ce serveur sera utilisé s'il est disponible)"
)
weight = models.PositiveIntegerField(
validators=[MaxValueValidator(65535)],
help_text="Poids relatif pour les enregistrements de même priorité\
(valeur entière de 0 à 65535)"
)
port = models.PositiveIntegerField(
validators=[MaxValueValidator(65535)],
help_text="Port (tcp/udp)"
)
target = models.ForeignKey(
'Domain',
on_delete=models.PROTECT,
help_text="Serveur cible"
)
def __str__(self):
return str(self.service) + ' ' + str(self.protocole) + ' ' +\
str(self.extension) + ' ' + str(self.priority) +\
' ' + str(self.weight) + str(self.port) + str(self.target)
@cached_property
def dns_entry(self):
"""Renvoie l'enregistrement SRV complet pour le fichier de zone"""
return str(self.service) + '._' + str(self.protocole).lower() +\
str(self.extension) + '. ' + str(self.ttl) + ' IN SRV ' +\
str(self.priority) + ' ' + str(self.weight) + ' ' +\
str(self.port) + ' ' + str(self.target) + '.'
class Interface(models.Model): class Interface(models.Model):
""" Une interface. Objet clef de l'application machine : """ Une interface. Objet clef de l'application machine :
- une address mac unique. Possibilité de la rendre unique avec le - une address mac unique. Possibilité de la rendre unique avec le

View file

@ -33,6 +33,7 @@ from machines.models import (
Domain, Domain,
Txt, Txt,
Mx, Mx,
Srv,
Service_link, Service_link,
Ns, Ns,
OuverturePortList, OuverturePortList,
@ -212,6 +213,32 @@ class TxtSerializer(serializers.ModelSerializer):
return str(obj.dns_entry) return str(obj.dns_entry)
class SrvSerializer(serializers.ModelSerializer):
"""Serialisation d'un srv : zone cible et l'entrée txt"""
extension = serializers.SerializerMethodField('get_extension_name')
srv_entry = serializers.SerializerMethodField('get_srv_name')
class Meta:
model = Srv
fields = (
'service',
'protocole',
'extension',
'ttl',
'priority',
'weight',
'port',
'target',
'srv_entry'
)
def get_extension_name(self, obj):
return str(obj.extension.name)
def get_srv_name(self, obj):
return str(obj.dns_entry)
class NsSerializer(serializers.ModelSerializer): class NsSerializer(serializers.ModelSerializer):
"""Serialisation d'un NS : la zone, l'entrée ns complète et le serveur """Serialisation d'un NS : la zone, l'entrée ns complète et le serveur
ns sont évalués à part""" ns sont évalués à part"""

View file

@ -0,0 +1,60 @@
{% 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 %}
<table class="table table-striped">
<thead>
<tr>
<th>Service</th>
<th>Protocole</th>
<th>Extension</th>
<th>TTL</th>
<th>Priorité</th>
<th>Poids</th>
<th>Port</th>
<th>Cible</th>
<th></th>
<th></th>
</tr>
</thead>
{% for srv in srv_list %}
<tr>
<td>{{ srv.service }}</td>
<td>{{ srv.protocole }}</td>
<td>{{ srv.extension }}</td>
<td>{{ srv.ttl }}</td>
<td>{{ srv.prority }}</td>
<td>{{ srv.weight }}</td>
<td>{{ srv.port }}</td>
<td>{{ srv.target }}</td>
<td class="text-right">
{% if is_infra %}
{% include 'buttons/edit.html' with href='machines:edit-srv' id=srv.id %}
{% endif %}
{% include 'buttons/history.html' with href='machines:history' name='srv' id=srv.id %}
</td>
</tr>
{% endfor %}
</table>

View file

@ -59,6 +59,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement TXT</a> <a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-txt' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement TXT</a>
{% endif %} {% endif %}
{% include "machines/aff_txt.html" with txt_list=txt_list %} {% include "machines/aff_txt.html" with txt_list=txt_list %}
<h2>Liste des enregistrements SRV</h2>
{% if is_infra %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'machines:add-srv' %}"><i class="glyphicon glyphicon-plus"></i> Ajouter un enregistrement SRV</a>
<a class="btn btn-danger btn-sm" role="button" href="{% url 'machines:del-srv' %}"><i class="glyphicon glyphicon-trash"></i> Supprimer un enregistrement SRV</a>
{% endif %}
{% include "machines/aff_srv.html" with srv_list=srv_list %}
<br /> <br />
<br /> <br />
<br /> <br />

View file

@ -57,6 +57,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if txtform %} {% if txtform %}
{% bootstrap_form_errors txtform %} {% bootstrap_form_errors txtform %}
{% endif %} {% endif %}
{% if srvform %}
{% bootstrap_form_errors srvform %}
{% endif %}
{% if aliasform %} {% if aliasform %}
{% bootstrap_form_errors aliasform %} {% bootstrap_form_errors aliasform %}
{% endif %} {% endif %}
@ -116,6 +119,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<h3>Enregistrement TXT</h3> <h3>Enregistrement TXT</h3>
{% bootstrap_form txtform %} {% bootstrap_form txtform %}
{% endif %} {% endif %}
{% if srvform %}
<h3>Enregistrement SRV</h3>
{% massive_bootstrap_form srvform 'target' %}
{% endif %}
{% if aliasform %} {% if aliasform %}
<h3>Alias</h3> <h3>Alias</h3>
{% bootstrap_form aliasform %} {% bootstrap_form aliasform %}

View file

@ -56,6 +56,9 @@ urlpatterns = [
url(r'^add_ns/$', views.add_ns, name='add-ns'), url(r'^add_ns/$', views.add_ns, name='add-ns'),
url(r'^edit_ns/(?P<nsid>[0-9]+)$', views.edit_ns, name='edit-ns'), url(r'^edit_ns/(?P<nsid>[0-9]+)$', views.edit_ns, name='edit-ns'),
url(r'^del_ns/$', views.del_ns, name='del-ns'), url(r'^del_ns/$', views.del_ns, name='del-ns'),
url(r'^add_srv/$', views.add_srv, name='add-srv'),
url(r'^edit_srv/(?P<srvid>[0-9]+)$', views.edit_srv, name='edit-srv'),
url(r'^del_srv/$', views.del_srv, name='del-srv'),
url(r'^index_extension/$', views.index_extension, name='index-extension'), url(r'^index_extension/$', views.index_extension, name='index-extension'),
url(r'^add_alias/(?P<interfaceid>[0-9]+)$', views.add_alias, name='add-alias'), url(r'^add_alias/(?P<interfaceid>[0-9]+)$', views.add_alias, name='add-alias'),
url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'), url(r'^edit_alias/(?P<aliasid>[0-9]+)$', views.edit_alias, name='edit-alias'),
@ -81,6 +84,7 @@ urlpatterns = [
url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>mx)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>ns)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>txt)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>srv)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>iptype)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>iptype)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>alias)/(?P<id>[0-9]+)$', views.history, name='history'),
url(r'^history/(?P<object>vlan)/(?P<id>[0-9]+)$', views.history, name='history'), url(r'^history/(?P<object>vlan)/(?P<id>[0-9]+)$', views.history, name='history'),
@ -95,6 +99,7 @@ urlpatterns = [
url(r'^rest/mx/$', views.mx, name='mx'), url(r'^rest/mx/$', views.mx, name='mx'),
url(r'^rest/ns/$', views.ns, name='ns'), url(r'^rest/ns/$', views.ns, name='ns'),
url(r'^rest/txt/$', views.txt, name='txt'), url(r'^rest/txt/$', views.txt, name='txt'),
url(r'^rest/srv/$', views.srv, name='srv'),
url(r'^rest/zones/$', views.zones, name='zones'), url(r'^rest/zones/$', views.zones, name='zones'),
url(r'^rest/service_servers/$', views.service_servers, name='service-servers'), url(r'^rest/service_servers/$', views.service_servers, name='service-servers'),
url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'), url(r'^rest/ouverture_ports/$', views.ouverture_ports, name='ouverture-ports'),

View file

@ -49,6 +49,7 @@ from machines.serializers import ( FullInterfaceSerializer,
TypeSerializer, TypeSerializer,
DomainSerializer, DomainSerializer,
TxtSerializer, TxtSerializer,
SrvSerializer,
MxSerializer, MxSerializer,
ExtensionSerializer, ExtensionSerializer,
ServiceServersSerializer, ServiceServersSerializer,
@ -91,7 +92,9 @@ from .forms import (
ServiceForm, ServiceForm,
DelServiceForm, DelServiceForm,
NasForm, NasForm,
DelNasForm DelNasForm,
SrvForm,
DelSrvForm,
) )
from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm
from .models import ( from .models import (
@ -110,8 +113,9 @@ from .models import (
Vlan, Vlan,
Nas, Nas,
Txt, Txt,
Srv,
OuverturePortList, OuverturePortList,
OuverturePort OuverturePort,
) )
from users.models import User from users.models import User
from preferences.models import GeneralOption, OptionalMachine from preferences.models import GeneralOption, OptionalMachine
@ -752,6 +756,54 @@ def del_txt(request):
return redirect(reverse('machines:index-extension')) return redirect(reverse('machines:index-extension'))
return form({'txtform': txt}, 'machines/machine.html', request) return form({'txtform': txt}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def add_srv(request):
srv = SrvForm(request.POST or None)
if srv.is_valid():
with transaction.atomic(), reversion.create_revision():
srv.save()
reversion.set_user(request.user)
reversion.set_comment("Création")
messages.success(request, "Cet enregistrement srv a été ajouté")
return redirect(reverse('machines:index-extension'))
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def edit_srv(request, srvid):
try:
srv_instance = Srv.objects.get(pk=srvid)
except Srv.DoesNotExist:
messages.error(request, u"Entrée inexistante" )
return redirect(reverse('machines:index-extension'))
srv = SrvForm(request.POST or None, instance=srv_instance)
if srv.is_valid():
with transaction.atomic(), reversion.create_revision():
srv.save()
reversion.set_user(request.user)
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in srv.changed_data))
messages.success(request, "Srv modifié")
return redirect(reverse('machines:index-extension'))
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required
@permission_required('infra')
def del_srv(request):
srv = DelSrvForm(request.POST or None)
if srv.is_valid():
srv_dels = srv.cleaned_data['srv']
for srv_del in srv_dels:
try:
with transaction.atomic(), reversion.create_revision():
srv_del.delete()
reversion.set_user(request.user)
messages.success(request, "L'srv a été supprimée")
except ProtectedError:
messages.error(request, "Erreur le Srv suivant %s ne peut être supprimé" % srv_del)
return redirect(reverse('machines:index-extension'))
return form({'srvform': srv}, 'machines/machine.html', request)
@login_required @login_required
def add_alias(request, interfaceid): def add_alias(request, interfaceid):
try: try:
@ -1046,7 +1098,8 @@ def index_extension(request):
mx_list = Mx.objects.order_by('zone').select_related('zone').select_related('name__extension') mx_list = Mx.objects.order_by('zone').select_related('zone').select_related('name__extension')
ns_list = Ns.objects.order_by('zone').select_related('zone').select_related('ns__extension') ns_list = Ns.objects.order_by('zone').select_related('zone').select_related('ns__extension')
txt_list = Txt.objects.all().select_related('zone') txt_list = Txt.objects.all().select_related('zone')
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'txt_list' : txt_list}) srv_list = Srv.objects.all().select_related('extension').select_related('target__extension')
return render(request, 'machines/index_extension.html', {'extension_list':extension_list, 'soa_list': soa_list, 'mx_list': mx_list, 'ns_list': ns_list, 'txt_list' : txt_list, 'srv_list': srv_list})
@login_required @login_required
def index_alias(request, interfaceid): def index_alias(request, interfaceid):
@ -1145,6 +1198,12 @@ def history(request, object, id):
except Txt.DoesNotExist: except Txt.DoesNotExist:
messages.error(request, "Txt inexistant") messages.error(request, "Txt inexistant")
return redirect(reverse('machines:index')) return redirect(reverse('machines:index'))
elif object == 'srv' and request.user.has_perms(('cableur',)):
try:
object_instance = Srv.objects.get(pk=id)
except Srv.DoesNotExist:
messages.error(request, "Srv inexistant")
return redirect(reverse('machines:index'))
elif object == 'ns' and request.user.has_perms(('cableur',)): elif object == 'ns' and request.user.has_perms(('cableur',)):
try: try:
object_instance = Ns.objects.get(pk=id) object_instance = Ns.objects.get(pk=id)
@ -1343,6 +1402,14 @@ def txt(request):
seria = TxtSerializer(txt, many=True) seria = TxtSerializer(txt, many=True)
return JSONResponse(seria.data) return JSONResponse(seria.data)
@csrf_exempt
@login_required
@permission_required('serveur')
def srv(request):
srv = Srv.objects.all().select_related('extension').select_related('target__extension')
seria = SrvSerializer(srv, many=True)
return JSONResponse(seria.data)
@csrf_exempt @csrf_exempt
@login_required @login_required
@permission_required('serveur') @permission_required('serveur')