This commit is contained in:
Sebastian Lohff 2017-03-01 03:21:03 +01:00
parent 814a8b62cb
commit f9b98a03f5
7 changed files with 190 additions and 18 deletions

View File

@ -0,0 +1,16 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Header</div>
<div class="panel-body">
{{ inetnum }}
{{ inetnum.address }}/{{ inetnum.netmask }}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -48,7 +48,7 @@
{% include "whoisdb/handle_table_row.html" with obj=asnumber objType="AS Number" prefix="contact" %} {% include "whoisdb/handle_table_row.html" with obj=asnumber objType="AS Number" prefix="contact" %}
{% endfor %} {% endfor %}
{% for netblock in netblocks %} {% for netblock in netblocks %}
{% include "whoisdb/handle_table_row.html" with obj=netblock objType="IP Netblock" prefix="contact" %} {% include "whoisdb/handle_table_row.html" with obj=netblock objType="IP Netblock" prefix="inetnum" %}
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>

View File

@ -6,6 +6,10 @@ from django import forms
from .models import Maintainer, Contact, InetNum from .models import Maintainer, Contact, InetNum
import re
import ipaddress
class WhoisObjectMixin(object): class WhoisObjectMixin(object):
def __init__(self, user, *args, **kwargs): def __init__(self, user, *args, **kwargs):
super(WhoisObjectMixin, self).__init__(*args, **kwargs) super(WhoisObjectMixin, self).__init__(*args, **kwargs)
@ -24,9 +28,12 @@ class WhoisObjectMixin(object):
def clean(self): def clean(self):
cleaned_data = super(WhoisObjectMixin, self).clean() cleaned_data = super(WhoisObjectMixin, self).clean()
if cleaned_data.get("handle") == "AUTO": if cleaned_data.get("handle") == "AUTO" and not self.errors:
cleaned_data['handle'] = self._meta.model.genGenericHandle(cleaned_data.get("name")) cleaned_data['handle'] = self._meta.model.genGenericHandle(cleaned_data.get("name"))
return cleaned_data
class MntForm(forms.ModelForm): class MntForm(forms.ModelForm):
class Meta: class Meta:
model = Maintainer model = Maintainer
@ -38,11 +45,13 @@ class MntForm(forms.ModelForm):
if 'admin_c' in self.fields: if 'admin_c' in self.fields:
self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by=user.maintainer_set.all()) self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by=user.maintainer_set.all())
class MntInitialForm(MntForm): class MntInitialForm(MntForm):
class Meta: class Meta:
model = Maintainer model = Maintainer
fields = ['handle', 'description'] fields = ['handle', 'description']
class ContactForm(WhoisObjectMixin, forms.ModelForm): class ContactForm(WhoisObjectMixin, forms.ModelForm):
class Meta: class Meta:
model = Contact model = Contact
@ -51,24 +60,76 @@ class ContactForm(WhoisObjectMixin, forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs) super(ContactForm, self).__init__(*args, **kwargs)
class ContactInitialForm(ContactForm): class ContactInitialForm(ContactForm):
class Meta: class Meta:
model = Contact model = Contact
fields = ['handle', 'name'] fields = ['handle', 'name']
class InetNumForm(WhoisObjectMixin, forms.ModelForm): class InetNumForm(WhoisObjectMixin, forms.ModelForm):
prefix = forms.CharField()
protectedFields = ['handle', 'protocol', 'parent_range', 'mnt_by', 'prefix']
class Meta: class Meta:
model = InetNum model = InetNum
fields = ['protocol', 'parent_range', 'address', 'description', 'mnt_lower'] fields = ['handle', 'protocol', 'parent_range', 'prefix', 'name', 'description', 'mnt_by', 'mnt_lower']
def __init__(self, lower=False, *args, **kwargs): def __init__(self, lower=False, *args, **kwargs):
super(MntForm, self).__init__(*args, **kwargs) super(InetNumForm, self).__init__(*args, **kwargs)
print("args", args, kwargs)
self._editLower = lower self._editLower = lower
if 'admin_c' in self.fields: if 'admin_c' in self.fields:
self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by=self.user.maintainer_set.all()) self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by__in=self.user.maintainer_set.all())
if self._editLower: if self._editLower:
for key in self.protectedFields:
self.fields[key].disabled = True
self.fields[key].widget.attrs['readonly'] = False
def clean_prefix(self):
# make sure this is a subnet we're getting
print("HALLO")
net = self.cleaned_data['prefix']
if not re.match(r"[0-9:.]+/[0-9]+", net):
raise forms.ValidationError("Address needs to be a subnet in the format of ip/cidr")
try:
net = ipaddress.ip_network(net)
except ValueError as e:
raise forms.ValidationError(str(e))
return net
def clean(self): def clean(self):
# FIXME: Reset certain field sto instance: # FIXME: Reset certain field sto instance:
cleaned_data = super(InetNumForm, self).clean()
if self._editLower:
# reset some fields, just in case
#for key in self.protectedFields:
# cleaned_data[key] = getattr(self.instance, key)
pass pass
else:
if all(x in cleaned_data for x in ('prefix', 'parent_range', 'protocol')):
prefix = cleaned_data['prefix']
parent = cleaned_data['parent_range']
parentNet = parent.getNetwork()
if cleaned_data['protocol'] != parent.protocol:
raise forms.ValidationError("Protocol type for prefix must be same as parent network")
# check if in parent block
if prefix.network_address not in parentNet or prefix.prefixlen < parentNet.prefixlen:
raise forms.ValidationError("Prefix must be inside parent network range")
# check if parent block has net that overlaps with us
for otherNet in parent.inetnum_set.all():
if self.instance and self.instance.pk == otherNet.pk:
continue
if otherNet.getNetwork().overlaps(prefix):
raise forms.ValidationError("The given prefix overlaps with network %s" % otherNet.handle)
self.instance.address = str(prefix.network_address)
self.instance.netmask = prefix.prefixlen
return cleaned_data

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-01 00:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('whoisdb', '0007_asblock_mnt_lower'),
]
operations = [
migrations.AddField(
model_name='inetnum',
name='name',
field=models.CharField(default='', max_length=64),
preserve_default=False,
),
migrations.AlterField(
model_name='inetnum',
name='protocol',
field=models.CharField(choices=[('ipv4', 'IPv4'), ('ipv6', 'IPv6')], max_length=4),
),
]

