diff --git a/dnmgmt/settings.py b/dnmgmt/settings.py index 80fc2ed..f5de88c 100644 --- a/dnmgmt/settings.py +++ b/dnmgmt/settings.py @@ -32,12 +32,12 @@ ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', 'crispy_forms', 'dncore', 'whoisdb', @@ -46,31 +46,31 @@ INSTALLED_APPS = [ ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'dnmgmt.urls' TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, "templates/")], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, "templates/")], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, ] WSGI_APPLICATION = 'dnmgmt.wsgi.application' @@ -80,10 +80,10 @@ WSGI_APPLICATION = 'dnmgmt.wsgi.application' # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } } @@ -91,18 +91,18 @@ DATABASES = { # https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, ] @@ -126,14 +126,13 @@ USE_TZ = True STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, "cstatic") STATICFILES_DIRS = [ - os.path.join(BASE_DIR, "static"), + os.path.join(BASE_DIR, "static"), ] CRISPY_TEMPLATE_PACK = 'bootstrap3' MESSAGE_TAGS = { - messages.ERROR: 'danger', + messages.ERROR: 'danger', } AUTH_USER_MODEL = 'dncore.User' LOGIN_REDIRECT_URL = '/' -LOGIN_URL = '/login/' - +LOGIN_URL = '/user/login/' diff --git a/domains/forms.py b/domains/forms.py index 0936e01..032b472 100644 --- a/domains/forms.py +++ b/domains/forms.py @@ -17,28 +17,35 @@ class DomainForm(MntFormMixin, forms.ModelForm): super(DomainForm, self).__init__(*args, **kwargs) + instance = getattr(self, "instance", None) + self._create = not (instance and instance.pk) + + if not self._create: + self.fields['name'].disabled = True + def clean_name(self): name = self.cleaned_data['name'].lower() - if not name.endswith("."): - name += "." + if self._create: + if not name.endswith("."): + name += "." - if not name.endswith("dn."): - raise forms.ValidationError("Only .dn domains can be registered at this point") + if not name.endswith("dn."): + raise forms.ValidationError("Only .dn domains can be registered at this point") - if name.count(".") > 2: - raise forms.ValidationError("No subdomains can be registered") + if name.count(".") > 2: + raise forms.ValidationError("No subdomains can be registered") - if not re.match("^[a-z0-9.-]+$", name): - raise forms.ValidationError("Only a-z, 0-9 and - are allowed inside the domain name") + if not re.match("^[a-z0-9.-]+$", name): + raise forms.ValidationError("Only a-z, 0-9 and - are allowed inside the domain name") - try: - Domain.objects.get(name=name) - raise forms.ValidationError("Domain already exists") - except Domain.DoesNotExist: - pass + try: + Domain.objects.get(name=name) + raise forms.ValidationError("Domain already exists") + except Domain.DoesNotExist: + pass return name - + class NameserverForm(MntFormMixin, forms.ModelForm): class Meta: @@ -50,6 +57,9 @@ class NameserverForm(MntFormMixin, forms.ModelForm): super(NameserverForm, self).__init__(*args, **kwargs) + instance = getattr(self, "instance", None) + self._create = not (instance and instance.pk) + def clean_name(self): name = self.cleaned_data['name'].lower().strip() if not name.endswith("."): @@ -62,8 +72,14 @@ class NameserverForm(MntFormMixin, forms.ModelForm): mnts = self._user.maintainer_set.all() domains = Domain.objects.filter(mnt_by__in=mnts) + found = False for domain in domains: - if domain.endswith(name) + if domain.name == zone: + found = True + break + + if not found: + raise forms.ValidationError("This nameserver is not under a domain you control.") return name diff --git a/domains/migrations/0001_initial.py b/domains/migrations/0001_initial.py index c2705cd..1b15b46 100644 --- a/domains/migrations/0001_initial.py +++ b/domains/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-03-13 11:18 +# Generated by Django 1.10.5 on 2017-03-14 21:04 from __future__ import unicode_literals from django.db import migrations, models @@ -35,7 +35,7 @@ class Migration(migrations.Migration): ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('created', models.DateTimeField(auto_now_add=True)), ('last_modified', models.DateTimeField(auto_now_add=True)), - ('name', models.CharField(max_length=256)), + ('name', models.CharField(max_length=256, unique=True)), ('glueIPv4', models.GenericIPAddressField(blank=True, null=True, protocol='IPv4')), ('glueIPv6', models.GenericIPAddressField(blank=True, null=True, protocol='IPv6')), ('admin_c', models.ManyToManyField(to='whoisdb.Contact')), @@ -45,38 +45,16 @@ class Migration(migrations.Migration): 'abstract': False, }, ), - migrations.CreateModel( - name='NameserverDomainAssignment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveSmallIntegerField(default=0)), - ('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='domains.Domain')), - ('nameserver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='domains.Nameserver')), - ], - ), - migrations.CreateModel( - name='NameserverReverseZoneAssignment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('order', models.PositiveSmallIntegerField()), - ('nameserver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='domains.Nameserver')), - ], - ), migrations.CreateModel( name='ReverseZone', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('address', models.GenericIPAddressField(db_index=True)), ('netmask', models.PositiveIntegerField()), - ('nameservers', models.ManyToManyField(through='domains.NameserverReverseZoneAssignment', to='domains.Nameserver')), + ('nameservers', models.ManyToManyField(to='domains.Nameserver')), ('parentNet', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='whoisdb.InetNum')), ], ), - migrations.AddField( - model_name='nameserverreversezoneassignment', - name='reversezone', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='domains.ReverseZone'), - ), migrations.AddField( model_name='domain', name='nameservers', diff --git a/domains/models.py b/domains/models.py index 1fe65b5..5d33fc8 100644 --- a/domains/models.py +++ b/domains/models.py @@ -6,6 +6,7 @@ from whoisdb.models import MntdObject, Contact, InetNum # generally allow domains for .dn to be created # allow owners of a subnet to create reverse dns record? + class Nameserver(MntdObject): handleSuffix = "NS" # dns name @@ -24,6 +25,7 @@ class Nameserver(MntdObject): def __str__(self): return self.name + class Domain(MntdObject): handle = None handleSuffix = "DOM" @@ -38,18 +40,29 @@ class Domain(MntdObject): def __str__(self): return self.name + def getNoDeleteReasons(self): + reasons = [] + + nameservers = Nameserver.objects.filter(name__endswith="." + self.name) + for ns in nameservers: + reasons.append("Nameserver %s depends on this domain" % ns.name) + + return reasons + #class NameserverDomainAssignment(models.Model): # domain = models.ForeignKey(Domain) # nameserver = models.ForeignKey(Nameserver) # # order = models.PositiveSmallIntegerField(default=0) + class ReverseZone(models.Model): parentNet = models.ForeignKey(InetNum) address = models.GenericIPAddressField(db_index=True) netmask = models.PositiveIntegerField() - nameservers = models.ManyToManyField(Nameserver, through='NameserverReverseZoneAssignment') + nameservers = models.ManyToManyField(Nameserver) + #class NameserverReverseZoneAssignment(models.Model): # reversezone = models.ForeignKey(ReverseZone) diff --git a/domains/urls.py b/domains/urls.py index b31bf0b..2bac5ef 100644 --- a/domains/urls.py +++ b/domains/urls.py @@ -6,8 +6,12 @@ urlpatterns = [ url(r'^$', domains_views.overview, name='overview'), url(r'domain/create/$', domains_views.DomainCreate.as_view(), name='domain-create'), - url(r'show/(?P[a-z0-9.-]+)/$', domains_views.DomainDetail, name='domain-show'), + url(r'domain/show/(?P[a-z0-9.-]+)/$', domains_views.DomainDetail.as_view(), name='domain-show'), + url(r'domain/edit/(?P[a-z0-9.-]+)/$', domains_views.DomainEdit.as_view(), name='domain-edit'), + url(r'domain/delete/(?P[a-z0-9.-]+)/$', domains_views.DomainDelete.as_view(), name='domain-delete'), url(r'nameserver/create/$', domains_views.NameserverCreate.as_view(), name='nameserver-create'), - url(r'nameserver/show/(?P\d+)/$', domains_views.NameserverDetail, name='nameserver-show'), + url(r'nameserver/show/(?P[a-z0-9.-]+)/$', domains_views.NameserverDetail.as_view(), name='nameserver-show'), + url(r'nameserver/edit/(?P[a-z0-9.-]+)/$', domains_views.NameserverEdit.as_view(), name='nameserver-edit'), + url(r'nameserver/delete/(?P[a-z0-9.-]+)/$', domains_views.NameserverDelete.as_view(), name='nameserver-delete'), ] diff --git a/domains/views.py b/domains/views.py index aaffc39..aab48a4 100644 --- a/domains/views.py +++ b/domains/views.py @@ -1,13 +1,15 @@ from django.shortcuts import render +from django.urls import reverse_lazy from django.contrib.auth.decorators import login_required from django.views.generic import DetailView, CreateView, UpdateView from django.contrib.auth.mixins import LoginRequiredMixin -from whoisdb.generic import MntGenericMixin +from whoisdb.generic import MntGenericMixin, DeleteCheckView from .models import Domain, Nameserver from .forms import DomainForm, NameserverForm + @login_required def overview(request): mnts = request.user.maintainer_set.all() @@ -18,6 +20,7 @@ def overview(request): return render(request, "domains/overview.html", {"domains": domains, "nameservers": nameservers}) + class DomainCreate(LoginRequiredMixin, CreateView): template_name = "domains/obj_create.html" form_class = DomainForm @@ -28,13 +31,33 @@ class DomainCreate(LoginRequiredMixin, CreateView): return kwargs + class DomainDetail(LoginRequiredMixin, DetailView): model = Domain slug_field = "name" slug_url_kwarg = "domain" + context_object_name = "domain" + class DomainEdit(MntGenericMixin, LoginRequiredMixin, UpdateView): - template_name = "domain" + model = Domain + form_class = DomainForm + slug_field = "name" + slug_url_kwarg = "domain" + template_name = "domains/obj_edit.html" + + def get_form_kwargs(self, *args, **kwargs): + kwargs = super(DomainEdit, self).get_form_kwargs(*args, **kwargs) + kwargs["user"] = self.request.user + return kwargs + + +class DomainDelete(MntGenericMixin, LoginRequiredMixin, DeleteCheckView): + template_name = "domains/obj_delete.html" + model = Domain + slug_field = "name" + slug_url_kwarg = "domain" + success_url = reverse_lazy("domains:overview") class NameserverCreate(LoginRequiredMixin, CreateView): @@ -47,8 +70,30 @@ class NameserverCreate(LoginRequiredMixin, CreateView): return kwargs + class NameserverDetail(LoginRequiredMixin, DetailView): model = Nameserver - #slug_field = "name" - #slug_url_kwarg = "domain" + slug_field = "name" + slug_url_kwarg = "domain" + context_object_name = "nameserver" + + +class NameserverEdit(MntGenericMixin, LoginRequiredMixin, UpdateView): + model = Nameserver + form_class = NameserverForm + slug_field = "name" + slug_url_kwarg = "domain" + template_name = "domains/obj_edit.html" + def get_form_kwargs(self, *args, **kwargs): + kwargs = super(NameserverEdit, self).get_form_kwargs(*args, **kwargs) + kwargs["user"] = self.request.user + return kwargs + + +class NameserverDelete(MntGenericMixin, LoginRequiredMixin, DeleteCheckView): + template_name = "domains/obj_delete.html" + model = Nameserver + slug_field = "name" + slug_url_kwarg = "domain" + success_url = reverse_lazy("domains:overview") diff --git a/templates/domains/domain_detail.html b/templates/domains/domain_detail.html new file mode 100644 index 0000000..01fa2d9 --- /dev/null +++ b/templates/domains/domain_detail.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
Header
+
+ {{ domain.name }} +
+
+
+
+{% endblock %} + diff --git a/templates/domains/nameserver_detail.html b/templates/domains/nameserver_detail.html new file mode 100644 index 0000000..574547c --- /dev/null +++ b/templates/domains/nameserver_detail.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} + +{% block content %} +
+
+
+
Header
+
+ {{ nameserver.name }} +
+
+
+
+{% endblock %} + diff --git a/templates/domains/obj_delete.html b/templates/domains/obj_delete.html new file mode 100644 index 0000000..fb02616 --- /dev/null +++ b/templates/domains/obj_delete.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+
Header
+
+ {% if reasons %} +

