Zwischencommit

This commit is contained in:
Sebastian Lohff 2017-03-14 20:18:57 +01:00
parent 3826078d75
commit 69abba4d36
39 changed files with 885 additions and 36 deletions

12
README
View File

@ -7,3 +7,15 @@ FIXME
contact soll nur einen mnt_by haben
mnt_by ==> mnt_by_in in filter statements
admin_c für welche objekte?
mnt_by von Contact auf ForeignKey droppen?
last-changed has to be changed by forms
Visibility of resources
MNT: Everywhere
ASBlocks: ASBlock, ASNumber
FIXME: Allow Maintainers to have multiple auth foo
multiple select without revealing everything?

View File

@ -39,9 +39,10 @@ INSTALLED_APPS = [
'django.contrib.messages',
'django.contrib.staticfiles',
'crispy_forms',
'taggit',
'dncore',
'whoisdb',
'rrequests',
'domains',
]
MIDDLEWARE = [

View File

@ -19,6 +19,8 @@ from django.contrib import admin
import dncore.urls
import dncore.views
import whoisdb.urls
import rrequests.urls
import domains.urls
urlpatterns = [
@ -28,4 +30,6 @@ urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/', include(dncore.urls, namespace='user')),
url(r'^whoisdb/', include(whoisdb.urls, namespace='whoisdb')),
url(r'^rrequests/', include(rrequests.urls, namespace='rrequests')),
url(r'^domains/', include(domains.urls, namespace='domains')),
]

0
domains/__init__.py Normal file
View File

7
domains/admin.py Normal file
View File

@ -0,0 +1,7 @@
from django.contrib import admin
from .models import Nameserver, Domain, ReverseZone
admin.site.register(Nameserver)
admin.site.register(Domain)
admin.site.register(ReverseZone)

5
domains/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class DomainsConfig(AppConfig):
name = 'domains'

73
domains/forms.py Normal file
View File

@ -0,0 +1,73 @@
from django import forms
from whoisdb.forms import MntFormMixin
from .models import Domain, Nameserver
import re
class DomainForm(MntFormMixin, forms.ModelForm):
class Meta:
model = Domain
fields = ['name', 'nameservers', 'mnt_by', 'admin_c']
def __init__(self, user, *args, **kwargs):
self._user = user
super(DomainForm, self).__init__(*args, **kwargs)
def clean_name(self):
name = self.cleaned_data['name'].lower()
if not name.endswith("."):
name += "."
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 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
return name
class NameserverForm(MntFormMixin, forms.ModelForm):
class Meta:
model = Nameserver
fields = ['name', 'glueIPv4', 'glueIPv6', 'mnt_by', 'admin_c']
def __init__(self, user, *args, **kwargs):
self._user = user
super(NameserverForm, self).__init__(*args, **kwargs)
def clean_name(self):
name = self.cleaned_data['name'].lower().strip()
if not name.endswith("."):
name += "."
if name.count(".") <= 2:
raise forms.ValidationError("Nameserver must be inside a domain (e.g. ns1.noot.dn.)")
zone = ".".join(name.split(".")[-3:])
mnts = self._user.maintainer_set.all()
domains = Domain.objects.filter(mnt_by__in=mnts)
for domain in domains:
if domain.endswith(name)
return name
def clean(self):
cleaned_data = super(NameserverForm, self).clean()
return cleaned_data

View File

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-13 11:18
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('whoisdb', '0013_auto_20170303_1206'),
]
operations = [
migrations.CreateModel(
name='Domain',
fields=[
('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(db_index=True, max_length=67, unique=True)),
('admin_c', models.ManyToManyField(to='whoisdb.Contact')),
('mnt_by', models.ManyToManyField(to='whoisdb.Maintainer')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Nameserver',
fields=[
('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)),
('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')),
('mnt_by', models.ManyToManyField(to='whoisdb.Maintainer')),
],
options={
'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')),
('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',
field=models.ManyToManyField(blank=True, to='domains.Nameserver'),
),
]

View File

58
domains/models.py Normal file
View File

@ -0,0 +1,58 @@
from django.db import models
from django.urls import reverse
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
# ip address, if glue
# ipv4/ipv6 address?
handle = None
name = models.CharField(max_length=256, unique=True)
glueIPv4 = models.GenericIPAddressField(protocol='IPv4', blank=True, null=True)
glueIPv6 = models.GenericIPAddressField(protocol='IPv6', blank=True, null=True)
admin_c = models.ManyToManyField(Contact)
def get_absolute_url(self):
return reverse("domains:nameserver-show", args=(self.name,))
def __str__(self):
return self.name
class Domain(MntdObject):
handle = None
handleSuffix = "DOM"
name = models.CharField(max_length=67, unique=True, db_index=True)
nameservers = models.ManyToManyField(Nameserver, blank=True)
admin_c = models.ManyToManyField(Contact)
def get_absolute_url(self):
return reverse("domains:domain-show", args=(self.name,))
def __str__(self):
return self.name
#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')
#class NameserverReverseZoneAssignment(models.Model):
# reversezone = models.ForeignKey(ReverseZone)
# nameserver = models.ForeignKey(Nameserver)
#
# order = models.PositiveSmallIntegerField()

3
domains/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
domains/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.conf.urls import url
from . import views as domains_views
urlpatterns = [
url(r'^$', domains_views.overview, name='overview'),
url(r'domain/create/$', domains_views.DomainCreate.as_view(), name='domain-create'),
url(r'show/(?P<domain>[a-z0-9.-]+)/$', domains_views.DomainDetail, name='domain-show'),
url(r'nameserver/create/$', domains_views.NameserverCreate.as_view(), name='nameserver-create'),
url(r'nameserver/show/(?P<pk>\d+)/$', domains_views.NameserverDetail, name='nameserver-show'),
]

54
domains/views.py Normal file
View File

@ -0,0 +1,54 @@
from django.shortcuts import render
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 .models import Domain, Nameserver
from .forms import DomainForm, NameserverForm
@login_required
def overview(request):
mnts = request.user.maintainer_set.all()
# get all domains and nameservers
domains = Domain.objects.filter(mnt_by__in=mnts)
nameservers = Nameserver.objects.filter(mnt_by__in=mnts)
return render(request, "domains/overview.html", {"domains": domains, "nameservers": nameservers})
class DomainCreate(LoginRequiredMixin, CreateView):
template_name = "domains/obj_create.html"
form_class = DomainForm
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(DomainCreate, self).get_form_kwargs(*args, **kwargs)
kwargs["user"] = self.request.user
return kwargs
class DomainDetail(LoginRequiredMixin, DetailView):
model = Domain
slug_field = "name"
slug_url_kwarg = "domain"
class DomainEdit(MntGenericMixin, LoginRequiredMixin, UpdateView):
template_name = "domain"
class NameserverCreate(LoginRequiredMixin, CreateView):
template_name = "domains/obj_create.html"
form_class = NameserverForm
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(NameserverCreate, self).get_form_kwargs(*args, **kwargs)
kwargs["user"] = self.request.user
return kwargs
class NameserverDetail(LoginRequiredMixin, DetailView):
model = Nameserver
#slug_field = "name"
#slug_url_kwarg = "domain"

0
rrequests/__init__.py Normal file
View File

6
rrequests/admin.py Normal file
View File

@ -0,0 +1,6 @@
from django.contrib import admin
from .models import Request, RequestMessage
admin.site.register(Request)
admin.site.register(RequestMessage)

5
rrequests/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class RrequestsConfig(AppConfig):
name = 'rrequests'

55
rrequests/forms.py Normal file
View File

@ -0,0 +1,55 @@
from django import forms
from django.db.models import Q
from .models import Request
from whoisdb.models import Maintainer
class RequestForm(forms.Form):
RESOURCES = map(lambda _x: (_x, _x), [
"AS Number (16bit)", "IPv4 /27", "IPv4 > /27", "IPv6", "other"
])
applicant = forms.ModelChoiceField(Maintainer.objects.none(), label="Applicant (you)", help_text="Maintainer you want to request resources for")
provider = forms.ModelChoiceField(Maintainer.objects.none(), label="Provider", help_text="LIR/RIR you want to request resources from")
subject = forms.CharField(label="Subject")
resources = forms.CheckboxSelectMultiple(choices=RESOURCES)
message = forms.CharField(widget=forms.Textarea, help_text="Describe shortly what resources you need and what for you need them")
def __init__(self, user, *args, **kwargs):
super(RequestForm, self).__init__(*args, **kwargs)
self._user = user
self.fields['applicant'].queryset = self._user.maintainer_set.all()
self.fields['provider'].queryset = Maintainer.objects.filter(Q(rir=True) | Q(lir=True))
def clean(self):
cleaned_data = super(RequestForm, self).clean()
if not forms.errors:
mnts = self._user.maintainer_set.all()
if cleaned_data['applicant'] in mnts and cleaned_data['provider'] in mnts:
raise forms.ValidationError("You could request resources from yourself, but this would actually not make that much sense.")
class ResponseForm(forms.Form):
new_status = forms.ChoiceField(choices=[("KEEP", "----", )] + list(Request.STATES), initial="KEEP", help_text="Only set this if you want to change the status of this request")
message = forms.CharField(widget=forms.Textarea)
def __init__(self, request, user, *args, **kwargs):
super(ResponseForm, self).__init__(*args, **kwargs)
self._request = request
self._user = user
def clean(self):
cleaned_data = super(ResponseForm, self).clean()
if not self.errors:
if cleaned_data['new_status'] == self._request.status:
raise forms.ValidationError("Status changed to same as ticket")
if self._request.status in (Request.STATE_RESOLVED, Request.STATE_REJECTED) and \
cleaned_data['new_status'] not in (Request.STATE_OPEN,):
raise forms.ValidationError("Please put this ticket in an open state before adding messages")
class ProviderResponseForm(ResponseForm):
createdResources = forms.CharField(label="Created resources", help_text="If you have created resources for this request, please enter their handles here", required=False)

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-03 12:21
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('whoisdb', '0013_auto_20170303_1206'),
]
operations = [
migrations.CreateModel(
name='Request',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('state', models.CharField(choices=[('Open', 'OPEN'), ('Resolved', 'RESOLVED'), ('Rejected', 'REJECTED')], max_length=16)),
('requestResources', models.TextField()),
('grantedResources', models.TextField()),
('created', models.DateTimeField(auto_now_add=True)),
('lastAction', models.DateTimeField(auto_now_add=True)),
('requestedFrom', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requestfrom_set', to='whoisdb.Maintainer')),
('requestedTo', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='whoisdb.Maintainer')),
],
),
migrations.CreateModel(
name='RequestMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('message', models.TextField()),
],
),
]

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-05 04:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('whoisdb', '0013_auto_20170303_1206'),
('rrequests', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='request',
old_name='requestedTo',
new_name='applicant',
),
migrations.RenameField(
model_name='request',
old_name='requestedFrom',
new_name='provider',
),
migrations.RenameField(
model_name='request',
old_name='title',
new_name='subject',
),
migrations.AddField(
model_name='requestmessage',
name='created',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='requestmessage',
name='creator',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='whoisdb.Maintainer'),
preserve_default=False,
),
]

