2017-02-23 22:28:06 +01:00
from django import forms
2017-03-02 18:28:15 +01:00
from django . db . models import Case , When , IntegerField
2017-02-23 22:28:06 +01:00
2017-03-02 18:28:15 +01:00
from . models import Maintainer , Contact , InetNum , ASBlock , ASNumber
2017-03-22 04:06:24 +01:00
from . validators import HandleValidatorWithSuffix , IP46CIDRValidator
2017-03-26 05:23:00 +02:00
from . formfields import MultiTextInput
2017-02-28 19:44:15 +01:00
2017-03-01 03:21:03 +01:00
import ipaddress
2017-03-14 20:18:57 +01:00
class WhoisObjectFormMixin ( object ) :
2017-02-28 19:44:15 +01:00
def __init__ ( self , user , * args , * * kwargs ) :
2017-03-14 20:18:57 +01:00
super ( WhoisObjectFormMixin , self ) . __init__ ( * args , * * kwargs )
2017-02-28 19:44:15 +01:00
self . _user = user
instance = getattr ( self , ' instance ' , None )
if instance and instance . pk :
2017-03-21 02:36:07 +01:00
self . _create = False
2017-03-22 04:06:24 +01:00
#self.fields['handle'].disabled = True
2017-03-21 02:36:07 +01:00
else :
self . _create = True
2017-02-28 19:44:15 +01:00
2017-03-22 04:06:24 +01:00
self . fields [ ' handle ' ] . help_text = " Handle for this object in uppercase with a suffix of - %s " % instance . handleSuffix
2017-03-02 18:28:15 +01:00
# only show users contacts and already present contacts
2017-03-21 02:36:07 +01:00
mnts = self . _user . maintainer_set . all ( )
2017-03-02 18:28:15 +01:00
if ' admin_c ' in self . fields :
2017-03-21 02:36:07 +01:00
self . fields [ ' admin_c ' ] . queryset = Contact . getMntQueryset ( mnts , self . instance , " admin_c " )
2017-03-02 18:28:15 +01:00
2017-02-28 19:44:15 +01:00
def clean_handle ( self ) :
2017-03-22 04:06:24 +01:00
HandleValidatorWithSuffix ( self . instance . handleSuffix ) ( self . cleaned_data [ ' handle ' ] )
return self . cleaned_data [ ' handle ' ]
2017-02-28 19:44:15 +01:00
def clean ( self ) :
2017-03-14 20:18:57 +01:00
cleaned_data = super ( WhoisObjectFormMixin , self ) . clean ( )
2017-03-01 03:21:03 +01:00
if cleaned_data . get ( " handle " ) == " AUTO " and not self . errors :
2017-03-22 04:06:24 +01:00
name = cleaned_data . get ( " name " )
if name is None :
name = self . _user . username
cleaned_data [ ' handle ' ] = self . _meta . model . genGenericHandle ( name )
2017-02-23 22:28:06 +01:00
2017-03-01 03:21:03 +01:00
return cleaned_data
2017-03-02 18:28:15 +01:00
class MntFormMixin ( object ) :
protectedFields = [ ]
2017-03-22 04:06:24 +01:00
2017-03-02 18:28:15 +01:00
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
instance = getattr ( self , " instance " , None )
2017-03-29 12:25:51 +02:00
if not hasattr ( self , " _create " ) :
self . _create = not ( instance and instance . pk )
2017-03-02 18:28:15 +01:00
mntWhens = [ When ( auth = self . _user , then = 2 ) ]
2017-03-29 12:25:51 +02:00
if not self . _create :
2017-03-02 18:28:15 +01:00
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 " )
2017-03-29 12:25:51 +02:00
# NOTE: We cannot use distinct on a field as some db backends don't support it
#self.fields["mnt_by"].queryset = mntQs.distinct("handle")
2017-03-02 18:28:15 +01:00
self . fields [ " mnt_by " ] . queryset = mntQs
2017-03-21 02:36:07 +01:00
if " mnt_lower " in self . fields :
self . fields [ " mnt_lower " ] . queryset = mntQs
2017-03-22 04:06:24 +01:00
2017-03-29 12:25:51 +02:00
def clean ( self ) :
cleaned_data = super ( MntFormMixin , self ) . clean ( )
if not self . errors and self . _create :
mnts = self . _user . maintainer_set . all ( )
for mnt in cleaned_data [ ' mnt_by ' ] :
if mnt in mnts :
break
else :
raise forms . ValidationError ( " On object creation at least one maintainer needs to be under your control " )
2017-03-21 02:36:07 +01:00
class MntForm ( WhoisObjectFormMixin , forms . ModelForm ) :
2017-02-28 19:44:15 +01:00
class Meta :
model = Maintainer
2017-03-26 05:23:00 +02:00
fields = [ ' handle ' , ' description ' , ' admin_c ' , ' auth ' ]
widgets = { ' auth ' : MultiTextInput ( ) }
help_texts = {
' auth ' : ' Enter names of users which can edit this object (space separated; '
' and yes, validation is somewhat broken (values disappear on error - just reload)) '
}
def __init__ ( self , * args , * * kwargs ) :
super ( MntForm , self ) . __init__ ( * args , * * kwargs )
print ( args , kwargs )
#if self._create:
# self.fields['auth'].text("noot")
2017-02-28 19:44:15 +01:00
2017-03-01 03:21:03 +01:00
2017-02-28 19:44:15 +01:00
class MntInitialForm ( MntForm ) :
2017-02-23 22:28:06 +01:00
class Meta :
model = Maintainer
fields = [ ' handle ' , ' description ' ]
2017-03-01 03:21:03 +01:00
2017-03-14 20:18:57 +01:00
class ContactForm ( WhoisObjectFormMixin , forms . ModelForm ) :
2017-02-23 22:28:06 +01:00
class Meta :
model = Contact
2017-03-14 20:18:57 +01:00
fields = [ ' handle ' , ' name ' , ' mnt_by ' ]
2017-02-23 22:28:06 +01:00
2017-02-28 19:44:15 +01:00
def __init__ ( self , * args , * * kwargs ) :
2017-02-23 22:28:06 +01:00
super ( ContactForm , self ) . __init__ ( * args , * * kwargs )
2017-02-28 19:44:15 +01:00
2017-03-22 04:06:24 +01:00
if " mnt_by " in self . fields :
self . fields [ ' mnt_by ' ] . queryset = Maintainer . objects . filter ( auth = self . _user ) . distinct ( )
2017-03-14 20:18:57 +01:00
2017-03-01 03:21:03 +01:00
2017-02-28 19:44:15 +01:00
class ContactInitialForm ( ContactForm ) :
class Meta :
model = Contact
fields = [ ' handle ' , ' name ' ]
2017-03-01 03:21:03 +01:00
2017-03-14 20:18:57 +01:00
class InetNumForm ( MntFormMixin , WhoisObjectFormMixin , forms . ModelForm ) :
2017-03-01 03:21:03 +01:00
prefix = forms . CharField ( )
2017-03-22 04:06:24 +01:00
protectedFields = [ ' protocol ' , ' parent_range ' , ' mnt_by ' , ' prefix ' ]
2017-03-01 03:21:03 +01:00
2017-02-28 19:44:15 +01:00
class Meta :
model = InetNum
2017-03-27 00:33:29 +02:00
fields = [ ' handle ' , ' protocol ' , ' parent_range ' , ' prefix ' , ' name ' , ' description ' , ' origin_as ' , ' mnt_by ' , ' mnt_lower ' , ' admin_c ' ]
2017-02-28 19:44:15 +01:00
2017-03-02 18:28:15 +01:00
def __init__ ( self , * args , * * kwargs ) :
2017-03-01 03:21:03 +01:00
super ( InetNumForm , self ) . __init__ ( * args , * * kwargs )
2017-02-28 19:44:15 +01:00
if self . _editLower :
2017-03-01 03:21:03 +01:00
for key in self . protectedFields :
self . fields [ key ] . disabled = True
2017-03-21 02:36:07 +01:00
mnts = self . _user . maintainer_set . all ( )
#self.fields['parent_range'].queryset = InetNum.objects.filter(Q(mnt_by__in=mnts) | Q(mnt_lower__in=mnts))
#if not self._create:
# self.fields['parent_range'].queryset |= InetNum.objects.filter(pk=self.instance.pk)
#self.fields['parent_range'].queryset = self.fields['parent_range'].queryset.distinct()
self . fields [ ' parent_range ' ] . queryset = InetNum . getMntQueryset ( mnts , self . instance , " parent_range " )
2017-03-27 00:33:29 +02:00
self . fields [ ' origin_as ' ] . queryset = ASNumber . getMntQueryset ( mnts , self . instance , " origin_as " )
2017-03-01 03:21:03 +01:00
def clean_prefix ( self ) :
# make sure this is a subnet we're getting
2017-03-22 04:06:24 +01:00
net = self . cleaned_data [ ' prefix ' ] . lower ( )
IP46CIDRValidator ( net )
2017-03-01 03:21:03 +01:00
try :
net = ipaddress . ip_network ( net )
except ValueError as e :
raise forms . ValidationError ( str ( e ) )
return net
2017-02-28 19:44:15 +01:00
2017-03-14 20:18:57 +01:00
def clean_parent_range ( self ) :
parent_range = self . cleaned_data . get ( ' parent_range ' , None )
2017-03-22 04:06:24 +01:00
2017-03-27 00:33:29 +02:00
if parent_range . origin_as . count ( ) > 0 :
raise forms . ValidationError ( " Parent range has origin as set " )
2017-03-14 20:18:57 +01:00
# allow parent range to be unset for already present objects
2017-03-22 04:06:24 +01:00
if not parent_range and ( self . _create or not self . _create and self . instance . parent_range ) :
2017-03-14 20:18:57 +01:00
raise forms . ValidationError ( " Parent range must be set " )
2017-03-21 02:36:07 +01:00
if not self . _create and parent_range :
# make sure we don't have circular dependencies
obj = parent_range
while obj . parent_range :
if obj . pk == self . instance . pk :
raise forms . ValidationError ( " No circular dependencies allowed " )
obj = obj . parent_range
return parent_range
2017-02-28 19:44:15 +01:00
def clean ( self ) :
2017-03-01 03:21:03 +01:00
cleaned_data = super ( InetNumForm , self ) . clean ( )
2017-03-14 20:18:57 +01:00
if not self . _editLower :
if not self . errors :
2017-03-27 00:33:29 +02:00
if not self . _create and self . cleaned_data [ ' origin_as ' ] :
if self . instance . inetnum_set . count ( ) > 0 :
ranges = " , " . join ( map ( str , self . instance . inetnum_set . all ( ) ) )
raise forms . ValidationError ( " You cannot set an origin as if there are already existing subranges ( %s ) " % ( ranges ) )
2017-03-01 03:21:03 +01:00
prefix = cleaned_data [ ' prefix ' ]
parent = cleaned_data [ ' parent_range ' ]
2017-03-14 20:18:57 +01:00
if parent :
parentNet = parent . getNetwork ( )
2017-03-01 03:21:03 +01:00
2017-03-14 20:18:57 +01:00
if cleaned_data [ ' protocol ' ] != parent . protocol :
raise forms . ValidationError ( " Protocol type for prefix must be same as parent network " )
2017-03-01 03:21:03 +01:00
2017-03-14 20:18:57 +01:00
# 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 " )
2017-03-01 03:21:03 +01:00
2017-03-27 12:18:02 +02:00
2017-03-14 20:18:57 +01:00
# 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
2017-03-01 03:21:03 +01:00
2017-03-14 20:18:57 +01:00
if otherNet . getNetwork ( ) . overlaps ( prefix ) :
raise forms . ValidationError ( " The given prefix overlaps with network %s " % otherNet . handle )
2017-03-01 03:21:03 +01:00
2017-03-27 12:18:02 +02:00
# check if subnets to this subnet are (still) in current network
if not self . _create :
for subnet in self . instance . inetnum_set . all ( ) :
if subnet . getNetwork ( ) . network_address not in self . instance . getNetwork ( ) :
raise forms . ValidationError ( " Subnet %s with %s is not in block anymore " % ( subnet , subnet . getNetwork ( ) ) )
2017-03-01 03:21:03 +01:00
self . instance . address = str ( prefix . network_address )
self . instance . netmask = prefix . prefixlen
return cleaned_data
2017-03-02 18:28:15 +01:00
2017-03-22 04:06:24 +01:00
2017-03-14 20:18:57 +01:00
class ASBlockForm ( MntFormMixin , WhoisObjectFormMixin , forms . ModelForm ) :
2017-03-22 04:06:24 +01:00
protectedFields = [ ' parent_block ' , ' asBegin ' , ' asEnd ' , ' mnt_by ' ]
2017-03-02 18:28:15 +01:00
# FIXME: Filter blocks
class Meta :
model = ASBlock
2017-03-20 00:16:33 +01:00
fields = [ ' handle ' , ' parent_block ' , ' asBegin ' , ' asEnd ' , ' name ' , ' description ' , ' mnt_by ' , ' mnt_lower ' , ' admin_c ' ]
2017-03-02 18:28:15 +01:00
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
2017-03-21 02:36:07 +01:00
mnts = self . _user . maintainer_set . all ( )
self . fields [ ' parent_block ' ] . queryset = ASBlock . getMntQueryset ( mnts , self . instance , " parent_block " )
2017-03-22 04:06:24 +01:00
def clean_parent_block ( self ) :
parent_block = self . cleaned_data . get ( ' parent_block ' , None )
# allow parent range to be unset for already present objects
if not parent_block and ( self . _create or not self . _create and self . instance . parent_block ) :
raise forms . ValidationError ( " Parent block must be set " )
if not self . _create and parent_block :
# make sure we don't have circular dependencies
obj = parent_block
while obj . parent_block :
if obj . pk == self . instance . pk :
raise forms . ValidationError ( " No circular dependencies allowed " )
obj = obj . parent_block
return parent_block
2017-03-02 18:28:15 +01:00
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
2017-03-22 04:06:24 +01:00
2017-03-02 18:28:15 +01:00
# 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 \
2017-03-22 04:06:24 +01:00
asBegin < = block . asBegin < = asEnd or asBegin < = block . asEnd < = asEnd :
raise forms . ValidationError ( " Block overlaps with block %s " % block . handle )
2017-03-02 18:28:15 +01:00
2017-03-27 12:18:02 +02:00
if not self . _create :
# check if subblocks are still in range
for subblock in self . instance . asblock_set . all ( ) :
if not ( asBegin < = subblock . asBegin < = asEnd and asBegin < = subblock . asEnd < = asEnd ) :
raise forms . ValidationError ( " Subblock %s ( %s - %s ) is not contained in this block anymore " % ( subblock , subblock . asBegin , subblock . asEnd ) )
# check if asnumbers are still in range
for asnumber in self . instance . asnumber_set . all ( ) :
if not ( asBegin < = asnumber . number < = asEnd ) :
raise forms . ValidationError ( " AS %s ( %s ) is not contained in this block anymore " % ( asnumber , asnumber . number ) )
2017-03-02 18:28:15 +01:00
return cleaned_data
2017-03-22 04:06:24 +01:00
2017-03-14 20:18:57 +01:00
class ASNumberForm ( MntFormMixin , WhoisObjectFormMixin , forms . ModelForm ) :
2017-03-22 04:06:24 +01:00
protectedFields = [ ' asblock ' , ' number ' , ' mnt_by ' ]
2017-03-02 18:28:15 +01:00
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
2017-03-21 02:36:07 +01:00
mnts = self . _user . maintainer_set . all ( )
self . fields [ ' asblock ' ] . queryset = ASBlock . getMntQueryset ( mnts , self . instance , " asblock " )
2017-03-02 18:28:15 +01:00
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 ( ) ) ) , ) )