diff options
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/rpki/gui/app/admin.py | 41 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/forms.py | 25 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/glue.py | 68 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/models.py | 172 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/rpkigui/app_base.html | 2 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/rpkigui/child_view.html | 23 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/rpkigui/dashboard.html | 57 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/rpkigui/object_list.html | 6 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/rpkigui/roa_request_list.html | 11 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/urls.py | 33 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/views.py | 562 | ||||
-rw-r--r-- | rpkid/rpki/gui/urls.py | 2 |
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'}), ) |