View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-06 02:04
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('rrequests', '0002_auto_20170305_0436'),
]
operations = [
migrations.AddField(
model_name='requestmessage',
name='request',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='rrequests.Request'),
preserve_default=False,
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-06 11:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rrequests', '0003_requestmessage_request'),
]
operations = [
migrations.AddField(
model_name='requestmessage',
name='statusChanged',
field=models.CharField(blank=True, choices=[('Open', 'OPEN'), ('Resolved', 'RESOLVED'), ('Rejected', 'REJECTED')], default=None, max_length=16, null=True),
),
]

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-06 11:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rrequests', '0004_requestmessage_statuschanged'),
]
operations = [
migrations.RemoveField(
model_name='request',
name='state',
),
migrations.AddField(
model_name='request',
name='status',
field=models.CharField(choices=[('OPEN', 'Open'), ('RESOLVED', 'Resolved'), ('REJECTED', 'Rejected')], default='OPEN', max_length=16),
preserve_default=False,
),
migrations.AlterField(
model_name='requestmessage',
name='statusChanged',
field=models.CharField(blank=True, choices=[('OPEN', 'Open'), ('RESOLVED', 'Resolved'), ('REJECTED', 'Rejected')], default=None, max_length=16, null=True),
),
]

View File

44
rrequests/models.py Normal file
View File