View File

@ -4,6 +4,8 @@ from django.urls import reverse
from dncore.models import User from dncore.models import User
from .validators import HandleValidator, HandleValidatorWithSuffix from .validators import HandleValidator, HandleValidatorWithSuffix
import ipaddress
class WhoisObject(models.Model): class WhoisObject(models.Model):
class Meta: class Meta:
@ -83,7 +85,6 @@ class Maintainer(WhoisObject):
return reasons return reasons
class MntdObject(WhoisObject): class MntdObject(WhoisObject):
class Meta: class Meta:
abstract = True abstract = True
@ -115,6 +116,7 @@ class Contact(MntdObject):
return reasons return reasons
class ASBlock(MntdObject): class ASBlock(MntdObject):
handleSuffix = "ASB" handleSuffix = "ASB"
@ -123,6 +125,7 @@ class ASBlock(MntdObject):
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True) mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
class ASNumber(MntdObject): class ASNumber(MntdObject):
handleSuffix = "AS" handleSuffix = "AS"
@ -145,6 +148,20 @@ class InetNum(MntdObject):
address = models.GenericIPAddressField(db_index=True) address = models.GenericIPAddressField(db_index=True)
netmask = models.PositiveIntegerField() netmask = models.PositiveIntegerField()
parent_range = models.ForeignKey("InetNum", models.CASCADE, null=True, blank=True, default=None) parent_range = models.ForeignKey("InetNum", models.CASCADE, null=True, blank=True, default=None)
name = models.CharField(max_length=64)
description = models.CharField(max_length=64, blank=True) description = models.CharField(max_length=64, blank=True)
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True) mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_inetnum_set', blank=True)
def getNetwork(self):
return ipaddress.ip_network("%s/%s" % (self.address, self.netmask))
def get_absolute_url(self):
return reverse("whoisdb:inetnum-detail", kwargs={"handle": self.handle})
def getNoDeleteReasons(self):
reasons = []
if self.inetnum_set.all().count() > 0:
reasons.append("The following networks depend on this network: %s" % ", ".join(map(lambda _x: _x.handle, self.inetnum_set.all())))
return reasons

