@@ -219,6 +219,11 @@ def filter_qname(self, qname: str, **kwargs) -> models.query.QuerySet:
219219 name_length = Length ('name' ),
220220 ).filter (dotted_qname__endswith = F ('dotted_name' ), ** kwargs )
221221
222+ def most_specific_zone (self , fqdn : str ) -> Tuple [Domain , str ]:
223+ domain = self .filter_qname (fqdn ).order_by ('-name_length' ).first ()
224+ subname = fqdn [:- len (domain .name )].rstrip ('.' )
225+ return domain , subname
226+
222227
223228class Domain (ExportModelOperationsMixin ('Domain' ), models .Model ):
224229 @staticmethod
@@ -994,37 +999,39 @@ class Identity(models.Model):
994999 created = models .DateTimeField (auto_now_add = True )
9951000 owner = models .ForeignKey (User , on_delete = models .PROTECT , related_name = 'identities' )
9961001 default_ttl = models .PositiveIntegerField (default = 300 )
1002+ rrs = models .ManyToManyField (to = RR )
1003+ scheduled_removal = models .DateTimeField (null = True )
9971004
9981005 class Meta :
9991006 abstract = True
10001007
1001- def get_record_contents (self ) -> List [str ]:
1002- raise NotImplementedError
1003-
1004- def save_rrs (self ):
1008+ def get_rrs (self ) -> List [RR ]:
10051009 raise NotImplementedError
10061010
10071011 def save (self , * args , ** kwargs ):
1008- self .save_rrs ()
1012+ for rr in self .get_rrs ():
1013+ self .rrs .add (rr )
1014+ rr .rrset .save ()
1015+ rr .save ()
10091016 return super ().save (* args , ** kwargs )
10101017
1011- def delete_rrs (self ):
1012- raise NotImplementedError
1013-
10141018 def delete (self , using = None , keep_parents = False ):
1015- # TODO this will delete also RRs that may be covered by other identities
1016- self .delete_rrs ()
1019+ for rr in self .rrs .all (): # TODO use one query
1020+ if len (rr .identities ) == 1 :
1021+ rr .delete ()
10171022 return super ().delete (using , keep_parents )
10181023
1024+ # TODO move to RRset / RRset manager?
10191025 def get_or_create_rr_set (self , domain : Domain , subname : str ) -> RRset :
10201026 try :
10211027 return RRset .objects .get (domain = domain , subname = subname , type = self .rr_type )
10221028 except RRset .DoesNotExist :
1023- # TODO save this RRset?
10241029 return RRset (domain = domain , subname = subname , type = self .rr_type , ttl = self .default_ttl )
10251030
1026- @staticmethod
1027- def get_or_create_rr (rrset : RRset , content : str ) -> RR :
1031+ # TODO move to RR / RR manager?
1032+ def get_or_create_rr (self , fqdn : str , content : str ) -> RR :
1033+ domain , subname = self .owner .domains .most_specific_zone (fqdn )
1034+ rrset = self .get_or_create_rr_set (domain , subname )
10281035 try :
10291036 return RR .objects .get (rrset = rrset , content = content )
10301037 except RR .DoesNotExist :
@@ -1064,8 +1071,6 @@ class Protocol(models.TextChoices):
10641071 port = models .IntegerField (default = 443 )
10651072 protocol = models .TextField (choices = Protocol .choices , default = Protocol .TCP )
10661073
1067- scheduled_removal = models .DateTimeField (null = True )
1068-
10691074 def __init__ (self , * args , ** kwargs ):
10701075 super ().__init__ (* args , ** kwargs )
10711076 if 'not_valid_after' not in kwargs :
@@ -1123,20 +1128,9 @@ def subject_names(self) -> Set[str]:
11231128
11241129 return subject_names | subject_alternative_names
11251130
1126- @staticmethod
1127- def get_closest_ancestor (domain_name , owner : User ) -> Optional [Domain ]:
1128- # TODO move to Domain?
1129- labels = domain_name .split ('.' )
1130- ancestor_names = ['.' .join (labels [i :]) for i in range (len (labels ))]
1131- for ancestor_name in ancestor_names : # TODO do this with one query
1132- try :
1133- return Domain .objects .get (name = ancestor_name , owner = owner )
1134- except Domain .DoesNotExist :
1135- continue
1136- return None
1137-
1138- def domains_subnames (self ) -> Set [Tuple [Domain , str ]]:
1139- domains_subnames = set ()
1131+ @property
1132+ def subject_names_clean (self ) -> Set [str ]:
1133+ clean = set ()
11401134 for name in self .subject_names :
11411135 # cut off any wildcard prefix
11421136 name = name .lstrip ('*' ).lstrip ('.' )
@@ -1147,41 +1141,18 @@ def domains_subnames(self) -> Set[Tuple[Domain, str]]:
11471141 except ValidationError :
11481142 continue
11491143
1150- # find user-owned parent domain
1151- domain = self .get_closest_ancestor (name , self .owner )
1152- if not domain :
1153- continue
1154- subname = name [:- len (domain .name )].rstrip ('.' )
1155-
1156- # return subname, domain pair
1157- domains_subnames .add ((domain , f"_{ self .port :n} ._{ self .protocol } .{ subname } " .rstrip ('.' )))
1158- return domains_subnames
1159-
1160- def get_rrsets (self ) -> List [RRset ]:
1161- rrsets = []
1162- for domain , subname in self .domains_subnames ():
1163- rrsets .append (self .get_or_create_rr_set (domain , subname ))
1164- return rrsets
1144+ clean .add (name )
1145+ return clean
11651146
11661147 def get_rrs (self ) -> List [RR ]:
1167- rrs = []
1168- for domain , subname in self .domains_subnames ():
1169- rrset = self .get_or_create_rr_set (domain , subname )
1170- for content in self .get_record_contents ():
1171- rrs .append (self .get_or_create_rr (rrset = rrset , content = content ))
1172- return rrs
1173-
1174- def save_rrs (self ):
1175- for rr in self .get_rrs ():
1176- rr .rrset .save ()
1177- rr .save ()
1178-
1179- def delete_rrs (self ):
1180- for domain , subname in self .domains_subnames ():
1181- rrset = self .get_or_create_rr_set (domain , subname )
1182- rrset .records .filter (content__in = self .get_record_contents ()).delete ()
1183- if not len (rrset .records .all ()):
1184- rrset .delete ()
1148+ return [
1149+ self .get_or_create_rr (
1150+ fqdn = f"_{ self .port :n} ._{ self .protocol } .{ qname } " ,
1151+ content = content ,
1152+ )
1153+ for qname in self .subject_names_clean
1154+ for content in self .get_record_contents ()
1155+ ]
11851156
11861157 @property
11871158 def not_valid_before (self ):
0 commit comments