+ You cannot delete this object, as other objects in the database depend on it! +

+

+

    + {% for reason in reasons %} +
  • {{ reason }}
  • + {% endfor %} +
+ {% else %} + {{ obj }} +
+ {% csrf_token %} + +
+ {% endif %} +
+
+
+
+{% endblock %} + diff --git a/templates/domains/obj_edit.html b/templates/domains/obj_edit.html new file mode 100644 index 0000000..046e9df --- /dev/null +++ b/templates/domains/obj_edit.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} + +{% load crispy_forms_tags %} + +{% block content %} +
+
+
+
Header
+
+
+ {% csrf_token %} + {{ form | crispy }} + +
+
+
+
+
+{% endblock %} + diff --git a/templates/domains/overview.html b/templates/domains/overview.html index 8f796cd..368c2f0 100644 --- a/templates/domains/overview.html +++ b/templates/domains/overview.html @@ -8,15 +8,46 @@

Your nameservers (New nameserver) + + + + + + + + {% for nameserver in nameservers %} - {{ nameserver }}
+ + + + + + + {% endfor %} +
NameserverGlue IPv4Glue IPv6MNTs
{{ nameserver.name }}Edit Delete

Your domains (New domain) + + + + + + + + {% for domain in domains %} + + + + + + + {{ domain }}
{% endfor %} +
DomainNameserverGlue IPv6MNTs
{{ domain.name }}Edit Delete