@ -0,0 +1,44 @@
from django.db import models
from django.urls import reverse
from whoisdb.models import Maintainer
class Request(models.Model):
STATE_OPEN = "OPEN"
STATE_RESOLVED = "RESOLVED"
STATE_REJECTED = "REJECTED"
STATES = (
(STATE_OPEN, 'Open'),
(STATE_RESOLVED, 'Resolved'),
(STATE_REJECTED, 'Rejected'),
)
# request goes to mnt?
subject = models.CharField(max_length=200)
status = models.CharField(max_length=16, choices=STATES)
applicant = models.ForeignKey(Maintainer)
provider = models.ForeignKey(Maintainer, related_name='requestfrom_set')
requestResources = models.TextField()
grantedResources = models.TextField()
created = models.DateTimeField(auto_now_add=True)
lastAction = models.DateTimeField(auto_now_add=True)
def get_absolute_url(self):
return reverse("rrequests:show", args=(self.pk,))
def __str__(self):
return "(%s -> %s) [%s] %s" % (self.applicant, self.provider, self.state, self.subject)
class RequestMessage(models.Model):
request = models.ForeignKey(Request)
statusChanged = models.CharField(max_length=16, choices=Request.STATES, default=None, null=True, blank=True)
creator = models.ForeignKey(Maintainer)
message = models.TextField()
created = models.DateTimeField(auto_now_add=True)

