From c058cc7b1d50abe68e72fb4aa3a6e69ef387d715 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sat, 7 Nov 2015 16:10:38 +0000 Subject: [PATCH] Improve DNS table models This commit adds Meta information and __str__ methods to all DNS table models. The new methods are now covered with new tests. The new constants DNS_DOMAIN_METADATA_KINDS and DNS_TSIG_KEY_ALGORITHMS are defined and used in the DNSDomainMetadata and DNSTSIGKey models. A matching database schema migration is added. Addresses #17 --- .../migrations/0004_auto_20151107_1708.py | 44 ++++++++++ gnuviechadmin/domains/models.py | 84 ++++++++++++++++++- gnuviechadmin/domains/tests/test_models.py | 72 +++++++++++++++- 3 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py diff --git a/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py b/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py new file mode 100644 index 0000000..40d407d --- /dev/null +++ b/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('domains', '0003_auto_20151105_2133'), + ] + + operations = [ + migrations.AlterModelOptions( + name='dnscomment', + options={'verbose_name': 'DNS comment', 'verbose_name_plural': 'DNS comments'}, + ), + migrations.AlterModelOptions( + name='dnscryptokey', + options={'verbose_name': 'DNS crypto key', 'verbose_name_plural': 'DNS crypto keys'}, + ), + migrations.AlterModelOptions( + name='dnsdomainmetadata', + options={'verbose_name': 'DNS domain metadata item', 'verbose_name_plural': 'DNS domain metadata items'}, + ), + migrations.AlterModelOptions( + name='dnssupermaster', + options={'verbose_name': 'DNS supermaster', 'verbose_name_plural': 'DNS supermasters'}, + ), + migrations.AlterModelOptions( + name='dnstsigkey', + options={'verbose_name': 'DNS TSIG key', 'verbose_name_plural': 'DNS TSIG keys'}, + ), + migrations.AlterField( + model_name='dnsdomainmetadata', + name='kind', + field=models.CharField(max_length=32, choices=[('ALLOW-DNSUPDATE-FROM', 'ALLOW-DNSUPDATE-FROM'), ('ALSO-NOTIFY', 'ALSO-NOTIFY'), ('AXFR-MASTER-TSIG', 'AXFR-MASTER-TSIG'), ('AXFR-SOURCE', 'AXFR-SOURCE'), ('FORWARD-DNSUPDATE', 'FORWARD-DNSUPDATE'), ('GSS-ACCEPTOR-PRINCIPAL', 'GSS-ACCEPTOR-PRINCIPAL'), ('GSS-ALLOW-AXFR-PRINCIPAL', 'GSS-ALLOW-AXFR-PRINCIPAL'), ('LUA-AXFR-SCRIPT', 'LUA-AXFR-SCRIPT'), ('NSEC3NARROW', 'NSEC3NARROW'), ('NSEC3PARAM', 'NSEC3PARAM'), ('PRESIGNED', 'PRESIGNED'), ('PUBLISH_CDNSKEY', 'PUBLISH_CDNSKEY'), ('PUBLISH_CDS', 'PUBLISH_CDS'), ('SOA-EDIT', 'SOA-EDIT'), ('SOA-EDIT-DNSUPDATE', 'SOA-EDIT-DNSUPDATE'), ('TSIG-ALLOW-AXFR', 'TSIG-ALLOW-AXFR'), ('TSIG-ALLOW-DNSUPDATE', 'TSIG-ALLOW-DNSUPDATE')]), + ), + migrations.AlterField( + model_name='dnstsigkey', + name='algorithm', + field=models.CharField(max_length=50, 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')]), + ), + ] diff --git a/gnuviechadmin/domains/models.py b/gnuviechadmin/domains/models.py index d94cb3c..ed6640a 100644 --- a/gnuviechadmin/domains/models.py +++ b/gnuviechadmin/domains/models.py @@ -19,6 +19,36 @@ DNS_DOMAIN_TYPES = Choices( ('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')), +) + @python_2_unicode_compatible class DomainBase(TimeStampedModel): @@ -144,6 +174,7 @@ class DNSDomain(DomainBase): return self.domain +@python_2_unicode_compatible class DNSRecord(models.Model): """ This model represents a DNS record. The model is similar to the record @@ -195,7 +226,12 @@ class DNSRecord(models.Model): ['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 @@ -217,15 +253,24 @@ class DNSSupermaster(models.Model): settings.AUTH_USER_MODEL, verbose_name=_('customer')) 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/. + in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. The + comments table is used to store user comments related to individual DNS + records. CREATE TABLE comments ( id SERIAL PRIMARY KEY, @@ -257,17 +302,26 @@ class DNSComment(models.Model): # 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/. CREATE TABLE domainmetadata ( id SERIAL PRIMARY KEY, @@ -279,10 +333,19 @@ class DNSDomainMetadata(models.Model): CREATE INDEX domainidmetaindex ON domainmetadata(domain_id); """ domain = models.ForeignKey('DNSDomain') - kind = models.CharField(max_length=32) + 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 @@ -304,7 +367,16 @@ class DNSCryptoKey(models.Model): 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 @@ -321,11 +393,17 @@ class DNSTSIGKey(models.Model): CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm); """ name = models.CharField(max_length=255) - algorithm = models.CharField(max_length=50) + 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) diff --git a/gnuviechadmin/domains/tests/test_models.py b/gnuviechadmin/domains/tests/test_models.py index 76fdcc8..d116d5d 100644 --- a/gnuviechadmin/domains/tests/test_models.py +++ b/gnuviechadmin/domains/tests/test_models.py @@ -10,9 +10,16 @@ from django.test import TestCase from django.contrib.auth import get_user_model from domains.models import ( + DNSComment, + DNSCryptoKey, + DNSDomain, + DNSDomainMetadata, + DNSRecord, + DNSSupermaster, + DNSTSIGKey, DomainBase, - MailDomain, HostingDomain, + MailDomain, ) from hostingpackages.models import ( CustomerHostingPackage, @@ -27,14 +34,14 @@ TEST_USER = 'test' class DomainBaseTest(TestCase): - def test__str__(self): + def test___str__(self): db = DomainBase(domain='test') self.assertEqual(str(db), 'test') class MailDomainTest(TestCase): - def test__str__(self): + def test___str__(self): md = MailDomain.objects.create(domain='example.org') self.assertEqual(str(md), 'example.org') @@ -77,8 +84,65 @@ class HostingDomainManagerTest(TestCase): self.assertIsNotNone(hostingdomain) self.assertTrue(hostingdomain.customer, package.customer) + class HostingDomainTest(TestCase): - def test__str__(self): + def test___str__(self): hostingdomain = HostingDomain(domain='test') self.assertEqual(str(hostingdomain), 'test') + + +class DNSDomainTest(TestCase): + + def test___str__(self): + dnsdomain = DNSDomain(domain='test') + self.assertEqual(str(dnsdomain), 'test') + + +class DNSRecordTest(TestCase): + + def test___str__(self): + dnsrecord = DNSRecord( + name='localhost', recordtype='A', content='127.0.0.1') + self.assertEqual(str(dnsrecord), 'localhost IN A 127.0.0.1') + + +class DNSSupermasterTest(TestCase): + + def test___str__(self): + dnssupermaster = DNSSupermaster( + ip='127.0.0.1', nameserver='dns.example.org') + self.assertEqual(str(dnssupermaster), '127.0.0.1 dns.example.org') + + +class DNSCommentTest(TestCase): + + def test___str__(self): + dnscomment = DNSComment( + name='localhost', commenttype='A', comment='good stuff') + self.assertEqual(str(dnscomment), 'localhost IN A: good stuff') + + +class DNSDomainMetadataTest(TestCase): + + def test___str__(self): + dnsdomain = DNSDomain(domain='test') + dnsdomainmetadata = DNSDomainMetadata( + domain=dnsdomain, kind='SOA-EDIT', content='INCEPTION') + self.assertEqual(str(dnsdomainmetadata), 'test SOA-EDIT INCEPTION') + + +class DNSCryptoKeyTest(TestCase): + + def test___str__(self): + dnsdomain = DNSDomain(domain='test') + dnscryptokey = DNSCryptoKey(domain=dnsdomain, content='testvalue') + self.assertEqual(str(dnscryptokey), 'test testvalue') + + +class DNSTSIGKeyTest(TestCase): + + def test___str__(self): + dnstsigkey = DNSTSIGKey( + name='testkey', algorithm='hmac-md5', secret='dummykey') + self.assertEqual(str(dnstsigkey), 'testkey hmac-md5 XXXX')