aboutsummaryrefslogtreecommitdiff
path: root/rpki/gui
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/gui')
-rw-r--r--rpki/gui/app/check_expired.py53
-rw-r--r--rpki/gui/app/forms.py194
-rw-r--r--rpki/gui/app/glue.py79
-rw-r--r--rpki/gui/app/migrations/0001_initial.py439
-rw-r--r--rpki/gui/app/models.py9
-rwxr-xr-xrpki/gui/app/range_list.py2
-rw-r--r--rpki/gui/app/south_migrations/0001_initial.py192
-rw-r--r--rpki/gui/app/south_migrations/0002_auto__add_field_resourcecert_conf.py (renamed from rpki/gui/app/migrations/0002_auto__add_field_resourcecert_conf.py)0
-rw-r--r--rpki/gui/app/south_migrations/0003_set_conf_from_parent.py (renamed from rpki/gui/app/migrations/0003_set_conf_from_parent.py)0
-rw-r--r--rpki/gui/app/south_migrations/0004_auto__chg_field_resourcecert_conf.py (renamed from rpki/gui/app/migrations/0004_auto__chg_field_resourcecert_conf.py)0
-rw-r--r--rpki/gui/app/south_migrations/0005_auto__chg_field_resourcecert_parent.py (renamed from rpki/gui/app/migrations/0005_auto__chg_field_resourcecert_parent.py)0
-rw-r--r--rpki/gui/app/south_migrations/0006_add_conf_acl.py (renamed from rpki/gui/app/migrations/0006_add_conf_acl.py)0
-rw-r--r--rpki/gui/app/south_migrations/0007_default_acls.py (renamed from rpki/gui/app/migrations/0007_default_acls.py)0
-rw-r--r--rpki/gui/app/south_migrations/0008_add_alerts.py (renamed from rpki/gui/app/migrations/0008_add_alerts.py)0
-rw-r--r--rpki/gui/app/south_migrations/__init__.py (renamed from rpki/gui/cacheview/__init__.py)0
-rw-r--r--rpki/gui/app/views.py135
-rw-r--r--rpki/gui/cacheview/forms.py51
-rw-r--r--rpki/gui/cacheview/misc.py31
-rw-r--r--rpki/gui/cacheview/templates/cacheview/addressrange_detail.html18
-rw-r--r--rpki/gui/cacheview/templates/cacheview/cacheview_base.html10
-rw-r--r--rpki/gui/cacheview/templates/cacheview/cert_detail.html105
-rw-r--r--rpki/gui/cacheview/templates/cacheview/ghostbuster_detail.html13
-rw-r--r--rpki/gui/cacheview/templates/cacheview/global_summary.html26
-rw-r--r--rpki/gui/cacheview/templates/cacheview/query_result.html21
-rw-r--r--rpki/gui/cacheview/templates/cacheview/roa_detail.html18
-rw-r--r--rpki/gui/cacheview/templates/cacheview/search_form.html17
-rw-r--r--rpki/gui/cacheview/templates/cacheview/search_result.html42
-rw-r--r--rpki/gui/cacheview/templates/cacheview/signedobject_detail.html58
-rw-r--r--rpki/gui/cacheview/tests.py23
-rw-r--r--rpki/gui/cacheview/urls.py32
-rw-r--r--rpki/gui/cacheview/util.py441
-rw-r--r--rpki/gui/cacheview/views.py172
-rw-r--r--rpki/gui/default_settings.py188
-rw-r--r--rpki/gui/gui_rpki_cache/__init__.py0
-rw-r--r--rpki/gui/gui_rpki_cache/migrations/0001_initial.py136
-rw-r--r--rpki/gui/gui_rpki_cache/migrations/0002_auto_20160411_2311.py41
-rw-r--r--rpki/gui/gui_rpki_cache/migrations/0003_auto_20160420_2146.py24
-rw-r--r--rpki/gui/gui_rpki_cache/migrations/__init__.py0
-rw-r--r--rpki/gui/gui_rpki_cache/models.py (renamed from rpki/gui/cacheview/models.py)121
-rw-r--r--rpki/gui/gui_rpki_cache/util.py308
-rw-r--r--rpki/gui/models.py110
-rw-r--r--rpki/gui/routeview/api.py2
-rw-r--r--rpki/gui/routeview/models.py8
-rw-r--r--rpki/gui/routeview/util.py56
-rw-r--r--rpki/gui/script_util.py43
-rw-r--r--rpki/gui/urls.py3
46 files changed, 1283 insertions, 1938 deletions
diff --git a/rpki/gui/app/check_expired.py b/rpki/gui/app/check_expired.py
index a084af79..65f4315f 100644
--- a/rpki/gui/app/check_expired.py
+++ b/rpki/gui/app/check_expired.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012, 2013, 2014 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2013, 2014, 2016 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
@@ -21,13 +21,14 @@ from cStringIO import StringIO
import logging
import datetime
-from rpki.gui.cacheview.models import Cert
+from rpki.gui.gui_rpki_cache.models import Cert
from rpki.gui.app.models import Conf, ResourceCert, Timestamp, Alert
from rpki.gui.app.glue import list_received_resources
from rpki.irdb import Zookeeper
-from rpki.left_right import report_error_elt, list_published_objects_elt
from rpki.x509 import X509
+from rpki.left_right import version, nsmap, tag_msg, tag_list_published_objects
+from lxml.etree import Element, SubElement
from django.core.mail import send_mail
logger = logging.getLogger(__name__)
@@ -41,8 +42,8 @@ def check_cert(handle, p, errs):
The displayed object name defaults to the class name, but can be overridden
using the `object_name` argument.
-
"""
+
t = p.certificate.getNotAfter()
if t <= expire_time:
e = 'expired' if t <= now else 'will expire'
@@ -60,8 +61,8 @@ def check_expire(conf, errs):
# get certs for `handle'
cert_set = ResourceCert.objects.filter(conf=conf)
for cert in cert_set:
- # look up cert in cacheview db
- obj_set = Cert.objects.filter(repo__uri=cert.uri)
+ # look up cert in gui_rpki_cache db
+ obj_set = Cert.objects.filter(uri=cert.uri)
if not obj_set:
# since the <list_received_resources/> output is cached, this can
# occur if the cache is out of date as well..
@@ -76,7 +77,7 @@ def check_expire(conf, errs):
f = '*'
else:
f = ' '
- msg.append("%s [%d] uri=%s ski=%s name=%s expires=%s" % (f, n, c.repo.uri, c.keyid, c.name, c.not_after))
+ msg.append("%s [%d] uri=%s expires=%s" % (f, n, c.uri, c.not_after))
# find ghostbuster records attached to this cert
for gbr in c.ghostbusters.all():
@@ -102,30 +103,26 @@ def check_expire(conf, errs):
def check_child_certs(conf, errs):
"""Fetch the list of published objects from rpkid, and inspect the issued
resource certs (uri ending in .cer).
-
"""
+
z = Zookeeper(handle=conf.handle)
- req = list_published_objects_elt.make_pdu(action="list",
- tag="list_published_objects",
- self_handle=conf.handle)
+ req = Element(tag_msg, nsmap=nsmap, type="query", version=version)
+ SubElement(req, tag_list_published_objects,
+ tag="list_published_objects", tenant_handle=conf.handle)
pdus = z.call_rpkid(req)
for pdu in pdus:
- if isinstance(pdu, report_error_elt):
- logger.error("rpkid reported an error: %s", pdu.error_code)
- elif isinstance(pdu, list_published_objects_elt):
- if pdu.uri.endswith('.cer'):
- cert = X509()
- cert.set(Base64=pdu.obj)
- t = cert.getNotAfter()
- if t <= expire_time:
- e = 'expired' if t <= now else 'will expire'
- errs.write("%(handle)s's rescert for Child %(child)s %(expire)s on %(date)s uri=%(uri)s subject=%(subject)s\n" % {
- 'handle': conf.handle,
- 'child': pdu.child_handle,
- 'uri': pdu.uri,
- 'subject': cert.getSubject(),
- 'expire': e,
- 'date': t})
+ if pdu.get("uri").endswith('.cer'):
+ cert = X509(Base64=pdu.text)
+ t = cert.getNotAfter()
+ if t <= expire_time:
+ e = 'expired' if t <= now else 'will expire'
+ errs.write("%(handle)s's rescert for Child %(child)s %(expire)s on %(date)s uri=%(uri)s subject=%(subject)s\n" % {
+ 'handle': conf.handle,
+ 'child': pdu.get("child_handle"),
+ 'uri': pdu.get("uri"),
+ 'subject': cert.getSubject(),
+ 'expire': e,
+ 'date': t})
class NetworkError(Exception):
@@ -139,8 +136,8 @@ def notify_expired(expire_days=14, from_email=None):
expire_days: the number of days ahead of today to warn
from_email: set the From: address for the email
-
"""
+
global expire_time # so i don't have to pass it around
global now
diff --git a/rpki/gui/app/forms.py b/rpki/gui/app/forms.py
index a1214297..4a95c8da 100644
--- a/rpki/gui/app/forms.py
+++ b/rpki/gui/app/forms.py
@@ -170,105 +170,105 @@ def ROARequestFormFactory(conf):
"""
class Cls(forms.Form):
- """Form for entering a ROA request.
-
- Handles both IPv4 and IPv6."""
-
- prefix = forms.CharField(
- widget=forms.TextInput(attrs={
- 'autofocus': 'true', 'placeholder': 'Prefix',
- 'class': 'span4'
- })
- )
- max_prefixlen = forms.CharField(
- required=False,
- widget=forms.TextInput(attrs={
- 'placeholder': 'Max len',
- 'class': 'span1'
- })
- )
- asn = forms.IntegerField(
- widget=forms.TextInput(attrs={
- 'placeholder': 'ASN',
- 'class': 'span1'
- })
- )
+ """Form for entering a ROA request.
+
+ Handles both IPv4 and IPv6."""
+
+ prefix = forms.CharField(
+ widget=forms.TextInput(attrs={
+ 'autofocus': 'true', 'placeholder': 'Prefix',
+ 'class': 'span4'
+ })
+ )
+ max_prefixlen = forms.CharField(
+ required=False,
+ widget=forms.TextInput(attrs={
+ 'placeholder': 'Max len',
+ 'class': 'span1'
+ })
+ )
+ asn = forms.IntegerField(
+ widget=forms.TextInput(attrs={
+ 'placeholder': 'ASN',
+ 'class': 'span1'
+ })
+ )
protect_children = forms.BooleanField(required=False)
- def __init__(self, *args, **kwargs):
- kwargs['auto_id'] = False
- super(Cls, self).__init__(*args, **kwargs)
- self.conf = conf # conf is the arg to ROARequestFormFactory
- self.inline = True
- self.use_table = False
-
- def _as_resource_range(self):
- """Convert the prefix in the form to a
- rpki.resource_set.resource_range_ip object.
-
- If there is no mask provided, assume the closest classful mask.
-
- """
- prefix = self.cleaned_data.get('prefix')
- if '/' not in prefix:
- p = IPAddress(prefix)
-
- # determine the first nonzero bit starting from the lsb and
- # subtract from the address size to find the closest classful
- # mask that contains this single address
- prefixlen = 0
- while (p != 0) and (p & 1) == 0:
- prefixlen = prefixlen + 1
- p = p >> 1
- mask = p.bits - (8 * (prefixlen / 8))
- prefix = prefix + '/' + str(mask)
-
- return resource_range_ip.parse_str(prefix)
-
- def clean_asn(self):
- value = self.cleaned_data.get('asn')
- if value < 0:
- raise forms.ValidationError('AS must be a positive value or 0')
- return value
-
- def clean_prefix(self):
- try:
- r = self._as_resource_range()
- except:
- raise forms.ValidationError('invalid prefix')
-
- manager = models.ResourceRangeAddressV4 if r.version == 4 else models.ResourceRangeAddressV6
- if not manager.objects.filter(cert__conf=self.conf,
- prefix_min__lte=r.min,
- prefix_max__gte=r.max).exists():
- raise forms.ValidationError('prefix is not allocated to you')
- return str(r)
-
- def clean_max_prefixlen(self):
- v = self.cleaned_data.get('max_prefixlen')
- if v:
- if v[0] == '/':
- v = v[1:] # allow user to specify /24
- try:
- if int(v) < 0:
- raise forms.ValidationError('max prefix length must be positive or 0')
- except ValueError:
- raise forms.ValidationError('invalid integer value')
- return v
-
- def clean(self):
- if 'prefix' in self.cleaned_data:
- r = self._as_resource_range()
- max_prefixlen = self.cleaned_data.get('max_prefixlen')
- max_prefixlen = int(max_prefixlen) if max_prefixlen else r.prefixlen()
- if max_prefixlen < r.prefixlen():
- raise forms.ValidationError(
- 'max prefix length must be greater than or equal to the prefix length')
- if max_prefixlen > r.min.bits:
- raise forms.ValidationError(
- 'max prefix length (%d) is out of range for IP version (%d)' % (max_prefixlen, r.min.bits))
- self.cleaned_data['max_prefixlen'] = str(max_prefixlen)
- return self.cleaned_data
+ def __init__(self, *args, **kwargs):
+ kwargs['auto_id'] = False
+ super(Cls, self).__init__(*args, **kwargs)
+ self.conf = conf # conf is the arg to ROARequestFormFactory
+ self.inline = True
+ self.use_table = False
+
+ def _as_resource_range(self):
+ """Convert the prefix in the form to a
+ rpki.resource_set.resource_range_ip object.
+
+ If there is no mask provided, assume the closest classful mask.
+
+ """
+ prefix = self.cleaned_data.get('prefix')
+ if '/' not in prefix:
+ p = IPAddress(prefix)
+
+ # determine the first nonzero bit starting from the lsb and
+ # subtract from the address size to find the closest classful
+ # mask that contains this single address
+ prefixlen = 0
+ while (p != 0) and (p & 1) == 0:
+ prefixlen = prefixlen + 1
+ p = p >> 1
+ mask = p.bits - (8 * (prefixlen / 8))
+ prefix = prefix + '/' + str(mask)
+
+ return resource_range_ip.parse_str(prefix)
+
+ def clean_asn(self):
+ value = self.cleaned_data.get('asn')
+ if value < 0:
+ raise forms.ValidationError('AS must be a positive value or 0')
+ return value
+
+ def clean_prefix(self):
+ try:
+ r = self._as_resource_range()
+ except:
+ raise forms.ValidationError('invalid prefix')
+
+ manager = models.ResourceRangeAddressV4 if r.version == 4 else models.ResourceRangeAddressV6
+ if not manager.objects.filter(cert__conf=self.conf,
+ prefix_min__lte=r.min,
+ prefix_max__gte=r.max).exists():
+ raise forms.ValidationError('prefix is not allocated to you')
+ return str(r)
+
+ def clean_max_prefixlen(self):
+ v = self.cleaned_data.get('max_prefixlen')
+ if v:
+ if v[0] == '/':
+ v = v[1:] # allow user to specify /24
+ try:
+ if int(v) < 0:
+ raise forms.ValidationError('max prefix length must be positive or 0')
+ except ValueError:
+ raise forms.ValidationError('invalid integer value')
+ return v
+
+ def clean(self):
+ if 'prefix' in self.cleaned_data:
+ r = self._as_resource_range()
+ max_prefixlen = self.cleaned_data.get('max_prefixlen')
+ max_prefixlen = int(max_prefixlen) if max_prefixlen else r.prefixlen()
+ if max_prefixlen < r.prefixlen():
+ raise forms.ValidationError(
+ 'max prefix length must be greater than or equal to the prefix length')
+ if max_prefixlen > r.min.bits:
+ raise forms.ValidationError(
+ 'max prefix length (%d) is out of range for IP version (%d)' % (max_prefixlen, r.min.bits))
+ self.cleaned_data['max_prefixlen'] = str(max_prefixlen)
+ return self.cleaned_data
return Cls
diff --git a/rpki/gui/app/glue.py b/rpki/gui/app/glue.py
index a2dddb51..c312618f 100644
--- a/rpki/gui/app/glue.py
+++ b/rpki/gui/app/glue.py
@@ -16,7 +16,6 @@
"""
This file contains code that interfaces between the django views implementing
the portal gui and the rpki.* modules.
-
"""
from __future__ import with_statement
@@ -28,17 +27,19 @@ from datetime import datetime
from rpki.resource_set import (resource_set_as, resource_set_ipv4,
resource_set_ipv6, resource_range_ipv4,
resource_range_ipv6)
-from rpki.left_right import list_received_resources_elt, report_error_elt
from rpki.irdb.zookeeper import Zookeeper
from rpki.gui.app import models
from rpki.exceptions import BadIPResource
+from rpki.left_right import nsmap, version, tag_msg, tag_list_received_resources
+from lxml.etree import Element, SubElement
from django.contrib.auth.models import User
-from django.db.transaction import commit_on_success
+from django.db.transaction import atomic
def ghostbuster_to_vcard(gbr):
"""Convert a GhostbusterRequest object into a vCard object."""
+
import vobject
vcard = vobject.vCard()
@@ -66,19 +67,7 @@ def ghostbuster_to_vcard(gbr):
return vcard.serialize()
-class LeftRightError(Exception):
- """Class for wrapping report_error_elt errors from Zookeeper.call_rpkid().
-
- It expects a single argument, which is the associated report_error_elt instance."""
-
- def __str__(self):
- return 'Error occurred while communicating with rpkid: handle=%s code=%s text=%s' % (
- self.args[0].self_handle,
- self.args[0].error_code,
- self.args[0].error_text)
-
-
-@commit_on_success
+@atomic
def list_received_resources(log, conf):
"""
Query rpkid for this resource handle's received resources.
@@ -86,11 +75,12 @@ def list_received_resources(log, conf):
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, disable_signal_handlers=True)
- pdus = z.call_rpkid(list_received_resources_elt.make_pdu(self_handle=conf.handle))
+ req = Element(tag_msg, nsmap=nsmap, type="query", version=version)
+ SubElement(req, tag_list_received_resources, tenant_handle=conf.handle)
+ pdus = z.call_rpkid(req)
# pdus is sometimes None (see https://trac.rpki.net/ticket/681)
if pdus is None:
print >>log, 'error: call_rpkid() returned None for handle %s when fetching received resources' % conf.handle
@@ -99,34 +89,27 @@ def list_received_resources(log, conf):
models.ResourceCert.objects.filter(conf=conf).delete()
for pdu in pdus:
- if isinstance(pdu, report_error_elt):
- # this will cause the db to be rolled back so the above delete()
- # won't clobber existing resources
- raise LeftRightError(pdu)
- elif isinstance(pdu, list_received_resources_elt):
- if pdu.parent_handle != conf.handle:
- parent = models.Parent.objects.get(issuer=conf,
- handle=pdu.parent_handle)
- else:
- # root cert, self-signed
- parent = None
-
- not_before = datetime.strptime(pdu.notBefore, "%Y-%m-%dT%H:%M:%SZ")
- not_after = datetime.strptime(pdu.notAfter, "%Y-%m-%dT%H:%M:%SZ")
-
- cert = models.ResourceCert.objects.create(
- conf=conf, parent=parent, not_before=not_before,
- not_after=not_after, uri=pdu.uri)
-
- for asn in resource_set_as(pdu.asn):
- cert.asn_ranges.create(min=asn.min, max=asn.max)
-
- for rng in resource_set_ipv4(pdu.ipv4):
- cert.address_ranges.create(prefix_min=rng.min,
- prefix_max=rng.max)
-
- for rng in resource_set_ipv6(pdu.ipv6):
- cert.address_ranges_v6.create(prefix_min=rng.min,
- prefix_max=rng.max)
+ if pdu.get("parent_handle") != conf.handle:
+ parent = models.Parent.objects.get(issuer=conf,
+ handle=pdu.get("parent_handle"))
else:
- print >>log, "error: unexpected pdu from rpkid type=%s" % type(pdu)
+ # root cert, self-signed
+ parent = None
+
+ not_before = datetime.strptime(pdu.get("notBefore"), "%Y-%m-%dT%H:%M:%SZ")
+ not_after = datetime.strptime(pdu.get("notAfter"), "%Y-%m-%dT%H:%M:%SZ")
+
+ cert = models.ResourceCert.objects.create(
+ conf=conf, parent=parent, not_before=not_before,
+ not_after=not_after, uri=pdu.get("uri"))
+
+ for asn in resource_set_as(pdu.get("asn")):
+ cert.asn_ranges.create(min=asn.min, max=asn.max)
+
+ for rng in resource_set_ipv4(pdu.get("ipv4")):
+ cert.address_ranges.create(prefix_min=rng.min,
+ prefix_max=rng.max)
+
+ for rng in resource_set_ipv6(pdu.get("ipv6")):
+ cert.address_ranges_v6.create(prefix_min=rng.min,
+ prefix_max=rng.max)
diff --git a/rpki/gui/app/migrations/0001_initial.py b/rpki/gui/app/migrations/0001_initial.py
index 80877901..79d21324 100644
--- a/rpki/gui/app/migrations/0001_initial.py
+++ b/rpki/gui/app/migrations/0001_initial.py
@@ -1,192 +1,249 @@
# -*- coding: utf-8 -*-
-import datetime
-from south.db import db
-from south.v2 import SchemaMigration
-from django.db import models
-
-
-class Migration(SchemaMigration):
-
- def forwards(self, orm):
- # Adding model 'ResourceCert'
- db.create_table('app_resourcecert', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name='certs', to=orm['irdb.Parent'])),
- ('not_before', self.gf('django.db.models.fields.DateTimeField')()),
- ('not_after', self.gf('django.db.models.fields.DateTimeField')()),
- ('uri', self.gf('django.db.models.fields.CharField')(max_length=255)),
- ))
- db.send_create_signal('app', ['ResourceCert'])
-
- # Adding model 'ResourceRangeAddressV4'
- db.create_table('app_resourcerangeaddressv4', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('prefix_min', self.gf('rpki.gui.models.IPv4AddressField')(db_index=True)),
- ('prefix_max', self.gf('rpki.gui.models.IPv4AddressField')(db_index=True)),
- ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='address_ranges', to=orm['app.ResourceCert'])),
- ))
- db.send_create_signal('app', ['ResourceRangeAddressV4'])
-
- # Adding model 'ResourceRangeAddressV6'
- db.create_table('app_resourcerangeaddressv6', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('prefix_min', self.gf('rpki.gui.models.IPv6AddressField')(db_index=True)),
- ('prefix_max', self.gf('rpki.gui.models.IPv6AddressField')(db_index=True)),
- ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='address_ranges_v6', to=orm['app.ResourceCert'])),
- ))
- db.send_create_signal('app', ['ResourceRangeAddressV6'])
-
- # Adding model 'ResourceRangeAS'
- db.create_table('app_resourcerangeas', (
- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
- ('min', self.gf('django.db.models.fields.PositiveIntegerField')()),
- ('max', self.gf('django.db.models.fields.PositiveIntegerField')()),
- ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='asn_ranges', to=orm['app.ResourceCert'])),
- ))
- db.send_create_signal('app', ['ResourceRangeAS'])
-
- # Adding model 'GhostbusterRequest'
- db.create_table('app_ghostbusterrequest', (
- ('ghostbusterrequest_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['irdb.GhostbusterRequest'], unique=True, primary_key=True)),
- ('full_name', self.gf('django.db.models.fields.CharField')(max_length=40)),
- ('family_name', self.gf('django.db.models.fields.CharField')(max_length=20)),
- ('given_name', self.gf('django.db.models.fields.CharField')(max_length=20)),
- ('additional_name', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True)),
- ('honorific_prefix', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
- ('honorific_suffix', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
- ('email_address', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True, blank=True)),
- ('organization', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
- ('telephone', self.gf('rpki.gui.app.models.TelephoneField')(max_length=40, null=True, blank=True)),
- ('box', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
- ('extended', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
- ('street', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
- ('city', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
- ('region', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
- ('code', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
- ('country', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
- ))
- db.send_create_signal('app', ['GhostbusterRequest'])
-
- # Adding model 'Timestamp'
- db.create_table('app_timestamp', (
- ('name', self.gf('django.db.models.fields.CharField')(max_length=30, primary_key=True)),
- ('ts', self.gf('django.db.models.fields.DateTimeField')()),
- ))
- db.send_create_signal('app', ['Timestamp'])
-
-
- def backwards(self, orm):
- # Deleting model 'ResourceCert'
- db.delete_table('app_resourcecert')
-
- # Deleting model 'ResourceRangeAddressV4'
- db.delete_table('app_resourcerangeaddressv4')
-
- # Deleting model 'ResourceRangeAddressV6'
- db.delete_table('app_resourcerangeaddressv6')
-
- # Deleting model 'ResourceRangeAS'
- db.delete_table('app_resourcerangeas')
-
- # Deleting model 'GhostbusterRequest'
- db.delete_table('app_ghostbusterrequest')
-
- # Deleting model 'Timestamp'
- db.delete_table('app_timestamp')
-
-
- models = {
- 'app.ghostbusterrequest': {
- 'Meta': {'ordering': "('family_name', 'given_name')", 'object_name': 'GhostbusterRequest', '_ormbases': ['irdb.GhostbusterRequest']},
- 'additional_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
- 'box': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
- 'city': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
- 'code': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
- 'country': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
- 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
- 'extended': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'family_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
- 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
- 'ghostbusterrequest_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['irdb.GhostbusterRequest']", 'unique': 'True', 'primary_key': 'True'}),
- 'given_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
- 'honorific_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'honorific_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
- 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'region': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
- 'street': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
- 'telephone': ('rpki.gui.app.models.TelephoneField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'})
- },
- 'app.resourcecert': {
- 'Meta': {'object_name': 'ResourceCert'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'not_after': ('django.db.models.fields.DateTimeField', [], {}),
- 'not_before': ('django.db.models.fields.DateTimeField', [], {}),
- 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'certs'", 'to': "orm['irdb.Parent']"}),
- 'uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- },
- 'app.resourcerangeaddressv4': {
- 'Meta': {'ordering': "('prefix_min',)", 'object_name': 'ResourceRangeAddressV4'},
- 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'address_ranges'", 'to': "orm['app.ResourceCert']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'prefix_max': ('rpki.gui.models.IPv4AddressField', [], {'db_index': 'True'}),
- 'prefix_min': ('rpki.gui.models.IPv4AddressField', [], {'db_index': 'True'})
- },
- 'app.resourcerangeaddressv6': {
- 'Meta': {'ordering': "('prefix_min',)", 'object_name': 'ResourceRangeAddressV6'},
- 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'address_ranges_v6'", 'to': "orm['app.ResourceCert']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'prefix_max': ('rpki.gui.models.IPv6AddressField', [], {'db_index': 'True'}),
- 'prefix_min': ('rpki.gui.models.IPv6AddressField', [], {'db_index': 'True'})
- },
- 'app.resourcerangeas': {
- 'Meta': {'ordering': "('min', 'max')", 'object_name': 'ResourceRangeAS'},
- 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'asn_ranges'", 'to': "orm['app.ResourceCert']"}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'max': ('django.db.models.fields.PositiveIntegerField', [], {}),
- 'min': ('django.db.models.fields.PositiveIntegerField', [], {})
- },
- 'app.timestamp': {
- 'Meta': {'object_name': 'Timestamp'},
- 'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}),
- 'ts': ('django.db.models.fields.DateTimeField', [], {})
- },
- 'irdb.ghostbusterrequest': {
- 'Meta': {'object_name': 'GhostbusterRequest'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ghostbuster_requests'", 'to': "orm['irdb.ResourceHolderCA']"}),
- 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ghostbuster_requests'", 'null': 'True', 'to': "orm['irdb.Parent']"}),
- 'vcard': ('django.db.models.fields.TextField', [], {})
- },
- 'irdb.parent': {
- 'Meta': {'unique_together': "(('issuer', 'handle'),)", 'object_name': 'Parent', '_ormbases': ['irdb.Turtle']},
- 'certificate': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
- 'child_handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
- 'handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
- 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parents'", 'to': "orm['irdb.ResourceHolderCA']"}),
- 'parent_handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
- 'referral_authorization': ('rpki.irdb.models.SignedReferralField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
- 'referrer': ('rpki.irdb.models.HandleField', [], {'max_length': '120', 'null': 'True', 'blank': 'True'}),
- 'repository_type': ('rpki.irdb.models.EnumField', [], {}),
- 'ta': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
- 'turtle_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['irdb.Turtle']", 'unique': 'True', 'primary_key': 'True'})
- },
- 'irdb.resourceholderca': {
- 'Meta': {'object_name': 'ResourceHolderCA'},
- 'certificate': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
- 'handle': ('rpki.irdb.models.HandleField', [], {'unique': 'True', 'max_length': '120'}),
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'last_crl_update': ('rpki.irdb.models.SundialField', [], {}),
- 'latest_crl': ('rpki.irdb.models.CRLField', [], {'default': 'None', 'blank': 'True'}),
- 'next_crl_number': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}),
- 'next_crl_update': ('rpki.irdb.models.SundialField', [], {}),
- 'next_serial': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}),
- 'private_key': ('rpki.irdb.models.RSAKeyField', [], {'default': 'None', 'blank': 'True'})
- },
- 'irdb.turtle': {
- 'Meta': {'object_name': 'Turtle'},
- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
- 'service_uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
- }
- }
-
- complete_apps = ['app'] \ No newline at end of file
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+from django.conf import settings
+import rpki.gui.models
+import rpki.gui.app.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('irdb', '0001_initial'),
+ ('routeview', '__first__'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Alert',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('severity', models.SmallIntegerField(default=0, choices=[(0, b'info'), (1, b'warning'), (2, b'error')])),
+ ('when', models.DateTimeField(auto_now_add=True)),
+ ('seen', models.BooleanField(default=False)),
+ ('subject', models.CharField(max_length=66)),
+ ('text', models.TextField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ConfACL',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='GhostbusterRequest',
+ fields=[
+ ('ghostbusterrequest_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='irdb.GhostbusterRequest')),
+ ('full_name', models.CharField(max_length=40)),
+ ('family_name', models.CharField(max_length=20)),
+ ('given_name', models.CharField(max_length=20)),
+ ('additional_name', models.CharField(max_length=20, null=True, blank=True)),
+ ('honorific_prefix', models.CharField(max_length=10, null=True, blank=True)),
+ ('honorific_suffix', models.CharField(max_length=10, null=True, blank=True)),
+ ('email_address', models.EmailField(max_length=254, null=True, blank=True)),
+ ('organization', models.CharField(max_length=255, null=True, blank=True)),
+ ('telephone', rpki.gui.app.models.TelephoneField(max_length=40, null=True, blank=True)),
+ ('box', models.CharField(max_length=40, null=True, verbose_name=b'P.O. Box', blank=True)),
+ ('extended', models.CharField(max_length=255, null=True, blank=True)),
+ ('street', models.CharField(max_length=255, null=True, blank=True)),
+ ('city', models.CharField(max_length=40, null=True, blank=True)),
+ ('region', models.CharField(help_text=b'state or province', max_length=40, null=True, blank=True)),
+ ('code', models.CharField(max_length=40, null=True, verbose_name=b'Postal Code', blank=True)),
+ ('country', models.CharField(max_length=40, null=True, blank=True)),
+ ],
+ options={
+ 'ordering': ('family_name', 'given_name'),
+ },
+ bases=('irdb.ghostbusterrequest',),
+ ),
+ migrations.CreateModel(
+ name='ResourceCert',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('not_before', models.DateTimeField()),
+ ('not_after', models.DateTimeField()),
+ ('uri', models.CharField(max_length=255)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='ResourceRangeAddressV4',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ('cert', models.ForeignKey(related_name='address_ranges', to='app.ResourceCert')),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='ResourceRangeAddressV6',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ('cert', models.ForeignKey(related_name='address_ranges_v6', to='app.ResourceCert')),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='ResourceRangeAS',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('min', models.BigIntegerField(validators=[rpki.gui.models.validate_asn])),
+ ('max', models.BigIntegerField(validators=[rpki.gui.models.validate_asn])),
+ ('cert', models.ForeignKey(related_name='asn_ranges', to='app.ResourceCert')),
+ ],
+ options={
+ 'ordering': ('min', 'max'),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Timestamp',
+ fields=[
+ ('name', models.CharField(max_length=30, serialize=False, primary_key=True)),
+ ('ts', models.DateTimeField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Child',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ 'verbose_name_plural': 'children',
+ },
+ bases=('irdb.child',),
+ ),
+ migrations.CreateModel(
+ name='ChildASN',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.childasn',),
+ ),
+ migrations.CreateModel(
+ name='ChildNet',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.childnet',),
+ ),
+ migrations.CreateModel(
+ name='Client',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'Client',
+ 'proxy': True,
+ },
+ bases=('irdb.client',),
+ ),
+ migrations.CreateModel(
+ name='Conf',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.resourceholderca',),
+ ),
+ migrations.CreateModel(
+ name='Parent',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.parent',),
+ ),
+ migrations.CreateModel(
+ name='Repository',
+ fields=[
+ ],
+ options={
+ 'verbose_name': 'Repository',
+ 'proxy': True,
+ 'verbose_name_plural': 'Repositories',
+ },
+ bases=('irdb.repository',),
+ ),
+ migrations.CreateModel(
+ name='ROARequest',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.roarequest',),
+ ),
+ migrations.CreateModel(
+ name='ROARequestPrefix',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('irdb.roarequestprefix',),
+ ),
+ migrations.CreateModel(
+ name='RouteOrigin',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('routeview.routeorigin',),
+ ),
+ migrations.CreateModel(
+ name='RouteOriginV6',
+ fields=[
+ ],
+ options={
+ 'proxy': True,
+ },
+ bases=('routeview.routeoriginv6',),
+ ),
+ migrations.AddField(
+ model_name='resourcecert',
+ name='conf',
+ field=models.ForeignKey(related_name='certs', to='app.Conf'),
+ ),
+ migrations.AddField(
+ model_name='resourcecert',
+ name='parent',
+ field=models.ForeignKey(related_name='certs', to='app.Parent', null=True),
+ ),
+ migrations.AddField(
+ model_name='confacl',
+ name='conf',
+ field=models.ForeignKey(to='app.Conf'),
+ ),
+ migrations.AddField(
+ model_name='confacl',
+ name='user',
+ field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='alert',
+ name='conf',
+ field=models.ForeignKey(related_name='alerts', to='app.Conf'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='confacl',
+ unique_together=set([('user', 'conf')]),
+ ),
+ ]
diff --git a/rpki/gui/app/models.py b/rpki/gui/app/models.py
index 40bdbe2c..fb1cafff 100644
--- a/rpki/gui/app/models.py
+++ b/rpki/gui/app/models.py
@@ -1,5 +1,5 @@
# Copyright (C) 2010 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2016 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
@@ -26,7 +26,6 @@ import rpki.irdb.models
import rpki.gui.models
import rpki.gui.routeview.models
import rpki.oids
-from south.modelsinspector import add_introspection_rules
class TelephoneField(models.CharField):
@@ -35,8 +34,6 @@ class TelephoneField(models.CharField):
kwargs['max_length'] = 40
models.CharField.__init__(self, **kwargs)
-add_introspection_rules([], [r'^rpki\.gui\.app\.models\.TelephoneField'])
-
class Parent(rpki.irdb.models.Parent):
"""proxy model for irdb Parent"""
@@ -123,7 +120,7 @@ class Alert(models.Model):
class Conf(rpki.irdb.models.ResourceHolderCA):
"""This is the center of the universe, also known as a place to
- have a handle on a resource-holding entity. It's the <self>
+ have a handle on a resource-holding entity. It's the <tenant/>
in the rpkid schema.
"""
@@ -262,7 +259,7 @@ class ResourceCert(models.Model):
not_after = models.DateTimeField()
# Locator for this object. Used to look up the validation status, expiry
- # of ancestor certs in cacheview
+ # of ancestor certs in gui_rpki_cache
uri = models.CharField(max_length=255)
def __unicode__(self):
diff --git a/rpki/gui/app/range_list.py b/rpki/gui/app/range_list.py
index 21fd1f29..5cb4f5e4 100755
--- a/rpki/gui/app/range_list.py
+++ b/rpki/gui/app/range_list.py
@@ -70,6 +70,7 @@ class RangeList(list):
def difference(self, other):
"""Return a RangeList object which contains ranges in this object which
are not in "other"."""
+
it = iter(other)
try:
@@ -85,6 +86,7 @@ class RangeList(list):
def V(v):
"""convert the integer value to the appropriate type for this
range"""
+
return x.__class__.datum_type(v)
try:
diff --git a/rpki/gui/app/south_migrations/0001_initial.py b/rpki/gui/app/south_migrations/0001_initial.py
new file mode 100644
index 00000000..80877901
--- /dev/null
+++ b/rpki/gui/app/south_migrations/0001_initial.py
@@ -0,0 +1,192 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'ResourceCert'
+ db.create_table('app_resourcecert', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(related_name='certs', to=orm['irdb.Parent'])),
+ ('not_before', self.gf('django.db.models.fields.DateTimeField')()),
+ ('not_after', self.gf('django.db.models.fields.DateTimeField')()),
+ ('uri', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('app', ['ResourceCert'])
+
+ # Adding model 'ResourceRangeAddressV4'
+ db.create_table('app_resourcerangeaddressv4', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('prefix_min', self.gf('rpki.gui.models.IPv4AddressField')(db_index=True)),
+ ('prefix_max', self.gf('rpki.gui.models.IPv4AddressField')(db_index=True)),
+ ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='address_ranges', to=orm['app.ResourceCert'])),
+ ))
+ db.send_create_signal('app', ['ResourceRangeAddressV4'])
+
+ # Adding model 'ResourceRangeAddressV6'
+ db.create_table('app_resourcerangeaddressv6', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('prefix_min', self.gf('rpki.gui.models.IPv6AddressField')(db_index=True)),
+ ('prefix_max', self.gf('rpki.gui.models.IPv6AddressField')(db_index=True)),
+ ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='address_ranges_v6', to=orm['app.ResourceCert'])),
+ ))
+ db.send_create_signal('app', ['ResourceRangeAddressV6'])
+
+ # Adding model 'ResourceRangeAS'
+ db.create_table('app_resourcerangeas', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('min', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('max', self.gf('django.db.models.fields.PositiveIntegerField')()),
+ ('cert', self.gf('django.db.models.fields.related.ForeignKey')(related_name='asn_ranges', to=orm['app.ResourceCert'])),
+ ))
+ db.send_create_signal('app', ['ResourceRangeAS'])
+
+ # Adding model 'GhostbusterRequest'
+ db.create_table('app_ghostbusterrequest', (
+ ('ghostbusterrequest_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['irdb.GhostbusterRequest'], unique=True, primary_key=True)),
+ ('full_name', self.gf('django.db.models.fields.CharField')(max_length=40)),
+ ('family_name', self.gf('django.db.models.fields.CharField')(max_length=20)),
+ ('given_name', self.gf('django.db.models.fields.CharField')(max_length=20)),
+ ('additional_name', self.gf('django.db.models.fields.CharField')(max_length=20, null=True, blank=True)),
+ ('honorific_prefix', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
+ ('honorific_suffix', self.gf('django.db.models.fields.CharField')(max_length=10, null=True, blank=True)),
+ ('email_address', self.gf('django.db.models.fields.EmailField')(max_length=75, null=True, blank=True)),
+ ('organization', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('telephone', self.gf('rpki.gui.app.models.TelephoneField')(max_length=40, null=True, blank=True)),
+ ('box', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
+ ('extended', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('street', self.gf('django.db.models.fields.CharField')(max_length=255, null=True, blank=True)),
+ ('city', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
+ ('region', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
+ ('code', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
+ ('country', self.gf('django.db.models.fields.CharField')(max_length=40, null=True, blank=True)),
+ ))
+ db.send_create_signal('app', ['GhostbusterRequest'])
+
+ # Adding model 'Timestamp'
+ db.create_table('app_timestamp', (
+ ('name', self.gf('django.db.models.fields.CharField')(max_length=30, primary_key=True)),
+ ('ts', self.gf('django.db.models.fields.DateTimeField')()),
+ ))
+ db.send_create_signal('app', ['Timestamp'])
+
+
+ def backwards(self, orm):
+ # Deleting model 'ResourceCert'
+ db.delete_table('app_resourcecert')
+
+ # Deleting model 'ResourceRangeAddressV4'
+ db.delete_table('app_resourcerangeaddressv4')
+
+ # Deleting model 'ResourceRangeAddressV6'
+ db.delete_table('app_resourcerangeaddressv6')
+
+ # Deleting model 'ResourceRangeAS'
+ db.delete_table('app_resourcerangeas')
+
+ # Deleting model 'GhostbusterRequest'
+ db.delete_table('app_ghostbusterrequest')
+
+ # Deleting model 'Timestamp'
+ db.delete_table('app_timestamp')
+
+
+ models = {
+ 'app.ghostbusterrequest': {
+ 'Meta': {'ordering': "('family_name', 'given_name')", 'object_name': 'GhostbusterRequest', '_ormbases': ['irdb.GhostbusterRequest']},
+ 'additional_name': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
+ 'box': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'city': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'code': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'country': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
+ 'extended': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'family_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+ 'full_name': ('django.db.models.fields.CharField', [], {'max_length': '40'}),
+ 'ghostbusterrequest_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['irdb.GhostbusterRequest']", 'unique': 'True', 'primary_key': 'True'}),
+ 'given_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}),
+ 'honorific_prefix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'honorific_suffix': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
+ 'organization': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'region': ('django.db.models.fields.CharField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}),
+ 'street': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ 'telephone': ('rpki.gui.app.models.TelephoneField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'})
+ },
+ 'app.resourcecert': {
+ 'Meta': {'object_name': 'ResourceCert'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'not_after': ('django.db.models.fields.DateTimeField', [], {}),
+ 'not_before': ('django.db.models.fields.DateTimeField', [], {}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'certs'", 'to': "orm['irdb.Parent']"}),
+ 'uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'app.resourcerangeaddressv4': {
+ 'Meta': {'ordering': "('prefix_min',)", 'object_name': 'ResourceRangeAddressV4'},
+ 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'address_ranges'", 'to': "orm['app.ResourceCert']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'prefix_max': ('rpki.gui.models.IPv4AddressField', [], {'db_index': 'True'}),
+ 'prefix_min': ('rpki.gui.models.IPv4AddressField', [], {'db_index': 'True'})
+ },
+ 'app.resourcerangeaddressv6': {
+ 'Meta': {'ordering': "('prefix_min',)", 'object_name': 'ResourceRangeAddressV6'},
+ 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'address_ranges_v6'", 'to': "orm['app.ResourceCert']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'prefix_max': ('rpki.gui.models.IPv6AddressField', [], {'db_index': 'True'}),
+ 'prefix_min': ('rpki.gui.models.IPv6AddressField', [], {'db_index': 'True'})
+ },
+ 'app.resourcerangeas': {
+ 'Meta': {'ordering': "('min', 'max')", 'object_name': 'ResourceRangeAS'},
+ 'cert': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'asn_ranges'", 'to': "orm['app.ResourceCert']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'max': ('django.db.models.fields.PositiveIntegerField', [], {}),
+ 'min': ('django.db.models.fields.PositiveIntegerField', [], {})
+ },
+ 'app.timestamp': {
+ 'Meta': {'object_name': 'Timestamp'},
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'primary_key': 'True'}),
+ 'ts': ('django.db.models.fields.DateTimeField', [], {})
+ },
+ 'irdb.ghostbusterrequest': {
+ 'Meta': {'object_name': 'GhostbusterRequest'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ghostbuster_requests'", 'to': "orm['irdb.ResourceHolderCA']"}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ghostbuster_requests'", 'null': 'True', 'to': "orm['irdb.Parent']"}),
+ 'vcard': ('django.db.models.fields.TextField', [], {})
+ },
+ 'irdb.parent': {
+ 'Meta': {'unique_together': "(('issuer', 'handle'),)", 'object_name': 'Parent', '_ormbases': ['irdb.Turtle']},
+ 'certificate': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
+ 'child_handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
+ 'handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
+ 'issuer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'parents'", 'to': "orm['irdb.ResourceHolderCA']"}),
+ 'parent_handle': ('rpki.irdb.models.HandleField', [], {'max_length': '120'}),
+ 'referral_authorization': ('rpki.irdb.models.SignedReferralField', [], {'default': 'None', 'null': 'True', 'blank': 'True'}),
+ 'referrer': ('rpki.irdb.models.HandleField', [], {'max_length': '120', 'null': 'True', 'blank': 'True'}),
+ 'repository_type': ('rpki.irdb.models.EnumField', [], {}),
+ 'ta': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
+ 'turtle_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['irdb.Turtle']", 'unique': 'True', 'primary_key': 'True'})
+ },
+ 'irdb.resourceholderca': {
+ 'Meta': {'object_name': 'ResourceHolderCA'},
+ 'certificate': ('rpki.irdb.models.CertificateField', [], {'default': 'None', 'blank': 'True'}),
+ 'handle': ('rpki.irdb.models.HandleField', [], {'unique': 'True', 'max_length': '120'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_crl_update': ('rpki.irdb.models.SundialField', [], {}),
+ 'latest_crl': ('rpki.irdb.models.CRLField', [], {'default': 'None', 'blank': 'True'}),
+ 'next_crl_number': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}),
+ 'next_crl_update': ('rpki.irdb.models.SundialField', [], {}),
+ 'next_serial': ('django.db.models.fields.BigIntegerField', [], {'default': '1'}),
+ 'private_key': ('rpki.irdb.models.RSAKeyField', [], {'default': 'None', 'blank': 'True'})
+ },
+ 'irdb.turtle': {
+ 'Meta': {'object_name': 'Turtle'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'service_uri': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ }
+ }
+
+ complete_apps = ['app'] \ No newline at end of file
diff --git a/rpki/gui/app/migrations/0002_auto__add_field_resourcecert_conf.py b/rpki/gui/app/south_migrations/0002_auto__add_field_resourcecert_conf.py
index d3326f90..d3326f90 100644
--- a/rpki/gui/app/migrations/0002_auto__add_field_resourcecert_conf.py
+++ b/rpki/gui/app/south_migrations/0002_auto__add_field_resourcecert_conf.py
diff --git a/rpki/gui/app/migrations/0003_set_conf_from_parent.py b/rpki/gui/app/south_migrations/0003_set_conf_from_parent.py
index a90a11cc..a90a11cc 100644
--- a/rpki/gui/app/migrations/0003_set_conf_from_parent.py
+++ b/rpki/gui/app/south_migrations/0003_set_conf_from_parent.py
diff --git a/rpki/gui/app/migrations/0004_auto__chg_field_resourcecert_conf.py b/rpki/gui/app/south_migrations/0004_auto__chg_field_resourcecert_conf.py
index a236ad4a..a236ad4a 100644
--- a/rpki/gui/app/migrations/0004_auto__chg_field_resourcecert_conf.py
+++ b/rpki/gui/app/south_migrations/0004_auto__chg_field_resourcecert_conf.py
diff --git a/rpki/gui/app/migrations/0005_auto__chg_field_resourcecert_parent.py b/rpki/gui/app/south_migrations/0005_auto__chg_field_resourcecert_parent.py
index 11e9c814..11e9c814 100644
--- a/rpki/gui/app/migrations/0005_auto__chg_field_resourcecert_parent.py
+++ b/rpki/gui/app/south_migrations/0005_auto__chg_field_resourcecert_parent.py
diff --git a/rpki/gui/app/migrations/0006_add_conf_acl.py b/rpki/gui/app/south_migrations/0006_add_conf_acl.py
index 88fe8171..88fe8171 100644
--- a/rpki/gui/app/migrations/0006_add_conf_acl.py
+++ b/rpki/gui/app/south_migrations/0006_add_conf_acl.py
diff --git a/rpki/gui/app/migrations/0007_default_acls.py b/rpki/gui/app/south_migrations/0007_default_acls.py
index 40656d0f..40656d0f 100644
--- a/rpki/gui/app/migrations/0007_default_acls.py
+++ b/rpki/gui/app/south_migrations/0007_default_acls.py
diff --git a/rpki/gui/app/migrations/0008_add_alerts.py b/rpki/gui/app/south_migrations/0008_add_alerts.py
index 77af68d2..77af68d2 100644
--- a/rpki/gui/app/migrations/0008_add_alerts.py
+++ b/rpki/gui/app/south_migrations/0008_add_alerts.py
diff --git a/rpki/gui/cacheview/__init__.py b/rpki/gui/app/south_migrations/__init__.py
index e69de29b..e69de29b 100644
--- a/rpki/gui/cacheview/__init__.py
+++ b/rpki/gui/app/south_migrations/__init__.py
diff --git a/rpki/gui/app/views.py b/rpki/gui/app/views.py
index bf152f8e..03c7c168 100644
--- a/rpki/gui/app/views.py
+++ b/rpki/gui/app/views.py
@@ -1,5 +1,5 @@
# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2012, 2014 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2014, 2016 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
@@ -23,7 +23,6 @@ __version__ = '$Id$'
import os
import os.path
-from tempfile import NamedTemporaryFile
import cStringIO
import csv
import logging
@@ -42,7 +41,8 @@ from django.forms.formsets import formset_factory, BaseFormSet
from django.contrib import messages
from django.db.models import Q
-from rpki.irdb import Zookeeper, ChildASN, ChildNet, ROARequestPrefix
+from rpki.irdb import Zookeeper
+from rpki.irdb.models import ChildASN, ChildNet, ROARequestPrefix
from rpki.gui.app import models, forms, glue, range_list
from rpki.resource_set import (resource_range_as, resource_range_ip,
roa_prefix_ipv4)
@@ -50,7 +50,6 @@ from rpki import sundial
import rpki.exceptions
import rpki.csv_utils
-from rpki.gui.cacheview.models import ROA
from rpki.gui.routeview.models import RouteOrigin
from rpki.gui.decorators import tls_required
@@ -136,10 +135,6 @@ def generic_import(request, queryset, configure, form_class=None,
if request.method == 'POST':
form = form_class(request.POST, request.FILES)
if form.is_valid():
- tmpf = NamedTemporaryFile(prefix='import', suffix='.xml',
- delete=False)
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
z = Zookeeper(handle=conf.handle)
handle = form.cleaned_data.get('handle')
# CharField uses an empty string for the empty value, rather than
@@ -148,27 +143,25 @@ def generic_import(request, queryset, configure, form_class=None,
if handle == '':
handle = None
try:
- # configure_repository returns None, so can't use tuple expansion
- # here. Unpack the tuple below if post_import_redirect is None.
- r = configure(z, tmpf.name, handle)
+ # configure_repository returns None, so can't use tuple expansion
+ # here. Unpack the tuple below if post_import_redirect is None.
+ r = configure(z, form.cleaned_data['xml'], handle)
except lxml.etree.XMLSyntaxError as e:
- logger.exception('caught XMLSyntaxError while parsing uploaded file')
+ logger.exception('caught XMLSyntaxError while parsing uploaded file')
messages.error(
request,
'The uploaded file has an invalid XML syntax'
)
else:
- # force rpkid run now
- z.synchronize_ca(poke=True)
- if post_import_redirect:
- url = post_import_redirect
- else:
- _, handle = r
- url = queryset.get(issuer=conf,
- handle=handle).get_absolute_url()
- return http.HttpResponseRedirect(url)
- finally:
- os.remove(tmpf.name)
+ # force rpkid run now
+ z.synchronize_ca(poke=True)
+ if post_import_redirect:
+ url = post_import_redirect
+ else:
+ _, handle = r
+ url = queryset.get(issuer=conf,
+ handle=handle).get_absolute_url()
+ return http.HttpResponseRedirect(url)
else:
form = form_class()
@@ -298,10 +291,10 @@ def serve_xml(content, basename, ext='xml'):
`basename` is the prefix to specify for the XML filename.
- `csv` is the type (default: xml)
+ `ext` is the type (default: xml)
"""
- resp = http.HttpResponse(content, mimetype='application/%s' % ext)
+ resp = http.HttpResponse(content, content_type='application/%s' % ext)
resp['Content-Disposition'] = 'attachment; filename=%s.%s' % (basename, ext)
return resp
@@ -332,13 +325,10 @@ def import_asns(request):
if request.method == 'POST':
form = forms.ImportCSVForm(request.POST, request.FILES)
if form.is_valid():
- f = NamedTemporaryFile(prefix='asns', suffix='.csv', delete=False)
- f.write(request.FILES['csv'].read())
- f.close()
z = Zookeeper(handle=conf.handle, disable_signal_handlers=True)
try:
z.load_asns(
- f.name,
+ request.FILES['csv'],
ignore_missing_children=form.cleaned_data['ignore_missing_children']
)
except rpki.irdb.models.Child.DoesNotExist:
@@ -353,8 +343,6 @@ def import_asns(request):
z.run_rpkid_now()
messages.success(request, 'Successfully imported AS delgations from CSV file.')
return redirect(dashboard)
- finally:
- os.unlink(f.name)
else:
form = forms.ImportCSVForm()
return render(request, 'app/import_resource_form.html', {
@@ -381,13 +369,10 @@ def import_prefixes(request):
if request.method == 'POST':
form = forms.ImportCSVForm(request.POST, request.FILES)
if form.is_valid():
- f = NamedTemporaryFile(prefix='prefixes', suffix='.csv', delete=False)
- f.write(request.FILES['csv'].read())
- f.close()
z = Zookeeper(handle=conf.handle, disable_signal_handlers=True)
try:
z.load_prefixes(
- f.name,
+ request.FILES['csv'],
ignore_missing_children=form.cleaned_data['ignore_missing_children']
)
except rpki.irdb.models.Child.DoesNotExist:
@@ -399,8 +384,6 @@ def import_prefixes(request):
z.run_rpkid_now()
messages.success(request, 'Successfully imported AS delgations from CSV file.')
return redirect(dashboard)
- finally:
- os.unlink(f.name)
else:
form = forms.ImportCSVForm()
return render(request, 'app/import_resource_form.html', {
@@ -474,10 +457,10 @@ def child_add_prefix(request, pk):
child.address_ranges.create(start_ip=str(r.min), end_ip=str(r.max),
version=version)
Zookeeper(
- handle=conf.handle,
- logstream=logstream,
- disable_signal_handlers=True
- ).run_rpkid_now()
+ handle=conf.handle,
+ logstream=logstream,
+ disable_signal_handlers=True
+ ).run_rpkid_now()
return http.HttpResponseRedirect(child.get_absolute_url())
else:
form = forms.AddNetForm(child=child)
@@ -497,10 +480,10 @@ def child_add_asn(request, pk):
r = resource_range_as.parse_str(asns)
child.asns.create(start_as=r.min, end_as=r.max)
Zookeeper(
- handle=conf.handle,
- logstream=logstream,
- disable_signal_handlers=True
- ).run_rpkid_now()
+ handle=conf.handle,
+ logstream=logstream,
+ disable_signal_handlers=True
+ ).run_rpkid_now()
return http.HttpResponseRedirect(child.get_absolute_url())
else:
form = forms.AddASNForm(child=child)
@@ -531,10 +514,10 @@ def child_edit(request, pk):
models.ChildASN.objects.filter(child=child).exclude(pk__in=form.cleaned_data.get('as_ranges')).delete()
models.ChildNet.objects.filter(child=child).exclude(pk__in=form.cleaned_data.get('address_ranges')).delete()
Zookeeper(
- handle=conf.handle,
- logstream=logstream,
- disable_signal_handlers=True
- ).run_rpkid_now()
+ handle=conf.handle,
+ logstream=log,
+ disable_signal_handlers=True
+ ).run_rpkid_now()
return http.HttpResponseRedirect(child.get_absolute_url())
else:
form = form_class(initial={
@@ -713,27 +696,27 @@ def roa_create_multi(request):
v = []
rng.chop_into_prefixes(v)
init.extend([{'asn': asn, 'prefix': str(p)} for p in v])
- extra = 0 if init else 1
+ extra = 0 if init else 1
formset = formset_factory(forms.ROARequestFormFactory(conf), extra=extra)(initial=init)
elif request.method == 'POST':
formset = formset_factory(forms.ROARequestFormFactory(conf), extra=0)(request.POST, request.FILES)
- # We need to check .has_changed() because .is_valid() will return true
- # if the user clicks the Preview button without filling in the blanks
- # in the ROA form, leaving the form invalid from this view's POV.
+ # We need to check .has_changed() because .is_valid() will return true
+ # if the user clicks the Preview button without filling in the blanks
+ # in the ROA form, leaving the form invalid from this view's POV.
if formset.has_changed() and formset.is_valid():
routes = []
v = []
query = Q() # for matching routes
roas = []
for form in formset:
- asn = form.cleaned_data['asn']
- rng = resource_range_ip.parse_str(form.cleaned_data['prefix'])
- max_prefixlen = int(form.cleaned_data['max_prefixlen'])
+ asn = form.cleaned_data['asn']
+ rng = resource_range_ip.parse_str(form.cleaned_data['prefix'])
+ max_prefixlen = int(form.cleaned_data['max_prefixlen'])
protect_children = form.cleaned_data['protect_children']
roas.append((rng, max_prefixlen, asn, protect_children))
- v.append({'prefix': str(rng), 'max_prefixlen': max_prefixlen,
- 'asn': asn})
+ v.append({'prefix': str(rng), 'max_prefixlen': max_prefixlen,
+ 'asn': asn})
query |= Q(prefix_min__gte=rng.min, prefix_max__lte=rng.max)
@@ -903,14 +886,10 @@ def roa_import(request):
if request.method == 'POST':
form = forms.ImportCSVForm(request.POST, request.FILES)
if form.is_valid():
- import tempfile
- tmp = tempfile.NamedTemporaryFile(suffix='.csv', prefix='roas', delete=False)
- tmp.write(request.FILES['csv'].read())
- tmp.close()
z = Zookeeper(handle=request.session['handle'],
disable_signal_handlers=True)
try:
- z.load_roa_requests(tmp.name)
+ z.load_roa_requests(request.FILES['csv'])
except rpki.csv_utils.BadCSVSyntax as e:
messages.error(request,
'CSV has bad syntax: %s' % (e,))
@@ -918,8 +897,6 @@ def roa_import(request):
z.run_rpkid_now()
messages.success(request, 'Successfully imported ROAs.')
return redirect(dashboard)
- finally:
- os.unlink(tmp.name)
else:
form = forms.ImportCSVForm()
return render(request, 'app/import_resource_form.html', {
@@ -939,7 +916,7 @@ def roa_export(request):
# each roa prefix gets a unique group so rpkid will issue separate roas
for group, roapfx in enumerate(ROARequestPrefix.objects.filter(roa_request__issuer=conf)):
csv_writer.writerow([str(roapfx.as_roa_prefix()), roapfx.roa_request.asn, '%s-%d' % (conf.handle, group)])
- resp = http.HttpResponse(f.getvalue(), mimetype='application/csv')
+ resp = http.HttpResponse(f.getvalue(), content_type='application/csv')
resp['Content-Disposition'] = 'attachment; filename=roas.csv'
return resp
@@ -1215,7 +1192,7 @@ def resource_holder_delete(request, pk):
form = forms.Empty(request.POST)
if form.is_valid():
z = Zookeeper(handle=conf.handle, logstream=log)
- z.delete_self()
+ z.delete_tenant()
z.synchronize_deleted_ca()
return redirect(resource_holder_list)
else:
@@ -1239,22 +1216,13 @@ def resource_holder_create(request):
zk_child = Zookeeper(handle=handle, logstream=log)
identity_xml = zk_child.initialize_resource_bpki()
if parent:
- # FIXME etree_wrapper should allow us to deal with file objects
- t = NamedTemporaryFile(delete=False)
- t.close()
-
- identity_xml.save(t.name)
zk_parent = Zookeeper(handle=parent.handle, logstream=log)
- parent_response, _ = zk_parent.configure_child(t.name)
- parent_response.save(t.name)
+ parent_response, _ = zk_parent.configure_child(identity_xml)
zk_parent.synchronize_ca()
- repo_req, _ = zk_child.configure_parent(t.name)
- repo_req.save(t.name)
- repo_resp, _ = zk_parent.configure_publication_client(t.name)
- repo_resp.save(t.name)
+ repo_req, _ = zk_child.configure_parent(parent_response)
+ repo_resp, _ = zk_parent.configure_publication_client(repo_req)
zk_parent.synchronize_pubd()
- zk_child.configure_repository(t.name)
- os.remove(t.name)
+ zk_child.configure_repository(repo_resp)
zk_child.synchronize_ca()
return redirect(resource_holder_list)
else:
@@ -1460,14 +1428,9 @@ class RouterImportView(FormView):
def form_valid(self, form):
conf = get_conf(self.request.user, self.request.session['handle'])
- tmpf = NamedTemporaryFile(prefix='import', suffix='.xml',
- delete=False)
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
z = Zookeeper(handle=conf.handle, disable_signal_handlers=True)
- z.add_router_certificate_request(tmpf.name)
+ z.add_router_certificate_request(form.cleaned_data['xml'])
z.run_rpkid_now()
- os.remove(tmpf.name)
return super(RouterImportView, self).form_valid(form)
def get_context_data(self, **kwargs):
diff --git a/rpki/gui/cacheview/forms.py b/rpki/gui/cacheview/forms.py
deleted file mode 100644
index 7ae3601f..00000000
--- a/rpki/gui/cacheview/forms.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# Copyright (C) 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2013 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
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-__version__ = '$Id$'
-
-from django import forms
-
-from rpki.gui.cacheview.misc import parse_ipaddr
-from rpki.exceptions import BadIPResource
-from rpki.resource_set import resource_range_as
-
-
-class SearchForm(forms.Form):
- asn = forms.CharField(required=False, help_text='AS or range', label='AS')
- addr = forms.CharField(required=False, max_length=40, help_text='range/CIDR', label='IP Address')
-
- def clean(self):
- asn = self.cleaned_data.get('asn')
- addr = self.cleaned_data.get('addr')
- if (asn and addr) or ((not asn) and (not addr)):
- raise forms.ValidationError('Please specify either an AS or IP range, not both')
-
- if asn:
- try:
- resource_range_as.parse_str(asn)
- except ValueError:
- raise forms.ValidationError('invalid AS range')
-
- if addr:
- #try:
- parse_ipaddr(addr)
- #except BadIPResource:
- # raise forms.ValidationError('invalid IP address range/prefix')
-
- return self.cleaned_data
-
-
-class SearchForm2(forms.Form):
- resource = forms.CharField(required=True)
diff --git a/rpki/gui/cacheview/misc.py b/rpki/gui/cacheview/misc.py
deleted file mode 100644
index 54431224..00000000
--- a/rpki/gui/cacheview/misc.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-from rpki.resource_set import resource_range_ipv4, resource_range_ipv6
-from rpki.exceptions import BadIPResource
-
-def parse_ipaddr(s):
- # resource_set functions only accept str
- if isinstance(s, unicode):
- s = s.encode()
- s = s.strip()
- r = resource_range_ipv4.parse_str(s)
- try:
- r = resource_range_ipv4.parse_str(s)
- return 4, r
- except BadIPResource:
- r = resource_range_ipv6.parse_str(s)
- return 6, r
-
-# vim:sw=4 ts=8 expandtab
diff --git a/rpki/gui/cacheview/templates/cacheview/addressrange_detail.html b/rpki/gui/cacheview/templates/cacheview/addressrange_detail.html
deleted file mode 100644
index 76edc1ba..00000000
--- a/rpki/gui/cacheview/templates/cacheview/addressrange_detail.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block content %}
-<h1>{% block title %}IP Range Detail{% endblock %}</h1>
-
-<p>
-IP Range: {{ object }}
-</p>
-
-<p>Covered by the following resource certs:</p>
-
-<ul>
-{% for cert in object.certs.all %}
-<li><a href="{{ cert.get_absolute_url }}">{{ cert }}</a></li>
-{% endfor %}
-</ul>
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/cacheview_base.html b/rpki/gui/cacheview/templates/cacheview/cacheview_base.html
deleted file mode 100644
index ec71d740..00000000
--- a/rpki/gui/cacheview/templates/cacheview/cacheview_base.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "base.html" %}
-{% load url from future %}
-
-{% block sidebar %}
-<form method='post' action='{% url 'res-search' %}'>
- {% csrf_token %}
- <input type='text' id='id_resource' name='resource' placeholder='prefix or AS'>
- <button type='submit'>Search</button>
-</form>
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/cert_detail.html b/rpki/gui/cacheview/templates/cacheview/cert_detail.html
deleted file mode 100644
index 256e7780..00000000
--- a/rpki/gui/cacheview/templates/cacheview/cert_detail.html
+++ /dev/null
@@ -1,105 +0,0 @@
-{% extends "cacheview/signedobject_detail.html" %}
-
-{% block title %}
-Resource Certificate Detail
-{% endblock %}
-
-{% block detail %}
-
-<h2>RFC3779 Resources</h2>
-
-<table class='table table-striped'>
- <thead>
- <tr><th>AS Ranges</th><th>IP Ranges</th></tr>
- </thead>
- <tbody>
- <tr>
- <td style='text-align:left;vertical-align:top'>
- <ul class='compact'>
- {% for asn in object.asns.all %}
- <li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a></li>
- {% endfor %}
- </ul>
- </td>
- <td style='text-align:left;vertical-align:top'>
- <ul class='compact'>
- {% for rng in object.addresses.all %}
- <li><a href="{{ rng.get_absolute_url }}">{{ rng }}</a></li>
- {% endfor %}
- </ul>
- </td>
- </tr>
- </tbody>
-</table>
-
-<div class='section'>
-<h2>Issued Objects</h2>
-<ul>
-
-{% if object.ghostbusters.all %}
- <li>
-<h3>Ghostbusters</h3>
-
-<table class='table table-striped'>
- <thead>
- <tr><th>Name</th><th>Expires</th></tr>
- </thead>
- <tbody>
-
-{% for g in object.ghostbusters.all %}
- <tr class='{{ g.status_id }}'>
- <td><a href="{{ g.get_absolute_url }}">{{ g }}</a></td>
- <td>{{ g.not_after }}</td>
- </tr>
- </tbody>
-{% endfor %}
-
-</table>
-{% endif %}
-
-{% if object.roas.all %}
- <li>
-<h3>ROAs</h3>
-<table class='table table-striped'>
- <thead>
- <tr><th>#</th><th>Prefix</th><th>AS</th><th>Expires</th></tr>
- </thead>
- <tbody>
- {% for roa in object.roas.all %}
- {% for pfx in roa.prefixes.all %}
- <tr class='{{ roa.status_id }}'>
- <td><a href="{{ roa.get_absolute_url }}">#</a></td>
- <td>{{ pfx }}</td>
- <td>{{ roa.asid }}</td>
- <td>{{ roa.not_after }}</td>
- </tr>
- {% endfor %}
- {% endfor %}
- </tbody>
-</table>
-{% endif %}
-
-{% if object.children.all %}
-<li>
-<h3>Children</h3>
-<table class='table table-striped'>
- <thead>
- <tr><th>Name</th><th>Expires</th></tr>
- </thead>
- <tbody>
-
- {% for child in object.children.all %}
- <tr class='{{ child.status_id }}'>
- <td><a href="{{ child.get_absolute_url }}">{{ child.name }}</a></td>
- <td>{{ child.not_after }}</td>
- </tr>
- {% endfor %}
- </tbody>
-</table>
-{% endif %}
-
-</ul>
-
-</div><!--issued objects-->
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/ghostbuster_detail.html b/rpki/gui/cacheview/templates/cacheview/ghostbuster_detail.html
deleted file mode 100644
index 4215f757..00000000
--- a/rpki/gui/cacheview/templates/cacheview/ghostbuster_detail.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "cacheview/signedobject_detail.html" %}
-
-{% block title %}Ghostbuster Detail{% endblock %}
-
-{% block detail %}
-<p>
-<table class='table'>
- <tr><td>Full Name</td><td>{{ object.full_name }}</td></tr>
- <tr><td>Organization</td><td>{{ object.organization }}</td></tr>
- <tr><td>Email</td><td>{{ object.email_address }}</td></tr>
- <tr><td>Telephone</td><td>{{ object.telephone }}</td></tr>
-</table>
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/global_summary.html b/rpki/gui/cacheview/templates/cacheview/global_summary.html
deleted file mode 100644
index 0dbd0ffc..00000000
--- a/rpki/gui/cacheview/templates/cacheview/global_summary.html
+++ /dev/null
@@ -1,26 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block content %}
-<div class='page-header'>
- <h1>Browse Global RPKI</h1>
-</div>
-
-<table class="table table-striped">
- <thead>
- <tr>
- <th>Name</th>
- <th>Expires</th>
- <th>URI</th>
- </tr>
- </thead>
- <tbody>
- {% for r in roots %}
- <tr>
- <td><a href="{{ r.get_absolute_url }}">{{ r.name }}</a></td>
- <td>{{ r.not_after }}</td>
- <td>{{ r.repo.uri }}</td>
- </tr>
- {% endfor %}
- </tbody>
-</table>
-{% endblock content %}
diff --git a/rpki/gui/cacheview/templates/cacheview/query_result.html b/rpki/gui/cacheview/templates/cacheview/query_result.html
deleted file mode 100644
index 0694c531..00000000
--- a/rpki/gui/cacheview/templates/cacheview/query_result.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block content %}
-
-<h1>{% block title %}Query Results{% endblock %}</h1>
-
-<table>
- <tr><th>Prefix</th><th>AS</th><th>Valid</th><th>Until</th></tr>
- {% for object in object_list %}
- <tr class='{{ object.1.status.kind_as_str }}'>
- <td>{{ object.0 }}</td>
- <td>{{ object.1.asid }}</td>
- <td><a href="{{ object.1.get_absolute_url }}">{{ object.1.ok }}</a></td>
- <td>{{ object.1.not_after }}</td>
- </tr>
- {% endfor %}
-</table>
-
-<p><a href="{% url rpki.gui.cacheview.views.query_view %}">new query</a></p>
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/roa_detail.html b/rpki/gui/cacheview/templates/cacheview/roa_detail.html
deleted file mode 100644
index 39cc547b..00000000
--- a/rpki/gui/cacheview/templates/cacheview/roa_detail.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{% extends "cacheview/signedobject_detail.html" %}
-
-{% block title %}ROA Detail{% endblock %}
-
-{% block detail %}
-<p>
-<table>
- <tr><td>AS</td><td>{{ object.asid }}</td></tr>
-</table>
-
-<h2>Prefixes</h2>
-
-<ul>
-{% for pfx in object.prefixes.all %}
-<li>{{ pfx }}
-{% endfor %}
-</ul>
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/search_form.html b/rpki/gui/cacheview/templates/cacheview/search_form.html
deleted file mode 100644
index 1141615d..00000000
--- a/rpki/gui/cacheview/templates/cacheview/search_form.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block title %}
-{{ search_type }} Search
-{% endblock %}
-
-{% block content %}
-
-<h1>{{search_type}} Search</h1>
-
-<form method='post' action='{{ request.url }}'>
- {% csrf_token %}
- {{ form.as_p }}
- <input type='submit' name='Search'>
-</form>
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/search_result.html b/rpki/gui/cacheview/templates/cacheview/search_result.html
deleted file mode 100644
index 7cbf852e..00000000
--- a/rpki/gui/cacheview/templates/cacheview/search_result.html
+++ /dev/null
@@ -1,42 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block content %}
-
-<div class='page-header'>
- <h1>Search Results <small>{{ resource }}</small></h1>
-</div>
-
-<h2>Matching Resource Certificates</h2>
-{% if certs %}
-<ul>
-{% for cert in certs %}
-<li><a href="{{ cert.get_absolute_url }}">{{ cert }}</a>
-{% endfor %}
-</ul>
-{% else %}
-<p>none</p>
-{% endif %}
-
-<h2>Matching ROAs</h2>
-{% if roas %}
-<table class='table table-striped'>
- <thead>
- <tr>
- <th>#</th><th>Prefix</th><th>AS</th>
- </tr>
- </thead>
- <tbody>
-{% for roa in roas %}
-<tr>
- <td><a href="{{ roa.get_absolute_url }}">#</a></td>
- <td>{{ roa.prefixes.all.0 }}</td>
- <td>{{ roa.asid }}</td>
-</tr>
-{% endfor %}
-</tbody>
-</table>
-{% else %}
-<p>none</p>
-{% endif %}
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html b/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html
deleted file mode 100644
index 22ae3d27..00000000
--- a/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html
+++ /dev/null
@@ -1,58 +0,0 @@
-{% extends "cacheview/cacheview_base.html" %}
-
-{% block content %}
-<div class='page-header'>
-<h1>{% block title %}Signed Object Detail{% endblock %}</h1>
-</div>
-
-<h2>Cert Info</h2>
-<table class='table table-striped'>
- <tr><td>Subject Name</td><td>{{ object.name }}</td></tr>
- <tr><td>SKI</td><td>{{ object.keyid }}</td></tr>
- {% if object.sia %}
- <tr><td>SIA</td><td>{{ object.sia }}</td></tr>
- {% endif %}
- <tr><td>Not Before</td><td>{{ object.not_before }}</td></tr>
- <tr><td>Not After</td><td>{{ object.not_after }}</td></tr>
-</table>
-
-<h2>Metadata</h2>
-
-<table class='table table-striped'>
- <tr><td>URI</td><td>{{ object.repo.uri }}</td></tr>
- <tr><td>Last Modified</td><td>{{ object.mtime_as_datetime|date:"DATETIME_FORMAT" }}</td></tr>
-</table>
-
-<h2>Validation Status</h2>
-<table class='table table-striped'>
- <thead>
- <tr><th>Timestamp</th><th>Generation</th><th>Status</th></tr>
- </thead>
- <tbody>
- {% for status in object.repo.statuses.all %}
- <tr class="{{ status.status.get_kind_display }}"><td>{{ status.timestamp }}</td><td>{{ status.get_generation_display }}</td><td>{{ status.status.status }}</td></tr>
- {% endfor %}
- </tbody>
-</table>
-
-<h2>X.509 Certificate Chain</h2>
-
-<table class='table table-striped'>
- <thead>
- <tr><th>Depth</th><th>Name</th></tr>
- </thead>
- <tbody>
-
-{% for cert in chain %}
-<tr class='{{ cert.1.status_id }}'>
- <td>{{ cert.0 }}</td>
- <td><a href="{{ cert.1.get_absolute_url }}">{{ cert.1.name }}</a></td>
-</tr>
-{% endfor %}
-</tbody>
-
-</table>
-
-{% block detail %}{% endblock %}
-
-{% endblock %}
diff --git a/rpki/gui/cacheview/tests.py b/rpki/gui/cacheview/tests.py
deleted file mode 100644
index 2247054b..00000000
--- a/rpki/gui/cacheview/tests.py
+++ /dev/null
@@ -1,23 +0,0 @@
-"""
-This file demonstrates two different styles of tests (one doctest and one
-unittest). These will both pass when you run "manage.py test".
-
-Replace these with more appropriate tests for your application.
-"""
-
-from django.test import TestCase
-
-class SimpleTest(TestCase):
- def test_basic_addition(self):
- """
- Tests that 1 + 1 always equals 2.
- """
- self.failUnlessEqual(1 + 1, 2)
-
-__test__ = {"doctest": """
-Another way to test that 1 + 1 is equal to 2.
-
->>> 1 + 1 == 2
-True
-"""}
-
diff --git a/rpki/gui/cacheview/urls.py b/rpki/gui/cacheview/urls.py
deleted file mode 100644
index cc03a587..00000000
--- a/rpki/gui/cacheview/urls.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2013 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
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-__version__ = '$Id$'
-
-from django.conf.urls import patterns, url
-from rpki.gui.cacheview.views import (CertDetailView, RoaDetailView,
- GhostbusterDetailView)
-
-urlpatterns = patterns('',
- url(r'^search$', 'rpki.gui.cacheview.views.search_view',
- name='res-search'),
- url(r'^cert/(?P<pk>[^/]+)$', CertDetailView.as_view(), name='cert-detail'),
- url(r'^gbr/(?P<pk>[^/]+)$', GhostbusterDetailView.as_view(),
- name='ghostbuster-detail'),
- url(r'^roa/(?P<pk>[^/]+)$', RoaDetailView.as_view(), name='roa-detail'),
- (r'^$', 'rpki.gui.cacheview.views.global_summary'),
-)
-
-# vim:sw=4 ts=8 expandtab
diff --git a/rpki/gui/cacheview/util.py b/rpki/gui/cacheview/util.py
deleted file mode 100644
index 47425c8c..00000000
--- a/rpki/gui/cacheview/util.py
+++ /dev/null
@@ -1,441 +0,0 @@
-# Copyright (C) 2011 SPARTA, Inc. dba Cobham
-# Copyright (C) 2012, 2013 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
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-__version__ = '$Id$'
-__all__ = ('import_rcynic_xml')
-
-default_logfile = '/var/rcynic/data/rcynic.xml'
-default_root = '/var/rcynic/data'
-object_accepted = None # set by import_rcynic_xml()
-
-import time
-import vobject
-import logging
-import os
-import stat
-from socket import getfqdn
-from cStringIO import StringIO
-
-from django.db import transaction
-import django.db.models
-
-import rpki
-import rpki.gui.app.timestamp
-from rpki.gui.app.models import Conf, Alert
-from rpki.gui.cacheview import models
-from rpki.rcynic import rcynic_xml_iterator, label_iterator
-from rpki.sundial import datetime
-from rpki.irdb.zookeeper import Zookeeper
-
-logger = logging.getLogger(__name__)
-
-
-class SomeoneShowMeAWayToGetOuttaHere(Exception):
- "'Cause I constantly pray I'll get outta here."
-
-
-def rcynic_cert(cert, obj):
- if not cert.sia_directory_uri:
- raise SomeoneShowMeAWayToGetOuttaHere
-
- obj.sia = cert.sia_directory_uri
-
- # object must be saved for the related manager methods below to work
- obj.save()
-
- # for the root cert, we can't set inst.issuer = inst until
- # after inst.save() has been called.
- if obj.issuer is None:
- obj.issuer = obj
- obj.save()
-
- # resources can change when a cert is updated
- obj.asns.clear()
- obj.addresses.clear()
-
- if cert.resources.asn.inherit:
- # FIXME: what happens when the parent's resources change and the child
- # cert is not reissued?
- obj.asns.add(*obj.issuer.asns.all())
- else:
- for asr in cert.resources.asn:
- logger.debug('processing %s', asr)
-
- attrs = {'min': asr.min, 'max': asr.max}
- q = models.ASRange.objects.filter(**attrs)
- if not q:
- obj.asns.create(**attrs)
- else:
- obj.asns.add(q[0])
-
- # obj.issuer is None the first time we process the root cert in the
- # hierarchy, so we need to guard against dereference
- for cls, addr_obj, addrset, parentset in (
- models.AddressRange, obj.addresses, cert.resources.v4,
- obj.issuer.addresses.all() if obj.issuer else []
- ), (
- models.AddressRangeV6, obj.addresses_v6, cert.resources.v6,
- obj.issuer.addresses_v6.all() if obj.issuer else []
- ):
- if addrset.inherit:
- addr_obj.add(*parentset)
- else:
- for rng in addrset:
- logger.debug('processing %s', rng)
-
- attrs = {'prefix_min': rng.min, 'prefix_max': rng.max}
- q = cls.objects.filter(**attrs)
- if not q:
- addr_obj.create(**attrs)
- else:
- addr_obj.add(q[0])
-
-
-def rcynic_roa(roa, obj):
- obj.asid = roa.asID
- # object must be saved for the related manager methods below to work
- obj.save()
- obj.prefixes.clear()
- obj.prefixes_v6.clear()
- for pfxset in roa.prefix_sets:
- if pfxset.__class__.__name__ == 'roa_prefix_set_ipv6':
- roa_cls = models.ROAPrefixV6
- prefix_obj = obj.prefixes_v6
- else:
- roa_cls = models.ROAPrefixV4
- prefix_obj = obj.prefixes
-
- for pfx in pfxset:
- attrs = {'prefix_min': pfx.min(),
- 'prefix_max': pfx.max(),
- 'max_length': pfx.max_prefixlen}
- q = roa_cls.objects.filter(**attrs)
- if not q:
- prefix_obj.create(**attrs)
- else:
- prefix_obj.add(q[0])
-
-
-def rcynic_gbr(gbr, obj):
- vcard = vobject.readOne(gbr.vcard)
- obj.full_name = vcard.fn.value if hasattr(vcard, 'fn') else None
- obj.email_address = vcard.email.value if hasattr(vcard, 'email') else None
- obj.telephone = vcard.tel.value if hasattr(vcard, 'tel') else None
- obj.organization = vcard.org.value[0] if hasattr(vcard, 'org') else None
- obj.save()
-
-LABEL_CACHE = {}
-
-# dict keeping mapping of uri to (handle, old status, new status) for objects
-# published by the local rpkid
-uris = {}
-
-dispatch = {
- 'rcynic_certificate': rcynic_cert,
- 'rcynic_roa': rcynic_roa,
- 'rcynic_ghostbuster': rcynic_gbr
-}
-
-model_class = {
- 'rcynic_certificate': models.Cert,
- 'rcynic_roa': models.ROA,
- 'rcynic_ghostbuster': models.Ghostbuster
-}
-
-
-def save_status(repo, vs):
- timestamp = datetime.fromXMLtime(vs.timestamp).to_sql()
- status = LABEL_CACHE[vs.status]
- g = models.generations_dict[vs.generation] if vs.generation else None
- repo.statuses.create(generation=g, timestamp=timestamp, status=status)
-
- # if this object is in our interest set, update with the current validation
- # status
- if repo.uri in uris:
- x, y, z, q = uris[repo.uri]
- valid = z or (status is object_accepted) # don't clobber previous True value
- uris[repo.uri] = x, y, valid, repo
-
- if status is not object_accepted:
- return
-
- cls = model_class[vs.file_class.__name__]
- # find the instance of the signedobject subclass that is associated with
- # this repo instance (may be empty when not accepted)
- inst_qs = cls.objects.filter(repo=repo)
-
- logger.debug('processing %s', vs.filename)
-
- if not inst_qs:
- inst = cls(repo=repo)
- logger.debug('object not found in db, creating new object cls=%s id=%s',
- cls, id(inst))
- else:
- inst = inst_qs[0]
-
- try:
- # determine if the object is changed/new
- mtime = os.stat(vs.filename)[stat.ST_MTIME]
- except OSError as e:
- logger.error('unable to stat %s: %s %s',
- vs.filename, type(e), e)
- # treat as if missing from rcynic.xml
- # use inst_qs rather than deleting inst so that we don't raise an
- # exception for newly created objects (inst_qs will be empty)
- inst_qs.delete()
- return
-
- if mtime != inst.mtime:
- inst.mtime = mtime
- try:
- obj = vs.obj # causes object to be lazily loaded
- except Exception, e:
- logger.warning('Caught %s while processing %s: %s',
- type(e), vs.filename, e)
- return
-
- inst.not_before = obj.notBefore.to_sql()
- inst.not_after = obj.notAfter.to_sql()
- inst.name = obj.subject
- inst.keyid = obj.ski
-
- # look up signing cert
- if obj.issuer == obj.subject:
- # self-signed cert (TA)
- assert isinstance(inst, models.Cert)
- inst.issuer = None
- else:
- # if an object has moved in the repository, the entry for
- # the old location will still be in the database, but
- # without any object_accepted in its validtion status
- qs = models.Cert.objects.filter(
- keyid=obj.aki,
- name=obj.issuer,
- repo__statuses__status=object_accepted
- )
- ncerts = len(qs)
- if ncerts == 0:
- logger.warning('unable to find signing cert with ski=%s (%s)', obj.aki, obj.issuer)
- return
- else:
- if ncerts > 1:
- # multiple matching certs, all of which are valid
- logger.warning('Found multiple certs matching ski=%s sn=%s', obj.aki, obj.issuer)
- for c in qs:
- logger.warning(c.repo.uri)
- # just use the first match
- inst.issuer = qs[0]
-
- try:
- # do object-specific tasks
- dispatch[vs.file_class.__name__](obj, inst)
- except SomeoneShowMeAWayToGetOuttaHere:
- logger.error("something wrong with %s, skipping", vs.filename)
- inst_qs.delete()
- return
- except:
- logger.error('caught exception while processing rcynic_object:\n'
- 'vs=' + repr(vs) + '\nobj=' + repr(obj))
- # .show() writes to stdout
- obj.show()
- raise
-
- logger.debug('object saved id=%s', id(inst))
- else:
- logger.debug('object is unchanged')
-
-
-@transaction.commit_on_success
-def process_cache(root, xml_file):
-
- last_uri = None
- repo = None
-
- logger.info('clearing validation statuses')
- models.ValidationStatus.objects.all().delete()
-
- logger.info('updating validation status')
- for vs in rcynic_xml_iterator(root, xml_file):
- if vs.uri != last_uri:
- repo, created = models.RepositoryObject.objects.get_or_create(uri=vs.uri)
- last_uri = vs.uri
- save_status(repo, vs)
-
- # garbage collection
- # remove all objects which have no ValidationStatus references, which
- # means they did not appear in the last XML output
- logger.info('performing garbage collection')
-
- # Delete all objects that have zero validation status elements.
- models.RepositoryObject.objects.annotate(num_statuses=django.db.models.Count('statuses')).filter(num_statuses=0).delete()
-
- # Delete all SignedObject instances that were not accepted. There may
- # exist rows for objects that were previously accepted.
- # See https://trac.rpki.net/ticket/588#comment:30
- #
- # We have to do this here rather than in save_status() because the
- # <validation_status/> elements are not guaranteed to be consecutive for a
- # given URI. see https://trac.rpki.net/ticket/625#comment:5
- models.SignedObject.objects.exclude(repo__statuses__status=object_accepted).delete()
-
- # ROAPrefixV* objects are M2M so they are not automatically deleted when
- # their ROA object disappears
- models.ROAPrefixV4.objects.annotate(num_roas=django.db.models.Count('roas')).filter(num_roas=0).delete()
- models.ROAPrefixV6.objects.annotate(num_roas=django.db.models.Count('roas')).filter(num_roas=0).delete()
- logger.info('done with garbage collection')
-
-
-@transaction.commit_on_success
-def process_labels(xml_file):
- logger.info('updating labels...')
-
- for label, kind, desc in label_iterator(xml_file):
- logger.debug('label=%s kind=%s desc=%s', label, kind, desc)
- if kind:
- q = models.ValidationLabel.objects.filter(label=label)
- if not q:
- obj = models.ValidationLabel(label=label)
- else:
- obj = q[0]
-
- obj.kind = models.kinds_dict[kind]
- obj.status = desc
- obj.save()
-
- LABEL_CACHE[label] = obj
-
-
-def fetch_published_objects():
- """Query rpkid for all objects published by local users, and look up the
- current validation status of each object. The validation status is used
- later to send alerts for objects which have transitioned to invalid.
-
- """
- logger.info('querying for published objects')
-
- handles = [conf.handle for conf in Conf.objects.all()]
- req = [rpki.left_right.list_published_objects_elt.make_pdu(action='list', self_handle=h, tag=h) for h in handles]
- z = Zookeeper()
- pdus = z.call_rpkid(*req)
- for pdu in pdus:
- if isinstance(pdu, rpki.left_right.list_published_objects_elt):
- # Look up the object in the rcynic cache
- qs = models.RepositoryObject.objects.filter(uri=pdu.uri)
- if qs:
- # get the current validity state
- valid = qs[0].statuses.filter(status=object_accepted).exists()
- uris[pdu.uri] = (pdu.self_handle, valid, False, None)
- logger.debug('adding ' + pdu.uri)
- else:
- # this object is not in the cache. it was either published
- # recently, or disappared previously. if it disappeared
- # previously, it has already been alerted. in either case, we
- # omit the uri from the list since we are interested only in
- # objects which were valid and are no longer valid
- pass
- elif isinstance(pdu, rpki.left_right.report_error_elt):
- logging.error('rpkid reported an error: %s', pdu.error_code)
-
-
-class Handle(object):
- def __init__(self):
- self.invalid = []
- self.missing = []
-
- def add_invalid(self, v):
- self.invalid.append(v)
-
- def add_missing(self, v):
- self.missing.append(v)
-
-
-def notify_invalid():
- """Send email alerts to the addresses registered in ghostbuster records for
- any invalid objects that were published by users of this system.
-
- """
-
- logger.info('sending notifications for invalid objects')
-
- # group invalid objects by user
- notify = {}
- for uri, v in uris.iteritems():
- handle, old_status, new_status, obj = v
-
- if obj is None:
- # object went missing
- n = notify.get(handle, Handle())
- n.add_missing(uri)
- # only select valid->invalid
- elif old_status and not new_status:
- n = notify.get(handle, Handle())
- n.add_invalid(obj)
-
- for handle, v in notify.iteritems():
- conf = Conf.objects.get(handle)
-
- msg = StringIO()
- msg.write('This is an alert about problems with objects published by '
- 'the resource handle %s.\n\n' % handle)
-
- if v.invalid:
- msg.write('The following objects were previously valid, but are '
- 'now invalid:\n')
-
- for o in v.invalid:
- msg.write('\n')
- msg.write(o.repo.uri)
- msg.write('\n')
- for s in o.statuses.all():
- msg.write('\t')
- msg.write(s.status.label)
- msg.write(': ')
- msg.write(s.status.status)
- msg.write('\n')
-
- if v.missing:
- msg.write('The following objects were previously valid but are no '
- 'longer in the cache:\n')
-
- for o in v.missing:
- msg.write(o)
- msg.write('\n')
-
- msg.write("""--
-You are receiving this email because your address is published in a Ghostbuster
-record, or is the default email address for this resource holder account on
-%s.""" % getfqdn())
-
- from_email = 'root@' + getfqdn()
- subj = 'invalid RPKI object alert for resource handle %s' % conf.handle
- conf.send_alert(subj, msg.getvalue(), from_email, severity=Alert.ERROR)
-
-
-def import_rcynic_xml(root=default_root, logfile=default_logfile):
- """Load the contents of rcynic.xml into the rpki.gui.cacheview database."""
-
- global object_accepted
-
- start = time.time()
- process_labels(logfile)
- object_accepted = LABEL_CACHE['object_accepted']
- fetch_published_objects()
- process_cache(root, logfile)
- notify_invalid()
-
- rpki.gui.app.timestamp.update('rcynic_import')
-
- stop = time.time()
- logger.info('elapsed time %d seconds.', (stop - start))
diff --git a/rpki/gui/cacheview/views.py b/rpki/gui/cacheview/views.py
deleted file mode 100644
index 94870eb2..00000000
--- a/rpki/gui/cacheview/views.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# Copyright (C) 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2013 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
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
-# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
-# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
-# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
-# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
-# PERFORMANCE OF THIS SOFTWARE.
-
-__version__ = '$Id$'
-
-from django.views.generic import DetailView
-from django.shortcuts import render
-from django.db.models import F
-
-from rpki.gui.cacheview import models, forms, misc
-from rpki.resource_set import resource_range_as, resource_range_ip
-from rpki.POW import IPAddress
-from rpki.exceptions import BadIPResource
-
-
-def cert_chain(obj):
- """
- returns an iterator covering all certs from the root cert down to the EE.
- """
- chain = [obj]
- while obj != obj.issuer:
- obj = obj.issuer
- chain.append(obj)
- return zip(range(len(chain)), reversed(chain))
-
-
-class SignedObjectDetailView(DetailView):
- def get_context_data(self, **kwargs):
- context = super(SignedObjectDetailView,
- self).get_context_data(**kwargs)
- context['chain'] = cert_chain(self.object)
- return context
-
-
-class RoaDetailView(SignedObjectDetailView):
- model = models.ROA
-
-
-class CertDetailView(SignedObjectDetailView):
- model = models.Cert
-
-
-class GhostbusterDetailView(SignedObjectDetailView):
- model = models.Ghostbuster
-
-
-def search_view(request):
- certs = None
- roas = None
-
- if request.method == 'POST':
- form = forms.SearchForm2(request.POST, request.FILES)
- if form.is_valid():
- resource = form.cleaned_data.get('resource')
- # try to determine the type of input given
- try:
- r = resource_range_as.parse_str(resource)
- certs = models.Cert.objects.filter(asns__min__gte=r.min,
- asns__max__lte=r.max)
- roas = models.ROA.objects.filter(asid__gte=r.min,
- asid__lte=r.max)
- except:
- try:
- r = resource_range_ip.parse_str(resource)
- if r.version == 4:
- certs = models.Cert.objects.filter(
- addresses__prefix_min__lte=r.min,
- addresses__prefix_max__gte=r.max)
- roas = models.ROA.objects.filter(
- prefixes__prefix_min__lte=r.min,
- prefixes__prefix_max__gte=r.max)
- else:
- certs = models.Cert.objects.filter(
- addresses_v6__prefix_min__lte=r.min,
- addresses_v6__prefix_max__gte=r.max)
- roas = models.ROA.objects.filter(
- prefixes_v6__prefix_min__lte=r.min,
- prefixes_v6__prefix_max__gte=r.max)
- except BadIPResource:
- pass
-
- return render(request, 'cacheview/search_result.html',
- {'resource': resource, 'certs': certs, 'roas': roas})
-
-
-def cmp_prefix(x, y):
- r = cmp(x[0].family, y[0].family)
- if r == 0:
- r = cmp(x[2], y[2]) # integer address
- if r == 0:
- r = cmp(x[0].bits, y[0].bits)
- if r == 0:
- r = cmp(x[0].max_length, y[0].max_length)
- if r == 0:
- r = cmp(x[1].asid, y[1].asid)
- return r
-
-
-#def cmp_prefix(x,y):
-# for attr in ('family', 'prefix', 'bits', 'max_length'):
-# r = cmp(getattr(x[0], attr), getattr(y[0], attr))
-# if r:
-# return r
-# return cmp(x[1].asid, y[1].asid)
-
-
-def query_view(request):
- """
- Allow the user to search for an AS or prefix, and show all published ROA
- information.
- """
-
- if request.method == 'POST':
- form = forms.SearchForm(request.POST, request.FILES)
- if form.is_valid():
- certs = None
- roas = None
-
- addr = form.cleaned_data.get('addr')
- asn = form.cleaned_data.get('asn')
-
- if addr:
- family, r = misc.parse_ipaddr(addr)
- prefixes = models.ROAPrefix.objects.filter(family=family, prefix=str(r.min))
-
- prefix_list = []
- for pfx in prefixes:
- for roa in pfx.roas.all():
- prefix_list.append((pfx, roa))
- elif asn:
- r = resource_range_as.parse_str(asn)
- roas = models.ROA.objects.filter(asid__gte=r.min, asid__lte=r.max)
-
- # display the results sorted by prefix
- prefix_list = []
- for roa in roas:
- for pfx in roa.prefixes.all():
- addr = IPAddress(pfx.prefix.encode())
- prefix_list.append((pfx, roa, addr))
- prefix_list.sort(cmp=cmp_prefix)
-
- return render('cacheview/query_result.html',
- {'object_list': prefix_list}, request)
- else:
- form = forms.SearchForm()
-
- return render('cacheview/search_form.html', {
- 'form': form, 'search_type': 'ROA '}, request)
-
-
-def global_summary(request):
- """Display a table summarizing the state of the global RPKI."""
-
- roots = models.Cert.objects.filter(issuer=F('pk')) # self-signed
-
- return render(request, 'cacheview/global_summary.html', {
- 'roots': roots
- })
-
-# vim:sw=4 ts=8 expandtab
diff --git a/rpki/gui/default_settings.py b/rpki/gui/default_settings.py
deleted file mode 100644
index a30b0362..00000000
--- a/rpki/gui/default_settings.py
+++ /dev/null
@@ -1,188 +0,0 @@
-"""
-This module contains static configuration settings for the web portal.
-"""
-
-__version__ = '$Id$'
-
-import os
-import random
-import string
-import socket
-
-import rpki.config
-import rpki.autoconf
-
-# Where to put static files.
-STATIC_ROOT = rpki.autoconf.datarootdir + '/rpki/media'
-
-# Must end with a slash!
-STATIC_URL = '/media/'
-
-# Where to email server errors.
-ADMINS = (('Administrator', 'root@localhost'),)
-
-LOGGING = {
- 'version': 1,
- 'formatters': {
- 'verbose': {
- # see http://docs.python.org/2.7/library/logging.html#logging.LogRecord
- 'format': '%(levelname)s %(asctime)s %(name)s %(message)s'
- },
- },
- 'handlers': {
- 'stderr': {
- 'class': 'logging.StreamHandler',
- 'level': 'DEBUG',
- 'formatter': 'verbose',
- },
- 'mail_admins': {
- 'level': 'ERROR',
- 'class': 'django.utils.log.AdminEmailHandler',
- },
- },
- 'loggers': {
- 'rpki.async': {
- # enabled for tracking https://trac.rpki.net/ticket/681
- # need to change this to WARNING once ticket is closed
- 'level': 'DEBUG',
- },
- # The Django default LOGGING configuration disables propagate on these
- # two loggers. Re-enable propagate so they will hit our root logger.
- 'django.request': {
- 'propagate': True,
- },
- 'django.security': {
- 'propagate': True,
- },
- },
- 'root': {
- 'level': 'WARNING',
- 'handlers': ['stderr', 'mail_admins'],
- },
-}
-
-# Load the SQL authentication bits from the system rpki.conf.
-rpki_config = rpki.config.parser(section='web_portal')
-
-DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': rpki_config.get('sql-database'),
- 'USER': rpki_config.get('sql-username'),
- 'PASSWORD': rpki_config.get('sql-password'),
-
- # Ensure the default storage engine is InnoDB since we need
- # foreign key support. The Django documentation suggests
- # removing this after the syncdb is performed as an optimization,
- # but there isn't an easy way to do this automatically.
-
- # Setting charset to latin1 is a disgusting kludge, but without
- # this MySQL 5.6 (and, proably, later) gets tetchy about ASN.1
- # DER stored in BLOB columns not being well-formed UTF8 (sic).
- # If you know of a better solution, tell us.
-
- 'OPTIONS': {
- 'init_command': 'SET storage_engine=INNODB',
- 'charset': 'latin1',
- }
- }
-}
-
-
-def select_tz():
- "Find a supported timezone that looks like UTC"
- for tz in ('UTC', 'GMT', 'Etc/UTC', 'Etc/GMT'):
- if os.path.exists('/usr/share/zoneinfo/' + tz):
- return tz
- # Can't determine the proper timezone, fall back to UTC and let Django
- # report the error to the user.
- return 'UTC'
-
-# Local time zone for this installation. Choices can be found here:
-# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
-# although not all choices may be available on all operating systems.
-# If running in a Windows environment this must be set to the same as your
-# system time zone.
-TIME_ZONE = select_tz()
-
-def get_secret_key():
- """Retrieve the secret-key value from rpki.conf or generate a random value
- if it is not present."""
- d = string.letters + string.digits
- val = ''.join([random.choice(d) for _ in range(50)])
- return rpki_config.get('secret-key', val)
-
-# Make this unique, and don't share it with anybody.
-SECRET_KEY = get_secret_key()
-
-# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
-# for details on why you might need this.
-def get_allowed_hosts():
- allowed_hosts = set(rpki_config.multiget("allowed-hosts"))
- allowed_hosts.add(socket.getfqdn())
- try:
- import netifaces
- for interface in netifaces.interfaces():
- addresses = netifaces.ifaddresses(interface)
- for af in (netifaces.AF_INET, netifaces.AF_INET6):
- if af in addresses:
- for address in addresses[af]:
- if "addr" in address:
- allowed_hosts.add(address["addr"])
- except ImportError:
- pass
- return list(allowed_hosts)
-
-ALLOWED_HOSTS = get_allowed_hosts()
-
-DOWNLOAD_DIRECTORY = rpki_config.get('download-directory', '/var/tmp')
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
- 'django.template.loaders.filesystem.Loader',
- 'django.template.loaders.app_directories.Loader',
- 'django.template.loaders.eggs.Loader'
-)
-
-MIDDLEWARE_CLASSES = (
- 'django.middleware.common.CommonMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware'
-)
-
-ROOT_URLCONF = 'rpki.gui.urls'
-
-INSTALLED_APPS = (
- 'django.contrib.auth',
- #'django.contrib.admin',
- #'django.contrib.admindocs',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.staticfiles',
- 'rpki.irdb',
- 'rpki.gui.app',
- 'rpki.gui.cacheview',
- 'rpki.gui.routeview',
- 'south',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
- "django.contrib.auth.context_processors.auth",
- "django.core.context_processors.debug",
- "django.core.context_processors.i18n",
- "django.core.context_processors.media",
- "django.contrib.messages.context_processors.messages",
- "django.core.context_processors.request",
- "django.core.context_processors.static"
-)
-
-# Allow local site to override any setting above -- but if there's
-# anything that local sites routinely need to modify, please consider
-# putting that configuration into rpki.conf and just adding code here
-# to read that configuration.
-try:
- from local_settings import *
-except:
- pass
diff --git a/rpki/gui/gui_rpki_cache/__init__.py b/rpki/gui/gui_rpki_cache/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/__init__.py
diff --git a/rpki/gui/gui_rpki_cache/migrations/0001_initial.py b/rpki/gui/gui_rpki_cache/migrations/0001_initial.py
new file mode 100644
index 00000000..23625f56
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/migrations/0001_initial.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import rpki.gui.gui_rpki_cache.models
+import rpki.gui.models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AddressRange',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='AddressRangeV6',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='ASRange',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('min', models.BigIntegerField(validators=[rpki.gui.models.validate_asn])),
+ ('max', models.BigIntegerField(validators=[rpki.gui.models.validate_asn])),
+ ],
+ options={
+ 'ordering': ('min', 'max'),
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Cert',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('uri', models.TextField()),
+ ('sha256', models.SlugField(unique=True, max_length=64)),
+ ('not_before', models.DateTimeField()),
+ ('not_after', models.DateTimeField()),
+ ('ski', models.SlugField(max_length=40)),
+ ('addresses', models.ManyToManyField(related_name='certs', to='gui_rpki_cache.AddressRange')),
+ ('addresses_v6', models.ManyToManyField(related_name='certs', to='gui_rpki_cache.AddressRangeV6')),
+ ('asns', models.ManyToManyField(related_name='certs', to='gui_rpki_cache.ASRange')),
+ ('issuer', models.ForeignKey(related_name='children', to='gui_rpki_cache.Cert', null=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Ghostbuster',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('uri', models.TextField()),
+ ('sha256', models.SlugField(unique=True, max_length=64)),
+ ('not_before', models.DateTimeField()),
+ ('not_after', models.DateTimeField()),
+ ('full_name', models.CharField(max_length=40)),
+ ('email_address', models.EmailField(max_length=254, null=True, blank=True)),
+ ('organization', models.CharField(max_length=255, null=True, blank=True)),
+ ('telephone', rpki.gui.gui_rpki_cache.models.TelephoneField(max_length=255, null=True, blank=True)),
+ ('issuer', models.ForeignKey(related_name='ghostbusters', to='gui_rpki_cache.Cert')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='ROA',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('uri', models.TextField()),
+ ('sha256', models.SlugField(unique=True, max_length=64)),
+ ('not_before', models.DateTimeField()),
+ ('not_after', models.DateTimeField()),
+ ('asid', models.PositiveIntegerField()),
+ ('issuer', models.ForeignKey(related_name='roas', to='gui_rpki_cache.Cert')),
+ ],
+ options={
+ 'ordering': ('asid',),
+ },
+ ),
+ migrations.CreateModel(
+ name='ROAPrefixV4',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ('max_length', models.PositiveSmallIntegerField()),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ },
+ ),
+ migrations.CreateModel(
+ name='ROAPrefixV6',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('prefix_min', rpki.gui.models.IPAddressField(db_index=True)),
+ ('prefix_max', rpki.gui.models.IPAddressField(db_index=True)),
+ ('max_length', models.PositiveSmallIntegerField()),
+ ],
+ options={
+ 'ordering': ('prefix_min',),
+ },
+ ),
+ migrations.AddField(
+ model_name='roa',
+ name='prefixes',
+ field=models.ManyToManyField(related_name='roas', to='gui_rpki_cache.ROAPrefixV4'),
+ ),
+ migrations.AddField(
+ model_name='roa',
+ name='prefixes_v6',
+ field=models.ManyToManyField(related_name='roas', to='gui_rpki_cache.ROAPrefixV6'),
+ ),
+ ]
diff --git a/rpki/gui/gui_rpki_cache/migrations/0002_auto_20160411_2311.py b/rpki/gui/gui_rpki_cache/migrations/0002_auto_20160411_2311.py
new file mode 100644
index 00000000..e9ceaac0
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/migrations/0002_auto_20160411_2311.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('gui_rpki_cache', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='cert',
+ name='sha256',
+ ),
+ migrations.RemoveField(
+ model_name='ghostbuster',
+ name='sha256',
+ ),
+ migrations.RemoveField(
+ model_name='roa',
+ name='sha256',
+ ),
+ migrations.AlterField(
+ model_name='cert',
+ name='issuer',
+ field=models.ForeignKey(to='gui_rpki_cache.Cert', null=True),
+ ),
+ migrations.AlterField(
+ model_name='ghostbuster',
+ name='issuer',
+ field=models.ForeignKey(to='gui_rpki_cache.Cert', null=True),
+ ),
+ migrations.AlterField(
+ model_name='roa',
+ name='issuer',
+ field=models.ForeignKey(to='gui_rpki_cache.Cert', null=True),
+ ),
+ ]
diff --git a/rpki/gui/gui_rpki_cache/migrations/0003_auto_20160420_2146.py b/rpki/gui/gui_rpki_cache/migrations/0003_auto_20160420_2146.py
new file mode 100644
index 00000000..e43ab1de
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/migrations/0003_auto_20160420_2146.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('gui_rpki_cache', '0002_auto_20160411_2311'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='ghostbuster',
+ name='issuer',
+ field=models.ForeignKey(related_name='ghostbusters', to='gui_rpki_cache.Cert', null=True),
+ ),
+ migrations.AlterField(
+ model_name='roa',
+ name='issuer',
+ field=models.ForeignKey(related_name='roas', to='gui_rpki_cache.Cert', null=True),
+ ),
+ ]
diff --git a/rpki/gui/gui_rpki_cache/migrations/__init__.py b/rpki/gui/gui_rpki_cache/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/migrations/__init__.py
diff --git a/rpki/gui/cacheview/models.py b/rpki/gui/gui_rpki_cache/models.py
index c3ee8421..dd0739c0 100644
--- a/rpki/gui/cacheview/models.py
+++ b/rpki/gui/gui_rpki_cache/models.py
@@ -1,5 +1,5 @@
# Copyright (C) 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2016 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
@@ -13,16 +13,13 @@
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
-__version__ = '$Id$'
-
-from datetime import datetime
-import time
+__version__ = '$Id: $'
from django.db import models
-from django.core.urlresolvers import reverse
import rpki.resource_set
import rpki.gui.models
+import rpki.rcynicdb.models
class TelephoneField(models.CharField):
@@ -31,56 +28,13 @@ class TelephoneField(models.CharField):
models.CharField.__init__(self, *args, **kwargs)
-class AddressRange(rpki.gui.models.PrefixV4):
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.cacheview.views.addressrange_detail', [str(self.pk)])
-
-
-class AddressRangeV6(rpki.gui.models.PrefixV6):
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.cacheview.views.addressrange_detail_v6',
- [str(self.pk)])
-
-
-class ASRange(rpki.gui.models.ASN):
- @models.permalink
- def get_absolute_url(self):
- return ('rpki.gui.cacheview.views.asrange_detail', [str(self.pk)])
-
-kinds = list(enumerate(('good', 'warn', 'bad')))
-kinds_dict = dict((v, k) for k, v in kinds)
-
+class AddressRange(rpki.gui.models.PrefixV4): pass
-class ValidationLabel(models.Model):
- """
- Represents a specific error condition defined in the rcynic XML
- output file.
- """
- label = models.CharField(max_length=79, db_index=True, unique=True)
- status = models.CharField(max_length=255)
- kind = models.PositiveSmallIntegerField(choices=kinds)
-
- def __unicode__(self):
- return self.label
-
-
-class RepositoryObject(models.Model):
- """
- Represents a globally unique RPKI repository object, specified by its URI.
- """
- uri = models.URLField(unique=True, db_index=True)
-generations = list(enumerate(('current', 'backup')))
-generations_dict = dict((val, key) for (key, val) in generations)
+class AddressRangeV6(rpki.gui.models.PrefixV6): pass
-class ValidationStatus(models.Model):
- timestamp = models.DateTimeField()
- generation = models.PositiveSmallIntegerField(choices=generations, null=True)
- status = models.ForeignKey(ValidationLabel)
- repo = models.ForeignKey(RepositoryObject, related_name='statuses')
+class ASRange(rpki.gui.models.ASN): pass
class SignedObject(models.Model):
@@ -89,58 +43,47 @@ class SignedObject(models.Model):
The signing certificate is ommitted here in order to give a proper
value for the 'related_name' attribute.
"""
- repo = models.ForeignKey(RepositoryObject, related_name='cert', unique=True)
-
- # on-disk file modification time
- mtime = models.PositiveIntegerField(default=0)
- # SubjectName
- name = models.CharField(max_length=255)
+ class Meta:
+ abstract = True
- # value from the SKI extension
- keyid = models.CharField(max_length=60, db_index=True)
+ # Duplicate of rpki.rcynicdb.models.RPKIObject
+ uri = models.TextField()
# validity period from EE cert which signed object
not_before = models.DateTimeField()
not_after = models.DateTimeField()
- def mtime_as_datetime(self):
- """
- convert the local timestamp to UTC and convert to a datetime object
- """
- return datetime.utcfromtimestamp(self.mtime + time.timezone)
-
- def status_id(self):
- """
- Returns a HTML class selector for the current object based on its validation status.
- The selector is chosen based on the current generation only. If there is any bad status,
- return bad, else if there are any warn status, return warn, else return good.
- """
- for x in reversed(kinds):
- if self.repo.statuses.filter(generation=generations_dict['current'], status__kind=x[0]):
- return x[1]
- return None # should not happen
-
def __unicode__(self):
- return u'%s' % self.name
+ return u'%s' % self.uri
+
+ def __repr__(self):
+ return u'<%s name=%s uri=%s>' % (self.__class__.__name__, self.uri)
class Cert(SignedObject):
"""
- Object representing a resource certificate.
+ Object representing a resource CA certificate.
"""
+ # Duplicate of rpki.rcynicdb.models.RPKIObject
+ ski = models.SlugField(max_length=40) # hex SHA-1
+
addresses = models.ManyToManyField(AddressRange, related_name='certs')
addresses_v6 = models.ManyToManyField(AddressRangeV6, related_name='certs')
asns = models.ManyToManyField(ASRange, related_name='certs')
- issuer = models.ForeignKey('self', related_name='children', null=True)
- sia = models.CharField(max_length=255)
- def get_absolute_url(self):
- return reverse('cert-detail', args=[str(self.pk)])
+ issuer = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
+
+ def __repr__(self):
+ return u'<Cert uri=%s ski=%s not_before=%s not_after=%s>' % (self.uri, self.ski, self.not_before, self.not_after)
+
+ def __unicode__(self):
+ return u'RPKI CA Cert %s' % (self.uri,)
def get_cert_chain(self):
"""Return a list containing the complete certificate chain for this
certificate."""
+
cert = self
x = [cert]
while cert != cert.issuer:
@@ -180,6 +123,7 @@ class ROAPrefixV4(ROAPrefix, rpki.gui.models.PrefixV4):
@property
def routes(self):
"""return all routes covered by this roa prefix"""
+
return RouteOrigin.objects.filter(prefix_min__gte=self.prefix_min,
prefix_max__lte=self.prefix_max)
@@ -201,10 +145,7 @@ class ROA(SignedObject):
asid = models.PositiveIntegerField()
prefixes = models.ManyToManyField(ROAPrefixV4, related_name='roas')
prefixes_v6 = models.ManyToManyField(ROAPrefixV6, related_name='roas')
- issuer = models.ForeignKey('Cert', related_name='roas')
-
- def get_absolute_url(self):
- return reverse('roa-detail', args=[str(self.pk)])
+ issuer = models.ForeignKey(Cert, on_delete=models.CASCADE, null=True, related_name='roas')
class Meta:
ordering = ('asid',)
@@ -218,11 +159,7 @@ class Ghostbuster(SignedObject):
email_address = models.EmailField(blank=True, null=True)
organization = models.CharField(blank=True, null=True, max_length=255)
telephone = TelephoneField(blank=True, null=True)
- issuer = models.ForeignKey('Cert', related_name='ghostbusters')
-
- def get_absolute_url(self):
- # note that ghostbuster-detail is different from gbr-detail! sigh
- return reverse('ghostbuster-detail', args=[str(self.pk)])
+ issuer = models.ForeignKey(Cert, on_delete=models.CASCADE, null=True, related_name='ghostbusters')
def __unicode__(self):
if self.full_name:
diff --git a/rpki/gui/gui_rpki_cache/util.py b/rpki/gui/gui_rpki_cache/util.py
new file mode 100644
index 00000000..0bc4fa5d
--- /dev/null
+++ b/rpki/gui/gui_rpki_cache/util.py
@@ -0,0 +1,308 @@
+# Copyright (C) 2011 SPARTA, Inc. dba Cobham
+# Copyright (C) 2012, 2013, 2016 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
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND SPARTA DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL SPARTA BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+__version__ = '$Id: util.py 6335 2016-03-29 03:09:13Z sra $'
+
+import logging
+import time
+import vobject
+from socket import getfqdn
+from cStringIO import StringIO
+
+if __name__ == '__main__':
+ import os
+ logging.basicConfig(level=logging.DEBUG)
+ os.environ.update(DJANGO_SETTINGS_MODULE='rpki.django_settings.gui')
+ import django
+ django.setup()
+
+import os.path
+
+logger = logging.getLogger(__name__)
+
+from django.db import transaction
+import django.db.models
+
+import rpki
+import rpki.resource_set
+import rpki.left_right
+import rpki.gui.app.timestamp
+from rpki.gui.app.models import Conf, Alert
+from rpki.gui.gui_rpki_cache import models
+from rpki.irdb.zookeeper import Zookeeper
+
+from lxml.etree import Element, SubElement
+
+
+def process_certificate(auth, obj):
+ cert = models.Cert.objects.filter(ski=obj.ski).first()
+ if cert:
+ logger.debug('cache hit for CA cert uri=%s ski=%s' % (cert.uri, cert.ski))
+ return cert # cache hit
+
+ logger.debug('parsing cert at %s' % (obj.uri,))
+
+ """Process Resource CA Certificates"""
+ x509 = rpki.POW.X509.derRead(obj.der)
+
+ # ensure this is a resource CA Certificate (ignore Router certs)
+ bc = x509.getBasicConstraints()
+ is_ca = bc is not None and bc[0]
+ if not is_ca:
+ return
+
+ # locate the parent certificate
+ if obj.aki and obj.aki != obj.ski:
+ try:
+ issuer = models.Cert.objects.get(ski=obj.aki)
+ except models.Cert.DoesNotExist:
+ # process parent cert first
+ issuer = process_certificate(auth, rpki.rcynicdb.models.RPKIObject.objects.get(ski=obj.aki, authenticated=auth))
+ else:
+ issuer = None # root
+
+ asns, v4, v6 = x509.getRFC3779()
+
+ cert = models.Cert.objects.create(
+ uri=obj.uri,
+ ski=obj.ski,
+ not_before=x509.getNotBefore(),
+ not_after=x509.getNotAfter(),
+ issuer=issuer
+ )
+
+ if issuer is None:
+ cert.issuer = cert # self-signed
+ cert.save()
+
+ if asns == 'inherit':
+ cert.asns.add(issuer.asns.all())
+ elif asns:
+ for asmin, asmax in asns:
+ asr, _ = models.ASRange.objects.get_or_create(min=asmin, max=asmax)
+ cert.asns.add(asr)
+
+ if v4 == 'inherit':
+ cert.addresses.add(issuer.addresses.all())
+ elif v4:
+ for v4min, v4max in v4:
+ pfx, _ = models.AddressRange.objects.get_or_create(prefix_min=v4min, prefix_max=v4max)
+ cert.addresses.add(pfx)
+
+ if v6 == 'inherit':
+ cert.addresses_v6.add(issuer.addresses_v6.all())
+ elif v6:
+ for v6min, v6max in v6:
+ pfx, _ = models.AddressRangeV6.objects.get_or_create(prefix_min=v6min, prefix_max=v6max)
+ cert.addresses_v6.add(pfx)
+
+ return cert
+
+def process_roa(auth, obj):
+ logger.debug('parsing roa at %s' % (obj.uri,))
+
+ r = rpki.POW.ROA.derRead(obj.der)
+ r.verify() # required in order to extract asID
+ ee = r.certs()[0] # rpki.POW.X509
+ aki = ee.getAKI().encode('hex')
+
+ logger.debug('looking for ca cert with ski=%s' % (aki,))
+
+ # Locate the Resource CA cert that issued the EE that signed this ROA
+ issuer = models.Cert.objects.get(ski=aki)
+
+ roa = models.ROA.objects.create(
+ uri=obj.uri,
+ asid=r.getASID(),
+ not_before=ee.getNotBefore(),
+ not_after=ee.getNotAfter(),
+ issuer=issuer)
+
+ prefixes = r.getPrefixes()
+ if prefixes[0]: # v4
+ for p in prefixes[0]:
+ v = rpki.resource_set.roa_prefix_ipv4(*p)
+ roapfx, _ = models.ROAPrefixV4.objects.get_or_create(prefix_min=v.min(), prefix_max=v.max(), max_length=v.max_prefixlen)
+ roa.prefixes.add(roapfx)
+ if prefixes[1]: # v6
+ for p in prefixes[1]:
+ v = rpki.resource_set.roa_prefix_ipv6(*p)
+ roapfx, _ = models.ROAPrefixV6.objects.get_or_create(prefix_min=v.min(), prefix_max=v.max(), max_length=v.max_prefixlen)
+ roa.prefixes_v6.add(roapfx)
+
+ return roa
+
+def process_ghostbuster(auth, obj):
+ logger.debug('parsing ghostbuster at %s' % (obj.uri,))
+ g = rpki.POW.CMS.derRead(obj.der)
+ ee = g.certs()[0] # rpki.POW.X509
+ aki = ee.getAKI().encode('hex')
+ vcard = vobject.readOne(g.verify())
+
+ # Locate the Resource CA cert that issued the EE that signed this ROA
+ issuer = models.Cert.objects.get(ski=aki)
+
+ gbr = models.Ghostbuster.objects.create(
+ uri=obj.uri,
+ issuer=issuer,
+ not_before=ee.getNotBefore(),
+ not_after=ee.getNotAfter(),
+ full_name = vcard.fn.value if hasattr(vcard, 'fn') else None,
+ email_address = vcard.email.value if hasattr(vcard, 'email') else None,
+ telephone = vcard.tel.value if hasattr(vcard, 'tel') else None,
+ organization = vcard.org.value[0] if hasattr(vcard, 'org') else None
+ )
+
+ return gbr
+
+@transaction.atomic
+def process_cache():
+ logger.info('processing rpki cache')
+
+ # foreign key constraints should cause all other objects to be removed
+ models.Cert.objects.all().delete()
+
+ # certs must be processed first in order to build proper foreign keys for roa/gbr
+ dispatch = {
+ '.cer': process_certificate,
+ '.gbr': process_ghostbuster,
+ '.roa': process_roa
+ }
+
+ auth = rpki.rcynicdb.models.Authenticated.objects.order_by('started').first()
+
+ # Resource CA Certs are processed first in order to attach ROAs and Ghostbusters
+ for suffix in ('.cer', '.roa', '.gbr'):
+ cb = dispatch[suffix]
+
+ for rpkiobj in auth.rpkiobject_set.filter(uri__endswith=suffix):
+ cb(auth, rpkiobj)
+
+ # Garbage collection - remove M2M relations for certs/ROAs which no longer exist
+ models.ASRange.objects.annotate(num_certs=django.db.models.Count('certs')).filter(num_certs=0).delete()
+ models.AddressRange.objects.annotate(num_certs=django.db.models.Count('certs')).filter(num_certs=0).delete()
+ models.AddressRangeV6.objects.annotate(num_certs=django.db.models.Count('certs')).filter(num_certs=0).delete()
+
+ models.ROAPrefixV4.objects.annotate(num_roas=django.db.models.Count('roas')).filter(num_roas=0).delete()
+ models.ROAPrefixV6.objects.annotate(num_roas=django.db.models.Count('roas')).filter(num_roas=0).delete()
+
+
+# dict mapping resource handle to list of published objects, use for notifying objects which have become invalid
+uris = {}
+model_map = { '.cer': models.Cert, '.roa': models.ROA, '.gbr': models.Ghostbuster }
+
+def fetch_published_objects():
+ """Query rpkid for all objects published by local users, and look up the
+ current validation status of each object. The validation status is used
+ later to send alerts for objects which have transitioned to invalid.
+ """
+ logger.info('querying for published objects')
+
+ handles = [conf.handle for conf in Conf.objects.all()]
+ q_msg = Element(rpki.left_right.tag_msg, nsmap = rpki.left_right.nsmap,
+ type = "query", version = rpki.left_right.version)
+ for h in handles:
+ SubElement(q_msg, rpki.left_right.tag_list_published_objects, tenant_handle=h, tag=h)
+ try:
+ z = Zookeeper()
+ r_msg = z.call_rpkid(q_msg)
+ except Exception as err:
+ logger.error('Unable to connect to rpkid to fetch list of published objects')
+ logger.exception(err)
+ # Should be safe to continue processing the rcynic cache, we just don't do any notifications
+ return
+
+ for r_pdu in r_msg:
+ if r_pdu.tag == rpki.left_right.tag_list_published_objects:
+ # Look up the object in the rcynic cache
+ uri = r_pdu.get('uri')
+ ext = os.path.splitext(uri)[1]
+ if ext in model_map:
+ model = model_map[ext]
+ handle = r_pdu.get('tenant_handle')
+
+ if model.objects.filter(uri=uri).exists():
+ v = uris.setdefault(handle, [])
+ v.append(uri)
+ logger.debug('adding %s', uri)
+ #else:
+ # this object is not in the cache. it was either published
+ # recently, or disappared previously. if it disappeared
+ # previously, it has already been alerted. in either case, we
+ # omit the uri from the list since we are interested only in
+ # objects which were valid and are no longer valid
+ else:
+ logger.debug('skipping object ext=%s uri=%s' % (ext, uri))
+
+ elif r_pdu.tag == rpki.left_right.tag_report_error:
+ logging.error('rpkid reported an error: %s', r_pdu.get("error_code"))
+
+
+def notify_invalid():
+ """Send email alerts to the addresses registered in ghostbuster records for
+ any invalid objects that were published by users of this system.
+ """
+
+ logger.info('sending notifications for invalid objects')
+
+ for handle, published_objects in uris.iteritems():
+ missing = []
+ for u in published_objects:
+ ext = os.path.splitext(u)[1]
+ model = model_map[ext]
+ if not model.objects.filter(uri=u).exists():
+ missing.append(u)
+
+ if missing:
+ conf = Conf.objects.get(handle)
+
+ msg = StringIO()
+ msg.write('This is an alert about problems with objects published by '
+ 'the resource handle %s.\n\n' % handle)
+
+ msg.write('The following objects were previously valid, but are '
+ 'now invalid:\n')
+
+ for u in missing:
+ msg.write('\n')
+ msg.write(u)
+ msg.write('\n')
+
+ msg.write("""--
+You are receiving this email because your address is published in a Ghostbuster
+record, or is the default email address for this resource holder account on
+%s.""" % getfqdn())
+
+ from_email = 'root@' + getfqdn()
+ subj = 'invalid RPKI object alert for resource handle %s' % conf.handle
+ conf.send_alert(subj, msg.getvalue(), from_email, severity=Alert.ERROR)
+
+
+def update_cache():
+ """Cache information from the current rcynicdb for display by the gui"""
+
+ start = time.time()
+ fetch_published_objects()
+ process_cache()
+ notify_invalid()
+
+ rpki.gui.app.timestamp.update('rcynic_import')
+
+ stop = time.time()
+ logger.info('elapsed time %d seconds.', (stop - start))
+
+
+if __name__ == '__main__':
+ process_cache()
diff --git a/rpki/gui/models.py b/rpki/gui/models.py
index 184383c0..4d56c18e 100644
--- a/rpki/gui/models.py
+++ b/rpki/gui/models.py
@@ -19,57 +19,72 @@ Common classes for reuse in apps.
__version__ = '$Id$'
from django.db import models
+from django.core.exceptions import ValidationError
import rpki.resource_set
import rpki.POW
-from south.modelsinspector import add_introspection_rules
-class IPv6AddressField(models.Field):
- "Field large enough to hold a 128-bit unsigned integer."
-
- __metaclass__ = models.SubfieldBase
-
- def db_type(self, connection):
- return 'binary(16)'
-
- def to_python(self, value):
- if isinstance(value, rpki.POW.IPAddress):
+class IPAddressField(models.CharField):
+ """
+ Field class for rpki.POW.IPAddress, stored as zero-padded
+ hexadecimal so lexicographic order is identical to numeric order.
+ """
+
+ # Django's CharField type doesn't distinguish between the length
+ # of the human readable form and the length of the storage form,
+ # so we have to leave room for IPv6 punctuation even though we
+ # only store hexadecimal digits and thus will never use the full
+ # width of the database field. Price we pay for portability.
+ #
+ # Documentation on the distinction between the various conversion
+ # methods is fairly opaque, to put it politely, and we have to
+ # handle database engines which sometimes return buffers or other
+ # classes instead of strings, so the conversions are a bit
+ # finicky. If this goes haywire, your best bet is probably to
+ # litter the code with logging.debug() calls and debug by printf.
+
+ def __init__(self, *args, **kwargs):
+ kwargs["max_length"] = 40
+ super(IPAddressField, self).__init__(*args, **kwargs)
+
+ def deconstruct(self):
+ name, path, args, kwargs = super(IPAddressField, self).deconstruct()
+ del kwargs["max_length"]
+ return name, path, args, kwargs
+
+ @staticmethod
+ def _value_to_ipaddress(value):
+ if value is None or isinstance(value, rpki.POW.IPAddress):
return value
- return rpki.POW.IPAddress.fromBytes(value)
-
- def get_db_prep_value(self, value, connection, prepared):
- """
- Note that we add a custom conversion to encode long values as hex
- strings in SQL statements. See settings.get_conv() for details.
-
- """
- return value.toBytes()
+ value = str(value)
+ if ":" in value or "." in value:
+ return rpki.POW.IPAddress(value)
+ else:
+ return rpki.POW.IPAddress.fromBytes(value.decode("hex"))
-
-class IPv4AddressField(models.Field):
- "Wrapper around rpki.POW.IPAddress."
-
- __metaclass__ = models.SubfieldBase
-
- def db_type(self, connection):
- return 'int UNSIGNED'
+ def from_db_value(self, value, expression, connection, context):
+ # Can't use super() here, see Django documentation.
+ return self._value_to_ipaddress(value)
def to_python(self, value):
+ return self._value_to_ipaddress(
+ super(IPAddressField, self).to_python(value))
+
+ @staticmethod
+ def _hex_from_ipaddress(value):
if isinstance(value, rpki.POW.IPAddress):
+ return value.toBytes().encode("hex")
+ else:
return value
- return rpki.POW.IPAddress(value, version=4)
- def get_db_prep_value(self, value, connection, prepared):
- return long(value)
+ def get_prep_value(self, value):
+ return super(IPAddressField, self).get_prep_value(
+ self._hex_from_ipaddress(value))
-add_introspection_rules(
- [
- ([IPv4AddressField, IPv6AddressField], [], {})
- ],
- [r'^rpki\.gui\.models\.IPv4AddressField',
- r'^rpki\.gui\.models\.IPv6AddressField']
-)
+ def get_db_prep_value(self, value, connection, prepared = False):
+ return self._hex_from_ipaddress(
+ super(IPAddressField, self).get_db_prep_value(value, connection, prepared))
class Prefix(models.Model):
@@ -82,6 +97,7 @@ class Prefix(models.Model):
"""
Returns the prefix as a rpki.resource_set.resource_range_ip object.
"""
+
return self.range_cls(self.prefix_min, self.prefix_max)
@property
@@ -96,6 +112,7 @@ class Prefix(models.Model):
def __unicode__(self):
"""This method may be overridden by subclasses. The default
implementation calls get_prefix_display(). """
+
return self.get_prefix_display()
class Meta:
@@ -110,8 +127,8 @@ class PrefixV4(Prefix):
range_cls = rpki.resource_set.resource_range_ipv4
- prefix_min = IPv4AddressField(db_index=True, null=False)
- prefix_max = IPv4AddressField(db_index=True, null=False)
+ prefix_min = IPAddressField(db_index=True, null=False)
+ prefix_max = IPAddressField(db_index=True, null=False)
class Meta(Prefix.Meta):
abstract = True
@@ -122,20 +139,25 @@ class PrefixV6(Prefix):
range_cls = rpki.resource_set.resource_range_ipv6
- prefix_min = IPv6AddressField(db_index=True, null=False)
- prefix_max = IPv6AddressField(db_index=True, null=False)
+ prefix_min = IPAddressField(db_index=True, null=False)
+ prefix_max = IPAddressField(db_index=True, null=False)
class Meta(Prefix.Meta):
abstract = True
+def validate_asn(value):
+ if value < 0 or value > 0xFFFFFFFFL:
+ raise ValidationError('%s is not valid autonomous sequence number' % value)
+
+
class ASN(models.Model):
"""Represents a range of ASNs.
This model is abstract, and is intended to be reused by applications."""
- min = models.PositiveIntegerField(null=False)
- max = models.PositiveIntegerField(null=False)
+ min = models.BigIntegerField(null=False, validators=[validate_asn])
+ max = models.BigIntegerField(null=False, validators=[validate_asn])
class Meta:
abstract = True
diff --git a/rpki/gui/routeview/api.py b/rpki/gui/routeview/api.py
index cf699c9a..b4ff297a 100644
--- a/rpki/gui/routeview/api.py
+++ b/rpki/gui/routeview/api.py
@@ -29,8 +29,8 @@ def route_list(request):
By default, only returns up to 10 matching routes, but the client may
request a different limit with the 'count=' query string parameter.
-
"""
+
hard_limit = 100
if request.method == 'GET' and 'prefix__in' in request.GET:
diff --git a/rpki/gui/routeview/models.py b/rpki/gui/routeview/models.py
index 052860c4..35039136 100644
--- a/rpki/gui/routeview/models.py
+++ b/rpki/gui/routeview/models.py
@@ -1,5 +1,5 @@
# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2016 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
@@ -31,7 +31,7 @@ class RouteOrigin(rpki.gui.models.PrefixV4):
@property
def roas(self):
"Return a queryset of ROAs which cover this route."
- return rpki.gui.cacheview.models.ROA.objects.filter(
+ return rpki.gui.gui_rpki_cache.models.ROA.objects.filter(
prefixes__prefix_min__lte=self.prefix_min,
prefixes__prefix_max__gte=self.prefix_max
)
@@ -39,7 +39,7 @@ class RouteOrigin(rpki.gui.models.PrefixV4):
@property
def roa_prefixes(self):
"Return a queryset of ROA prefixes which cover this route."
- return rpki.gui.cacheview.models.ROAPrefixV4.objects.filter(
+ return rpki.gui.gui_rpki_cache.models.ROAPrefixV4.objects.filter(
prefix_min__lte=self.prefix_min,
prefix_max__gte=self.prefix_max
)
@@ -78,4 +78,4 @@ class RouteOriginV6(rpki.gui.models.PrefixV6):
# this goes at the end of the file to avoid problems with circular imports
-import rpki.gui.cacheview.models
+import rpki.gui.gui_rpki_cache.models
diff --git a/rpki/gui/routeview/util.py b/rpki/gui/routeview/util.py
index 1340e9fa..14ac3cf9 100644
--- a/rpki/gui/routeview/util.py
+++ b/rpki/gui/routeview/util.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2012, 2013 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2013, 2016 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,7 +16,6 @@ __version__ = '$Id$'
__all__ = ('import_routeviews_dump')
import itertools
-import _mysql_exceptions
import os.path
import subprocess
import time
@@ -25,12 +24,13 @@ import urlparse
import bz2
from urllib import urlretrieve, unquote
-from django.db import transaction, connection
+from django.db import transaction
from django.conf import settings
from rpki.resource_set import resource_range_ipv4, resource_range_ipv6
from rpki.exceptions import BadIPResource
import rpki.gui.app.timestamp
+from rpki.gui.routeview.models import RouteOrigin
# globals
logger = logging.getLogger(__name__)
@@ -43,28 +43,17 @@ class ParseError(Exception): pass
class RouteDumpParser(object):
"""Base class for parsing various route dump formats."""
- table = 'routeview_routeorigin'
- sql = "INSERT INTO %s_new SET asn=%%s, prefix_min=%%s, prefix_max=%%s" % table
range_class = resource_range_ipv4
def __init__(self, path, *args, **kwargs):
+ transaction.set_autocommit(False)
+
self.path = path
- self.cursor = connection.cursor()
self.last_prefix = None
self.asns = set()
def parse(self):
- try:
- logger.info('Dropping existing staging table...')
- self.cursor.execute('DROP TABLE IF EXISTS %s_new' % self.table)
- except _mysql_exceptions.Warning:
- pass
-
- logger.info('Creating staging table...')
- self.cursor.execute('CREATE TABLE %(table)s_new LIKE %(table)s' % {'table': self.table})
-
- logger.info('Disabling autocommit...')
- self.cursor.execute('SET autocommit=0')
+ RouteOrigin.objects.all().delete()
logger.info('Adding rows to table...')
for line in self.input:
@@ -88,25 +77,13 @@ class RouteDumpParser(object):
self.ins_routes() # process data from last line
- logger.info('Committing...')
- self.cursor.execute('COMMIT')
-
- try:
- logger.info('Dropping old table...')
- self.cursor.execute('DROP TABLE IF EXISTS %s_old' % self.table)
- except _mysql_exceptions.Warning:
- pass
-
- logger.info('Swapping staging table with live table...')
- self.cursor.execute('RENAME TABLE %(table)s TO %(table)s_old, %(table)s_new TO %(table)s' % {'table': self.table})
-
self.cleanup() # allow cleanup function to throw prior to COMMIT
- transaction.commit_unless_managed()
-
logger.info('Updating timestamp metadata...')
rpki.gui.app.timestamp.update('bgp_v4_import')
+ transaction.commit() # not sure if requried, or if transaction.commit() will do it
+
def parse_line(self, row):
"Parse one line of input. Return a (prefix, origin_as) tuple."
return None
@@ -119,9 +96,8 @@ class RouteDumpParser(object):
if self.last_prefix is not None:
try:
rng = self.range_class.parse_str(self.last_prefix)
- rmin = long(rng.min)
- rmax = long(rng.max)
- self.cursor.executemany(self.sql, [(asn, rmin, rmax) for asn in self.asns])
+ for asn in self.asns:
+ RouteOrigin.objects.create(asn=asn, prefix_min=rng.min, prefix_max=rng.max)
except BadIPResource:
logger.warning('skipping bad prefix: ' + self.last_prefix)
self.asns = set() # reset
@@ -151,6 +127,10 @@ class TextDumpParser(RouteDumpParser):
except ValueError:
raise ParseError('bad AS value')
+ # FIXME Django doesn't have a field for positive integers up to 2^32-1
+ if origin_as < 0 or origin_as > 2147483647:
+ raise ParseError('AS value out of supported database range')
+
prefix = cols[1]
# validate the prefix since the "sh ip bgp" output is sometimes
@@ -215,8 +195,8 @@ def import_routeviews_dump(filename=DEFAULT_URL, filetype='text'):
filename [optional]: the full path to the downloaded file to parse
filetype [optional]: 'text' or 'mrt'
-
"""
+
start_time = time.time()
tmpname = None
@@ -229,10 +209,8 @@ def import_routeviews_dump(filename=DEFAULT_URL, filetype='text'):
logger.info("Downloading %s to %s", filename, tmpname)
if os.path.exists(tmpname):
- os.remove(tmpname)
- # filename is replaced with a local filename containing cached copy of
- # URL
- filename, headers = urlretrieve(filename, tmpname)
+ os.remove(tmpname)
+ filename, headers = urlretrieve(filename, tmpname)
try:
dispatch = {'text': TextDumpParser, 'mrt': MrtDumpParser}
diff --git a/rpki/gui/script_util.py b/rpki/gui/script_util.py
index c8248527..289dbbb7 100644
--- a/rpki/gui/script_util.py
+++ b/rpki/gui/script_util.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2013 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2013, 2016 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,13 +16,6 @@
This module contains utility functions for use in standalone scripts.
"""
-import django
-
-from django.conf import settings
-
-from rpki import config
-from rpki import autoconf
-
__version__ = '$Id$'
@@ -30,29 +23,11 @@ def setup():
"""
Configure Django enough to use the ORM.
"""
- cfg = config.parser(section='web_portal')
- # INSTALLED_APPS doesn't seem necessary so long as you are only accessing
- # existing tables.
- #
- # Setting charset to latin1 is a disgusting kludge, but without
- # this MySQL 5.6 (and, proably, later) gets tetchy about ASN.1 DER
- # stored in BLOB columns not being well-formed UTF8 (sic). If you
- # know of a better solution, tell us.
- settings.configure(
- DATABASES={
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': cfg.get('sql-database'),
- 'USER': cfg.get('sql-username'),
- 'PASSWORD': cfg.get('sql-password'),
- 'OPTIONS': {
- 'charset': 'latin1',
- }
- }
- },
- MIDDLEWARE_CLASSES = (),
- DOWNLOAD_DIRECTORY = cfg.get('download-directory', '/var/tmp'),
- )
- if django.VERSION >= (1, 7):
- from django.apps import apps
- apps.populate(settings.INSTALLED_APPS)
+
+ import os
+
+ os.environ.update(DJANGO_SETTINGS_MODULE = "rpki.django_settings.gui")
+
+ # Initialize Django.
+ import django
+ django.setup()
diff --git a/rpki/gui/urls.py b/rpki/gui/urls.py
index 955092f5..ac1d2916 100644
--- a/rpki/gui/urls.py
+++ b/rpki/gui/urls.py
@@ -1,5 +1,5 @@
# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-# Copyright (C) 2012, 2013 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2012, 2013, 2016 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
@@ -28,7 +28,6 @@ urlpatterns = patterns(
#(r'^admin/', include(admin.site.urls)),
(r'^api/', include('rpki.gui.api.urls')),
- (r'^cacheview/', include('rpki.gui.cacheview.urls')),
(r'^rpki/', include('rpki.gui.app.urls')),
(r'^accounts/login/$', 'rpki.gui.views.login'),