3
rrequests/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

10
rrequests/urls.py Normal file
View File

@ -0,0 +1,10 @@
from django.conf.urls import url
from . import views as rrequests_views
urlpatterns = [
url(r'^$', rrequests_views.listRequests, name='dashboard'),
url(r'create/$', rrequests_views.RrequestCreate.as_view(), name='create'),
url(r'show/(?P<pk>\d+)/$', rrequests_views.rrequestDetail, name='show'),
]

91
rrequests/views.py Normal file
View File

@ -0,0 +1,91 @@
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views.generic import FormView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.utils import timezone
from .models import Request, RequestMessage
from .forms import RequestForm, ResponseForm, ProviderResponseForm
@login_required
def listRequests(request):
mnts = request.user.maintainer_set.all()
requestedFromMe = Request.objects.filter(applicant=mnts)
requestedToMe = Request.objects.filter(provider__in=mnts)
return render(request, "rrequests/list.html", {"requestedFromMe": requestedFromMe, "requestedToMe": requestedToMe})
class RrequestCreate(LoginRequiredMixin, FormView):
template_name = "rrequests/request_create.html"
form_class = RequestForm
def get_form_kwargs(self, *args, **kwargs):
kwargs = super(RrequestCreate, self).get_form_kwargs(*args, **kwargs)
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
formData = form.cleaned_data
print(formData)
request = Request(
subject=formData['subject'],
status=Request.STATE_OPEN,
applicant=formData['applicant'],
provider=formData['provider'],
)
request.save()
requestMsg = RequestMessage(
request=request,
creator=formData['applicant'],
message=formData['message'],
)
requestMsg.save()
return HttpResponseRedirect(request.get_absolute_url())
@login_required
def rrequestDetail(request, pk):
reqObj = get_object_or_404(Request, pk=pk)
mnts = request.user.maintainer_set.all()
formClass = None
provider = None
if reqObj.provider in mnts:
provider = True
formClass = ProviderResponseForm
else:
provider = False
formClass = ResponseForm
if request.method == "POST":
form = formClass(request=reqObj, user=request.user, data=request.POST)
if form.is_valid():
# create message object
msg = RequestMessage(
request=reqObj,
creator=reqObj.provider if provider else reqObj.applicant,
message=form.cleaned_data['message']
)
if form.cleaned_data['new_status'] != "KEEP":
msg.statusChanged = form.cleaned_data['new_status']
reqObj.status = msg.statusChanged
msg.save()
reqObj.lastAction = timezone.now()
reqObj.save()
return HttpResponseRedirect(reverse("rrequests:show", args=(pk,)))
else:
form = formClass(request=reqObj, user=request.user)
return render(request, "rrequests/request_detail.html", {"request": reqObj, "form": form})

