aboutsummaryrefslogtreecommitdiff
path: root/rpkid
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid')
-rw-r--r--rpkid/rpki/gui/app/admin.py41
-rw-r--r--rpkid/rpki/gui/app/forms.py25
-rw-r--r--rpkid/rpki/gui/app/glue.py68
-rw-r--r--rpkid/rpki/gui/app/models.py172
-rw-r--r--rpkid/rpki/gui/app/templates/rpkigui/app_base.html2
-rw-r--r--rpkid/rpki/gui/app/templates/rpkigui/child_view.html23
-rw-r--r--rpkid/rpki/gui/app/templates/rpkigui/dashboard.html57
-rw-r--r--rpkid/rpki/gui/app/templates/rpkigui/object_list.html6
-rw-r--r--rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html11
-rw-r--r--rpkid/rpki/gui/app/urls.py33
-rw-r--r--rpkid/rpki/gui/app/views.py562
-rw-r--r--rpkid/rpki/gui/urls.py2
12 files changed, 300 insertions, 702 deletions
diff --git a/rpkid/rpki/gui/app/admin.py b/rpkid/rpki/gui/app/admin.py
index dada955c..6ad78fda 100644
--- a/rpkid/rpki/gui/app/admin.py
+++ b/rpkid/rpki/gui/app/admin.py
@@ -2,6 +2,7 @@
$Id$
Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+Copyright (C) 2012 SPARTA, Inc. a Parsons Company
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -16,52 +17,12 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
"""
-from django import forms
from django.contrib import admin
from rpki.gui.app import models
-class ConfAdmin( admin.ModelAdmin ):
- pass
-
-class ChildAdmin( admin.ModelAdmin ):
- pass
-
-class AddressRangeAdmin( admin.ModelAdmin ):
- #list_display = ('__unicode__', 'lo', 'hi')
- pass
-
-class AsnAdmin( admin.ModelAdmin ):
- #list_display = ('__unicode__',)
- pass
-
-class ParentAdmin( admin.ModelAdmin ):
- pass
-
-class RoaAdmin( admin.ModelAdmin ):
- pass
-
-class ResourceCertAdmin(admin.ModelAdmin):
- pass
-
-class RoaRequestAdmin(admin.ModelAdmin):
- pass
-
-class GhostbusterAdmin(admin.ModelAdmin):
- pass
-
class TimestampAdmin(admin.ModelAdmin):
list_display = ('name', 'ts')
- pass
-admin.site.register(models.AddressRange, AddressRangeAdmin)
-admin.site.register(models.Child, ChildAdmin)
-admin.site.register(models.Conf, ConfAdmin)
-admin.site.register(models.Asn, AsnAdmin)
-admin.site.register(models.Ghostbuster, GhostbusterAdmin)
-admin.site.register(models.Parent, ParentAdmin)
-admin.site.register(models.ResourceCert, ResourceCertAdmin)
-admin.site.register(models.Roa, RoaAdmin)
-admin.site.register(models.RoaRequest, RoaRequestAdmin)
admin.site.register(models.Timestamp, TimestampAdmin)
# vim:sw=4 ts=8
diff --git a/rpkid/rpki/gui/app/forms.py b/rpkid/rpki/gui/app/forms.py
index ecac5e0a..14a0a463 100644
--- a/rpkid/rpki/gui/app/forms.py
+++ b/rpkid/rpki/gui/app/forms.py
@@ -1,6 +1,7 @@
# $Id$
"""
Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+Copyright (C) 2012 SPARTA, Inc. a Parsons Company
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -247,6 +248,30 @@ def ChildWizardForm(parent, *args, **kwargs):
return wrapped(*args, **kwargs)
+class AddASNForm(forms.Form):
+ as_range = forms.CharField(max_length=30, required=True, help_text='single AS or range')
+
+ def clean_as_range(self):
+ try:
+ r = resource_set.resource_range_as.parse_str(self.cleaned_data['asrange'])
+ except:
+ raise forms.ValidationError, 'invalid AS or range'
+ return str(r)
+
+class AddAddressForm(forms.Form):
+ prefix = forms.CharField(max_length=70, required=True, help_text='single IP address, CIDR or range')
+
+ def clean_prefix(self):
+ v = self.cleaned_data['prefix']
+ try:
+ r = resource_set.resource_range_ipv4.parse_str(v)
+ except rpki.exceptions.BadIPResource:
+ try:
+ r = resource_set.resource_range_ipv6.parse_str(v)
+ except:
+ raise forms.ValidationError, 'bad IP address, CIDR or range'
+ return str(r)
+
class GenericConfirmationForm(forms.Form):
"""
stub form used for doing confirmations.
diff --git a/rpkid/rpki/gui/app/glue.py b/rpkid/rpki/gui/app/glue.py
index 674c6a79..fc6c2f31 100644
--- a/rpkid/rpki/gui/app/glue.py
+++ b/rpkid/rpki/gui/app/glue.py
@@ -176,68 +176,36 @@ def configure_resources(log, handle):
z.synchronize([handle])
def list_received_resources(log, conf):
- """Query rpkid for this resource handle's received resources."""
+ """Query rpkid for this resource handle's received resources.
+
+ The semantics are to clear the entire table and populate with the
+ list of certs received. Other models should not reference the
+ table directly with foreign keys."""
z = Zookeeper(handle=conf.handle)
pdus = z.call_rpkid(rpki.left_right.list_received_resources_elt.make_pdu(self_handle=conf.handle))
+ models.ResourceCert.objects.filter(parent__issuer=conf).delete()
+
for pdu in pdus:
if isinstance(pdu, rpki.left_right.list_received_resources_elt):
- try:
- # need to convert from irdb.models.Parent to app.models.Parent
- parent = conf.parents.get(handle=pdu.parent_handle).app_parent
- except rpki.irdb.models.Parent.DoesNotExist:
- print >>log, 'error: %s received <list_received_resources/> for unknown parent %s' % (conf.handle, pdu.parent_handle,)
- continue
+ parent = models.Parent.get(issuer=conf, handle=pdu.parent_handle)
not_before = datetime.strptime(pdu.notBefore, "%Y-%m-%dT%H:%M:%SZ")
not_after = datetime.strptime(pdu.notAfter, "%Y-%m-%dT%H:%M:%SZ")
- #print >>log, 'uri: %s, not before: %s, not after: %s' % (pdu.uri, not_before, not_after)
-
- # have we seen this resource cert before?
- cert_set = parent.resources.filter(uri=pdu.uri)
- if cert_set.count() == 0:
- cert = models.ResourceCert(uri=pdu.uri, parent=parent,
- not_before=not_before, not_after=not_after)
- else:
- cert = cert_set[0]
- # update timestamps since it could have been modified
- cert.not_before = not_before
- cert.not_after = not_after
- cert.save()
+ cert = models.ResourceCert.objects.create(parent=parent, not_before=not_before, not_after=not_after)
for asn in rpki.resource_set.resource_set_as(pdu.asn):
- # see if this resource is already part of the cert
- if cert.asn.filter(lo=asn.min, hi=asn.max).count() == 0:
- # ensure this range wasn't seen from another of our parents
- for v in models.Asn.objects.filter(lo=asn.min, hi=asn.max):
- # determine if resource is delegated from another parent
- if v.from_cert.filter(parent__in=conf.parents.all()).count():
- cert.asn.add(v)
- break
- else:
- cert.asn.create(lo=asn.min, hi=asn.max)
- cert.save()
-
- # IPv4/6 - not separated in the django db
- def add_missing_address(addr_set):
- for ip in addr_set:
- lo=str(ip.min)
- hi=str(ip.max)
- if cert.address_range.filter(lo=lo, hi=hi).count() == 0:
- # ensure that this range wasn't previously seen from another of our parents
- for v in models.AddressRange.objects.filter(lo=lo, hi=hi):
- # determine if this resource is delegated from another parent as well
- if v.from_cert.filter(parent__in=conf.parents.all()).count():
- cert.address_range.add(v)
- break
- else:
- cert.address_range.create(lo=lo, hi=hi)
- cert.save()
-
- add_missing_address(rpki.resource_set.resource_set_ipv4(pdu.ipv4))
- add_missing_address(rpki.resource_set.resource_set_ipv6(pdu.ipv6))
+ cert.asn_ranges.add(min=asn.min, max=asn.max)
+
+ for rng in rpki.resource_set.resource_set_ipv4(pdu.ipv4):
+ cert.address_ranges.add(min=rng.min, max=rng.max)
+
+ for rng in rpki.resource_set.resource_set_ipv6(pdu.ipv6):
+ cert.address_ranges_v6.add(min=rng.min, max=rng.max)
+ else:
+ print >>log, "error: unexpected pdu from rpkid type=%s" % type(pdu)
def config_from_template(dest, a):
"""
diff --git a/rpkid/rpki/gui/app/models.py b/rpkid/rpki/gui/app/models.py
index b3333986..bec4bdf7 100644
--- a/rpkid/rpki/gui/app/models.py
+++ b/rpkid/rpki/gui/app/models.py
@@ -27,10 +27,6 @@ import rpki.resource_set
import rpki.exceptions
import rpki.irdb.models
-class IPAddressField(models.CharField):
- def __init__( self, **kwargs ):
- models.CharField.__init__(self, max_length=40, **kwargs)
-
class TelephoneField(models.CharField):
def __init__( self, **kwargs ):
models.CharField.__init__(self, max_length=40, **kwargs)
@@ -66,148 +62,72 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
have a handle on a resource-holding entity. It's the <self>
in the rpkid schema.'''
- class Meta:
- proxy = True
-
-class AddressRange(models.Model):
- '''An address range/prefix.'''
- lo = IPAddressField(blank=False)
- hi = IPAddressField(blank=False)
- # parent address range
- parent = models.ForeignKey('AddressRange', related_name='children',
- blank=True, null=True)
- # child to which this resource is delegated
- allocated = models.ForeignKey(Child, related_name='address_range',
- blank=True, null=True)
-
- class Meta:
- ordering = ('lo', 'hi')
-
- def __unicode__(self):
- if self.lo == self.hi:
- return u"%s" % (self.lo,)
-
- try:
- # pretty print cidr
- return unicode(self.as_resource_range())
- except socket.error, err:
- print err
- # work around for bug when hi/lo get reversed
- except AssertionError, err:
- print err
- return u'%s - %s' % (self.lo, self.hi)
-
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.app.views.address_view', [str(self.pk)])
+ @property
+ def parents(self):
+ """Simulates irdb.models.Parent.objects, but returns app.models.Parent proxy objects."""
+ return Parent.objects.filter(issuer=self)
- def as_resource_range(self):
- '''Convert to rpki.resource_set.resource_range_ip.'''
- return str_to_range(self.lo, self.hi)
-
- def is_prefix(self):
- '''Returns True if this address range can be represented as a
- prefix.'''
- try:
- self.as_resource_range().prefixlen()
- except rpki.exceptions.MustBePrefix, err:
- return False
- return True
-
-class Asn(models.Model):
- '''An ASN or range thereof.'''
- lo = models.IntegerField(blank=False)
- hi = models.IntegerField(blank=False)
- # parent asn range
- parent = models.ForeignKey('Asn', related_name='children',
- blank=True, null=True)
- # child to which this resource is delegated
- allocated = models.ForeignKey(Child, related_name='asn',
- blank=True, null=True)
+ @property
+ def children(self):
+ """Simulates irdb.models.Child.objects, but returns app.models.Child proxy objects."""
+ return Child.objects.filter(issuer=self)
class Meta:
- ordering = ('lo', 'hi')
-
- def __unicode__(self):
- if self.lo == self.hi:
- return u"ASN %d" % (self.lo,)
- else:
- return u"ASNs %d - %d" % (self.lo, self.hi)
-
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.app.views.asn_view', [str(self.pk)])
-
- def as_resource_range(self):
- # we force conversion to long() here because resource_range_as() wants
- # the type of both arguments to be identical, and models.IntegerField
- # will be a long when the value is large
- return rpki.resource_set.resource_range_as(long(self.lo), long(self.hi))
+ proxy = True
class ResourceCert(models.Model):
- parent = models.ForeignKey(Parent, related_name='resources')
+ """Represents a resource certificate.
- # resources granted from my parent
- asn = models.ManyToManyField(Asn, related_name='from_cert', blank=True,
- null=True)
- address_range = models.ManyToManyField(AddressRange,
- related_name='from_cert', blank=True, null=True)
+ This model is used to cache the output of <list_received_resources/>."""
- # unique id for this resource certificate
- # FIXME: URLField(verify_exists=False) doesn't seem to work - the admin
- # editor won't accept a rsync:// scheme as valid
- uri = models.CharField(max_length=200)
+ # pointer to the parent object in the irdb
+ parent = models.ForeignKey(Parent, related_name='certs')
# certificate validity period
not_before = models.DateTimeField()
not_after = models.DateTimeField()
def __unicode__(self):
- return u"%s's resource cert from parent %s" % (self.parent.conf.handle,
- self.parent.handle)
+ return u"%s's resource cert from parent %s" % (self.parent.issuer.handle, self.parent.handle)
-class Roa(models.Model):
- '''Maps an ASN to the set of prefixes it can originate routes for.
- This differs from a real ROA in that prefixes from multiple
- parents/resource certs can be selected. The glue module contains
- code to split the ROAs into groups by common resource certs.'''
+class ResourceRangeAddressV4(rpki.models.PrefixV4):
+ cert = models.ForeignKey(ResourceCert, related_name='address_ranges')
- conf = models.ForeignKey(Conf, related_name='roas')
- asn = models.IntegerField()
- active = models.BooleanField()
+class ResourceRangeAddressV6(rpki.models.PrefixV6):
+ cert = models.ForeignKey(ResourceCert, related_name='address_ranges_v6')
- # the resource cert from which all prefixes for this roa are derived
- cert = models.ForeignKey(ResourceCert, related_name='roas')
+class ResourceRangeAS(models.Model):
+ min = models.PositiveIntegerField()
+ max = models.PositiveIntegerField()
+ cert = models.ForeignKey(ResourceCert, related_name='asn_ranges')
- def __unicode__(self):
- return u"%s's ROA for %d" % (self.conf, self.asn)
+class RoaRequest(rpki.irdb.models.RoaRequest):
+ class Meta:
+ prefix = True
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.app.views.roa_view', [str(self.pk)])
+ def __unicode__(self):
+ return u'roa request for asn %d' % self.asn
-class RoaRequest(models.Model):
- roa = models.ForeignKey(Roa, related_name='from_roa_request')
- max_length = models.IntegerField()
- prefix = models.ForeignKey(AddressRange, related_name='roa_requests')
+class RoaRequestPrefix(rpki.irdb.models.RoaRequestPrefix):
+ class Meta:
+ prefix = True
def __unicode__(self):
- return u'roa request for asn %d on %s-%d' % (self.roa.asn, self.prefix,
- self.max_length)
+ return u'roa request prefix %s/%d-%d for asn %d' % (self.prefix, self.prefixlen, self.max_prefixlen, self.roa_request.asn)
def as_roa_prefix(self):
- '''Convert to a rpki.resouce_set.roa_prefix subclass.'''
- r = self.prefix.as_resource_range()
- if isinstance(r, rpki.resource_set.resource_range_ipv4):
- return rpki.resource_set.roa_prefix_ipv4(r.min, r.prefixlen(),
- self.max_length)
+ if self.family == 4:
+ r = resource_set.roa_prefix_ipv4(ipaddrs.v4addr(self.prefix), self.prefixlen, self.max_prefixlen)
else:
- return rpki.resource_set.roa_prefix_ipv6(r.min, r.prefixlen(),
- self.max_length)
+ r = resource_set.roa_prefix_ipv6(ipaddrs.v6addr(self.prefix), self.prefixlen, self.max_prefixlen)
+ return r
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.app.views.roa_request_view', [str(self.pk)])
+ def as_resource_range(self):
+ if self.family == 4:
+ r = resource_set.resource_range_ipv4.make_prefix(ipaddrs.v4addr(self.prefix), self.prefixlen)
+ else:
+ r = resource_set.resource_range_ipv6.make_prefix(ipaddrs.v6addr(self.prefix), self.prefixlen)
+ return r
class Ghostbuster(models.Model):
"""
@@ -236,20 +156,18 @@ class Ghostbuster(models.Model):
code = models.CharField(verbose_name='Postal Code', blank=True, null=True, max_length=40)
country = models.CharField(blank=True, null=True, max_length=40)
- conf = models.ForeignKey(Conf, related_name='ghostbusters')
- # parent can be null when using the same record for all parents
- parent = models.ManyToManyField(Parent, related_name='ghostbusters',
- blank=True, null=True, help_text='use this record for a specific parent, or leave blank for all parents')
+ # pointer to the IRDB object matching this ghostbuster request
+ irdb = models.ForeignKey(rpki.irdb.models.Ghostbuster, related_name='app_ghostbuster')
def __unicode__(self):
- return u"%s's GBR: %s" % (self.conf, self.full_name)
+ return u"%s's GBR: %s" % (self.issuer.handle, self.full_name)
@models.permalink
def get_absolute_url(self):
return ('rpki.gui.app.views.ghostbuster_view', [str(self.pk)])
class Meta:
- ordering = ( 'family_name', 'given_name' )
+ ordering = ('family_name', 'given_name')
class Timestamp(models.Model):
"""Model to hold metadata about the collection of external data.
diff --git a/rpkid/rpki/gui/app/templates/rpkigui/app_base.html b/rpkid/rpki/gui/app/templates/rpkigui/app_base.html
index c61bc2f2..be5abc19 100644
--- a/rpkid/rpki/gui/app/templates/rpkigui/app_base.html
+++ b/rpkid/rpki/gui/app/templates/rpkigui/app_base.html
@@ -13,7 +13,7 @@
<li><a href="{% url rpki.gui.app.views.route_view %}">routes</a></li>
<li><a href="{% url rpki.gui.app.views.parent_list %}">parents</a></li>
<li><a href="{% url rpki.gui.app.views.child_list %}">children</a></li>
- <li><a href="{% url rpki.gui.app.views.roa_request_list %}">roas</a></li>
+ <li><a href="{% url rpki.gui.app.views.roa_list %}">roas</a></li>
<li><a href="{% url rpki.gui.app.views.ghostbusters_list %}">ghostbusters</a></li>
</ul>
diff --git a/rpkid/rpki/gui/app/templates/rpkigui/child_view.html b/rpkid/rpki/gui/app/templates/rpkigui/child_view.html
index 2e4570da..3be40287 100644
--- a/rpkid/rpki/gui/app/templates/rpkigui/child_view.html
+++ b/rpkid/rpki/gui/app/templates/rpkigui/child_view.html
@@ -1,19 +1,12 @@
{% extends "rpkigui/app_base.html" %}
{% block content %}
-<!--
-<ul class='breadcrumb'>
- <li><a href="{% url rpki.gui.app.views.dashboard %}">{{ request.session.handle.handle }}</a> <span class='divider'>/</span></li>
- <li>Children <span class='divider'>/</span></li>
- <li class='active'>{{ child.handle }}</li>
-</ul>
--->
<div class='page-header'>
- <h1>Child View</h1>
+ <h1>Child Detail</h1>
</div>
-<table>
+<table style='condensed-table'>
<tr>
<td>Child</td>
<td>{{ child.handle }}</td>
@@ -25,10 +18,10 @@
</table>
<h2>Delegated Addresses</h2>
-{% if child.address_range.all %}
+{% if child.address_ranges.all %}
<ul>
-{% for a in child.address_range.all %}
-<li><a href="{{ a.get_absolute_url }}">{{ a }}</a></li>
+{% for a in child.address_ranges.all %}
+<li>{{ a.as_resource_range }}</li>
{% endfor %}
</ul>
{% else %}
@@ -36,10 +29,10 @@
{% endif %}
<h2>Delegated ASNs</h2>
-{% if child.asn.all %}
+{% if child.asns.all %}
<ul>
-{% for a in child.asn.all %}
-<li><a href="{{ a.get_absolute_url }}">{{ a }}</a></li>
+{% for a in child.asns.all %}
+<li>{{ a.as_resource_range }}</li>
{% endfor %}
</ul>
{% else %}
diff --git a/rpkid/rpki/gui/app/templates/rpkigui/dashboard.html b/rpkid/rpki/gui/app/templates/rpkigui/dashboard.html
index ee8f749d..ab627b5e 100644
--- a/rpkid/rpki/gui/app/templates/rpkigui/dashboard.html
+++ b/rpkid/rpki/gui/app/templates/rpkigui/dashboard.html
@@ -26,13 +26,6 @@
{% block content %}
-<!--
-<ul class='breadcrumb'>
- <li><a href='{% url rpki.gui.app.views.dashboard %}'>{{ request.session.handle }}</a><span class="divider">/</span></li>
- <li class='active'>Dashboard</li>
-</ul>
--->
-
<div class='page-header'>
<h1>Dashboard</h1>
</div>
@@ -48,48 +41,60 @@
<th>Parent</th>
</tr>
- {% for object in asn_list %}
+ {% for object in asns %}
+ <tr>
+ <td>{{ object.as_resource_range }}</td>
+ <td>{{ object.cert.not_after }}</td>
+ <td>{{ object.cert.parent.handle }}</td>
+ </tr>
+ {% endfor %}
+
+ {% for object in prefixes %}
<tr>
- <td><a href="{{ object.get_absolute_url }}">{{ object }}</a></td>
- <td>{{ object.from_cert.all.0.not_after }}</td>
- <td><a href="{{ object.from_cert.all.0.parent.get_absolute_url }}">{{ object.from_cert.all.0.parent.handle }}</a></th>
+ <td>{{ object.as_resource_range }}</td>
+ <td>{{ object.cert.not_after }}</td>
+ <td>{{ object.cert.parent.handle }}</td>
</tr>
{% endfor %}
- {% for object in address_list %}
+ {% if prefixes_v6 %}
+ {% for object in prefixes_v6 %}
<tr>
- <td><a href="{{ object.get_absolute_url }}">{{ object }}</a></td>
- <td>{{ object.from_cert.all.0.not_after }}</td>
- <td><a href="{{ object.from_cert.all.0.parent.get_absolute_url }}">{{ object.from_cert.all.0.parent.handle }}</a></th>
+ <td>{{ object.as_resource_range }}</td>
+ <td>{{ object.cert.not_after }}</td>
+ <td>{{ object.cert.parent.handle }}</td>
</tr>
{% endfor %}
+ {% endif %}
</table>
<!-- UNALLOCATED ##############################################################-->
<div>
<h2>Unallocated Resources</h2>
- <p>The following resources have not been allocated to a child, or appear in a ROA.
-
- {% if asns or ars %}
+ <p>The following resources have not been allocated to a child, nor appear in a ROA.
- {% if asns %}
+ {% if unused_asns %}
<ul>
- {% for asn in asns %}
- <li>{{ asn.as_ul|safe }}
+ {% for asn in unused_asns %}
+ <li>{{ asn }}
{% endfor %} <!-- ASNs -->
</ul>
{% endif %}
- {% if ars %}
+ {% if unused_prefixes %}
<ul>
- {% for addr in ars %}
- <li>{{ addr.as_ul|safe }}
+ {% for addr in unused_prefixes %}
+ <li>{{ addr }}
{% endfor %} <!-- addrs -->
</ul>
{% endif %}
- {% else %}
- <p style='font-style:italic'>none</p>
+ {% if unused_prefixes_v6 %}
+ <ul>
+ {% for addr in unused_prefixes_v6 %}
+ <li>{{ addr }}
+ {% endfor %} <!-- addrs -->
+ </ul>
{% endif %}
</div>
diff --git a/rpkid/rpki/gui/app/templates/rpkigui/object_list.html b/rpkid/rpki/gui/app/templates/rpkigui/object_list.html
index b7b640bb..28294854 100644
--- a/rpkid/rpki/gui/app/templates/rpkigui/object_list.html
+++ b/rpkid/rpki/gui/app/templates/rpkigui/object_list.html
@@ -3,12 +3,6 @@
{# generic object list #}
{% block content %}
-<!--
-<ul class='breadcrumb'>
- <li><a href="{% url rpki.gui.app.views.dashboard %}">{{ request.session.handle }}</a> <span class='divider'>/</span></li>
- <li class='active'>{{ page_title }}</li>
-</ul>
--->
<div class='page-header'>
<h1>{{ page_title }}</h1>
diff --git a/rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html b/rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html
index a9ecf9c9..4531bca1 100644
--- a/rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html
+++ b/rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html
@@ -1,15 +1,6 @@
{% extends "rpkigui/app_base.html" %}
-{# generic object list #}
-
{% block content %}
-<!--
-<ul class='breadcrumb'>
- <li><a href="{% url rpki.gui.app.views.dashboard %}">{{ request.session.handle }}</a> <span class='divider'>/</span></li>
- <li class='active'>{{ page_title }}</li>
-</ul>
--->
-
<div class='page-header'>
<h1>{{ page_title }}</h1>
</div>
@@ -22,7 +13,7 @@
<td><a href="{{ object.prefix.get_absolute_url }}">{{ object.prefix }}</a></td>
<td>{{ object.max_length }}</td>
<td>{{ object.roa.asn }}</td>
- <td><a class='btn danger' href="{{ object.get_absolute_url}}/delete">Delete</a></td>
+ <td><a class='btn danger' href="{{ object.get_absolute_url }}/delete">Delete</a></td>
</tr>
{% endfor %}
</table>
diff --git a/rpkid/rpki/gui/app/urls.py b/rpkid/rpki/gui/app/urls.py
index bb7da0b4..4c87aa43 100644
--- a/rpkid/rpki/gui/app/urls.py
+++ b/rpkid/rpki/gui/app/urls.py
@@ -1,6 +1,7 @@
# $Id$
"""
Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+Copyright (C) 2012 SPARTA, Inc. a Parsons Company
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -24,32 +25,25 @@ urlpatterns = patterns('',
(r'^conf/list$', views.conf_list),
(r'^conf/select$', views.conf_select),
(r'^parent/$', views.parent_list),
- (r'^parent/(?P<parent_handle>[^/]+)$', views.parent_view),
- (r'^parent/(?P<parent_handle>[^/]+)/delete$', views.parent_delete),
+ (r'^parent/(?P<pk>\d+)$', views.parent_view),
+ (r'^parent/(?P<pk>\d+)/delete$', views.parent_delete),
(r'^child/$', views.child_list),
- (r'^child/(?P<child_handle>[^/]+)$', views.child_view),
- (r'^child/(?P<child_handle>[^/]+)/delete$', views.child_delete),
- (r'^child/(?P<child_handle>[^/]+)/edit$', views.child_edit),
- (r'^child/(?P<child_handle>[^/]+)/export$', views.export_child_response),
- (r'^child/(?P<child_handle>[^/]+)/export_repo$', views.export_child_repo_response),
- (r'^child/(?P<handle>[^/]+)/destroy$', views.destroy_handle),
- (r'^address/(?P<pk>\d+)$', views.address_view),
- (r'^address/(?P<pk>\d+)/split$', views.prefix_split_view),
- (r'^address/(?P<pk>\d+)/allocate$', views.prefix_allocate_view),
- (r'^address/(?P<pk>\d+)/roa$', views.prefix_roa_view),
- (r'^address/(?P<pk>\d+)/delete$', views.prefix_delete_view),
- (r'^asn/(?P<pk>\d+)$', views.asn_view),
- (r'^asn/(?P<pk>\d+)/allocate$', views.asn_allocate_view),
+ (r'^child/(?P<pk>\d+)$', views.child_view),
+ (r'^child/(?P<pk>\d+)/add_asn/$', views.child_add_asn),
+ (r'^child/(?P<pk>\d+)/add_address/$', views.child_add_address),
+ (r'^child/(?P<pk>\d+)/delete$', views.child_delete),
+ (r'^child/(?P<pk>\d+)/edit$', views.child_edit),
+ (r'^child/(?P<pk>\d+)/export$', views.export_child_response),
+ (r'^child/(?P<pk>\d+)/export_repo$', views.export_child_repo_response),
+ (r'^child/(?P<pk>\d+)/destroy$', views.destroy_handle),
(r'^gbr/$', views.ghostbusters_list),
(r'^gbr/create$', views.ghostbuster_create),
(r'^gbr/(?P<pk>\d+)$', views.ghostbuster_view),
(r'^gbr/(?P<pk>\d+)/edit$', views.ghostbuster_edit),
(r'^gbr/(?P<pk>\d+)/delete$', views.ghostbuster_delete),
(r'^refresh$', views.refresh),
- (r'^roa/(?P<pk>\d+)$', views.roa_view),
- (r'^roareq/$', views.roa_request_list),
- (r'^roareq/(?P<pk>\d+)$', views.roa_request_view),
- (r'^roareq/(?P<pk>\d+)/delete$', views.roa_request_delete_view),
+ (r'^roa/$', views.roa_list),
+ (r'^roa/(?P<pk>\d+)/delete$', views.roa_delete),
(r'^routes/$', views.route_view),
(r'^demo/down/asns/(?P<self_handle>[^/]+)$', views.download_asns),
(r'^demo/down/prefixes/(?P<self_handle>[^/]+)$', views.download_prefixes),
@@ -62,7 +56,6 @@ urlpatterns = patterns('',
(r'^import_parent$', views.import_parent),
(r'^import_pubclient$', views.import_pubclient),
(r'^import_repository$', views.import_repository),
-# (r'^initialize$', views.initialize),
(r'^child_wizard$', views.child_wizard),
(r'^update_bpki', views.update_bpki),
)
diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py
index 94aa1d65..39213266 100644
--- a/rpkid/rpki/gui/app/views.py
+++ b/rpkid/rpki/gui/app/views.py
@@ -2,6 +2,7 @@
"""
Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+Copyright (C) 2012 SPARTA, Inc. a Parsons Company
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ -34,6 +35,9 @@ from django.core.urlresolvers import reverse
from rpki.gui.app import models, forms, glue, misc, AllocationTree, settings
from rpki.gui.app.asnset import asnset
+from rpki import resource_set
+import rpki.irdb
+import rpki.exceptions
import rpki.gui.cacheview.models
import rpki.gui.routeview.models
@@ -57,6 +61,15 @@ def my_login_required(f):
return wrapped
+def superuser_required(f):
+ "Decorator which returns HttpResponseForbidden if the user does not have superuser permissions."
+ @login_required
+ def _wrapped(request, *args, **kwargs):
+ if not request.user.is_superuser:
+ raise http.HttpResponseForbidden()
+ return f(request, *args, **kwargs)
+ return _wrapped
+
# For each type of object, we have a detail view, a create view and
# an update view. We heavily leverage the generic views, only
# adding our own idea of authorization.
@@ -68,18 +81,17 @@ def handle_required(f):
if request.user.is_superuser:
conf = models.Conf.objects.all()
else:
- conf = models.Conf.objects.filter(owner=request.user)
+ conf = models.Conf.objects.filter(handle=request.user.username)
+
if conf.count() == 1:
- handle = conf[0]
+ request.session['handle'] = conf[0]
elif conf.count() == 0:
return render('rpkigui/conf_empty.html', {}, request)
- #return http.HttpResponseRedirect('/myrpki/conf/add')
else:
# Should reverse the view for this instead of hardcoding
# the URL.
return http.HttpResponseRedirect(
reverse(conf_list) + '?next=' + urlquote(request.get_full_path()))
- request.session[ 'handle' ] = handle
return f(request, *args, **kwargs)
return wrapped_fn
@@ -89,44 +101,67 @@ def render(template, context, request):
@handle_required
def dashboard(request, template_name='rpkigui/dashboard.html'):
- '''The user's dashboard.'''
- handle = request.session[ 'handle' ]
-
- asns=[]
- asn_list = models.Asn.objects.filter(from_cert__parent__in=handle.parents.all())
- for a in asn_list:
- f = AllocationTree.AllocationTreeAS(a)
- if f.unallocated():
- asns.append(f)
-
- prefixes = []
- address_list = models.AddressRange.objects.filter(from_cert__parent__in=handle.parents.all())
- for p in address_list:
- f = AllocationTree.AllocationTreeIP.from_prefix(p)
- if f.unallocated():
- prefixes.append(f)
-
- asns.sort(key=lambda x: x.range.min)
- prefixes.sort(key=lambda x: x.range.min)
+
+ conf = request.session['handle']
+
+ used_asns = resource_set.resource_set_as()
+
+ # asns used in my roas
+ roa_asns = set((obj.asn for obj in models.RoaRequest.objects.filter(issuer=conf)))
+ used_asns.extend((resource_set.resource_range_as(asn, asn) for asn in roa_asns))
+
+ # asns given to my children
+ child_asns = rpki.irdb.models.ChildASN(child__in=conf.children.all())
+ used_asns.extend((resource_set.resource_range_as(obj.start_as, obj.end_as) for obj in child_asns))
+
+ used_asns.canonize()
+
+ # my received asns
+ asn = models.ResourceRangeAS(cert__parent__issuer=conf)
+ my_asns = resource_set.resource_set_as([resource_set.resource_range_as(obj.min, obj.max) for obj in asns])
+
+ unused_asns = my_asns.difference(used_asns)
+
+ used_prefixes = resource_set.resource_set_ipv4()
+ used_prefixes_v6 = resource_set.resource_set_ipv6()
+
+ # prefixes used in my roas
+ used_prefixes.extend((obj.as_resource_range() for obj in models.RoaRequestPrefix.objects.filter(roa_request__issuer=conf, version=4)))
+ used_prefixes_v6.extend((obj.as_resource_range() for obj in models.RoaRequestPrefix.objects.filter(roa_request__issuer=conf, version=6)))
+
+ # prefixes given to my children
+ used_prefixes.extend((obj.as_resource_range() for obj in rpki.irdb.models.ChildNet(child__in=conf.children.all(), version=4)))
+ used_prefixes_v6.extend((obj.as_resource_range() for obj in rpki.irdb.models.ChildNet(child__in=conf.children.all(), version=6)))
+
+ used_prefixes.canonize()
+ used_prefixes_v6.canonize()
+
+ # my received prefixes
+ prefixes = models.ResourceRangeAddressV4.objects.filter(cert__parent__issuer=conf)
+ prefixes_v6 = models.ResourceRangeAddressV6.objects.filter(cert__parent__issuer=conf)
+ my_prefixes = resource_set.resource_set_ipv4([obj.as_resource_range() for obj in prefixes])
+ my_prefixes_v6 = resource_set.resource_set_ipv6([obj.as_resource_range() for obj in prefixes_v6])
+
+ unused_prefixes = my_prefixes.difference(used_prefixes)
+ unused_prefixes_v6 = my_prefixes_v6.difference(used_prefixes_v6)
return render(template_name, {
'conf': handle,
+ 'unused_asns': unused_asns,
+ 'unused_prefixes': unused_prefixes,
+ 'unused_prefixes_v6': unused_prefixes_v6,
'asns': asns,
- 'ars': prefixes,
- 'asn_list': asn_list,
- 'address_list': address_list }, request)
+ 'prefixes': prefixes,
+ 'prefixes_v6': prefixes }, request)
-@login_required
+@superuser_required
def conf_list(request):
"""Allow the user to select a handle."""
- if request.user.is_superuser:
- queryset = models.Conf.objects.all()
- else:
- queryset = models.Conf.objects.filter(owner=request.user)
+ queryset = models.Conf.objects.all()
return object_list(request, queryset,
template_name='rpkigui/conf_list.html', template_object_name='conf', extra_context={ 'select_url' : reverse(conf_select) })
-@login_required
+@superuser_required
def conf_select(request):
'''Change the handle for the current session.'''
if not 'handle' in request.GET:
@@ -135,19 +170,8 @@ def conf_select(request):
next_url = request.GET.get('next', reverse(dashboard))
if next_url == '':
next_url = reverse(dashboard)
-
- if request.user.is_superuser:
- conf = models.Conf.objects.filter(handle=handle)
- else:
- # since the handle is passed in as a parameter, need to verify that
- # the user is actually in the group
- conf = models.Conf.objects.filter(handle=handle,
- owner=request.user)
- if conf:
- request.session['handle'] = conf[0]
- return http.HttpResponseRedirect(next_url)
-
- return http.HttpResponseRedirect(reverse(conf_list) + '?next=' + next_url)
+ request.session['handle'] = get_object_or_404(models.Conf, handle=handle)
+ return http.HttpResponseRedirect(next_url)
def serve_xml(content, basename):
resp = http.HttpResponse(content , mimetype='application/xml')
@@ -164,7 +188,6 @@ def conf_export(request):
def parent_list(request):
"""List view for parent objects."""
conf = request.session['handle']
-
return object_list(request, queryset=conf.parents.all(), template_name='rpkigui/parent_list.html',
extra_context = { 'page_title': 'Parents' })
@@ -172,54 +195,62 @@ def parent_list(request):
def child_list(request):
"""List view for child objects."""
conf = request.session['handle']
-
return object_list(request, queryset=conf.children.all(),
template_name = 'rpkigui/child_list.html',
extra_context = { 'page_title': 'Children' })
@handle_required
-def parent_view(request, parent_handle):
- """Detail view for a particular parent."""
- handle = request.session['handle']
- parent = get_object_or_404(handle.parents, handle__exact=parent_handle)
+def child_add_resource(request, pk, form_class, unused_list, callback, template_name='rpkigui/child_add_resource_form.html'):
+ conf = request.session['handle']
+ child = models.Child.objects.filter(issuer=conf, pk=pk)
+ if request.method == 'POST':
+ form = form_class(request.POST, request.FILES)
+ if form.is_valid():
+ callback(child, form)
+ return http.HttpResponseRedirect(child.get_absolute_url())
+ else:
+ form = form_class()
- return render('rpkigui/parent_view.html', { 'parent': parent }, request)
+ return render(template_name, { 'object': child, 'form': form, 'unused': unused_list }, request)
+
+def add_asn_callback(child, form):
+ r = resource_set.resource_range_as.parse_str(form.as_range)
+ child.asns.create(min=r.min, max=r.max)
-def get_parents_or_404(handle, obj):
- '''Return the Parent object(s) that the given address range derives
- from, or raise a 404 error.'''
- cert_set = misc.top_parent(obj).from_cert.filter(parent__in=handle.parents.all())
- if cert_set.count() == 0:
- raise http.Http404, 'Object is not delegated from any parent'
- return [c.parent for c in cert_set]
+def child_add_asn(request, pk):
+ return child_add_resource(request, pk, form_class=forms.AddASNForm, add_asn_callback)
+
+def add_address_callback(child, form):
+ try:
+ r = resource_set.resource_range_ipv4.parse_str(form.prefix)
+ family = 4
+ except rpki.exceptions.BadIPResource:
+ r = resource_set.resource_range_ipv6.parse_str(form.prefix)
+ family = 6
+ child.address_ranges.create(min=str(r.min), max=str(r.max), family=family)
+
+def child_add_address(request, pk):
+ return child_add_resource(request, pk, form_class=forms.AddAddressForm, add_address_callback)
@handle_required
-def asn_view(request, pk):
- '''view/subdivide an asn range.'''
+def parent_view(request, pk):
+ """Detail view for a particular parent."""
handle = request.session['handle']
- obj = get_object_or_404(models.Asn.objects, pk=pk)
- # ensure this resource range belongs to a parent of the current conf
- parent_set = get_parents_or_404(handle, obj)
- roas = handle.roas.filter(asn=obj.lo) # roas which contain this asn
- unallocated = AllocationTree.AllocationTreeAS(obj).unallocated()
-
- return render('rpkigui/asn_view.html',
- { 'asn': obj, 'parent': parent_set, 'roas': roas,
- 'unallocated' : unallocated }, request)
+ parent = get_object_or_404(handle.parents.all(), pk=pk)
+ return render('rpkigui/parent_view.html', { 'parent': parent }, request)
@handle_required
-def child_view(request, child_handle):
+def child_view(request, pk):
'''Detail view of child for the currently selected handle.'''
handle = request.session['handle']
- child = get_object_or_404(handle.children, handle__exact=child_handle)
-
+ child = get_object_or_404(handle.children.all(), pk=pk)
return render('rpkigui/child_view.html', { 'child': child }, request)
@handle_required
-def child_edit(request, child_handle):
+def child_edit(request, pk):
"""Edit the end validity date for a resource handle's child."""
handle = request.session['handle']
- child = get_object_or_404(handle.children, handle__exact=child_handle)
+ child = get_object_or_404(handle.children.all(), pk=pk)
if request.method == 'POST':
form = forms.ChildForm(request.POST, request.FILES, instance=child)
@@ -232,224 +263,6 @@ def child_edit(request, child_handle):
return render('rpkigui/child_form.html', { 'child': child, 'form': form }, request)
-class PrefixView(object):
- '''Extensible view for address ranges/prefixes. This view can be
- subclassed to add form handling for editing the prefix.'''
-
- form = None
- form_title = None
- submit_value = 'Submit'
-
- def __init__(self, request, pk, form_class=None):
- self.handle = request.session['handle']
- self.obj = get_object_or_404(models.AddressRange.objects, pk=pk)
- # ensure this resource range belongs to a parent of the current conf
- self.parent_set = get_parents_or_404(self.handle, self.obj)
- self.form_class = form_class
- self.request = request
-
- def __call__(self, *args, **kwargs):
- if self.request.method == 'POST':
- resp = self.handle_post()
- else:
- resp = self.handle_get()
-
- # allow get/post handlers to return a custom response
- if resp:
- return resp
-
- u = AllocationTree.AllocationTreeIP.from_prefix(self.obj).unallocated()
-
- return render('rpkigui/prefix_view.html',
- { 'addr': self.obj, 'parent': self.parent_set, 'unallocated': u,
- 'form': self.form,
- 'form_title': self.form_title if self.form_title else 'Edit',
- 'submit_value': self.submit_value },
- self.request)
-
- def handle_get(self):
- '''Virtual method for extending GET handling. Default action is
- to call the form class constructor with the prefix object.'''
- if self.form_class:
- self.form = self.form_class(self.obj)
-
- def form_valid(self):
- '''Virtual method for handling a valid form. Called by the default
- implementation of handle_post().'''
- pass
-
- def handle_post(self):
- '''Virtual method for extending POST handling. Default implementation
- creates a form object using the form_class in the constructor and passing
- the prefix object. If the form's is_valid() method is True, it then
- invokes this class's form_valid() method.'''
- resp = None
- if self.form_class:
- self.form = self.form_class(self.obj, self.request.POST)
- if self.form.is_valid():
- resp = self.form_valid()
- return resp
-
-@handle_required
-def address_view(request, pk):
- return PrefixView(request, pk)()
-
-class PrefixSplitView(PrefixView):
- '''Class for handling the prefix split form.'''
-
- form_title = 'Split'
- submit_value = 'Split'
-
- def form_valid(self):
- r = misc.parse_resource_range(self.form.cleaned_data['prefix'])
- obj = models.AddressRange(lo=str(r.min), hi=str(r.max), parent=self.obj)
- obj.save()
- return http.HttpResponseRedirect(obj.get_absolute_url())
-
-@handle_required
-def prefix_split_view(request, pk):
- return PrefixSplitView(request, pk, form_class=forms.PrefixSplitForm)()
-
-class PrefixAllocateView(PrefixView):
- '''Class to handle the allocation to child form.'''
-
- form_title = 'Give to Child'
- submit_label = 'Allocate'
-
- def handle_get(self):
- self.form = forms.PrefixAllocateForm(
- self.obj.allocated.pk if self.obj.allocated else None,
- self.handle.children.all())
-
- def handle_post(self):
- self.form = forms.PrefixAllocateForm(None, self.handle.children.all(), self.request.POST)
- if self.form.is_valid():
- self.obj.allocated = self.form.cleaned_data['child']
- self.obj.save()
- glue.configure_resources(self.request.META['wsgi.errors'], self.handle)
- return http.HttpResponseRedirect(self.obj.get_absolute_url())
-
-@handle_required
-def prefix_allocate_view(request, pk):
- return PrefixAllocateView(request, pk)()
-
-def add_roa_requests(handle, prefix, asns, max_length):
- for asid in asns:
- if debug:
- print 'searching for a roa for AS %d containing %s-%d' % (asid, prefix, max_length)
- req_set = prefix.roa_requests.filter(roa__asn=asid, max_length=max_length)
- if not req_set:
- if debug:
- print 'no roa for AS %d containing %s-%d' % (asid, prefix, max_length)
-
- # find ROAs for prefixes derived from the same resource cert
- # as this prefix
- certs = misc.top_parent(prefix).from_cert.all()
- roa_set = handle.roas.filter(asn=asid, cert__in=certs)
-
- # FIXME: currently only creates a ROA/request for the first
- # resource cert, not all of them
- if roa_set:
- roa = roa_set[0]
- else:
- if debug:
- print 'creating new roa for AS %d containg %s-%d' % (asid, prefix, max_length)
- # no roa is present for this ASN, create a new one
- roa = models.Roa.objects.create(asn=asid, conf=handle,
- active=False, cert=certs[0])
- roa.save()
-
- req = models.RoaRequest.objects.create(prefix=prefix, roa=roa,
- max_length=max_length)
- req.save()
-
-class PrefixRoaView(PrefixView):
- '''Class for handling the ROA creation form.'''
-
- form_title = 'Issue ROA'
- submit_value = 'Create'
-
- def form_valid(self):
- asns = asnset(self.form.cleaned_data['asns'])
- add_roa_requests(self.handle, self.obj, asns, self.form.cleaned_data['max_length'])
- glue.configure_resources(self.request.META['wsgi.errors'], self.handle)
- return http.HttpResponseRedirect(self.obj.get_absolute_url())
-
-@handle_required
-def prefix_roa_view(request, pk):
- return PrefixRoaView(request, pk, form_class=forms.PrefixRoaForm)()
-
-class PrefixDeleteView(PrefixView):
- form_title = 'Delete'
-
- def form_valid(self):
- self.obj.delete()
- return http.HttpResponseRedirect(reverse(dashboard))
-
-@handle_required
-def prefix_delete_view(request, pk):
- return PrefixDeleteView(request, pk, form_class=forms.PrefixDeleteForm)()
-
-@handle_required
-def roa_request_delete_view(request, pk):
- "Remove a ROA request from a particular prefix."
-
- log = request.META['wsgi.errors']
- handle = request.session['handle']
- obj = get_object_or_404(models.RoaRequest.objects, pk=pk)
- prefix = obj.prefix
- # ensure this resource range belongs to a parent of the current conf
- parent_set = get_parents_or_404(handle, prefix)
-
- if request.method == 'POST':
- roa = obj.roa
- obj.delete()
- if not roa.from_roa_request.all():
- roa.delete()
- glue.configure_resources(log, handle)
- return http.HttpResponseRedirect(prefix.get_absolute_url())
-
- match = roa_match(prefix.as_resource_range())
-
- roa_pfx = obj.as_roa_prefix()
-
- pfx = 'prefixes' if isinstance(roa_pfx, rpki.resource_set.roa_prefix_ipv4) else 'prefixes_v6'
- args = { '%s__prefix_min' % pfx : roa_pfx.min(),
- '%s__prefix_max' % pfx : roa_pfx.max(),
- '%s__max_length' % pfx : roa_pfx.max_prefixlen }
-
- # exclude ROAs which seem to match this request and display the result
- routes = []
- for route, roas in match:
- qs = roas.exclude(asid=obj.roa.asn, **args)
- validate_route(route, qs)
- routes.append(route)
-
- return render('rpkigui/roa_request_confirm_delete.html', { 'object': obj,
- 'routes': routes }, request)
-
-@handle_required
-def asn_allocate_view(request, pk):
- log = request.META['wsgi.errors']
- handle = request.session['handle']
- obj = get_object_or_404(models.Asn.objects, pk=pk)
- # ensure this resource range belongs to a parent of the current conf
- parent_set = get_parents_or_404(handle, obj)
-
- if request.method == 'POST':
- form = forms.PrefixAllocateForm(None, handle.children.all(), request.POST)
- if form.is_valid():
- obj.allocated = form.cleaned_data['child']
- obj.save()
- glue.configure_resources(log, handle)
- return http.HttpResponseRedirect(obj.get_absolute_url())
- else:
- form = forms.PrefixAllocateForm(obj.allocated.pk if obj.allocated else None,
- handle.children.all())
-
- return render('rpkigui/asn_view.html', { 'form': form,
- 'asn': obj, 'form': form, 'parent': parent_set }, request)
-
# this is similar to handle_required, except that the handle is given in URL
def handle_or_404(request, handle):
"ensure the requested handle is available to this user"
@@ -482,101 +295,6 @@ def download_roas(request, self_handle):
def download_prefixes(request, self_handle):
return download_csv(request, self_handle, 'prefixes')
-def save_to_inbox(conf, request_type, content):
- """
- Save an incoming request from a client to the incoming mailbox
- for processing by a human.
- """
-
- user = conf.owner.all()[0]
- filename = request_type + '.xml'
-
- msg = email.message.Message()
- msg['Date'] = email.utils.formatdate()
- msg['From'] = '"%s" <%s>' % (conf.handle, user.email)
- msg['Message-ID'] = email.utils.make_msgid()
- msg['Subject'] = '%s for %s' % (filename, conf.handle)
- msg['X-rpki-self-handle'] = conf.handle
- msg['X-rpki-type'] = request_type
- msg.add_header('Content-Disposition', 'attachment', filename=filename)
- msg.set_type('application/x-rpki-setup')
- msg.set_payload(content)
-
- box = mailbox.Maildir(settings.INBOX)
- box.add(msg)
- box.close()
-
- return http.HttpResponse()
-
-def get_response(conf, request_type):
- """
- If there is cached response for the given request type, simply
- return it. Otherwise, look in the outbox mailbox for a response.
- """
- filename = glue.confpath(conf.handle) + '/' + request_type + '.xml'
- if not os.path.exists(filename):
- box = mailbox.Maildir(settings.OUTBOX, factory=None)
- for key, msg in box.iteritems():
- # look for parent responses for this child
- if msg.get('x-rpki-type') == request_type and msg.get('x-rpki-self-handle') == conf.handle:
- with open(filename, 'w') as f:
- f.write(msg.get_payload())
- break
- else:
- return http.HttpResponse('no response found', status=503)
-
- box.remove(key) # remove the msg from the outbox
-
- return serve_file(conf.handle, request_type + '.xml', 'application/xml')
-
-@my_login_required
-def parent_request(request, self_handle):
- conf = handle_or_404(request, self_handle)
-
- if request.method == 'POST':
- return save_to_inbox(conf, 'identity', request.POST['content'])
- else:
- return get_response(conf, 'parent')
-
-@my_login_required
-def repository_request(request, self_handle):
- conf = handle_or_404(request, self_handle)
-
- if request.method == 'POST':
- return save_to_inbox(conf, 'repository', request.POST['content'])
- else:
- return get_response(conf, 'repository')
-
-@my_login_required
-def myrpki_xml(request, self_handle):
- """
- Handles POST of the myrpki.xml file for a given resource handle.
- As a special case for resource handles hosted by APNIC, stash a
- copy of the first xml message in the rpki inbox mailbox as this
- will be required to complete the parent-child setup.
- """
- conf = handle_or_404(request, self_handle)
- log = request.META['wsgi.errors']
-
- if request.method == 'POST':
- fname = glue.confpath(self_handle, '/myrpki.xml')
-
- if not os.path.exists(fname):
- print >>log, 'Saving a copy of myrpki.xml for handle %s to inbox' % conf.handle
- save_to_inbox(conf, 'myrpki', request.POST['content'])
-
- print >>log, 'writing %s' % fname
- with open(fname, 'w') as myrpki_xml :
- myrpki_xml.write(request.POST['content'])
-
- # FIXME: used to run configure_daemons here, but it takes too
- # long with many hosted handles. rpkidemo still needs a way
- # to do initial bpki setup with rpkid!
-
- return http.HttpResponse('<p>success</p>')
- else:
- return serve_file(self_handle, 'myrpki.xml', 'application/xml')
-
def login(request):
"""
A version of django.contrib.auth.views.login that will return an
@@ -601,22 +319,53 @@ def login(request):
return http.HttpResponse('<p>This should never been seen by a human</p>')
@handle_required
-def roa_request_view(request, pk):
- """not yet implemented"""
- return
+def roa_list(request):
+ "Displays a list of RoaRequestPrefix objects for the current resource handle."
+ log = request.META['wsgi.errors']
+ conf = request.session['handle']
+ return object_list(request, queryset=models.RoaRequestPrefix.objects.filter(roa_request__issuer=conf),
+ template_name='rpkigui/roa_request_list.html',
+ extra_context = { 'page_title': 'ROA Requests' })
@handle_required
-def roa_view(request, pk):
- """not yet implemented"""
- return
+def roa_delete(request, pk):
+ """Handles deletion of a single RoaRequestPrefix object.
-@handle_required
-def roa_request_list(request):
+ Uses a form for double confirmation, displaying how the route
+ validation status may change as a result."""
+
+ log = request.META['wsgi.errors']
conf = request.session['handle']
+ obj = get_object_or_404(models.RoaRequestPrefix.objects, roa_request__issuer=conf, pk=pk)
- return object_list(request, queryset=models.RoaRequest.objects.filter(roa__in=conf.roas.all()),
- template_name='rpkigui/roa_request_list.html',
- extra_context = { 'page_title': 'ROA Requests' })
+ if request.method == 'POST':
+ roa = obj.roa_request
+ obj.delete()
+ # if this was the last prefix on the ROA, delete the ROA request
+ if not roa.prefixes.exists():
+ roa.delete()
+ return http.HttpResponseRedirect(reverse(roa_request_list))
+
+ ### Process GET ###
+
+ match = roa_match(obj.as_resource_range())
+
+ roa_pfx = obj.as_roa_prefix()
+
+ pfx = 'prefixes' if isinstance(roa_pfx, resource_set.roa_prefix_ipv4) else 'prefixes_v6'
+ args = { '%s__prefix_min' % pfx : roa_pfx.min(),
+ '%s__prefix_max' % pfx : roa_pfx.max(),
+ '%s__max_length' % pfx : roa_pfx.max_prefixlen }
+
+ # exclude ROAs which seem to match this request and display the result
+ routes = []
+ for route, roas in match:
+ qs = roas.exclude(asid=obj.roa.asn, **args)
+ validate_route(route, qs)
+ routes.append(route)
+
+ return render('rpkigui/roa_request_confirm_delete.html', { 'object': obj,
+ 'routes': routes }, request)
@handle_required
def ghostbusters_list(request):
@@ -624,8 +373,9 @@ def ghostbusters_list(request):
Display a list of all ghostbuster requests for the current Conf.
"""
conf = request.session['handle']
+ qs = models.Ghostbuster.filter(irdb__issuer=conf)
- return object_list(request, queryset=conf.ghostbusters.all(),
+ return object_list(request, queryset=qs,
template_name='rpkigui/ghostbuster_list.html',
extra_context = { 'page_title': 'Ghostbusters' })
@@ -635,23 +385,23 @@ def ghostbuster_view(request, pk):
Display an individual ghostbuster request.
"""
conf = request.session['handle']
+ qs = models.Ghostbuster.filter(irdb__issuer=conf)
- return object_detail(request, queryset=conf.ghostbusters.all(), object_id=pk, template_name='rpkigui/ghostbuster_detail.html')
+ return object_detail(request, queryset=qs, object_id=pk, template_name='rpkigui/ghostbuster_detail.html')
@handle_required
def ghostbuster_delete(request, pk):
conf = request.session['handle']
# verify that the object is owned by this conf
- obj = get_object_or_404(models.Ghostbuster, pk=pk, conf=conf)
+ obj = get_object_or_404(models.Ghostbuster, pk=pk, irdb__issuer=conf)
# modeled loosely on the generic delete_object() view.
if request.method == 'POST':
- obj.delete()
- glue.configure_resources(request.META['wsgi.errors'], conf)
+ obj.irdb.delete() # should cause a cascade delete of 'obj'
return http.HttpResponseRedirect(reverse(ghostbusters_list))
- else:
- return render('rpkigui/ghostbuster_confirm_delete.html', { 'object': obj }, request)
+
+ return render('rpkigui/ghostbuster_confirm_delete.html', { 'object': obj }, request)
def _ghostbuster_edit(request, obj=None):
"""
diff --git a/rpkid/rpki/gui/urls.py b/rpkid/rpki/gui/urls.py
index 22d76226..93fc9373 100644
--- a/rpkid/rpki/gui/urls.py
+++ b/rpkid/rpki/gui/urls.py
@@ -40,5 +40,5 @@ urlpatterns = patterns('',
# !!!REMOVE THIS BEFORE COMMITTING!!!
(r'^site_media/(?P<path>.*)$', 'django.views.static.serve',
- {'document_root': '/var/www/media'}),
+ {'document_root': '/var/www/html/media'}),
)