From efcb3e391ab5e198e438d08241b0b3067835bf6c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Mon, 29 Jan 2018 03:53:49 +0100 Subject: [PATCH 01/10] =?UTF-8?q?Nouvelle=20gestion=20des=20ipv6,=20table?= =?UTF-8?q?=20s=C3=A9par=C3=A9e=20et=20mode=20dhcpv6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- machines/forms.py | 12 ++ .../migrations/0073_auto_20180128_2203.py | 29 ++++ .../migrations/0074_auto_20180129_0352.py | 19 +++ machines/models.py | 126 +++++++++++++++++- machines/templates/machines/aff_ipv6.html | 51 +++++++ machines/templates/machines/aff_machines.html | 9 +- machines/templates/machines/index_ipv6.html | 41 ++++++ machines/templates/machines/machine.html | 7 + machines/urls.py | 4 + machines/views.py | 63 ++++++++- .../migrations/0028_auto_20180128_2203.py | 24 ++++ preferences/models.py | 20 ++- .../preferences/display_preferences.html | 2 +- re2o/templatetags/acl.py | 1 + re2o/views.py | 1 + 15 files changed, 401 insertions(+), 8 deletions(-) create mode 100644 machines/migrations/0073_auto_20180128_2203.py create mode 100644 machines/migrations/0074_auto_20180129_0352.py create mode 100644 machines/templates/machines/aff_ipv6.html create mode 100644 machines/templates/machines/index_ipv6.html create mode 100644 preferences/migrations/0028_auto_20180128_2203.py diff --git a/machines/forms.py b/machines/forms.py index 05fd6999..5dc9dd8b 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -57,6 +57,7 @@ from .models import ( Nas, IpType, OuverturePortList, + Ipv6List, ) @@ -274,6 +275,17 @@ class DelExtensionForm(Form): self.fields['extensions'].queryset = Extension.objects.all() +class Ipv6ListForm(FieldPermissionFormMixin, ModelForm): + """Gestion des ipv6 d'une machine""" + class Meta: + model = Ipv6List + fields = ['ipv6', 'slaac_ip'] + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop('prefix', self.Meta.model.__name__) + super(Ipv6ListForm, self).__init__(*args, prefix=prefix, **kwargs) + + class SOAForm(ModelForm): """Ajout et edition d'un SOA""" class Meta: diff --git a/machines/migrations/0073_auto_20180128_2203.py b/machines/migrations/0073_auto_20180128_2203.py new file mode 100644 index 00000000..b09b9c47 --- /dev/null +++ b/machines/migrations/0073_auto_20180128_2203.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-28 21:03 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0072_auto_20180108_1822'), + ] + + operations = [ + migrations.CreateModel( + name='Ipv6List', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ipv6', models.GenericIPAddressField(protocol='IPv6', unique=True)), + ('slaac_ip', models.BooleanField(default=False)), + ('interface', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='machines.Interface')), + ], + ), + migrations.AlterUniqueTogether( + name='ipv6list', + unique_together=set([('interface', 'slaac_ip')]), + ), + ] diff --git a/machines/migrations/0074_auto_20180129_0352.py b/machines/migrations/0074_auto_20180129_0352.py new file mode 100644 index 00000000..298f2a8e --- /dev/null +++ b/machines/migrations/0074_auto_20180129_0352.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-29 02:52 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0073_auto_20180128_2203'), + ] + + operations = [ + migrations.AlterModelOptions( + name='ipv6list', + options={'permissions': (('view_ipv6list', 'Peut voir un objet ipv6'), ('change_ipv6list_slaac_ip', 'Peut changer la valeur slaac sur une ipv6'))}, + ), + ] diff --git a/machines/models.py b/machines/models.py index 59965983..53e73ae6 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1188,7 +1188,7 @@ class Interface(FieldPermissionModelMixin,models.Model): return machine.active and user.has_access() @cached_property - def ipv6_object(self): + def ipv6_slaac(self): """ Renvoie un objet type ipv6 à partir du prefix associé à l'iptype parent""" if self.type.ip_type.prefix_v6: @@ -1198,10 +1198,29 @@ class Interface(FieldPermissionModelMixin,models.Model): else: return None - @cached_property + def sync_ipv6_slaac(self): + """Cree, mets à jour et supprime si il y a lieu l'ipv6 slaac associée + à la machine + Sans prefixe ipv6, on return + Si l'ip slaac n'est pas celle qu'elle devrait être, on maj""" + ipv6_slaac = self.ipv6_slaac + if not ipv6_slaac: + return + ipv6_object = Ipv6List.objects.filter(interface=self, slaac_ip=True).first() + if not ipv6_object: + ipv6_object = Ipv6List(interface=self, slaac_ip=True) + if ipv6_object.ipv6 != str(ipv6_slaac): + ipv6_object.ipv6 = str(ipv6_slaac) + ipv6_object.save() + def ipv6(self): - """ Renvoie l'ipv6 en str. Mise en cache et propriété de l'objet""" - return str(self.ipv6_object) + """ Renvoie le queryset de la liste des ipv6 + On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)""" + machine_options, _created = preferences.models.OptionalMachine.objects.get_or_create() + if machine_options.ipv6_mode == 'SLAAC': + return Ipv6List.objects.filter(interface=self) + else: + return Ipv6List.objects.filter(interface=self, slaac_ip=False) def mac_bare(self): """ Formatage de la mac type mac_bare""" @@ -1365,6 +1384,104 @@ class Interface(FieldPermissionModelMixin,models.Model): return self.ipv4 and not self.has_private_ip() +class Ipv6List(FieldPermissionModelMixin, models.Model): + PRETTY_NAME = 'Enregistrements Ipv6 des machines' + + ipv6 = models.GenericIPAddressField( + protocol='IPv6', + unique=True + ) + interface = models.ForeignKey('Interface', on_delete=models.CASCADE) + slaac_ip = models.BooleanField(default=False) + + class Meta: + unique_together = (("interface", "slaac_ip"),) + permissions = ( + ("view_ipv6list", "Peut voir un objet ipv6"), + ("change_ipv6list_slaac_ip", "Peut changer la valeur slaac sur une ipv6"), + ) + + def get_instance(ipv6listid, *args, **kwargs): + """Récupère une instance + :param interfaceid: Instance id à trouver + :return: Une instance interface évidemment""" + return Ipv6List.objects.get(pk=ipv6listid) + + def can_create(user_request, interfaceid, *args, **kwargs): + """Verifie que l'user a les bons droits infra pour créer + une ipv6, ou possède l'interface associée + :param interfaceid: Id de l'interface associée à cet objet domain + :param user_request: instance utilisateur qui fait la requête + :return: soit True, soit False avec la raison de l'échec""" + try: + interface = Interface.objects.get(pk=interfaceid) + except Interface.DoesNotExist: + return False, u"Interface inexistante" + if not user_request.has_perm('machines.add_ipv6list'): + if interface.machine.user != user_request: + return False, u"Vous ne pouvez pas ajouter un alias à une\ + machine d'un autre user que vous sans droit" + return True, None + + @staticmethod + def can_change_slaac_ip(user_request, *args, **kwargs): + return user_request.has_perm('machines.change_ipv6list_slaac_ip'), "Droit requis pour changer la valeur slaac ip" + + def can_edit(self, user_request, *args, **kwargs): + """Verifie que l'user a les bons droits infra pour editer + cette instance interface, ou qu'elle lui appartient + :param self: Instance interface à editer + :param user_request: Utilisateur qui fait la requête + :return: soit True, soit False avec la raison de l'échec""" + if self.interface.machine.user != user_request: + if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]: + return False, u"Vous ne pouvez pas éditer une machine\ + d'un autre user que vous sans droit" + return True, None + + def can_delete(self, user_request, *args, **kwargs): + """Verifie que l'user a les bons droits delete object pour del + cette instance interface, ou qu'elle lui appartient + :param self: Instance interface à del + :param user_request: Utilisateur qui fait la requête + :return: soit True, soit False avec la raison de l'échec""" + if self.interface.machine.user != user_request: + if not user_request.has_perm('machines.change_ipv6list') or not self.interface.machine.user.can_edit(user_request, *args, **kwargs)[0]: + return False, u"Vous ne pouvez pas éditer une machine\ + d'un autre user que vous sans droit" + return True, None + + def can_view_all(user_request, *args, **kwargs): + """Vérifie qu'on peut bien afficher l'ensemble des interfaces, + droit particulier view objet correspondant + :param user_request: instance user qui fait l'edition + :return: True ou False avec la raison de l'échec le cas échéant""" + if not user_request.has_perm('machines.view_ipv6list'): + return False, u"Vous n'avez pas le droit de voir des machines autre\ + que les vôtres" + return True, None + + def can_view(self, user_request, *args, **kwargs): + """Vérifie qu'on peut bien voir cette instance particulière avec + droit view objet ou qu'elle appartient à l'user + :param self: instance interface à voir + :param user_request: instance user qui fait l'edition + :return: True ou False avec la raison de l'échec le cas échéant""" + if not user_request.has_perm('machines.view_ipv6list') and self.interface.machine.user != user_request: + return False, u"Vous n'avez pas le droit de voir des machines autre\ + que les vôtres" + return True, None + + def __init__(self, *args, **kwargs): + super(Ipv6List, self).__init__(*args, **kwargs) + self.field_permissions = { + 'slaac_ip' : self.can_change_slaac_ip, + } + + def __str__(self): + return str(self.ipv6) + + 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 @@ -2039,6 +2156,7 @@ def interface_post_save(sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une interface""" interface = kwargs['instance'] + interface.sync_ipv6_slaac() user = interface.machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) # Regen services diff --git a/machines/templates/machines/aff_ipv6.html b/machines/templates/machines/aff_ipv6.html new file mode 100644 index 00000000..b6efa3f0 --- /dev/null +++ b/machines/templates/machines/aff_ipv6.html @@ -0,0 +1,51 @@ +{% 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 acl %} + + + + + + + + + + {% for ipv6 in ipv6_list %} + + + + + + {% endfor %} +
Ipv6Slaac
{{ ipv6.ipv6 }}{{ ipv6.slaac_ip }} + {% can_edit ipv6 %} + {% include 'buttons/edit.html' with href='machines:edit-ipv6list' id=ipv6.id %} + {% acl_end %} + {% can_delete ipv6 %} + {% include 'buttons/suppr.html' with href='machines:del-ipv6list' id=ipv6.id %} + {% acl_end %} + {% include 'buttons/history.html' with href='machines:history' name='ipv6list' id=ipv6.id %} +
+ diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 124aaf4d..ac71dc30 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -83,7 +83,7 @@ with this program; if not, write to the Free Software Foundation, Inc., IPv4 {{ interface.ipv4 }}
{% if ipv6_enabled and interface.ipv6 != 'None'%} - IPv6 {{ interface.ipv6 }} + IPv6 {{ interface.ipv6|join:"," }} {% endif %} @@ -110,6 +110,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} + {% can_create Ipv6List interface.id %} +
  • + + Gerer les ipv6 + +
  • + {% acl_end %} {% can_create OuverturePortList %}
  • diff --git a/machines/templates/machines/index_ipv6.html b/machines/templates/machines/index_ipv6.html new file mode 100644 index 00000000..3cc3933b --- /dev/null +++ b/machines/templates/machines/index_ipv6.html @@ -0,0 +1,41 @@ +{% 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 %} +{% load acl %} + +{% block title %}Machines{% endblock %} + +{% block content %} +

    Liste des ipv6 de l'interface

    + {% can_create Ipv6List interface_id %} +
    Ajouter une ipv6 + {% acl_end %} + {% include "machines/aff_ipv6.html" with ipv6_list=ipv6_list %} +
    +
    +
    +{% endblock %} + diff --git a/machines/templates/machines/machine.html b/machines/templates/machines/machine.html index 9159a31d..756b68d2 100644 --- a/machines/templates/machines/machine.html +++ b/machines/templates/machines/machine.html @@ -72,6 +72,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if nasform %} {% bootstrap_form_errors nasform %} {% endif %} +{% if ipv6form %} + {% bootstrap_form_errors ipv6form %} +{% endif %}
    {% csrf_token %} @@ -139,6 +142,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    NAS

    {% bootstrap_form nasform %} {% endif %} + {% if ipv6form %} +

    Ipv6

    + {% bootstrap_form ipv6form %} + {% endif %} {% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}

    diff --git a/machines/urls.py b/machines/urls.py index 41f443e9..e3454097 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -64,6 +64,10 @@ urlpatterns = [ url(r'^edit_alias/(?P[0-9]+)$', views.edit_alias, name='edit-alias'), url(r'^del_alias/(?P[0-9]+)$', views.del_alias, name='del-alias'), url(r'^index_alias/(?P[0-9]+)$', views.index_alias, name='index-alias'), + url(r'^new_ipv6list/(?P[0-9]+)$', views.new_ipv6list, name='new-ipv6list'), + url(r'^edit_ipv6list/(?P[0-9]+)$', views.edit_ipv6list, name='edit-ipv6list'), + url(r'^del_ipv6list/(?P[0-9]+)$', views.del_ipv6list, name='del-ipv6list'), + url(r'^index_ipv6/(?P[0-9]+)$', views.index_ipv6, name='index-ipv6'), url(r'^add_service/$', views.add_service, name='add-service'), url(r'^edit_service/(?P[0-9]+)$', views.edit_service, name='edit-service'), url(r'^del_service/$', views.del_service, name='del-service'), diff --git a/machines/views.py b/machines/views.py index c850609e..dff298ef 100644 --- a/machines/views.py +++ b/machines/views.py @@ -93,6 +93,7 @@ from .forms import ( DelNasForm, SrvForm, DelSrvForm, + Ipv6ListForm, ) from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm from .models import ( @@ -114,6 +115,7 @@ from .models import ( Srv, OuverturePortList, OuverturePort, + Ipv6List, ) from users.models import User from preferences.models import GeneralOption, OptionalMachine @@ -368,6 +370,59 @@ def del_interface(request, interface, interfaceid): )) return form({'objet': interface, 'objet_name': 'interface'}, 'machines/delete.html', request) +@login_required +@can_create(Ipv6List) +@can_edit(Interface) +def new_ipv6list(request, interface, interfaceid): + """Nouvelle ipv6""" + ipv6 = Ipv6ListForm(request.POST or None, user=request.user) + if ipv6.is_valid(): + ipv6 = ipv6.save(commit=False) + ipv6.interface = interface + with transaction.atomic(), reversion.create_revision(): + ipv6.save() + reversion.set_user(request.user) + reversion.set_comment("Création") + messages.success(request, "Ipv6 ajoutée") + return redirect(reverse( + 'machines:index-ipv6', + kwargs={'interfaceid':str(interface.id)} + )) + return form({'ipv6form': ipv6}, 'machines/machine.html', request) + +@login_required +@can_edit(Ipv6List) +def edit_ipv6list(request, ipv6list_instance, ipv6listid): + """Edition d'une ipv6""" + ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) + if ipv6.is_valid(): + with transaction.atomic(), reversion.create_revision(): + ipv6.save() + reversion.set_user(request.user) + reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in ipv6.changed_data)) + messages.success(request, "Ipv6 modifiée") + return redirect(reverse( + 'machines:index-ipv6', + kwargs={'interfaceid':str(ipv6list_instance.interface.id)} + )) + return form({'ipv6form': ipv6}, 'machines/machine.html', request) + +@login_required +@can_delete(Ipv6List) +def del_ipv6list(request, ipv6list, ipv6listid): + """ Supprime une ipv6""" + if request.method == "POST": + interfaceid = ipv6list.interface.id + with transaction.atomic(), reversion.create_revision(): + ipv6list.delete() + reversion.set_user(request.user) + messages.success(request, "L'ipv6 a été détruite") + return redirect(reverse( + 'machines:index-ipv6', + kwargs={'interfaceid':str(interfaceid)} + )) + return form({'objet': ipv6list, 'objet_name': 'ipv6'}, 'machines/delete.html', request) + @login_required @can_create(IpType) def add_iptype(request): @@ -994,6 +1049,12 @@ def index_alias(request, interface, interfaceid): alias_list = Domain.objects.filter(cname=Domain.objects.filter(interface_parent=interface)).order_by('name') return render(request, 'machines/index_alias.html', {'alias_list':alias_list, 'interface_id': interfaceid}) +@login_required +@can_edit(Interface) +def index_ipv6(request, interface, interfaceid): + ipv6_list = Ipv6List.objects.filter(interface=interface) + return render(request, 'machines/index_ipv6.html', {'ipv6_list':ipv6_list, 'interface_id': interfaceid}) + @login_required @can_view_all(Service) def index_service(request): @@ -1209,7 +1270,7 @@ def ouverture_ports(request): 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 = r['ipv6'].get(i.ipv6.first(), {}) 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"]) diff --git a/preferences/migrations/0028_auto_20180128_2203.py b/preferences/migrations/0028_auto_20180128_2203.py new file mode 100644 index 00000000..ac8894fd --- /dev/null +++ b/preferences/migrations/0028_auto_20180128_2203.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-28 21:03 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0027_merge_20180106_2019'), + ] + + operations = [ + migrations.RemoveField( + model_name='optionalmachine', + name='ipv6', + ), + migrations.AddField( + model_name='optionalmachine', + name='ipv6_mode', + field=models.CharField(choices=[('SLAAC', 'Autoconfiguration par RA'), ('DHCPV6', 'Attribution des ip par dhcpv6'), ('DISABLED', 'Désactivé')], default='DISABLED', max_length=32), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 8dfc4260..af437cf7 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -25,6 +25,7 @@ Reglages généraux, machines, utilisateurs, mail, general pour l'application. """ from __future__ import unicode_literals +from django.utils.functional import cached_property from django.db import models import cotisations.models @@ -115,10 +116,27 @@ class OptionalMachine(models.Model): sans droit, activation de l'ipv6""" PRETTY_NAME = "Options machines" + SLAAC = 'SLAAC' + DHCPV6 = 'DHCPV6' + DISABLED = 'DISABLED' + CHOICE_IPV6 = ( + (SLAAC, 'Autoconfiguration par RA'), + (DHCPV6, 'Attribution des ip par dhcpv6'), + (DISABLED, 'Désactivé'), + ) + password_machine = models.BooleanField(default=False) max_lambdauser_interfaces = models.IntegerField(default=10) max_lambdauser_aliases = models.IntegerField(default=10) - ipv6 = models.BooleanField(default=False) + ipv6_mode = models.CharField( + max_length=32, + choices=CHOICE_IPV6, + default='DISABLED' + ) + + @cached_property + def ipv6(self): + return not self.ipv6_mode == 'DISABLED' class Meta: permissions = ( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 7802929d..7ea53c40 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -74,7 +74,7 @@ with this program; if not, write to the Free Software Foundation, Inc., Alias dns autorisé par utilisateur {{ machineoptions.max_lambdauser_aliases }} Support de l'ipv6 - {{ machineoptions.ipv6 }} + {{ machineoptions.ipv6_mode }}

    Préférences topologie

    diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index a73d9278..28a7b4d3 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -106,6 +106,7 @@ MODEL_NAME = { 'Interface' : machines.models.Interface, 'Domain' : machines.models.Domain, 'IpList' : machines.models.IpList, + 'Ipv6List' : machines.models.Ipv6List, 'machines.Service' : machines.models.Service, 'Service_link' : machines.models.Service_link, 'OuverturePortList' : machines.models.OuverturePortList, diff --git a/re2o/views.py b/re2o/views.py index dce28b5d..cf08b08e 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -94,6 +94,7 @@ HISTORY_BIND = { 'service' : machines.models.Service, 'vlan' : machines.models.Vlan, 'nas' : machines.models.Vlan, + 'ipv6list' : machines.models.Ipv6List, }, } From c3ecbe4efe0b9f0db293f7a2a7761d0cc985de8d Mon Sep 17 00:00:00 2001 From: B Date: Mon, 29 Jan 2018 21:59:44 +0100 Subject: [PATCH 02/10] test ajout bouton IPV6 --- machines/templates/machines/aff_machines.html | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index ac71dc30..e8132c47 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -83,7 +83,10 @@ with this program; if not, write to the Free Software Foundation, Inc., IPv4 {{ interface.ipv4 }}
    {% if ipv6_enabled and interface.ipv6 != 'None'%} - IPv6 {{ interface.ipv6|join:"," }} + IPv6 + {% endif %} @@ -128,6 +131,25 @@ with this program; if not, write to the Free Software Foundation, Inc., + + {% if ipv6_enabled and interface.ipv6 != 'None'%} + + +
    +
      + {{interface.ipv6}} +
    +
    + + + {% endif %} + {% endfor %} + + + + {% endif %} + + {% if interface.domain.related_domain.all %} From e21b393563169f334708f8fac6344766e273d619 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 00:07:46 +0000 Subject: [PATCH 03/10] Volet js pour affichage ipv6 fonctionnel --- machines/templates/machines/aff_machines.html | 29 +++++++++++-------- .../migrations/0041_merge_20180130_0052.py | 16 ++++++++++ 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 preferences/migrations/0041_merge_20180130_0052.py diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index e8132c47..dbb3e40d 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -84,9 +84,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% if ipv6_enabled and interface.ipv6 != 'None'%} IPv6 - + {% endif %} @@ -132,22 +132,21 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% if ipv6_enabled and interface.ipv6 != 'None'%} - + {% if ipv6_enabled and interface.ipv6 != 'None'%} + -
    +
      - {{interface.ipv6}} -
    + {% for ipv6 in interface.ipv6.all %} +
  • + {{ipv6}} +
  • + {% endfor %} + {% endif %} - {% endfor %} - - - - {% endif %} {% if interface.domain.related_domain.all %} @@ -183,6 +182,12 @@ $("#machines_table").ready( function() { alias_div[i].collapse('hide'); } } ); +$("#machines_table").ready( function() { + var ipv6_div = [{% for machine in machines_list %}{% for interface in machine.interface_set.all %}{% if interface.ipv6.all %}$("#collapseIpv6_{{interface.id}}"), {% endif %}{% endfor %}{% endfor %}]; + for (var i=0 ; i {% if machines_list.paginator %} diff --git a/preferences/migrations/0041_merge_20180130_0052.py b/preferences/migrations/0041_merge_20180130_0052.py new file mode 100644 index 00000000..ce2a1e5c --- /dev/null +++ b/preferences/migrations/0041_merge_20180130_0052.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-29 23:52 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0028_auto_20180128_2203'), + ('preferences', '0040_auto_20180129_1745'), + ] + + operations = [ + ] From c33a0b9bb9cd67cf17889cce363fb1b974be5a87 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 02:01:06 +0000 Subject: [PATCH 04/10] =?UTF-8?q?Fix,=20nouvelle=20validation=20pour=20v?= =?UTF-8?q?=C3=A9rifier=20l'unicit=C3=A9=20de=20l'ip=20slaac?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0075_auto_20180130_0052.py | 19 +++++++++++++++++++ machines/models.py | 11 ++++++++++- machines/views.py | 5 ++--- 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 machines/migrations/0075_auto_20180130_0052.py diff --git a/machines/migrations/0075_auto_20180130_0052.py b/machines/migrations/0075_auto_20180130_0052.py new file mode 100644 index 00000000..473824b8 --- /dev/null +++ b/machines/migrations/0075_auto_20180130_0052.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.7 on 2018-01-29 23:52 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0074_auto_20180129_0352'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='ipv6list', + unique_together=set([]), + ), + ] diff --git a/machines/models.py b/machines/models.py index 0b6e2171..721d6199 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1395,7 +1395,6 @@ class Ipv6List(FieldPermissionModelMixin, models.Model): slaac_ip = models.BooleanField(default=False) class Meta: - unique_together = (("interface", "slaac_ip"),) permissions = ( ("view_ipv6list", "Peut voir un objet ipv6"), ("change_ipv6list_slaac_ip", "Peut changer la valeur slaac sur une ipv6"), @@ -1478,6 +1477,16 @@ class Ipv6List(FieldPermissionModelMixin, models.Model): 'slaac_ip' : self.can_change_slaac_ip, } + def clean(self, *args, **kwargs): + if self.slaac_ip and Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude(id=self.id): + raise ValidationError("Une ip slaac est déjà enregistrée") + super(Ipv6List, self).clean(*args, **kwargs) + + def save(self, *args, **kwargs): + """Force à avoir appellé clean avant""" + self.full_clean() + super(Ipv6List, self).save(*args, **kwargs) + def __str__(self): return str(self.ipv6) diff --git a/machines/views.py b/machines/views.py index dff298ef..eb42bb9b 100644 --- a/machines/views.py +++ b/machines/views.py @@ -375,10 +375,9 @@ def del_interface(request, interface, interfaceid): @can_edit(Interface) def new_ipv6list(request, interface, interfaceid): """Nouvelle ipv6""" - ipv6 = Ipv6ListForm(request.POST or None, user=request.user) + ipv6list_instance = Ipv6List(interface=interface) + ipv6 = Ipv6ListForm(request.POST or None, instance=ipv6list_instance, user=request.user) if ipv6.is_valid(): - ipv6 = ipv6.save(commit=False) - ipv6.interface = interface with transaction.atomic(), reversion.create_revision(): ipv6.save() reversion.set_user(request.user) From 623f1056c3dc20b3e81d809d23e9d0b553ab4b84 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 02:02:43 +0000 Subject: [PATCH 05/10] Bug fix creation d'extensions --- machines/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machines/models.py b/machines/models.py index 721d6199..dbba2c30 100644 --- a/machines/models.py +++ b/machines/models.py @@ -815,7 +815,7 @@ class Extension(models.Model): def __str__(self): return self.name - def clean(self): + def clean(self, *args, **kwargs): if self.name and self.name[0] != '.': raise ValidationError("Une extension doit commencer par un point") super(Extension, self).clean(*args, **kwargs) From f11f577b1de5d2ed977c7c2beda9b29d1847a646 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 03:51:15 +0000 Subject: [PATCH 06/10] Gestion du prefix, warning et remplacement en cas de changement --- machines/models.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/machines/models.py b/machines/models.py index dbba2c30..e4c77f83 100644 --- a/machines/models.py +++ b/machines/models.py @@ -26,6 +26,7 @@ from __future__ import unicode_literals from datetime import timedelta import re from netaddr import mac_bare, EUI, IPSet, IPRange, IPNetwork, IPAddress +from ipaddress import IPv6Address from django.db import models from django.db.models.signals import post_save, post_delete @@ -336,6 +337,14 @@ class IpType(models.Model): for ip in self.ip_objects(): ip.delete() + def check_replace_prefixv6(self): + """Remplace les prefixv6 des interfaces liées à ce type d'ip""" + if not self.prefix_v6: + return + else: + for ipv6 in Ipv6List.objects.filter(interface__in=Interface.objects.filter(type__in=MachineType.objects.filter(ip_type=self))): + ipv6.check_and_replace_prefix(prefix=self.prefix_v6) + def clean(self): """ Nettoyage. Vérifie : - Que ip_stop est après ip_start @@ -1477,9 +1486,17 @@ class Ipv6List(FieldPermissionModelMixin, models.Model): 'slaac_ip' : self.can_change_slaac_ip, } + def check_and_replace_prefix(self, prefix=None): + """Si le prefixe v6 est incorrect, on maj l'ipv6""" + if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix or self.interface.type.ip_type.prefix_v6).exploded[:20]: + self.ipv6 = IPv6Address(IPv6Address(prefix or self.interface.type.ip_type.prefix_v6).exploded[:20] + IPv6Address(self.ipv6).exploded[20:]) + self.save() + def clean(self, *args, **kwargs): if self.slaac_ip and Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude(id=self.id): raise ValidationError("Une ip slaac est déjà enregistrée") + if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(self.interface.type.ip_type.prefix_v6).exploded[:20]: + raise ValidationError("Le prefixv6 est incorrect et ne correspond pas au type associé à la machine") super(Ipv6List, self).clean(*args, **kwargs) def save(self, *args, **kwargs): @@ -2187,6 +2204,7 @@ def iptype_post_save(sender, **kwargs): """Generation des objets ip après modification d'un range ip""" iptype = kwargs['instance'] iptype.gen_ip_range() + iptype.check_replace_prefixv6() @receiver(post_save, sender=MachineType) From 185fa6068a6b57e3d5a5ea34a75f77a16890f55d Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 05:33:56 +0000 Subject: [PATCH 07/10] =?UTF-8?q?Gestion=20compl=C3=A8te=20avec=20dhcpv6?= =?UTF-8?q?=20si=20necessaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- machines/models.py | 45 ++++++++++++++++++++++++++++++++++++++----- preferences/models.py | 12 ++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/machines/models.py b/machines/models.py index e4c77f83..542a6043 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1207,6 +1207,26 @@ class Interface(FieldPermissionModelMixin,models.Model): else: return None + @cached_property + def gen_ipv6_dhcpv6(self): + """Cree une ip, à assigner avec dhcpv6 sur une machine""" + prefix_v6 = self.type.ip_type.prefix_v6 + if not prefix_v6: + return None + return IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.id).exploded[20:]) + + def sync_ipv6_dhcpv6(self): + """Affecte une ipv6 dhcpv6 calculée à partir de l'id de la machine""" + ipv6_dhcpv6 = self.gen_ipv6_dhcpv6 + if not ipv6_dhcpv6: + return + ipv6 = Ipv6List.objects.filter(ipv6=str(ipv6_dhcpv6)).first() + if not ipv6: + ipv6 = Ipv6List(ipv6=str(ipv6_dhcpv6)) + ipv6.interface = self + ipv6.save() + return + def sync_ipv6_slaac(self): """Cree, mets à jour et supprime si il y a lieu l'ipv6 slaac associée à la machine @@ -1222,6 +1242,16 @@ class Interface(FieldPermissionModelMixin,models.Model): ipv6_object.ipv6 = str(ipv6_slaac) ipv6_object.save() + def sync_ipv6(self): + """Cree et met à jour l'ensemble des ipv6 en fonction du mode choisi""" + machine_options, _created = preferences.models.OptionalMachine.objects.get_or_create() + if machine_options.ipv6_mode == 'SLAAC': + self.sync_ipv6_slaac() + elif machine_options.ipv6_mode == 'DHCPV6': + self.sync_ipv6_dhcpv6() + else: + return + def ipv6(self): """ Renvoie le queryset de la liste des ipv6 On renvoie l'ipv6 slaac que si le mode slaac est activé (et non dhcpv6)""" @@ -1488,15 +1518,20 @@ class Ipv6List(FieldPermissionModelMixin, models.Model): def check_and_replace_prefix(self, prefix=None): """Si le prefixe v6 est incorrect, on maj l'ipv6""" - if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix or self.interface.type.ip_type.prefix_v6).exploded[:20]: - self.ipv6 = IPv6Address(IPv6Address(prefix or self.interface.type.ip_type.prefix_v6).exploded[:20] + IPv6Address(self.ipv6).exploded[20:]) + prefix_v6 = prefix or self.interface.type.ip_type.prefix_v6 + if not prefix_v6: + return + if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]: + self.ipv6 = IPv6Address(IPv6Address(prefix_v6).exploded[:20] + IPv6Address(self.ipv6).exploded[20:]) self.save() def clean(self, *args, **kwargs): if self.slaac_ip and Ipv6List.objects.filter(interface=self.interface, slaac_ip=True).exclude(id=self.id): raise ValidationError("Une ip slaac est déjà enregistrée") - if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(self.interface.type.ip_type.prefix_v6).exploded[:20]: - raise ValidationError("Le prefixv6 est incorrect et ne correspond pas au type associé à la machine") + prefix_v6 = self.interface.type.ip_type.prefix_v6 + if prefix_v6: + if IPv6Address(self.ipv6).exploded[:20] != IPv6Address(prefix_v6).exploded[:20]: + raise ValidationError("Le prefixv6 est incorrect et ne correspond pas au type associé à la machine") super(Ipv6List, self).clean(*args, **kwargs) def save(self, *args, **kwargs): @@ -2182,7 +2217,7 @@ def interface_post_save(sender, **kwargs): """Synchronisation ldap et régen parefeu/dhcp lors de la modification d'une interface""" interface = kwargs['instance'] - interface.sync_ipv6_slaac() + interface.sync_ipv6() user = interface.machine.user user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) # Regen services diff --git a/preferences/models.py b/preferences/models.py index 764e5332..a2837e52 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -28,6 +28,9 @@ from __future__ import unicode_literals from django.utils.functional import cached_property from django.db import models import cotisations.models +import machines.models +from django.db.models.signals import post_save, post_delete +from django.dispatch import receiver from .aes_field import AESEncryptedField @@ -216,6 +219,15 @@ class OptionalMachine(models.Model): de voir les préférences concernant les machines" +@receiver(post_save, sender=OptionalMachine) +def interface_post_save(sender, **kwargs): + """Synchronisation ipv6""" + machine_pref = kwargs['instance'] + if machine_pref.ipv6_mode != "DISABLED": + for interface in machines.models.Interface.objects.all(): + interface.sync_ipv6() + + class OptionalTopologie(models.Model): """Reglages pour la topologie : mode d'accès radius, vlan où placer les machines en accept ou reject""" From 56e88cfe979555a4be2d79a65fa1ffe13fc5e37c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 06:14:28 +0000 Subject: [PATCH 08/10] Serialisation des ipv6 et export des ouvertures de ports --- machines/serializers.py | 12 ++++++++++-- machines/views.py | 16 +++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/machines/serializers.py b/machines/serializers.py index d1876e06..ed2093de 100644 --- a/machines/serializers.py +++ b/machines/serializers.py @@ -37,7 +37,8 @@ from machines.models import ( Service_link, Ns, OuverturePortList, - OuverturePort + OuverturePort, + Ipv6List ) @@ -57,6 +58,12 @@ class IpListSerializer(serializers.ModelSerializer): fields = ('ipv4', 'ip_type') +class Ipv6ListSerializer(serializers.ModelSerializer): + class Meta: + model = Ipv6List + fields = ('ipv6', 'slaac_ip') + + 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 @@ -81,8 +88,9 @@ class InterfaceSerializer(serializers.ModelSerializer): class FullInterfaceSerializer(serializers.ModelSerializer): - """Serialisation complete d'une interface avec l'ipv6 en plus""" + """Serialisation complete d'une interface avec les ipv6 en plus""" ipv4 = IpListSerializer(read_only=True) + ipv6 = Ipv6ListSerializer(read_only=True, many=True) mac_address = serializers.SerializerMethodField('get_macaddress') domain = serializers.SerializerMethodField('get_dns') extension = serializers.SerializerMethodField('get_interface_extension') diff --git a/machines/views.py b/machines/views.py index eb42bb9b..dccdfae7 100644 --- a/machines/views.py +++ b/machines/views.py @@ -1268,14 +1268,16 @@ def ouverture_ports(request): 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.first(), {}) - 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 + if i.ipv6(): + for ipv6 in i.ipv6(): + d = r['ipv6'].get(ipv6.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'][ipv6.ipv6] = d return JSONResponse(r) + @csrf_exempt @login_required @permission_required('machines.serveur') From 4039a9125b29c5d619a43b9d2e25021487006d3c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 06:17:47 +0000 Subject: [PATCH 09/10] Fix bug --- machines/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machines/views.py b/machines/views.py index dccdfae7..9b766779 100644 --- a/machines/views.py +++ b/machines/views.py @@ -539,7 +539,7 @@ def edit_extension(request, extension_instance, extensionid): extension.save() reversion.set_user(request.user) reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in extension.changed_data)) - mssages.success(request, "Extension modifiée") + messages.success(request, "Extension modifiée") return redirect(reverse('machines:index-extension')) return form({'extensionform': extension}, 'machines/machine.html', request) From 1d05ad03bc453e8c34a8537dc9c524a3842df819 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 30 Jan 2018 06:36:22 +0000 Subject: [PATCH 10/10] =?UTF-8?q?Ne=20renvoie=20une=20ipv6=20que=20si=20el?= =?UTF-8?q?le=20est=20activ=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- machines/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/machines/models.py b/machines/models.py index 542a6043..b0e0086d 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1258,8 +1258,10 @@ class Interface(FieldPermissionModelMixin,models.Model): machine_options, _created = preferences.models.OptionalMachine.objects.get_or_create() if machine_options.ipv6_mode == 'SLAAC': return Ipv6List.objects.filter(interface=self) - else: + elif machine_options.ipv6_mode == 'DHCPV6': return Ipv6List.objects.filter(interface=self, slaac_ip=False) + else: + return None def mac_bare(self): """ Formatage de la mac type mac_bare"""