View File

@ -53,6 +53,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Whois DB <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{% url "whoisdb:dashboard" %}">Overview</a></li>
<li><a href="{% url "domains:overview" %}">Domains</a></li>
</ul>
</li>
</ul>

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Header</div>
<div class="panel-body">
<form method="post" action="#">
{% csrf_token %}
{{ form | crispy }}
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,25 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Resource Requests</div>
<div class="panel-body">
<p>
Your nameservers (<a href="{% url "domains:nameserver-create" %}">New nameserver</a>)
{% for nameserver in nameservers %}
{{ nameserver }}<br />
{% endfor %}
</p>
<p>
Your domains (<a href="{% url "domains:domain-create" %}">New domain</a>)
{% for domain in domains %}
{{ domain }}<br />
{% endfor %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,29 @@
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Resource Requests</div>
<div class="panel-body">
<p>
<a href="{% url "rrequests:create" %}">Create resource request</a>
</p>
<p>
Requests to you
<ul>
{% for r in requestedFromMe %}
{% endfor %}
</ul>
<ul>
{% for r in requestedToMe %}
<li><a href="{% url "rrequests:show" r.pk %}">{{ r.subject }} {{ r.created }} {{ r.applicant }} {{ r.provider }}</a></li>
{% endfor %}
</ul>
</p>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Header</div>
<div class="panel-body">
<form method="post" action="#">
{% csrf_token %}
{{ form | crispy }}
<button type="submit" class="btn btn-primary">Create</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,33 @@
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row">
<div class="col-sm-12">
<div class="panel panel-default">
<div class="panel-heading">Header</div>
<div class="panel-body">
<h2>{{ request.subject }}</h2>
<p>
{% for message in request.requestmessage_set.all %}
<h4>{{ message.creator }} {{ message.created }}</h4>
{{ message.message | linebreaks }}
{% if message.statusChanged %}
Status changed to {{ message.statusChanged }}.
{% endif %}
{% endfor %}
</p>
<h4>Add / change</h4>
<form method="post" action="#">
{% csrf_token %}
{{ form | crispy }}
<button class="btn btn-primary">Submit</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -12,7 +12,7 @@
<ul>
<li><a href="{% url "whoisdb:mnt-create" %}">Create new Maintainer</a></li>
<li><a href="{% url "whoisdb:contact-create" %}">Create new Role/Person</a></li>
<li><a href="">Request resources</a></li>
<li><a href="{% url "rrequests:dashboard" %}">Request resources</a></li>
{% if netblocks %}
<li><a href="{% url "whoisdb:inetnum-create" %}">Create Subnet</a></li>
{% endif %}

View File

@ -7,9 +7,9 @@ import re
import ipaddress
class WhoisObjectMixin(object):
class WhoisObjectFormMixin(object):
def __init__(self, user, *args, **kwargs):
super(WhoisObjectMixin, self).__init__(*args, **kwargs)
super(WhoisObjectFormMixin, self).__init__(*args, **kwargs)
self._user = user
instance = getattr(self, 'instance', None)
@ -30,7 +30,7 @@ class WhoisObjectMixin(object):
return self.cleaned_data['handle']
def clean(self):
cleaned_data = super(WhoisObjectMixin, self).clean()
cleaned_data = super(WhoisObjectFormMixin, self).clean()
if cleaned_data.get("handle") == "AUTO" and not self.errors:
cleaned_data['handle'] = self._meta.model.genGenericHandle(cleaned_data.get("name"))
@ -55,7 +55,6 @@ class MntFormMixin(object):
mntWhens.append(When(handle__in=instance.mnt_by.all().values_list("handle"), then=1))
mntQs = Maintainer.objects.annotate(card=Case(*mntWhens, default=0, output_field=IntegerField())).order_by("-card")
self.fields["mnt_by"].queryset = mntQs
print(mntQs.query)
class MntForm(forms.ModelForm):
class Meta:
@ -75,14 +74,16 @@ class MntInitialForm(MntForm):
fields = ['handle', 'description']
class ContactForm(WhoisObjectMixin, forms.ModelForm):
class ContactForm(WhoisObjectFormMixin, forms.ModelForm):
class Meta:
model = Contact
fields = ['type', 'handle', 'name', 'mnt_by']
fields = ['handle', 'name', 'mnt_by']
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
self.fields['mnt_by'].queryset = Maintainer.objects.filter(auth=self._user)
class ContactInitialForm(ContactForm):
class Meta:
@ -90,7 +91,7 @@ class ContactInitialForm(ContactForm):
fields = ['handle', 'name']
class InetNumForm(MntFormMixin, WhoisObjectMixin, forms.ModelForm):
class InetNumForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
prefix = forms.CharField()
protectedFields = ['handle', 'protocol', 'parent_range', 'mnt_by', 'prefix']
@ -123,41 +124,44 @@ class InetNumForm(MntFormMixin, WhoisObjectMixin, forms.ModelForm):
return net
def clean_parent_range(self):
parent_range = self.cleaned_data.get('parent_range', None)
# allow parent range to be unset for already present objects
if not (self.instance and self.instance.pk) and not parent_range:
raise forms.ValidationError("Parent range must be set")
def clean(self):
# FIXME: Reset certain field sto instance:
cleaned_data = super(InetNumForm, self).clean()
if self._editLower:
# reset some fields, just in case
#for key in self.protectedFields:
# cleaned_data[key] = getattr(self.instance, key)
pass
else:
if all(x in cleaned_data for x in ('prefix', 'parent_range', 'protocol')):
if not self._editLower:
if not self.errors:
prefix = cleaned_data['prefix']
parent = cleaned_data['parent_range']
parentNet = parent.getNetwork()
if parent:
parentNet = parent.getNetwork()
if cleaned_data['protocol'] != parent.protocol:
raise forms.ValidationError("Protocol type for prefix must be same as parent network")
if cleaned_data['protocol'] != parent.protocol:
raise forms.ValidationError("Protocol type for prefix must be same as parent network")
# check if in parent block
if prefix.network_address not in parentNet or prefix.prefixlen < parentNet.prefixlen:
raise forms.ValidationError("Prefix must be inside parent network range")
# check if in parent block
if prefix.network_address not in parentNet or prefix.prefixlen < parentNet.prefixlen:
raise forms.ValidationError("Prefix must be inside parent network range")
# check if parent block has net that overlaps with us
for otherNet in parent.inetnum_set.all():
if self.instance and self.instance.pk == otherNet.pk:
continue
# check if parent block has net that overlaps with us
for otherNet in parent.inetnum_set.all():
if self.instance and self.instance.pk == otherNet.pk:
continue
if otherNet.getNetwork().overlaps(prefix):
raise forms.ValidationError("The given prefix overlaps with network %s" % otherNet.handle)
if otherNet.getNetwork().overlaps(prefix):
raise forms.ValidationError("The given prefix overlaps with network %s" % otherNet.handle)
self.instance.address = str(prefix.network_address)
self.instance.netmask = prefix.prefixlen
return cleaned_data
class ASBlockForm(MntFormMixin, WhoisObjectMixin, forms.ModelForm):
class ASBlockForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
protectedFields = ['handle', 'parent_block', 'asBegin', 'asEnd', 'mnt_by']
# FIXME: Filter blocks
@ -205,13 +209,12 @@ class ASBlockForm(MntFormMixin, WhoisObjectMixin, forms.ModelForm):
if block.asBegin <= asBegin <= block.asEnd or block.asBegin <= asEnd <= block.asEnd or \
asBegin <= block.asBegin <= asEnd or asBegin <= block.asEnd <= asEnd:
print("AS OVERLAP TEST", parent.asBegin, parent.asEnd, asBegin, asEnd)
raise forms.ValidationError("Block overlaps with block %s" % block.handle)
return cleaned_data
class ASNumberForm(MntFormMixin, WhoisObjectMixin, forms.ModelForm):
class ASNumberForm(MntFormMixin, WhoisObjectFormMixin, forms.ModelForm):
protectedFields = ['handle', 'asblock', 'number', 'mnt_by']
class Meta:

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-03 12:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('whoisdb', '0012_auto_20170302_0314'),
]
operations = [
migrations.AddField(
model_name='maintainer',
name='lir',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='maintainer',
name='rir',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='contact',
name='type',
field=models.CharField(choices=[('person', 'PERSON')], default='PERSON', max_length=10),
),
]

