255 lines
8.6 KiB
Python
255 lines
8.6 KiB
Python
from django import forms
|
|
from django.db.models import Case, When, IntegerField
|
|
|
|
from .models import Maintainer, Contact, InetNum, ASBlock, ASNumber
|
|
|
|
import re
|
|
import ipaddress
|
|
|
|
|
|
class WhoisObjectFormMixin(object):
|
|
def __init__(self, user, *args, **kwargs):
|
|
super(WhoisObjectFormMixin, self).__init__(*args, **kwargs)
|
|
self._user = user
|
|
|
|
instance = getattr(self, 'instance', None)
|
|
if instance and instance.pk:
|
|
self.fields['handle'].widget.attrs['readonly'] = True
|
|
|
|
# only show users contacts and already present contacts
|
|
if 'admin_c' in self.fields:
|
|
self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by__in=self._user.maintainer_set.all())
|
|
if instance and instance.pk:
|
|
self.fields['admin_c'].queryset |= instance.admin_c.all()
|
|
|
|
def clean_handle(self):
|
|
instance = getattr(self, 'instance', None)
|
|
if instance and instance.pk:
|
|
return instance.handle
|
|
else:
|
|
return self.cleaned_data['handle']
|
|
|
|
def clean(self):
|
|
cleaned_data = super(WhoisObjectFormMixin, self).clean()
|
|
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 MntFormMixin(object):
|
|
protectedFields = []
|
|
def __init__(self, lower=False, *args, **kwargs):
|
|
super(MntFormMixin, self).__init__(*args, **kwargs)
|
|
|
|
self._editLower = lower
|
|
|
|
if self._editLower:
|
|
for key in self.protectedFields:
|
|
self.fields[key].disabled = True
|
|
|
|
#self.fields["mnt_by"].widget.attrs["data-role"] = "tagsinput"
|
|
instance = getattr(self, "instance", None)
|
|
mntWhens = [When(auth=self._user, then=2)]
|
|
if instance and instance.pk:
|
|
mntWhens.append(When(handle__in=instance.mnt_by.all().values_list("handle"), then=1))
|
|
mntQs = Maintainer.objects.annotate(card=Case(*mntWhens, default=0, output_field=IntegerField())).order_by("-card")
|
|
self.fields["mnt_by"].queryset = mntQs
|
|
|
|
class MntForm(forms.ModelForm):
|
|
class Meta:
|
|
model = Maintainer
|
|
fields = ['handle', 'description', 'admin_c']
|
|
|
|
def __init__(self, user, *args, **kwargs):
|
|
super(MntForm, self).__init__(*args, **kwargs)
|
|
self._user = user
|
|
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(WhoisObjectFormMixin, forms.ModelForm):
|
|
class Meta:
|
|
model = Contact
|
|
fields = ['handle', 'name', 'mnt_by']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(ContactForm, self).__init__(*args, **kwargs)
|
|
|
|
self.fields['mnt_by'].queryset = Maintainer.objects.filter(auth=self._user)
|
|
|
|
|
|
class ContactInitialForm(ContactForm):
|
|
class Meta:
|
|
model = Contact
|
|
fields = ['handle', 'name']
|
|
|
|
|
|
class InetNumForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
|
|
prefix = forms.CharField()
|
|
protectedFields = ['handle', 'protocol', 'parent_range', 'mnt_by', 'prefix']
|
|
|
|
class Meta:
|
|
model = InetNum
|
|
fields = ['handle', 'protocol', 'parent_range', 'prefix', 'name', 'description', 'mnt_by', 'mnt_lower']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(InetNumForm, self).__init__(*args, **kwargs)
|
|
if 'admin_c' in self.fields:
|
|
self.fields['admin_c'].queryset = Contact.objects.filter(mnt_by__in=self.user.maintainer_set.all())
|
|
instance = getattr(self, "instance", None)
|
|
if instance and instance.pk:
|
|
self.fields['admin_c'].queryset |= instance.admin_c.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
|
|
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_parent_range(self):
|
|
parent_range = self.cleaned_data.get('parent_range', None)
|
|
|
|
# allow parent range to be unset for already present objects
|
|
if not (self.instance and self.instance.pk) and not parent_range:
|
|
raise forms.ValidationError("Parent range must be set")
|
|
|
|
def clean(self):
|
|
cleaned_data = super(InetNumForm, self).clean()
|
|
|
|
if not self._editLower:
|
|
if not self.errors:
|
|
prefix = cleaned_data['prefix']
|
|
parent = cleaned_data['parent_range']
|
|
if parent:
|
|
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
|
|
|
|
class ASBlockForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
|
|
protectedFields = ['handle', 'parent_block', 'asBegin', 'asEnd', 'mnt_by']
|
|
|
|
# FIXME: Filter blocks
|
|
class Meta:
|
|
model = ASBlock
|
|
fields = ['handle', 'parent_block', 'asBegin', 'asEnd', 'name', 'description', 'mnt_by', 'mnt_lower']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(ASBlockForm, self).__init__(*args, **kwargs)
|
|
|
|
if not self.instance or self.instance and self.instance and self.instance.parent_block:
|
|
self.fields["parent_block"].required = True
|
|
if self.instance and self.instance.pk:
|
|
self.fields["parent_block"].disabled = True
|
|
|
|
def clean(self):
|
|
cleaned_data = super(ASBlockForm, self).clean()
|
|
|
|
if not self.errors:
|
|
asBegin = cleaned_data['asBegin']
|
|
asEnd = cleaned_data['asEnd']
|
|
parent = cleaned_data['parent_block']
|
|
# check if somebody is already using this block
|
|
|
|
# check if in range
|
|
if asBegin > asEnd:
|
|
raise forms.ValidationError("AS beginning must be smaller or equal to AS end")
|
|
|
|
if parent:
|
|
if parent.asnumber_set.count() > 0:
|
|
raise forms.ValidationError("The parent AS block is already references by following AS number objects: %s" % (", ".join(map(lambda _x: _x.handle, parent.asnumber_set.all())),))
|
|
|
|
|
|
# check if same range
|
|
if not (asBegin >= parent.asBegin and asEnd <= parent.asEnd):
|
|
raise forms.ValidationError("AS beginning and end must be inside the range of the parent AS block")
|
|
|
|
if parent.asBegin == asBegin and parent.asEnd == asEnd:
|
|
raise forms.ValidationError("The range of this block cannot be the same range AS the parent AS block")
|
|
|
|
# check for overlap with other asblocks
|
|
for block in parent.asblock_set.all():
|
|
if self.instance and self.instance.pk and block.pk == self.instance.pk:
|
|
continue
|
|
|
|
if block.asBegin <= asBegin <= block.asEnd or block.asBegin <= asEnd <= block.asEnd or \
|
|
asBegin <= block.asBegin <= asEnd or asBegin <= block.asEnd <= asEnd:
|
|
raise forms.ValidationError("Block overlaps with block %s" % block.handle)
|
|
|
|
|
|
return cleaned_data
|
|
|
|
class ASNumberForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
|
|
protectedFields = ['handle', 'asblock', 'number', 'mnt_by']
|
|
|
|
class Meta:
|
|
model = ASNumber
|
|
fields = ['handle', 'asblock', 'number', 'volatile', 'name', 'description', 'mnt_by', 'mnt_lower', 'admin_c']
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(ASNumberForm, self).__init__(*args, **kwargs)
|
|
|
|
if not (self.instance and self.instance.pk):
|
|
self.fields["asblock"].required = True
|
|
else:
|
|
self.fields["asblock"].disabled = True
|
|
|
|
def clean(self):
|
|
cleaned_data = super(ASNumberForm, self).clean()
|
|
|
|
if not self.errors:
|
|
number = cleaned_data['number']
|
|
block = cleaned_data['asblock']
|
|
|
|
# belongs to asblock?
|
|
if number < block.asBegin or number > block.asEnd:
|
|
raise forms.ValidationError("AS number is not inside AS block")
|
|
|
|
# does an entry already exist?
|
|
try:
|
|
otherAS = ASNumber.objects.get(number=number)
|
|
if not (self.instance and self.instance.pk and self.instance.pk == otherAS.pk):
|
|
raise forms.ValidationError("This AS number is already represented by %s" % otherAS.handle)
|
|
except ASNumber.DoesNotExist:
|
|
pass
|
|
|
|
# has already other asblock?
|
|
if block.asblock_set.count() > 0:
|
|
raise forms.ValidationError("The given AS block is already references by following sub AS blocks: %s" % (", ".join(map(lambda _x: _x.handle, block.asblock_set.all())),))
|
|
|