2018-01-17 00:23:00 +01:00
# This file is part of dnmgmt, a number resource management system
# Licensed under GNU General Public License v3 or later
# Written by Sebastian Lohff (seba@someserver.de)
2017-02-10 02:45:45 +01:00
from django . db import models
2017-03-21 02:36:07 +01:00
from django . db . models import Q
2017-02-28 19:44:15 +01:00
from django . urls import reverse
2017-02-10 02:45:45 +01:00
from dncore . models import User
2017-02-24 03:43:59 +01:00
from . validators import HandleValidator , HandleValidatorWithSuffix
2017-02-10 02:45:45 +01:00
2017-03-01 03:21:03 +01:00
import ipaddress
2017-02-15 02:35:46 +01:00
class WhoisObject ( models . Model ) :
2019-05-30 22:10:55 +02:00
class Meta :
abstract = True
handleSuffix = " "
handle = models . SlugField ( max_length = 32 , unique = True , verbose_name = ' handle ' , validators = [ HandleValidator ( ) ] )
created = models . DateTimeField ( auto_now_add = True )
last_modified = models . DateTimeField ( auto_now_add = True )
#def __init__(self, *args, **kwargs):
# super(WhoisObject, self).__init__(*args, **kwargs)
# if getattr(self, "handle"):
# field = self._meta.get_field("handle")
# if HandleValidatorWithSuffix not in map(type, field.validators):
# print(self.handle, "NOOOOT")
# field.validators.append(HandleValidatorWithSuffix(self.handleSuffix))
# else:
# print(self.handle, list(map(type, field.validators)))
def getPK ( self ) :
return self . handle
def __str__ ( self ) :
return self . handle
def getAppName ( self ) :
return " whoisdb "
def getClassName ( self ) :
return self . _meta . object_name
def genHandle ( self , main = None ) :
if not main :
main = self . name
return self . genGenericHandle ( main )
@classmethod
def genGenericHandle ( clazz , main ) :
prefix = " "
if " " in main :
parts = main . split ( " " )
prefix = " " . join ( map ( lambda _x : _x [ 0 ] , parts ) )
if len ( prefix ) < 3 and len ( parts [ - 1 ] ) > 1 :
prefix + = parts [ - 1 ] [ 1 : 4 - len ( prefix ) ]
else :
prefix = main [ 0 : 3 ]
prefix = prefix . upper ( )
i = 1
handle = " %s %% d- %s " % ( prefix , clazz . handleSuffix )
while True :
try :
prefix
clazz . objects . get ( handle = handle % i )
i + = 1
except clazz . DoesNotExist :
break
return handle % i
def getNoDeleteReasons ( self ) :
raise NotImplementedError ( " Delete reason checking is not implemented for this model " )
def canBeDeleted ( self ) :
return not bool ( self . getNoDeleteReasons ( ) )
def handleAuto ( self , name = None ) :
if self . handle == " AUTO " :
self . handle = self . genHandle ( name )
2017-02-24 03:43:59 +01:00
2017-02-15 02:35:46 +01:00
class Maintainer ( WhoisObject ) :
2019-05-30 22:10:55 +02:00
handleSuffix = " MNT "
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
auth = models . ManyToManyField ( User )
handle = models . SlugField ( max_length = 32 , unique = True , verbose_name = ' handle ' , validators = [ HandleValidatorWithSuffix ( ' MNT ' ) ] , help_text = " Must end with -MNT, eg FOO3-MNT " )
description = models . CharField ( max_length = 64 , blank = True , help_text = " Short description what this maintainer is for " )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
admin_c = models . ManyToManyField ( " Contact " , verbose_name = " Administrative Contact " )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
rir = models . BooleanField ( default = False )
lir = models . BooleanField ( default = False )
2017-03-14 20:18:57 +01:00
2019-05-30 22:10:55 +02:00
# autoInclude = models.BooleanField(default=True)
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
def get_absolute_url ( self ) :
return reverse ( " whoisdb:handle-detail " , kwargs = { " handle " : self . handle } )
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
def getNoDeleteReasons ( self ) :
reasons = [ ]
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
# FIXME: Tempfix for circular dependency problem
import domains . models
2017-03-29 12:26:37 +02:00
2019-05-30 22:10:55 +02:00
mntables = [ Contact , ASBlock , ASNumber , InetNum , domains . models . Domain , domains . models . Nameserver ]
for mntable in mntables :
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 )
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
return reasons
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
def canEdit ( self , user ) :
return user in self . auth . all ( )
2017-03-20 00:16:33 +01:00
2017-02-24 03:43:59 +01:00
class MntdObject ( WhoisObject ) :
2019-05-30 22:10:55 +02:00
class Meta :
abstract = True
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
mnt_by = models . ManyToManyField ( Maintainer , help_text = " You can select multiple maintainers here " )
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
def canEdit ( self , user ) :
if not hasattr ( user , " maintainer_set " ) :
return False
2017-03-27 13:36:03 +02:00
2019-05-30 22:10:55 +02:00
mnts = user . maintainer_set . all ( )
objmnts = self . mnt_by . all ( )
if hasattr ( self , " mnt_lower " ) :
objmnts | = self . mnt_lower . all ( )
2017-03-20 00:16:33 +01:00
2019-05-30 22:10:55 +02:00
for objmnt in objmnts :
if objmnt in mnts :
return True
return False
2017-03-20 00:16:33 +01:00
2019-05-30 22:10:55 +02:00
@classmethod
def getMntQueryset ( clazz , mnts , instance , attr = None ) :
mntQ = Q ( mnt_by__in = mnts )
if hasattr ( clazz , " mnt_lower " ) :
mntQ | = Q ( mnt_lower__in = mnts )
2017-03-21 02:36:07 +01:00
2019-05-30 22:10:55 +02:00
qs = clazz . objects . filter ( mntQ )
if attr and instance and instance . pk :
if type ( instance . _meta . get_field ( attr ) ) == models . ManyToManyField :
qs | = getattr ( instance , attr ) . all ( )
elif getattr ( instance , attr ) is not None :
qs | = clazz . objects . filter ( pk = getattr ( instance , attr ) . pk )
2017-03-21 02:36:07 +01:00
2019-05-30 22:10:55 +02:00
return qs . distinct ( )
2017-02-15 02:35:46 +01:00
2017-03-22 04:06:24 +01:00
2017-02-15 02:35:46 +01:00
class Contact ( MntdObject ) :
2019-05-30 22:10:55 +02:00
handleSuffix = " DN "
TYPE_PERSON = ' PERSON '
TYPE_ROLE = ' ROLE '
TYPE = ( ( ' person ' , TYPE_PERSON ) , ( ' role ' , TYPE_ROLE ) )
TYPE = ( ( ' person ' , TYPE_PERSON ) , )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
name = models . CharField ( max_length = 128 )
type = models . CharField ( max_length = 10 , choices = TYPE , default = TYPE_PERSON )
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
def get_absolute_url ( self ) :
return reverse ( " whoisdb:handle-detail " , kwargs = { " handle " : self . handle } )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
def getNoDeleteReasons ( self ) :
reasons = [ ]
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
contactables = [ Maintainer , ASBlock , ASNumber , InetNum ]
for contactable in contactables :
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 )
2017-03-01 03:21:03 +01:00
2019-05-30 22:10:55 +02:00
return reasons
2017-02-24 03:43:59 +01:00
2017-03-01 03:21:03 +01:00
2017-02-21 20:45:44 +01:00
class ASBlock ( MntdObject ) :
2019-05-30 22:10:55 +02:00
handleSuffix = " ASB "
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
parent_block = models . ForeignKey ( " ASBlock " , models . CASCADE , null = True , blank = True , default = None )
name = models . CharField ( max_length = 32 )
asBegin = models . PositiveIntegerField ( )
asEnd = models . PositiveIntegerField ( )
description = models . CharField ( max_length = 64 , blank = True )
admin_c = models . ManyToManyField ( " Contact " )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
mnt_lower = models . ManyToManyField ( Maintainer , related_name = ' lower_asblock_set ' , blank = True )
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
def contains ( self , block ) :
return self . asBegin < = block . asBegin < = self . asEnd and self . asBegin < = block . asEnd < = self . asEnd
2017-03-27 12:18:02 +02:00
2019-05-30 22:10:55 +02:00
def getResource ( self ) :
return " %s - %s " % ( self . asBegin , self . asEnd )
2017-03-20 00:16:33 +01:00
2019-05-30 22:10:55 +02:00
def get_absolute_url ( self ) :
return reverse ( " whoisdb:handle-detail " , kwargs = { " handle " : self . handle } )
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
def getNoDeleteReasons ( self ) :
reasons = [ ]
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
if self . asblock_set . count ( ) > 0 :
reasons . append ( " The AS block is referenced by the following other blocks: %s " % ( " , " . join ( map ( lambda _x : _x . handle , self . asblock_set . all ( ) ) ) ) )
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
if self . asnumber_set . count ( ) > 0 :
reasons . append ( " The AS block is referenced by the following as numbers: %s " % ( " , " . join ( map ( lambda _x : _x . handle , self . asnumber_set . all ( ) ) ) ) )
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
return reasons
2017-03-01 03:21:03 +01:00
2017-03-15 03:07:43 +01:00
2017-02-21 20:45:44 +01:00
class ASNumber ( MntdObject ) :
2019-05-30 22:10:55 +02:00
handleSuffix = " AS "
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
number = models . PositiveIntegerField ( unique = True , db_index = True )
volatile = models . BooleanField ( default = False , help_text = " Check if this AS is not going to be online 24/7 (for example on a laptop) " )
asblock = models . ForeignKey ( ASBlock , models . CASCADE )
name = models . CharField ( max_length = 32 )
description = models . CharField ( max_length = 64 , blank = True )
admin_c = models . ManyToManyField ( " Contact " )
2017-02-21 20:45:44 +01:00
2019-05-30 22:10:55 +02:00
mnt_lower = models . ManyToManyField ( Maintainer , related_name = ' lower_asnumber_set ' , blank = True )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
def get_absolute_url ( self ) :
return reverse ( " whoisdb:handle-detail " , kwargs = { " handle " : self . handle } )
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
def getNoDeleteReasons ( self ) :
reasons = [ ]
2017-03-02 18:28:15 +01:00
2019-05-30 22:10:55 +02:00
return reasons
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
def getResource ( self ) :
return str ( self . number )
2017-03-15 03:07:43 +01:00
2017-03-22 04:06:24 +01:00
2017-02-28 19:44:15 +01:00
class InetNum ( MntdObject ) :
2019-05-30 22:10:55 +02:00
class Meta :
unique_together = (
( " address " , " netmask " ) ,
)
2017-03-27 00:33:29 +02:00
2019-05-30 22:10:55 +02:00
handleSuffix = " NET "
2017-02-24 03:43:59 +01:00
2019-05-30 22:10:55 +02:00
IPv4 = " ipv4 "
IPv6 = " ipv6 "
2017-02-28 19:44:15 +01:00
2019-05-30 22:10:55 +02:00
PROTO = ( ( IPv4 , ' IPv4 ' ) , ( IPv6 , ' IPv6 ' ) )
protocol = models . CharField ( max_length = 4 , choices = PROTO )
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 )
origin_as = models . ManyToManyField ( ASNumber , blank = True )
admin_c = models . ManyToManyField ( " Contact " )
2017-02-15 02:35:46 +01:00
2019-05-30 22:10:55 +02:00
mnt_lower = models . ManyToManyField ( Maintainer , related_name = ' lower_inetnum_set ' , blank = True )
2017-03-01 03:21:03 +01:00
2019-05-30 22:10:55 +02:00
def getResource ( self ) :
return self . prefix ( )
2017-03-20 00:16:33 +01:00
2019-05-30 22:10:55 +02:00
def prefix ( self ) :
""" Helper function, mainly used in templates """
return " %s / %s " % ( self . address , self . netmask )
2017-03-15 03:07:43 +01:00
2019-05-30 22:10:55 +02:00
def getNetwork ( self ) :
return ipaddress . ip_network ( self . prefix ( ) )
2017-03-01 03:21:03 +01:00
2019-05-30 22:10:55 +02:00
def get_absolute_url ( self ) :
return reverse ( " whoisdb:handle-detail " , kwargs = { " handle " : self . handle } )
2017-03-01 03:21:03 +01:00
2019-05-30 22:10:55 +02:00
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 ( ) ) ) )
2017-03-01 03:21:03 +01:00
2019-05-30 22:10:55 +02:00
return reasons