diff --git a/gnuviechadmin/domains/admin.py b/gnuviechadmin/domains/admin.py index 0369116..87be497 100644 --- a/gnuviechadmin/domains/admin.py +++ b/gnuviechadmin/domains/admin.py @@ -5,7 +5,24 @@ with the django admin site. """ from django.contrib import admin -from domains.models import HostingDomain, MailDomain +from .models import ( + DNSComment, + DNSCryptoKey, + DNSDomain, + DNSDomainMetadata, + DNSRecord, + DNSSupermaster, + DNSTSIGKey, + HostingDomain, + MailDomain, +) admin.site.register(MailDomain) admin.site.register(HostingDomain) +admin.site.register(DNSComment) +admin.site.register(DNSCryptoKey) +admin.site.register(DNSDomain) +admin.site.register(DNSDomainMetadata) +admin.site.register(DNSRecord) +admin.site.register(DNSSupermaster) +admin.site.register(DNSTSIGKey) diff --git a/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py b/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py deleted file mode 100644 index 87e9c32..0000000 --- a/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py +++ /dev/null @@ -1,74 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-15 09:53 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('domains', '0004_auto_20151107_1708'), - ] - - operations = [ - migrations.AlterIndexTogether( - name='dnscomment', - index_together=None, - ), - migrations.RemoveField( - model_name='dnscomment', - name='customer', - ), - migrations.RemoveField( - model_name='dnscomment', - name='domain', - ), - migrations.RemoveField( - model_name='dnscryptokey', - name='domain', - ), - migrations.RemoveField( - model_name='dnsdomain', - name='customer', - ), - migrations.RemoveField( - model_name='dnsdomainmetadata', - name='domain', - ), - migrations.AlterIndexTogether( - name='dnsrecord', - index_together=None, - ), - migrations.RemoveField( - model_name='dnsrecord', - name='domain', - ), - migrations.AlterUniqueTogether( - name='dnssupermaster', - unique_together=None, - ), - migrations.RemoveField( - model_name='dnssupermaster', - name='customer', - ), - migrations.DeleteModel( - name='DNSTSIGKey', - ), - migrations.DeleteModel( - name='DNSComment', - ), - migrations.DeleteModel( - name='DNSCryptoKey', - ), - migrations.DeleteModel( - name='DNSDomain', - ), - migrations.DeleteModel( - name='DNSDomainMetadata', - ), - migrations.DeleteModel( - name='DNSRecord', - ), - migrations.DeleteModel( - name='DNSSupermaster', - ), - ] diff --git a/gnuviechadmin/domains/models.py b/gnuviechadmin/domains/models.py index 7332dd6..00a96e3 100644 --- a/gnuviechadmin/domains/models.py +++ b/gnuviechadmin/domains/models.py @@ -7,8 +7,45 @@ from __future__ import absolute_import from django.conf import settings from django.db import models, transaction from django.utils.translation import gettext as _ +from model_utils import Choices from model_utils.models import TimeStampedModel +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): """ @@ -103,3 +140,296 @@ class HostingDomain(DomainBase): def __str__(self): return self.domain + + +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 + + +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 + ) + + +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) + + +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 + ) + + +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 + ) + + +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 + ) + + +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 + ) diff --git a/gnuviechadmin/gnuviechadmin/__init__.py b/gnuviechadmin/gnuviechadmin/__init__.py index 7dfb6e8..1fd249f 100644 --- a/gnuviechadmin/gnuviechadmin/__init__.py +++ b/gnuviechadmin/gnuviechadmin/__init__.py @@ -1,4 +1,4 @@ # import celery_app to initialize it from gnuviechadmin.celery import app as celery_app # NOQA -__version__ = "0.13.0" +__version__ = '0.12.1' diff --git a/gnuviechadmin/gnuviechadmin/settings.py b/gnuviechadmin/gnuviechadmin/settings.py index 243b6e8..b1a6d1c 100644 --- a/gnuviechadmin/gnuviechadmin/settings.py +++ b/gnuviechadmin/gnuviechadmin/settings.py @@ -86,6 +86,7 @@ USE_TZ = True LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),) + # ######### MEDIA CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root MEDIA_ROOT = normpath(join(SITE_ROOT, "media")) @@ -179,6 +180,7 @@ AUTHENTICATION_BACKENDS = ( "allauth.account.auth_backends.AuthenticationBackend", ) + # ######### URL CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf ROOT_URLCONF = "%s.urls" % SITE_NAME @@ -206,7 +208,6 @@ DJANGO_APPS = ( # Flatpages for about page "django.contrib.flatpages", "crispy_forms", - "impersonate", ) ALLAUTH_APPS = ( @@ -276,7 +277,7 @@ LOGGING = { "formatters": { "verbose": { "format": "%(levelname)s %(asctime)s %(name)s " - "%(module)s:%(lineno)d %(process)d %(thread)d %(message)s" + "%(module)s:%(lineno)d %(process)d %(thread)d %(message)s" }, "simple": {"format": "%(levelname)s %(name)s:%(lineno)d %(message)s"}, }, @@ -365,10 +366,7 @@ def show_debug_toolbar(request): # See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa INSTALLED_APPS += ("debug_toolbar",) -MIDDLEWARE += [ - "impersonate.middleware.ImpersonateMiddleware", - "debug_toolbar.middleware.DebugToolbarMiddleware", -] +MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] DEBUG_TOOLBAR_CONFIG = { "SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar" @@ -405,21 +403,21 @@ if GVA_ENVIRONMENT == "local": [ (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True}) for key in [ - "dashboard", - "domains", - "fileservertasks", - "gvacommon", - "gvawebcore", - "hostingpackages", - "ldaptasks", - "managemails", - "mysqltasks", - "osusers", - "pgsqltasks", - "taskresults", - "userdbs", - "websites", - ] + "dashboard", + "domains", + "fileservertasks", + "gvacommon", + "gvawebcore", + "hostingpackages", + "ldaptasks", + "managemails", + "mysqltasks", + "osusers", + "pgsqltasks", + "taskresults", + "userdbs", + "websites", + ] ] ) ) @@ -440,21 +438,21 @@ elif GVA_ENVIRONMENT == "test": [ (key, {"handlers": ["console"], "level": "ERROR", "propagate": True}) for key in [ - "dashboard", - "domains", - "fileservertasks", - "gvacommon", - "gvawebcore", - "hostingpackages", - "ldaptasks", - "managemails", - "mysqltasks", - "osusers", - "pgsqltasks", - "taskresults", - "userdbs", - "websites", - ] + "dashboard", + "domains", + "fileservertasks", + "gvacommon", + "gvawebcore", + "hostingpackages", + "ldaptasks", + "managemails", + "mysqltasks", + "osusers", + "pgsqltasks", + "taskresults", + "userdbs", + "websites", + ] ] ) ) diff --git a/gnuviechadmin/gnuviechadmin/urls.py b/gnuviechadmin/gnuviechadmin/urls.py index 8bcfca5..8be802e 100644 --- a/gnuviechadmin/gnuviechadmin/urls.py +++ b/gnuviechadmin/gnuviechadmin/urls.py @@ -11,8 +11,6 @@ admin.autodiscover() urlpatterns = [ re_path(r"", include("dashboard.urls")), - re_path(r"^admin/", admin.site.urls), - re_path(r"^impersonate/", include("impersonate.urls")), re_path(r"^accounts/", include("allauth.urls")), re_path(r"^database/", include("userdbs.urls")), re_path(r"^domains/", include("domains.urls")), @@ -20,6 +18,7 @@ urlpatterns = [ re_path(r"^website/", include("websites.urls")), re_path(r"^mail/", include("managemails.urls")), re_path(r"^osuser/", include("osusers.urls")), + re_path(r"^admin/", admin.site.urls), re_path(r"^contact/", include("contact_form.urls")), re_path(r"^impressum/$", views.flatpage, {"url": "/impressum/"}, name="imprint"), ] diff --git a/gnuviechadmin/templates/base.html b/gnuviechadmin/templates/base.html index de0c35b..8040f63 100644 --- a/gnuviechadmin/templates/base.html +++ b/gnuviechadmin/templates/base.html @@ -71,7 +71,6 @@
- {% trans "List all users" %} -
- -- {% if query and page.object_list %} -
- {% if query and page.has_previous %} - Previous - Page - {% endif %} - - {% if query and page.has_next %} - Next - Page - {% endif %} -
-{% endblock %} \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 9825743..720f7ea 100644 --- a/poetry.lock +++ b/poetry.lock @@ -666,17 +666,6 @@ files = [ django = ">=3.2.4" sqlparse = ">=0.2" -[[package]] -name = "django-impersonate" -version = "1.9.1" -description = "Django app to allow superusers to impersonate other users." -category = "main" -optional = false -python-versions = "*" -files = [ - {file = "django-impersonate-1.9.1.tar.gz", hash = "sha256:0befdb096198b458507239a6f21574c9e0f608ab01fad352d71eb9284e5bb9c9"}, -] - [[package]] name = "django-model-utils" version = "4.3.1" @@ -1753,4 +1742,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = "^3.7" -content-hash = "dd56e0233689448f08dfcae943871bf9d72c05ad7bfd326c69f9ecb33ea8a461" +content-hash = "6041c8bb49cd1df098f1948f8ad2cbd48fd8f42ff44e410f3fecb61be7e80a18" diff --git a/pyproject.toml b/pyproject.toml index d28421e..3f3b448 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "gva" -version = "0.13.0" +version = "0.12.1" description = "gnuviechadmin web interface" authors = ["Jan Dittberner