View File

@ -68,6 +68,9 @@ class Maintainer(WhoisObject):
admin_c = models.ManyToManyField("Contact")
rir = models.BooleanField(default=False)
lir = models.BooleanField(default=False)
# autoInclude = models.BooleanField(default=True)
def get_absolute_url(self):
@ -100,7 +103,7 @@ class Contact(MntdObject):
TYPE = (('person', TYPE_PERSON),)
name = models.CharField(max_length=128)
type = models.CharField(max_length=10, choices=TYPE)
type = models.CharField(max_length=10, choices=TYPE, default=TYPE_PERSON)
def get_absolute_url(self):
return reverse("whoisdb:contact-detail", kwargs={"handle": self.handle})

View File

@ -21,7 +21,6 @@ class HandleValidatorWithSuffix(validators.RegexValidator):
def __init__(self, suffix):
self.regex = r'^(?:[A-Z]+[0-9]+-%s|AUTO)' % re.escape(suffix)
print(self.regex)
self.message = _(
'Enter a valid handle with suffix %s (all uppercase)' % suffix
)

View File

@ -46,8 +46,6 @@ def dbDashboard(request):
mntForm = MntInitialForm(user=request.user, prefix="mnt", initial={'handle': 'AUTO', 'description': 'Primary maintainer of %s' % request.user.username})
contactForm = ContactInitialForm(user=request.user, person=True, initial={'handle': 'AUTO', 'name': request.user.username.capitalize()}, prefix='contact')
print(asnumbers, asblocks, netblocks, mnts)
return render(request, "whoisdb/overview.html", {"mnts": mnts, "contacts": contacts, "mntForm": mntForm, "contactForm": contactForm, "netblocks": netblocks, "asblocks": asblocks, "asnumbers": asnumbers})
@ -156,6 +154,7 @@ class InetNumCreate(LoginRequiredMixin, CreateView):
form_class = InetNumForm
def get_form_kwargs(self, *args, **kwargs):
print("NOOOOOOOOOOOOOOOOOOOOOOOOOOOOT", args, kwargs)
kwargs = super(InetNumCreate, self).get_form_kwargs(*args, **kwargs)
kwargs["user"] = self.request.user
kwargs["initial"] = {