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" %}
{% endfor %}
{% 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 %}
</tbody>
</table>

View File

@ -6,6 +6,10 @@ from django import forms
from .models import Maintainer, Contact, InetNum
import re
import ipaddress
class WhoisObjectMixin(object):
def __init__(self, user, *args, **kwargs):
super(WhoisObjectMixin, self).__init__(*args, **kwargs)
@ -24,9 +28,12 @@ class WhoisObjectMixin(object):
def clean(self):
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"))
return cleaned_data
class MntForm(forms.ModelForm):
class Meta:
model = Maintainer
@ -38,11 +45,13 @@ class MntForm(forms.ModelForm):
if 'admin_c' in self.fields:
self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by=user.maintainer_set.all())
class MntInitialForm(MntForm):
class Meta:
model = Maintainer
fields = ['handle', 'description']
class ContactForm(WhoisObjectMixin, forms.ModelForm):
class Meta:
model = Contact
@ -51,24 +60,76 @@ class ContactForm(WhoisObjectMixin, forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
class ContactInitialForm(ContactForm):
class Meta:
model = Contact
fields = ['handle', 'name']
class InetNumForm(WhoisObjectMixin, forms.ModelForm):
prefix = forms.CharField()
protectedFields = ['handle', 'protocol', 'parent_range', 'mnt_by', 'prefix']
class Meta:
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):
super(MntForm, self).__init__(*args, **kwargs)
super(InetNumForm, self).__init__(*args, **kwargs)
print("args", args, kwargs)
self._editLower = lower
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:
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):
# FIXME: Reset certain field sto instance:
pass
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
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 .validators import HandleValidator, HandleValidatorWithSuffix
import ipaddress
class WhoisObject(models.Model):
class Meta:
@ -79,9 +81,8 @@ class Maintainer(WhoisObject):
candidates = mntable.objects.filter(mnt_by=self).annotate(mntCount=models.Count('mnt_by')).filter(mntCount__lte=1)
for candidate in candidates:
reasons.append("Object %s would have no maintainers left." % candidate.handle)
return reasons
return reasons
class MntdObject(WhoisObject):
@ -112,9 +113,10 @@ class Contact(MntdObject):
candidates = contactable.objects.filter(admin_c=self).annotate(contactCount=models.Count('admin_c')).filter(contactCount__lte=1)
for candidate in candidates:
reasons.append("Object %s would have no contact left." % candidate.handle)
return reasons
class ASBlock(MntdObject):
handleSuffix = "ASB"
@ -123,6 +125,7 @@ class ASBlock(MntdObject):
mnt_lower = models.ManyToManyField(Maintainer, related_name='lower_asblock_set', blank=True)
class ASNumber(MntdObject):
handleSuffix = "AS"
@ -145,6 +148,20 @@ class InetNum(MntdObject):
address = models.GenericIPAddressField(db_index=True)
netmask = models.PositiveIntegerField()
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)
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'^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/edit/(?P<handle>[A-Z0-9-]+)/$', whoisdb_views.ContactEdit.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/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.InetNumEdit.as_view(), name='inetnum-edit'),
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 .generic import DeleteCheckView
@login_required
def dbDashboard(request):
mnts = request.user.maintainer_set.all()
contacts = Contact.objects.filter(mnt_by__in=mnts)
netblocks = InetNum.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))
asnumbers = ASNumber.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)).distinct()
asnumbers = ASNumber.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts)).distinct()
mntForm = contactForm = None
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})
class MaintainerCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html"
form_class = MntForm
@ -71,6 +73,7 @@ class MaintainerCreate(LoginRequiredMixin, CreateView):
return super(MaintainerCreate, self).form_valid(form)
class MaintainerEdit(LoginRequiredMixin, UpdateView):
template_name = "whoisdb/maintainer_edit.html"
model = Maintainer
@ -86,6 +89,7 @@ class MaintainerEdit(LoginRequiredMixin, UpdateView):
def get_queryset(self):
return self.model.objects.filter(auth=self.request.user)
class MaintainerDelete(LoginRequiredMixin, DeleteCheckView):
template_name = "whoisdb/obj_delete.html"
model = Maintainer
@ -95,7 +99,7 @@ class MaintainerDelete(LoginRequiredMixin, DeleteCheckView):
def get_queryset(self):
return self.model.objects.filter(auth=self.request.user)
class MaintainerDetail(LoginRequiredMixin, DetailView):
model = Maintainer
@ -103,12 +107,14 @@ class MaintainerDetail(LoginRequiredMixin, DetailView):
slug_url_kwarg = "handle"
context_object_name = "mnt"
class ContactDetail(DetailView):
model = Contact
slug_field = "handle"
slug_url_kwarg = "handle"
context_object_name = "contact"
class ContactEdit(LoginRequiredMixin, UpdateView):
template_name = "whoisdb/obj_edit.html"
model = Contact
@ -125,6 +131,7 @@ class ContactEdit(LoginRequiredMixin, UpdateView):
# 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())
class ContactCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html"
form_class = ContactForm
@ -138,6 +145,7 @@ class ContactCreate(LoginRequiredMixin, CreateView):
}
return kwargs
class ContactDelete(LoginRequiredMixin, DeleteCheckView):
template_name = "whoisdb/obj_delete.html"
model = Contact
@ -148,6 +156,7 @@ class ContactDelete(LoginRequiredMixin, DeleteCheckView):
def get_queryset(self):
return self.model.objects.filter(mnt_by__in=self.request.user.maintainer_set.all())
# InetNum
class InetNumCreate(LoginRequiredMixin, CreateView):
template_name = "whoisdb/obj_create.html"
@ -158,7 +167,51 @@ class InetNumCreate(LoginRequiredMixin, CreateView):
kwargs["user"] = self.request.user
kwargs["initial"] = {
"handle": "AUTO",
"type": Contact.TYPE_PERSON
}
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))