View File

@ -16,8 +16,7 @@ urlpatterns = [
url(r'^contact/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDelete.as_view(), name='contact-delete'), url(r'^contact/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDelete.as_view(), name='contact-delete'),
url(r'^inetnum/create/$', whoisdb_views.InetNumCreate.as_view(), name='inetnum-create'), url(r'^inetnum/create/$', whoisdb_views.InetNumCreate.as_view(), name='inetnum-create'),
url(r'^inetnum/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDetail.as_view(), name='inetnum-detail'), url(r'^inetnum/show/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.InetNumDetail.as_view(), name='inetnum-detail'),
url(r'^inetnum/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactEdit.as_view(), name='inetnum-edit'), url(r'^inetnum/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.InetNumEdit.as_view(), name='inetnum-edit'),
url(r'^inetnum/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactDelete.as_view(), name='inetnum-delete'), url(r'^inetnum/delete/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.InetNumDelete.as_view(), name='inetnum-delete'),
] ]

View File

@ -10,13 +10,14 @@ from .models import Maintainer, Contact, InetNum, ASBlock, ASNumber
from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm from .forms import MntForm, MntInitialForm, ContactForm, ContactInitialForm, InetNumForm
from .generic import DeleteCheckView from .generic import DeleteCheckView
@login_required @login_required
def dbDashboard(request): def dbDashboard(request):
mnts = request.user.maintainer_set.all() mnts = request.user.maintainer_set.all()
contacts = Contact.objects.filter(mnt_by__in=mnts) contacts = Contact.objects.filter(mnt_by__in=mnts)
netblocks = InetNum.objects.filter(Q(mnt_by__in=mnts)|Q(mnt_lower__in=mnts)) netblocks = InetNum.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
asblocks = ASBlock.objects.filter(Q(mnt_by__in=mnts)|Q(mnt_lower__in=mnts)) asblocks = ASBlock.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
asnumbers = ASNumber.objects.filter(Q(mnt_by__in=mnts)|Q(mnt_lower__in=mnts)) asnumbers = ASNumber.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
mntForm = contactForm = None mntForm = contactForm = None
if mnts.count() == 0: if mnts.count() == 0:
@ -49,6 +50,7 @@ def dbDashboard(request):
return render(request, "whoisdb/overview.html", {"mnts": mnts, "contacts": contacts, "mntForm": mntForm, "contactForm": contactForm, "netblocks": netblocks, "asblocks": asblocks, "asnumbers": asnumbers}) return render(request, "whoisdb/overview.html", {"mnts": mnts, "contacts": contacts, "mntForm": mntForm, "contactForm": contactForm, "netblocks": netblocks, "asblocks": asblocks, "asnumbers": asnumbers})
class MaintainerCreate(LoginRequiredMixin, CreateView): class MaintainerCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html" template_name = "whoisdb/obj_create.html"
form_class = MntForm form_class = MntForm
@ -71,6 +73,7 @@ class MaintainerCreate(LoginRequiredMixin, CreateView):
return super(MaintainerCreate, self).form_valid(form) return super(MaintainerCreate, self).form_valid(form)
class MaintainerEdit(LoginRequiredMixin, UpdateView): class MaintainerEdit(LoginRequiredMixin, UpdateView):
template_name = "whoisdb/maintainer_edit.html" template_name = "whoisdb/maintainer_edit.html"
model = Maintainer model = Maintainer
@ -86,6 +89,7 @@ class MaintainerEdit(LoginRequiredMixin, UpdateView):
def get_queryset(self): def get_queryset(self):
return self.model.objects.filter(auth=self.request.user) return self.model.objects.filter(auth=self.request.user)
class MaintainerDelete(LoginRequiredMixin, DeleteCheckView): class MaintainerDelete(LoginRequiredMixin, DeleteCheckView):
template_name = "whoisdb/obj_delete.html" template_name = "whoisdb/obj_delete.html"
model = Maintainer model = Maintainer
@ -103,12 +107,14 @@ class MaintainerDetail(LoginRequiredMixin, DetailView):
slug_url_kwarg = "handle" slug_url_kwarg = "handle"
context_object_name = "mnt" context_object_name = "mnt"
class ContactDetail(DetailView): class ContactDetail(DetailView):
model = Contact model = Contact
slug_field = "handle" slug_field = "handle"
slug_url_kwarg = "handle" slug_url_kwarg = "handle"
context_object_name = "contact" context_object_name = "contact"
class ContactEdit(LoginRequiredMixin, UpdateView): class ContactEdit(LoginRequiredMixin, UpdateView):
template_name = "whoisdb/obj_edit.html" template_name = "whoisdb/obj_edit.html"
model = Contact model = Contact
@ -125,6 +131,7 @@ class ContactEdit(LoginRequiredMixin, UpdateView):
# FIXME: we need all maintainers to be available. autofill own maintainers # FIXME: we need all maintainers to be available. autofill own maintainers
return self.model.objects.filter(mnt_by__in=self.request.user.maintainer_set.all()) return self.model.objects.filter(mnt_by__in=self.request.user.maintainer_set.all())
class ContactCreate(LoginRequiredMixin, CreateView): class ContactCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html" template_name = "whoisdb/obj_create.html"
form_class = ContactForm form_class = ContactForm
@ -138,6 +145,7 @@ class ContactCreate(LoginRequiredMixin, CreateView):
} }
return kwargs return kwargs
class ContactDelete(LoginRequiredMixin, DeleteCheckView): class ContactDelete(LoginRequiredMixin, DeleteCheckView):
template_name = "whoisdb/obj_delete.html" template_name = "whoisdb/obj_delete.html"
model = Contact model = Contact
@ -148,6 +156,7 @@ class ContactDelete(LoginRequiredMixin, DeleteCheckView):
def get_queryset(self): def get_queryset(self):
return self.model.objects.filter(mnt_by__in=self.request.user.maintainer_set.all()) return self.model.objects.filter(mnt_by__in=self.request.user.maintainer_set.all())
# InetNum # InetNum
class InetNumCreate(LoginRequiredMixin, CreateView): class InetNumCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html" template_name = "whoisdb/obj_create.html"
@ -158,7 +167,51 @@ class InetNumCreate(LoginRequiredMixin, CreateView):
kwargs["user"] = self.request.user kwargs["user"] = self.request.user
kwargs["initial"] = { kwargs["initial"] = {
"handle": "AUTO", "handle": "AUTO",
"type": Contact.TYPE_PERSON
} }
return kwargs return kwargs
class InetNumDetail(DetailView):
model = InetNum
slug_field = "handle"
slug_url_kwarg = "handle"
context_object_name = "inetnum"
class InetNumEdit(LoginRequiredMixin, UpdateView):
template_name = "whoisdb/obj_edit.html"
model = InetNum
form_class = InetNumForm
slug_field = "handle"
slug_url_kwarg = "handle"
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(InetNumEdit, self).get_form_kwargs(*args, **kwargs)
kwargs["user"] = self.request.user
mnts = self.request.user.maintainer_set.all()
if not any(mnt in self.object.mnt_by.all() for mnt in mnts):
# we are in mnt_lower
kwargs["lower"] = True
kwargs["initial"] = {'prefix': str(self.object.getNetwork())}
return kwargs
def get_queryset(self):
# FIXME: we need all maintainers to be available. autofill own maintainers
mnts = self.request.user.maintainer_set.all()
return self.model.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
class InetNumDelete(LoginRequiredMixin, DeleteCheckView):
template_name = "whoisdb/obj_delete.html"
model = InetNum
slug_field = "handle"
slug_url_kwarg = "handle"
success_url = reverse_lazy("whoisdb:dashboard")
def get_queryset(self):
mnts = self.request.user.maintainer_set.all()
return self.model.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts))