Jan Dittberner
6cebd80c89
This commit is a rough port to Django 2.1, Python 3 and a Docker based local development setup. Tests fail/error but migrations and the web frontend are already runnable. Task queue functionality is untested and translations seem to have trouble.
431 lines
14 KiB
Python
431 lines
14 KiB
Python
"""
|
|
This module contains models related to domain names.
|
|
|
|
"""
|
|
from __future__ import absolute_import, unicode_literals
|
|
|
|
from django.db import models, transaction
|
|
from django.conf import settings
|
|
from django.utils.encoding import python_2_unicode_compatible
|
|
from django.utils.translation import ugettext as _
|
|
|
|
from model_utils.models import TimeStampedModel
|
|
from model_utils import Choices
|
|
|
|
|
|
DNS_DOMAIN_TYPES = Choices(
|
|
('MASTER', _('Master')),
|
|
('SLAVE', _('Slave')),
|
|
('NATIVE', _('Native')),
|
|
)
|
|
|
|
# see https://doc.powerdns.com/md/authoritative/domainmetadata/
|
|
DNS_DOMAIN_METADATA_KINDS = Choices(
|
|
'ALLOW-DNSUPDATE-FROM',
|
|
'ALSO-NOTIFY',
|
|
'AXFR-MASTER-TSIG',
|
|
'AXFR-SOURCE',
|
|
'FORWARD-DNSUPDATE',
|
|
'GSS-ACCEPTOR-PRINCIPAL',
|
|
'GSS-ALLOW-AXFR-PRINCIPAL',
|
|
'LUA-AXFR-SCRIPT',
|
|
'NSEC3NARROW',
|
|
'NSEC3PARAM',
|
|
'PRESIGNED',
|
|
'PUBLISH_CDNSKEY',
|
|
'PUBLISH_CDS',
|
|
'SOA-EDIT',
|
|
'SOA-EDIT-DNSUPDATE',
|
|
'TSIG-ALLOW-AXFR',
|
|
'TSIG-ALLOW-DNSUPDATE',
|
|
)
|
|
|
|
DNS_TSIG_KEY_ALGORITHMS = Choices(
|
|
('hmac-md5', _('HMAC MD5')),
|
|
('hmac-sha1', _('HMAC SHA1')),
|
|
('hmac-sha224', _('HMAC SHA224')),
|
|
('hmac-sha256', _('HMAC SHA256')),
|
|
('hmac-sha384', _('HMAC SHA384')),
|
|
('hmac-sha512', _('HMAC SHA512')),
|
|
)
|
|
|
|
|
|
class DomainBase(TimeStampedModel):
|
|
"""
|
|
This is the base model for domains.
|
|
|
|
"""
|
|
domain = models.CharField(_('domain name'), max_length=255, unique=True)
|
|
customer = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL, verbose_name=_('customer'), blank=True,
|
|
null=True, on_delete=models.CASCADE)
|
|
|
|
class Meta:
|
|
abstract = True
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class MailDomain(DomainBase):
|
|
"""
|
|
This is the model for mail domains. Mail domains are used to configure the
|
|
mail servers (SMTP/IMAP/POP3). Mail addresses are assigned to these mail
|
|
domains.
|
|
|
|
"""
|
|
class Meta:
|
|
verbose_name = _('Mail domain')
|
|
verbose_name_plural = _('Mail domains')
|
|
|
|
def __str__(self):
|
|
return self.domain
|
|
|
|
def get_mailaddresses(self):
|
|
"""
|
|
Get a list of mail addresses assigned to this mail domain.
|
|
|
|
"""
|
|
return self.mailaddress_set.all()
|
|
mailaddresses = property(get_mailaddresses)
|
|
|
|
|
|
class HostingDomainManager(models.Manager):
|
|
"""
|
|
Default Manager for :py:class:`HostingDomain`.
|
|
|
|
"""
|
|
@transaction.atomic
|
|
def create_for_hosting_package(
|
|
self, hosting_package, domain, commit, **kwargs
|
|
):
|
|
from hostingpackages.models import CustomerHostingPackageDomain
|
|
hostingdomain = self.create(
|
|
customer=hosting_package.customer, domain=domain, **kwargs)
|
|
hostingdomain.maildomain = MailDomain.objects.create(
|
|
customer=hosting_package.customer, domain=domain)
|
|
custdomain = CustomerHostingPackageDomain.objects.create(
|
|
hosting_package=hosting_package, domain=hostingdomain)
|
|
if commit:
|
|
hostingdomain.save()
|
|
custdomain.save()
|
|
return hostingdomain
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class HostingDomain(DomainBase):
|
|
"""
|
|
This is the model for hosting domains. A hosting domain is linked to a
|
|
customer hosting account.
|
|
|
|
"""
|
|
maildomain = models.OneToOneField(
|
|
MailDomain, verbose_name=_('mail domain'), blank=True, null=True,
|
|
help_text=_('assigned mail domain for this domain'),
|
|
on_delete=models.CASCADE,
|
|
)
|
|
|
|
objects = HostingDomainManager()
|
|
|
|
class Meta:
|
|
verbose_name = _('Hosting domain')
|
|
verbose_name_plural = _('Hosting domains')
|
|
|
|
def __str__(self):
|
|
return self.domain
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSDomain(DomainBase):
|
|
"""
|
|
This model represents a DNS zone. The model is similar to the domain table
|
|
in the PowerDNS schema specified in
|
|
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE domains (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
master VARCHAR(128) DEFAULT NULL,
|
|
last_check INT DEFAULT NULL,
|
|
type VARCHAR(6) NOT NULL,
|
|
notified_serial INT DEFAULT NULL,
|
|
account VARCHAR(40) DEFAULT NULL,
|
|
CONSTRAINT c_lowercase_name CHECK (
|
|
((name)::TEXT = LOWER((name)::TEXT)))
|
|
);
|
|
|
|
CREATE UNIQUE INDEX name_index ON domains(name);
|
|
|
|
"""
|
|
# name is represented by domain
|
|
master = models.CharField(max_length=128, blank=True, null=True)
|
|
last_check = models.IntegerField(null=True)
|
|
domaintype = models.CharField(
|
|
max_length=6, choices=DNS_DOMAIN_TYPES, db_column='type')
|
|
notified_serial = models.IntegerField(null=True)
|
|
# account is represented by customer_id
|
|
# check constraint is added via RunSQL in migration
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS domain')
|
|
verbose_name_plural = _('DNS domains')
|
|
|
|
def __str__(self):
|
|
return self.domain
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSRecord(models.Model):
|
|
"""
|
|
This model represents a DNS record. The model is similar to the record
|
|
table in the PowerDNS schema specified in
|
|
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE records (
|
|
id SERIAL PRIMARY KEY,
|
|
domain_id INT DEFAULT NULL,
|
|
name VARCHAR(255) DEFAULT NULL,
|
|
type VARCHAR(10) DEFAULT NULL,
|
|
content VARCHAR(65535) DEFAULT NULL,
|
|
ttl INT DEFAULT NULL,
|
|
prio INT DEFAULT NULL,
|
|
change_date INT DEFAULT NULL,
|
|
disabled BOOL DEFAULT 'f',
|
|
ordername VARCHAR(255),
|
|
auth BOOL DEFAULT 't',
|
|
CONSTRAINT domain_exists
|
|
FOREIGN KEY(domain_id) REFERENCES domains(id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT c_lowercase_name CHECK (
|
|
((name)::TEXT = LOWER((name)::TEXT)))
|
|
);
|
|
|
|
CREATE INDEX rec_name_index ON records(name);
|
|
CREATE INDEX nametype_index ON records(name,type);
|
|
CREATE INDEX domain_id ON records(domain_id);
|
|
CREATE INDEX recordorder ON records (
|
|
domain_id, ordername text_pattern_ops);
|
|
|
|
"""
|
|
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
name = models.CharField(
|
|
max_length=255, blank=True, null=True, db_index=True)
|
|
recordtype = models.CharField(
|
|
max_length=10, blank=True, null=True, db_column='type')
|
|
content = models.CharField(max_length=65535, blank=True, null=True)
|
|
ttl = models.IntegerField(null=True)
|
|
prio = models.IntegerField(null=True)
|
|
change_date = models.IntegerField(null=True)
|
|
disabled = models.BooleanField(default=False)
|
|
ordername = models.CharField(max_length=255)
|
|
auth = models.BooleanField(default=True)
|
|
# check constraint and index recordorder are added via RunSQL in migration
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS record')
|
|
verbose_name_plural = _('DNS records')
|
|
index_together = [
|
|
['name', 'recordtype']
|
|
]
|
|
|
|
def __str__(self):
|
|
return "{name} IN {type} {content}".format(
|
|
name=self.name, type=self.recordtype, content=self.content)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSSupermaster(models.Model):
|
|
"""
|
|
This model represents the supermasters table in the PowerDNS schema
|
|
specified in
|
|
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE supermasters (
|
|
ip INET NOT NULL,
|
|
nameserver VARCHAR(255) NOT NULL,
|
|
account VARCHAR(40) NOT NULL,
|
|
PRIMARY KEY(ip, nameserver)
|
|
);
|
|
|
|
"""
|
|
ip = models.GenericIPAddressField()
|
|
nameserver = models.CharField(max_length=255)
|
|
# account is replaced by customer
|
|
customer = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL, verbose_name=_('customer'),
|
|
on_delete=models.CASCADE)
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS supermaster')
|
|
verbose_name_plural = _('DNS supermasters')
|
|
unique_together = (
|
|
('ip', 'nameserver')
|
|
)
|
|
|
|
def __str__(self):
|
|
return "{ip} {nameserver}".format(
|
|
ip=self.ip, nameserver=self.nameserver)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSComment(models.Model):
|
|
"""
|
|
This model represents the comments table in the PowerDNS schema specified
|
|
in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. The
|
|
comments table is used to store user comments related to individual DNS
|
|
records.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE comments (
|
|
id SERIAL PRIMARY KEY,
|
|
domain_id INT NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
type VARCHAR(10) NOT NULL,
|
|
modified_at INT NOT NULL,
|
|
account VARCHAR(40) DEFAULT NULL,
|
|
comment VARCHAR(65535) NOT NULL,
|
|
CONSTRAINT domain_exists
|
|
FOREIGN KEY(domain_id) REFERENCES domains(id)
|
|
ON DELETE CASCADE,
|
|
CONSTRAINT c_lowercase_name CHECK (
|
|
((name)::TEXT = LOWER((name)::TEXT)))
|
|
);
|
|
|
|
CREATE INDEX comments_domain_id_idx ON comments (domain_id);
|
|
CREATE INDEX comments_name_type_idx ON comments (name, type);
|
|
CREATE INDEX comments_order_idx ON comments (domain_id, modified_at);
|
|
|
|
"""
|
|
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
name = models.CharField(max_length=255)
|
|
commenttype = models.CharField(max_length=10, db_column='type')
|
|
modified_at = models.IntegerField()
|
|
# account is replaced by customer
|
|
customer = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL, verbose_name=_('customer'),
|
|
on_delete=models.CASCADE)
|
|
comment = models.CharField(max_length=65535)
|
|
# check constraint is added via RunSQL in migration
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS comment')
|
|
verbose_name_plural = _('DNS comments')
|
|
index_together = [
|
|
['name', 'commenttype'],
|
|
['domain', 'modified_at']
|
|
]
|
|
|
|
def __str__(self):
|
|
return "{name} IN {type}: {comment}".format(
|
|
name=self.name, type=self.commenttype, comment=self.comment)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSDomainMetadata(models.Model):
|
|
"""
|
|
This model represents the domainmetadata table in the PowerDNS schema
|
|
specified in
|
|
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
The domainmetadata table is used to store domain meta data as described in
|
|
https://doc.powerdns.com/md/authoritative/domainmetadata/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE domainmetadata (
|
|
id SERIAL PRIMARY KEY,
|
|
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
|
|
kind VARCHAR(32),
|
|
content TEXT
|
|
);
|
|
|
|
CREATE INDEX domainidmetaindex ON domainmetadata(domain_id);
|
|
|
|
"""
|
|
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
kind = models.CharField(max_length=32, choices=DNS_DOMAIN_METADATA_KINDS)
|
|
content = models.TextField()
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS domain metadata item')
|
|
verbose_name_plural = _('DNS domain metadata items')
|
|
|
|
def __str__(self):
|
|
return "{domain} {kind} {content}".format(
|
|
domain=self.domain.domain, kind=self.kind, content=self.content)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSCryptoKey(models.Model):
|
|
"""
|
|
This model represents the cryptokeys table in the PowerDNS schema
|
|
specified in
|
|
https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE cryptokeys (
|
|
id SERIAL PRIMARY KEY,
|
|
domain_id INT REFERENCES domains(id) ON DELETE CASCADE,
|
|
flags INT NOT NULL,
|
|
active BOOL,
|
|
content TEXT
|
|
);
|
|
|
|
CREATE INDEX domainidindex ON cryptokeys(domain_id);
|
|
|
|
"""
|
|
domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE)
|
|
flags = models.IntegerField()
|
|
active = models.BooleanField(default=True)
|
|
content = models.TextField()
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS crypto key')
|
|
verbose_name_plural = _('DNS crypto keys')
|
|
|
|
def __str__(self):
|
|
return "{domain} {content}".format(
|
|
domain=self.domain.domain, content=self.content)
|
|
|
|
|
|
@python_2_unicode_compatible
|
|
class DNSTSIGKey(models.Model):
|
|
"""
|
|
This model represents the tsigkeys table in the PowerDNS schema specified
|
|
in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/.
|
|
|
|
.. code-block:: sql
|
|
|
|
CREATE TABLE tsigkeys (
|
|
id SERIAL PRIMARY KEY,
|
|
name VARCHAR(255),
|
|
algorithm VARCHAR(50),
|
|
secret VARCHAR(255),
|
|
CONSTRAINT c_lowercase_name CHECK (
|
|
((name)::TEXT = LOWER((name)::TEXT)))
|
|
);
|
|
|
|
CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm);
|
|
|
|
"""
|
|
name = models.CharField(max_length=255)
|
|
algorithm = models.CharField(
|
|
max_length=50, choices=DNS_TSIG_KEY_ALGORITHMS)
|
|
secret = models.CharField(max_length=255)
|
|
# check constraint is added via RunSQL in migration
|
|
|
|
class Meta:
|
|
verbose_name = _('DNS TSIG key')
|
|
verbose_name_plural = _('DNS TSIG keys')
|
|
unique_together = [
|
|
['name', 'algorithm']
|
|
]
|
|
|
|
def __str__(self):
|
|
return "{name} {algorithm} XXXX".format(
|
|
name=self.name, algorithm=self.algorithm)
|