aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rpkid/rpki/gui/app/forms.py85
-rw-r--r--rpkid/rpki/gui/app/glue.py357
-rwxr-xr-xrpkid/rpki/gui/app/range_list.py16
-rw-r--r--rpkid/rpki/gui/app/templates/app/bootstrap_form.html2
-rw-r--r--rpkid/rpki/gui/app/templates/app/child_import_form.html (renamed from rpkid/rpki/gui/app/templates/app/import_child_form.html)0
-rw-r--r--rpkid/rpki/gui/app/templates/app/client_import_form.html (renamed from rpkid/rpki/gui/app/templates/app/import_pubclient_form.html)0
-rw-r--r--rpkid/rpki/gui/app/templates/app/parent_import_form.html (renamed from rpkid/rpki/gui/app/templates/app/import_parent_form.html)0
-rw-r--r--rpkid/rpki/gui/app/templates/app/respository_import_form.html (renamed from rpkid/rpki/gui/app/templates/app/import_repository_form.html)0
-rw-r--r--rpkid/rpki/gui/app/views.py495
9 files changed, 354 insertions, 601 deletions
diff --git a/rpkid/rpki/gui/app/forms.py b/rpkid/rpki/gui/app/forms.py
index 69d4b335..0755b6a0 100644
--- a/rpkid/rpki/gui/app/forms.py
+++ b/rpkid/rpki/gui/app/forms.py
@@ -1,20 +1,19 @@
-# $Id$
-"""
-Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-Copyright (C) 2012 SPARTA, Inc. a Parsons Company
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-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.
-"""
+# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# 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
@@ -46,13 +45,6 @@ class AddConfForm(forms.Form):
help_text='email address for the operator of your pubd instance')
-class ImportForm(forms.Form):
- '''Form used for uploading parent/child identity xml files'''
- handle = forms.CharField(max_length=30,
- help_text='your name for this entity')
- xml = forms.FileField(help_text='xml filename')
-
-
class GhostbusterRequestForm(forms.ModelForm):
"""
Generate a ModelForm with the subset of parents for the current
@@ -95,40 +87,27 @@ class GhostbusterRequestForm(forms.ModelForm):
return self.cleaned_data
-def ImportChildForm(parent_conf, *args, **kwargs):
- class wrapped(forms.Form):
- handle = forms.CharField(max_length=30, help_text="Child's RPKI handle")
- xml = forms.FileField(help_text="Child's identity.xml file")
-
- def clean_handle(self):
- if parent_conf.children.filter(handle=self.cleaned_data['handle']):
- raise forms.ValidationError, "a child with that handle already exists"
- return self.cleaned_data['handle']
-
- return wrapped(*args, **kwargs)
-
-
-def ImportParentForm(conf, *args, **kwargs):
- class wrapped(forms.Form):
- handle = forms.CharField(max_length=30, help_text="Parent's RPKI handle", required=True)
- xml = forms.FileField(help_text="XML response from parent", required=True,
- widget=forms.FileInput(attrs={'class': 'xlarge'}))
-
- def clean_handle(self):
- if conf.parents.filter(handle=self.cleaned_data['handle']):
- raise forms.ValidationError, "a parent with that handle already exists"
- return self.cleaned_data['handle']
-
- return wrapped(*args, **kwargs)
+class ImportForm(forms.Form):
+ """Form used for uploading parent/child identity xml files."""
+ handle = forms.CharField(required=False,
+ widget=forms.TextInput(attrs={'class': 'xlarge'}),
+ help_text='Optional. Your name for this entity, or blank to accept name in XML')
+ xml = forms.FileField(label='XML file',
+ widget=forms.FileInput(attrs={'class': 'input-file'}))
class ImportRepositoryForm(forms.Form):
- parent_handle = forms.CharField(max_length=30, required=False, help_text='(optional)')
- xml = forms.FileField(help_text='xml file from repository operator')
+ handle = forms.CharField(max_length=30, required=False,
+ label='Parent Handle',
+ help_text='Optional. Must be specified if you use a different name for this parent')
+ xml = forms.FileField(label='XML file',
+ widget=forms.FileInput(attrs={'class': 'input-file'}))
-class ImportPubClientForm(forms.Form):
- xml = forms.FileField(help_text='xml file from publication client')
+class ImportClientForm(forms.Form):
+ """Form used for importing publication client requests."""
+ xml = forms.FileField(label='XML file',
+ widget=forms.FileInput(attrs={'class': 'input-file'}))
def ChildWizardForm(parent, *args, **kwargs):
diff --git a/rpkid/rpki/gui/app/glue.py b/rpkid/rpki/gui/app/glue.py
index 99086ae6..2cc3733f 100644
--- a/rpkid/rpki/gui/app/glue.py
+++ b/rpkid/rpki/gui/app/glue.py
@@ -1,93 +1,47 @@
-# $Id$
-"""
-Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# 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.
-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.
+"""
+This file contains code that interfaces between the django views implementing
+the portal gui and the rpki.* modules.
-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 __future__ import with_statement
-import os, os.path, csv, stat, sys
-from datetime import datetime, timedelta
+__version__ = '$Id$'
-from django.db.models import F
+from datetime import datetime
-import rpki
-import rpki.left_right
-import rpki.publication
+from rpki.resource_set import (resource_set_as, resource_set_ipv4,
+ resource_set_ipv6)
+from rpki.left_right import list_received_resources_elt
from rpki.irdb.zookeeper import Zookeeper
from rpki.gui.app import models, settings
-def confpath(*handle):
- """
- Return the absolute pathname to the configuration directory for the
- given resource handle. If additional arguments are given, they are
- taken to mean files/subdirectories relative to the configuration
- directory.
-
- """
- argv = [ settings.CONFDIR ]
- argv.extend(handle)
- return os.path.join(*argv)
-
-def read_file_from_handle(handle, fname):
- """
- read a filename relative to the directory for the given resource
- handle. returns a tuple of (content, mtime)
-
- """
- with open(confpath(handle, fname), 'r') as fp:
- data = fp.read()
- mtime = os.fstat(fp.fileno())[stat.ST_MTIME]
- return data, mtime
-
-read_identity = lambda h: read_file_from_handle(h, 'entitydb/identity.xml')[0]
-
-def output_asns(path, handle):
- """Write out csv file containing asns delegated to my children."""
- qs = models.Asn.objects.filter(lo=F('hi'), allocated__in=handle.children.all())
- w = rpki.myrpki.csv_writer(path)
- w.writerows([asn.allocated.handle, asn.lo] for asn in qs)
- w.close()
-
-def output_prefixes(path, handle):
- """Write out csv file containing prefixes delegated to my children."""
- qs = models.AddressRange.objects.filter(allocated__in=handle.children.all())
- w = rpki.myrpki.csv_writer(path)
- w.writerows([p.allocated.handle, p.as_resource_range()] for p in qs)
- w.close()
-
-def output_roas(path, handle):
- """Write out csv file containing my roas."""
- qs = models.RoaRequest.objects.filter(roa__in=handle.roas.all())
- w = rpki.myrpki.csv_writer(path)
- w.writerows([req.as_roa_prefix(), req.roa.asn,
- '%s-group-%d' % (handle.handle, req.roa.pk)] for req in qs)
- w.close()
-
-def qualify_path(pfx, fname):
- """Ensure 'path' is an absolute filename."""
- return fname if fname.startswith('/') else os.path.join(pfx, fname)
def ghostbuster_to_vcard(gbr):
"""Convert a GhostbusterRequest object into a vCard object."""
import vobject
vcard = vobject.vCard()
- vcard.add('N').value = vobject.vcard.Name(family=gbr.family_name, given=gbr.given_name)
+ vcard.add('N').value = vobject.vcard.Name(family=gbr.family_name,
+ given=gbr.given_name)
- adr_fields = [ 'box', 'extended', 'street', 'city', 'region', 'code', 'country' ]
+ adr_fields = ['box', 'extended', 'street', 'city', 'region', 'code',
+ 'country']
adr_dict = dict((f, getattr(gbr, f, '')) for f in adr_fields)
if any(adr_dict.itervalues()):
vcard.add('ADR').value = vobject.vcard.Address(**adr_dict)
@@ -96,100 +50,36 @@ def ghostbuster_to_vcard(gbr):
# the ORG type is a sequence of organization unit names, so
# transform the org name into a tuple before stuffing into the
# vCard object
- attrs = [ ('FN', 'full_name', None),
- ('TEL', 'telephone', None),
- ('ORG', 'organization', lambda x: (x,)),
- ('EMAIL', 'email_address', None) ]
+ attrs = [('FN', 'full_name', None),
+ ('TEL', 'telephone', None),
+ ('ORG', 'organization', lambda x: (x,)),
+ ('EMAIL', 'email_address', None)]
for vtype, field, transform in attrs:
v = getattr(gbr, field)
if v:
vcard.add(vtype).value = transform(v) if transform else v
return vcard.serialize()
-def qualify_path(pfx, fname):
- """Ensure 'path' is an absolute filename."""
- return fname if fname.startswith('/') else os.path.join(pfx, fname)
-def configure_resources(log, handle):
+def list_received_resources(log, conf):
"""
- This function should be called when resources for this resource
- holder have changed. It updates IRDB and notifies rpkid to
- immediately process the changes, rather than waiting for the cron
- job to run.
+ Query rpkid for this resource handle's received resources.
- For backwards compatability (and backups), it also writes the csv
- files for use with the myrpki.py command line script.
+ 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.
"""
- path = confpath(handle.handle)
-
- # Read rpki.conf to determine the paths for the csv files.
- if handle.host:
- cfg = rpki.config.parser(os.path.join(path, 'rpki.conf'), section='myrpki')
- else:
- # Use the system rpki.conf for the self-hosted handle.
- cfg = get_system_config()
-
- output_asns(qualify_path(path, cfg.get('asn_csv')), handle)
- output_prefixes(qualify_path(path, cfg.get('prefix_csv')), handle)
- output_roas(qualify_path(path, cfg.get('roa_csv')), handle)
-
- roa_requests = []
- for roa in handle.roas.all():
- v4 = rpki.resource_set.roa_prefix_set_ipv4()
- v6 = rpki.resource_set.roa_prefix_set_ipv6()
- for req in roa.from_roa_request.all():
- pfx = req.as_roa_prefix()
- if isinstance(pfx, rpki.resource_set.roa_prefix_ipv4):
- v4.append(pfx)
- else:
- v6.append(pfx)
- roa_requests.append((roa.asn, v4, v6))
-
- children = []
- for child in handle.children.all():
- asns = rpki.resource_set.resource_set_as([a.as_resource_range() for a in child.asn.all()])
-
- v4 = rpki.resource_set.resource_set_ipv4()
- v6 = rpki.resource_set.resource_set_ipv6()
- for pfx in child.address_range.all():
- rng = pfx.as_resource_range()
- if isinstance(rng, rpki.resource_set.resource_range_ipv4):
- v4.append(rng)
- else:
- v6.append(rng)
-
- # Convert from datetime.datetime to rpki.sundial.datetime
- valid_until = rpki.sundial.datetime.fromdatetime(child.valid_until)
- children.append((child.handle, asns, v4, v6, valid_until))
-
- ghostbusters = []
- for gbr in handle.ghostbusters.all():
- vcard = ghostbuster_to_vcard(gbr)
- parent_set = gbr.parent.all()
- if parent_set:
- for p in parent_set:
- ghostbusters.append((p, vcard))
- else:
- ghostbusters.append((None, vcard))
-
- z.synchronize([handle])
-
-def list_received_resources(log, conf):
- """Query rpkid for this resource handle's received resources.
-
- The semantics are to clear the entire table and populate with the
- list of certs received. Other models should not reference the
- table directly with foreign keys."""
z = Zookeeper(handle=conf.handle)
- pdus = z.call_rpkid(rpki.left_right.list_received_resources_elt.make_pdu(self_handle=conf.handle))
+ pdus = z.call_rpkid(list_received_resources_elt.make_pdu(self_handle=conf.handle))
models.ResourceCert.objects.filter(parent__issuer=conf).delete()
for pdu in pdus:
- if isinstance(pdu, rpki.left_right.list_received_resources_elt):
- parent = models.Parent.objects.get(issuer=conf, handle=pdu.parent_handle)
+ if isinstance(pdu, list_received_resources_elt):
+ parent = models.Parent.objects.get(issuer=conf,
+ handle=pdu.parent_handle)
not_before = datetime.strptime(pdu.notBefore, "%Y-%m-%dT%H:%M:%SZ")
not_after = datetime.strptime(pdu.notAfter, "%Y-%m-%dT%H:%M:%SZ")
@@ -198,23 +88,25 @@ def list_received_resources(log, conf):
not_before=not_before, not_after=not_after,
uri=pdu.uri)
- for asn in rpki.resource_set.resource_set_as(pdu.asn):
+ for asn in resource_set_as(pdu.asn):
cert.asn_ranges.create(min=asn.min, max=asn.max)
- for rng in rpki.resource_set.resource_set_ipv4(pdu.ipv4):
+ for rng in resource_set_ipv4(pdu.ipv4):
print >>log, 'adding v4 address range: %s' % rng
- cert.address_ranges.create(prefix_min=rng.min, prefix_max=rng.max)
+ cert.address_ranges.create(prefix_min=rng.min,
+ prefix_max=rng.max)
- for rng in rpki.resource_set.resource_set_ipv6(pdu.ipv6):
- cert.address_ranges_v6.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)
else:
print >>log, "error: unexpected pdu from rpkid type=%s" % type(pdu)
+
def config_from_template(dest, a):
"""
- Create a new rpki.conf file from a generic template. Go line by
- line through the template and substitute directives from the
- dictionary 'a'.
+ Create a new rpki.conf file from a generic template. Go line by line
+ through the template and substitute directives from the dictionary 'a'.
"""
with open(dest, 'w') as f:
@@ -228,154 +120,3 @@ def config_from_template(dest, a):
print >>f, r,
else:
print >>f, r,
-
-def initialize_handle(log, handle, host, owner=None, commit=True):
- """Create a new Conf object for this user."""
- print >>log, "initializing new resource handle %s" % handle
-
- qs = models.Conf.objects.filter(handle=handle)
- if not qs:
- conf = models.Conf(handle=handle, host=host)
- conf.save()
- if owner:
- conf.owner.add(owner)
- else:
- conf = qs[0]
-
- # Create the config directory if it doesn't already exist
- top = confpath(conf.handle)
- if not os.path.exists(top):
- os.makedirs(top)
-
- # Create rpki.conf file if it doesn't exist
-# if not os.path.exists(cfg_file):
-# print >>log, "generating rpki.conf for %s" % conf.handle
-# config_from_template(cfg_file,
-# {
-# 'handle' : conf.handle,
-# 'configuration_directory': top,
-# 'run_rpkid' : 'false',
-# 'run_pubd' : 'false',
-# 'run_rootd' : 'false',
-# 'openssl' : get_system_config().get('openssl')
-# })
-
- # Load configuration for self
- z = Zookeeper(handle=conf.handle)
- identity_xml = z.initialize()
-
- if commit:
- m.synchronize([conf.handle])
-
-### CHILD ###
-
-def import_child(log, conf, child_handle, xml_file):
- """Import a child's identity.xml."""
- z = Zookeeper(handle=conf.handle)
- wrapper, handle = z.configure_child(xml_file)
- z.synchronize([conf.handle])
-
-def delete_child(log, conf, child_handle):
- z = Zookeeper(handle=conf.handle)
- z.delete_child(child_handle)
- z.synchronize([conf.handle])
-
-### PARENT ###
-
-def import_parent(log, conf, parent_handle, xml_file):
- z = Zookeeper(handle=conf.handle)
- wrapper, handle = z.configure_parent(xml_file)
- z.synchronize([conf.handle])
-
-def delete_parent(log, conf, parent_handle):
- z = Zookeeper(handle=conf.handle)
- z.delete_parent(parent_handle)
- z.synchronize([conf.handle])
-
-### PUBCLIENT ###
-
-def import_pubclient(log, conf, xml_file):
- z = Zookeeper(handle=conf.handle)
- wrapper, client_handle = z.configure_publication_client(xml_file)
- z.synchronize([conf.handle])
-
-def delete_publication_client(log, conf, client_handle):
- z = Zookeeper(handle=conf.handle)
- z.delete_publication_client(client_handle)
- z.synchronize([conf.handle])
-
-### REPO ###
-
-def import_repository(log, conf, xml_file):
- z = Zookeeper(handle=conf.handle)
- z.configure_repository(xml_file)
- z.synchronize([conf.handle])
-
-def delete_repository(log, conf, repository_handle):
- z = Zookeeper(handle=conf.handle)
- z.delete_publication_client(repository_handle)
- z.synchronize([conf.handle])
-
-def create_child(log, parent_conf, child_handle):
- """
- Implements the child create wizard to create a new locally hosted child
-
- """
- child_conf, child = initialize_handle(log, handle=child_handle, host=parent_conf, commit=False)
-
- parent_handle = parent_conf.handle
-
- parent = get_myrpki(parent_conf)
-
- child_identity_xml = os.path.join(child.cfg.get("entitydb_dir"), 'identity.xml')
- parent_response_xml = os.path.join(parent.cfg.get("entitydb_dir"), 'children', child_handle + '.xml')
- repo_req_xml = os.path.join(child.cfg.get('entitydb_dir'), 'repositories', parent_handle + '.xml')
- # XXX for now we assume the child is hosted by parent's pubd
- repo_resp_xml = os.path.join(parent.cfg.get('entitydb_dir'), 'pubclients', '%s.%s.xml' % (parent_handle, child_handle))
-
- parent.do_configure_child(child_identity_xml)
-
- child.do_configure_parent(parent_response_xml)
-
- parent.do_configure_publication_client(repo_req_xml)
-
- child.do_configure_repository(repo_resp_xml)
-
- # run twice the first time to get bsc cert issued
- sys.stdout = sys.stderr
- configure_daemons(log, child_conf, child)
- configure_daemons(log, child_conf, child)
-
-def destroy_handle(log, handle):
- conf = models.Conf.objects.get(handle=handle)
-
- z = Zookeeper(handle=conf.host.handle)
- z.delete_child(conf.handle)
- z.delete_self(handle=conf.handle)
- z.delete_publication_client(client_handle=conf.handle)
- z.synchronize([conf.host.handle])
-
- conf.delete()
-
-def read_child_response(log, conf, child_handle):
- m = get_myrpki(conf)
- bname = child_handle + '.xml'
- return open(os.path.join(m.cfg.get('entitydb_dir'), 'children', bname)).read()
-
-def read_child_repo_response(log, conf, child_handle):
- """
- Return the XML file for the configure_publication_client response to the
- child.
-
- Note: the current model assumes the publication client is a child of this
- handle.
-
- """
- m = get_myrpki(conf)
- return open(os.path.join(m.cfg.get('entitydb_dir'), 'pubclients', '%s.%s.xml' % (conf.handle, child_handle))).read()
-
-def update_bpki(log, conf):
- z = Zookeeper(handle=conf.handle)
- z.update_bpki()
-
-# vim:sw=4 ts=8 expandtab
diff --git a/rpkid/rpki/gui/app/range_list.py b/rpkid/rpki/gui/app/range_list.py
index 76547555..5fa36891 100755
--- a/rpkid/rpki/gui/app/range_list.py
+++ b/rpkid/rpki/gui/app/range_list.py
@@ -1,3 +1,19 @@
+# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# 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$'
+
import bisect
import unittest
diff --git a/rpkid/rpki/gui/app/templates/app/bootstrap_form.html b/rpkid/rpki/gui/app/templates/app/bootstrap_form.html
index df9620fe..f30cb4ad 100644
--- a/rpkid/rpki/gui/app/templates/app/bootstrap_form.html
+++ b/rpkid/rpki/gui/app/templates/app/bootstrap_form.html
@@ -11,7 +11,7 @@
{% else %}
<div class='clearfix {% if field.errors %}error{% endif %}'>
{{ field.label_tag }}
- {% if field.required %}<span class='required-field'>*</span>{% endif %}
+ {% if field.required %}*{% endif %}
<div class='input'>
{{ field }}
{% if field.help_text %}
diff --git a/rpkid/rpki/gui/app/templates/app/import_child_form.html b/rpkid/rpki/gui/app/templates/app/child_import_form.html
index 4b0cf9d2..4b0cf9d2 100644
--- a/rpkid/rpki/gui/app/templates/app/import_child_form.html
+++ b/rpkid/rpki/gui/app/templates/app/child_import_form.html
diff --git a/rpkid/rpki/gui/app/templates/app/import_pubclient_form.html b/rpkid/rpki/gui/app/templates/app/client_import_form.html
index acd6bf61..acd6bf61 100644
--- a/rpkid/rpki/gui/app/templates/app/import_pubclient_form.html
+++ b/rpkid/rpki/gui/app/templates/app/client_import_form.html
diff --git a/rpkid/rpki/gui/app/templates/app/import_parent_form.html b/rpkid/rpki/gui/app/templates/app/parent_import_form.html
index c192a4a4..c192a4a4 100644
--- a/rpkid/rpki/gui/app/templates/app/import_parent_form.html
+++ b/rpkid/rpki/gui/app/templates/app/parent_import_form.html
diff --git a/rpkid/rpki/gui/app/templates/app/import_repository_form.html b/rpkid/rpki/gui/app/templates/app/respository_import_form.html
index acd6bf61..acd6bf61 100644
--- a/rpkid/rpki/gui/app/templates/app/import_repository_form.html
+++ b/rpkid/rpki/gui/app/templates/app/respository_import_form.html
diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py
index 9f7e6df3..0d007706 100644
--- a/rpkid/rpki/gui/app/views.py
+++ b/rpkid/rpki/gui/app/views.py
@@ -1,29 +1,29 @@
-# $Id$
+# Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
+# Copyright (C) 2012 SPARTA, Inc. a Parsons Company
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# 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.
"""
-Copyright (C) 2010, 2011 SPARTA, Inc. dba Cobham Analytic Solutions
-Copyright (C) 2012 SPARTA, Inc. a Parsons Company
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-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.
+This module contains the view functions implementing the web portal
+interface.
+
"""
-from __future__ import with_statement
+__version__ = '$Id$'
-import email.message
-import email.utils
import os
import os.path
-import tempfile
+from tempfile import NamedTemporaryFile
from django.contrib.auth.decorators import login_required
from django.contrib import auth
@@ -32,30 +32,29 @@ from django.utils.http import urlquote
from django.template import RequestContext
from django import http
from django.views.generic.list_detail import object_list, object_detail
-from django.views.generic.create_update import (delete_object, update_object,
- create_object)
+from django.views.generic.create_update import delete_object
from django.core.urlresolvers import reverse
+from rpki.irdb import Zookeeper, ChildASN, ChildNet
from rpki.gui.app import models, forms, glue, range_list
-from rpki import resource_set
-import rpki.irdb
-import rpki.exceptions
+from rpki.resource_set import (resource_range_as, resource_range_ipv4,
+ resource_range_ipv6, roa_prefix_ipv4)
+from rpki.exceptions import BadIPResource
import rpki.gui.cacheview.models
import rpki.gui.routeview.models
-debug = False
-
def my_login_required(f):
"""
- A version of django.contrib.auth.decorators.login_required
- that will fail instead of redirecting to the login page when
- the user is not logged in.
+ A version of django.contrib.auth.decorators.login_required that will
+ fail instead of redirecting to the login page when the user is not
+ logged in.
For use with the rpkidemo service URLs where we want to detect
- failure to log in. Otherwise django will return code 200 with
- the login form, and fools rpkidemo.
+ failure to log in. Otherwise django will return code 200 with the
+ login form, and fools rpkidemo.
+
"""
def wrapped(request, *args, **kwargs):
if not request.user.is_authenticated():
@@ -66,9 +65,10 @@ def my_login_required(f):
def superuser_required(f):
- """Decorator which returns HttpResponseForbidden if the user does not have
- superuser permissions."""
+ """Decorator which returns HttpResponseForbidden if the user does
+ not have superuser permissions.
+ """
@login_required
def _wrapped(request, *args, **kwargs):
if not request.user.is_superuser:
@@ -76,12 +76,23 @@ def superuser_required(f):
return f(request, *args, **kwargs)
return _wrapped
-# For each type of object, we have a detail view, a create view and
-# an update view. We heavily leverage the generic views, only
-# adding our own idea of authorization.
+
+# FIXME This method is included in Django 1.3 and can be removed when Django
+# 1.2 is out of its support window.
+def render(request, template, context):
+ """
+ https://docs.djangoproject.com/en/1.3/topics/http/shortcuts/#render
+
+ """
+ return render_to_response(template, context,
+ context_instance=RequestContext(request))
def handle_required(f):
+ """Decorator for view functions which require the user to be logged in and
+ a resource handle selected for the session.
+
+ """
@login_required
def wrapped_fn(request, *args, **kwargs):
if 'handle' not in request.session:
@@ -93,7 +104,7 @@ def handle_required(f):
if conf.count() == 1:
request.session['handle'] = conf[0]
elif conf.count() == 0:
- return render('app/conf_empty.html', {}, request)
+ return render(request, 'app/conf_empty.html', {})
else:
# Should reverse the view for this instead of hardcoding
# the URL.
@@ -105,9 +116,62 @@ def handle_required(f):
return wrapped_fn
-def render(template, context, request):
- return render_to_response(template, context,
- context_instance=RequestContext(request))
+@handle_required
+def generic_import(request, queryset, configure, form_class=None,
+ template_name=None, post_import_redirect=None):
+ """
+ Generic view function for importing XML files used in the setup
+ process.
+
+ queryset
+ queryset containing all objects of the type being imported
+
+ configure
+ method on Zookeeper to invoke with the imported XML file
+
+ form_class
+ specifies the form to use for import. If None, uses the generic
+ forms.ImportForm.
+
+ template_name
+ path to the html template to use to render the form. If None, defaults
+ to "app/<model>_import_form.html", where <model> is introspected from
+ the "queryset" argument.
+
+ post_import_redirect
+ if None (default), the user will be redirected to the detail page for
+ the imported object. Otherwise, the user will be redirected to the
+ specified URL.
+
+ """
+ conf = request.session['handle']
+ if template_name is None:
+ template_name = 'app/%s_import_form.html' % queryset.model.__name__.lower()
+ if form_class is None:
+ form_class = forms.ImportForm
+ 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)
+ # 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, form.cleaned_data.get('handle'))
+ os.remove(tmpf.name)
+ 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()
+
+ return render(request, template_name, {'form': form})
@handle_required
@@ -120,15 +184,15 @@ def dashboard(request):
# asns used in my roas
qs = models.ROARequest.objects.filter(issuer=conf)
roa_asns = set((obj.asn for obj in qs))
- used_asns.extend((resource_set.resource_range_as(asn, asn) for asn in roa_asns))
+ used_asns.extend((resource_range_as(asn, asn) for asn in roa_asns))
# asns given to my children
- child_asns = rpki.irdb.models.ChildASN.objects.filter(child__in=conf.children.all())
- used_asns.extend((resource_set.resource_range_as(obj.start_as, obj.end_as) for obj in child_asns))
+ child_asns = ChildASN.objects.filter(child__in=conf.children.all())
+ used_asns.extend((resource_range_as(obj.start_as, obj.end_as) for obj in child_asns))
# my received asns
asns = models.ResourceRangeAS.objects.filter(cert__parent__issuer=conf)
- my_asns = range_list.RangeList([resource_set.resource_range_as(obj.min, obj.max) for obj in asns])
+ my_asns = range_list.RangeList([resource_range_as(obj.min, obj.max) for obj in asns])
unused_asns = my_asns.difference(used_asns)
@@ -136,17 +200,21 @@ def dashboard(request):
used_prefixes_v6 = range_list.RangeList()
# prefixes used in my roas
- for obj in models.ROARequestPrefix.objects.filter(roa_request__issuer=conf, version='IPv4'):
+ for obj in models.ROARequestPrefix.objects.filter(roa_request__issuer=conf,
+ version='IPv4'):
used_prefixes.append(obj.as_resource_range())
- for obj in models.ROARequestPrefix.objects.filter(roa_request__issuer=conf, version='IPv6'):
+ for obj in models.ROARequestPrefix.objects.filter(roa_request__issuer=conf,
+ version='IPv6'):
used_prefixes_v6.append(obj.as_resource_range())
# prefixes given to my children
- for obj in rpki.irdb.models.ChildNet.objects.filter(child__in=conf.children.all(), version='IPv4'):
+ for obj in ChildNet.objects.filter(child__in=conf.children.all(),
+ version='IPv4'):
used_prefixes.append(obj.as_resource_range())
- for obj in rpki.irdb.models.ChildNet.objects.filter(child__in=conf.children.all(), version='IPv6'):
+ for obj in ChildNet.objects.filter(child__in=conf.children.all(),
+ version='IPv6'):
used_prefixes_v6.append(obj.as_resource_range())
# my received prefixes
@@ -158,19 +226,21 @@ def dashboard(request):
unused_prefixes = my_prefixes.difference(used_prefixes)
unused_prefixes_v6 = my_prefixes_v6.difference(used_prefixes_v6)
- return render('app/dashboard.html', {
+ return render(request, 'app/dashboard.html', {
'conf': conf,
'unused_asns': unused_asns,
'unused_prefixes': unused_prefixes,
'unused_prefixes_v6': unused_prefixes_v6,
'asns': asns,
'prefixes': prefixes,
- 'prefixes_v6': prefixes}, request)
+ 'prefixes_v6': prefixes})
@superuser_required
def conf_list(request):
- """Allow the user to select a handle."""
+ """Allow the user to select a handle.
+
+ """
queryset = models.Conf.objects.all()
return object_list(request, queryset,
template_name='app/conf_list.html', template_object_name='conf',
@@ -179,7 +249,9 @@ def conf_list(request):
@superuser_required
def conf_select(request):
- '''Change the handle for the current session.'''
+ """Change the handle for the current session.
+
+ """
if not 'handle' in request.GET:
return http.HttpResponseRedirect('/myrpki/conf/select')
handle = request.GET['handle']
@@ -198,7 +270,9 @@ def serve_xml(content, basename):
@handle_required
def conf_export(request):
- """Return the identity.xml for the current handle."""
+ """Return the identity.xml for the current handle.
+
+ """
handle = request.session['handle']
return serve_xml(glue.read_identity(handle.handle), 'identity')
@@ -206,25 +280,8 @@ def conf_export(request):
@handle_required
def parent_import(request):
conf = request.session['handle']
- log = request.META['wsgi.errors']
-
- if request.method == 'POST':
- form = forms.ImportParentForm(conf, request.POST, request.FILES)
- if form.is_valid():
- tmpf = tempfile.NamedTemporaryFile(prefix='parent', suffix='.xml', delete=False)
- f = tmpf.name
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
-
- glue.import_parent(log, conf, form.cleaned_data['handle'], f)
-
- os.remove(tmpf.name)
-
- return http.HttpResponseRedirect(reverse(dashboard))
- else:
- form = forms.ImportParentForm(conf)
-
- return render('app/import_parent_form.html', {'form': form}, request)
+ return generic_import(request, conf.parents,
+ Zookeeper.configure_parent)
@handle_required
@@ -257,29 +314,9 @@ def parent_delete(request, pk):
@handle_required
def child_import(request):
- """
- Import a repository response.
- """
conf = request.session['handle']
- log = request.META['wsgi.errors']
-
- if request.method == 'POST':
- form = forms.ImportChildForm(conf, request.POST, request.FILES)
- if form.is_valid():
- tmpf = tempfile.NamedTemporaryFile(prefix='identity', suffix='.xml', delete=False)
- f = tmpf.name
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
-
- glue.import_child(log, conf, form.cleaned_data['handle'], f)
-
- os.remove(tmpf.name)
-
- return http.HttpResponseRedirect(reverse(dashboard))
- else:
- form = forms.ImportChildForm(conf)
-
- return render('app/import_child_form.html', {'form': form}, request)
+ return generic_import(request, conf.children, Zookeeper.configure_child,
+ post_import_redirect=reverse(child_list))
@handle_required
@@ -294,7 +331,8 @@ def child_list(request):
@handle_required
-def child_add_resource(request, pk, form_class, unused_list, callback, template_name='app/child_add_resource_form.html'):
+def child_add_resource(request, pk, form_class, unused_list, callback,
+ template_name='app/child_add_resource_form.html'):
conf = request.session['handle']
child = models.Child.objects.filter(issuer=conf, pk=pk)
if request.method == 'POST':
@@ -305,12 +343,12 @@ def child_add_resource(request, pk, form_class, unused_list, callback, template_
else:
form = form_class()
- return render(template_name, {'object': child, 'form': form,
- 'unused': unused_list}, request)
+ return render(request, template_name, {'object': child, 'form': form,
+ 'unused': unused_list})
def add_asn_callback(child, form):
- r = resource_set.resource_range_as.parse_str(form.as_range)
+ r = resource_range_as.parse_str(form.as_range)
child.asns.create(min=r.min, max=r.max)
@@ -321,10 +359,10 @@ def child_add_asn(request, pk):
def add_address_callback(child, form):
try:
- r = resource_set.resource_range_ipv4.parse_str(form.prefix)
+ r = resource_range_ipv4.parse_str(form.prefix)
family = 4
- except rpki.exceptions.BadIPResource:
- r = resource_set.resource_range_ipv6.parse_str(form.prefix)
+ except BadIPResource:
+ r = resource_range_ipv6.parse_str(form.prefix)
family = 6
child.address_ranges.create(min=str(r.min), max=str(r.max), family=family)
@@ -339,7 +377,7 @@ def child_view(request, pk):
'''Detail view of child for the currently selected handle.'''
handle = request.session['handle']
child = get_object_or_404(handle.children.all(), pk=pk)
- return render('app/child_view.html', {'child': child}, request)
+ return render(request, 'app/child_view.html', {'child': child})
@handle_required
@@ -357,45 +395,8 @@ def child_edit(request, pk):
else:
form = forms.ChildForm(instance=child)
- return render('app/child_form.html', {'child': child, 'form': form}, request)
-
-
-# this is similar to handle_required, except that the handle is given in URL
-def handle_or_404(request, handle):
- "ensure the requested handle is available to this user"
- if request.user.is_superuser:
- conf_set = models.Conf.objects.filter(handle=handle)
- else:
- conf_set = models.Conf.objects.filter(owner=request.user, handle=handle)
- if not conf_set:
- raise http.Http404, 'resource handle not found'
- return conf_set[0]
-
-
-def serve_file(handle, fname, content_type, error_code=404):
- content, mtime = glue.read_file_from_handle(handle, fname)
- resp = http.HttpResponse(content, mimetype=content_type)
- resp['Content-Disposition'] = 'attachment; filename=%s' % (os.path.basename(fname), )
- resp['Last-Modified'] = email.utils.formatdate(mtime, usegmt=True)
- return resp
-
-
-@my_login_required
-def download_csv(request, self_handle, fname):
- conf = handle_or_404(request, self_handle)
- return serve_file(conf.handle, fname + '.csv', 'text/csv')
-
-
-def download_asns(request, self_handle):
- return download_csv(request, self_handle, 'asns')
-
-
-def download_roas(request, self_handle):
- return download_csv(request, self_handle, 'roas')
-
-
-def download_prefixes(request, self_handle):
- return download_csv(request, self_handle, 'prefixes')
+ return render(request, 'app/child_form.html',
+ {'child': child, 'form': form})
def login(request):
@@ -405,6 +406,7 @@ def login(request):
use with rpkidemo to properly detect errors. The django login
view will return 200 with the login page when the login fails,
which is not desirable when using rpkidemo.
+
"""
log = request.META['wsgi.errors']
@@ -426,8 +428,10 @@ def login(request):
def roa_create(request):
"""Present the user with a form to create a ROA.
- Doesn't use the generic create_object() form because we need to create both
- the ROARequest and ROARequestPrefix objects."""
+ Doesn't use the generic create_object() form because we need to
+ create both the ROARequest and ROARequestPrefix objects.
+
+ """
routes = []
if request.method == 'POST':
@@ -439,16 +443,18 @@ def roa_create(request):
max_prefixlen = int(form.cleaned_data.get('max_prefixlen'))
if form.cleaned_data.get('confirmed'):
- roarequests = models.ROARequest.objects.filter(issuer=conf, asn=asn)
+ roarequests = models.ROARequest.objects.filter(issuer=conf,
+ asn=asn)
if roarequests:
- # FIXME need to handle the case where there are multiple ROAs
- # for the same AS due to prefixes delegated from different
- # resource certs.
+ # FIXME need to handle the case where there are
+ # multiple ROAs for the same AS due to prefixes
+ # delegated from different resource certs.
roa = roarequests[0]
else:
- roa = models.ROARequest.objects.create(issuer=conf, asn=asn)
+ roa = models.ROARequest.objects.create(issuer=conf,
+ asn=asn)
version = 'IPv4' if isinstance(rng,
- resource_set.resource_range_ipv4) else 'IPv6'
+ resource_range_ipv4) else 'IPv6'
roa.prefixes.create(version=version, prefix=str(rng.min),
prefixlen=rng.prefixlen(), max_prefixlen=max_prefixlen)
return http.HttpResponseRedirect(reverse(roa_list))
@@ -467,13 +473,18 @@ def roa_create(request):
else:
form = forms.ROARequest()
- return render('app/roarequest_form.html', {'form': form, 'routes': routes},
- request)
+ return render(request, 'app/roarequest_form.html',
+ {'form': form, 'routes': routes})
@handle_required
def roa_list(request):
- "Displays a list of ROARequestPrefix objects for the current resource handle."
+ """
+ Display a list of ROARequestPrefix objects for the current resource
+ handle.
+
+ """
+
conf = request.session['handle']
qs = models.ROARequestPrefix.objects.filter(roa_request__issuer=conf)
return object_list(request, queryset=qs,
@@ -483,8 +494,13 @@ def roa_list(request):
@handle_required
def roa_detail(request, pk):
- """Not implemented. This is a placeholder so that models.ROARequestPrefix.get_absolute_url
- works. The only reason it exist is so that the /delete URL works."""
+ """Not implemented.
+
+ This is a placeholder so that
+ models.ROARequestPrefix.get_absolute_url works. The only reason it
+ exist is so that the /delete URL works.
+
+ """
pass
@@ -493,10 +509,13 @@ def roa_delete(request, pk):
"""Handles deletion of a single ROARequestPrefix object.
Uses a form for double confirmation, displaying how the route
- validation status may change as a result."""
+ validation status may change as a result.
+
+ """
conf = request.session['handle']
- obj = get_object_or_404(models.ROARequestPrefix.objects, roa_request__issuer=conf, pk=pk)
+ obj = get_object_or_404(models.ROARequestPrefix.objects, roa_request__issuer=conf,
+ pk=pk)
if request.method == 'POST':
roa = obj.roa_request
@@ -512,7 +531,7 @@ def roa_delete(request, pk):
roa_pfx = obj.as_roa_prefix()
- pfx = 'prefixes' if isinstance(roa_pfx, resource_set.roa_prefix_ipv4) else 'prefixes_v6'
+ pfx = 'prefixes' if isinstance(roa_pfx, roa_prefix_ipv4) else 'prefixes_v6'
args = {'%s__prefix_min' % pfx: roa_pfx.min(),
'%s__prefix_max' % pfx: roa_pfx.max(),
'%s__max_length' % pfx: roa_pfx.max_prefixlen}
@@ -524,14 +543,15 @@ def roa_delete(request, pk):
validate_route(route, qs)
routes.append(route)
- return render('app/roa_request_confirm_delete.html', {'object': obj,
- 'routes': routes}, request)
+ return render(request, 'app/roa_request_confirm_delete.html',
+ {'object': obj, 'routes': routes})
@handle_required
def ghostbuster_list(request):
"""
Display a list of all ghostbuster requests for the current Conf.
+
"""
conf = request.session['handle']
qs = models.GhostbusterRequest.objects.filter(issuer=conf)
@@ -542,6 +562,7 @@ def ghostbuster_list(request):
def ghostbuster_view(request, pk):
"""
Display an individual ghostbuster request.
+
"""
conf = request.session['handle']
qs = models.GhostbusterRequest.objects.filter(issuer=conf)
@@ -551,17 +572,26 @@ def ghostbuster_view(request, pk):
@handle_required
def ghostbuster_delete(request, pk):
+ """
+ Handle deletion of a GhostbusterRequest object.
+
+ """
conf = request.session['handle']
- get_object_or_404(models.GhostbusterRequest, issuer=conf, pk=pk) # permission check
- return delete_object(request, model=models.GhostbusterRequest, object_id=pk,
- post_delete_redirect=reverse(ghostbuster_list),
- template_name='app/ghostbusterrequest_detail.html',
- extra_context={'confirm_delete': True})
+
+ # Ensure the GhosbusterRequest object belongs to the current user.
+ get_object_or_404(models.GhostbusterRequest, issuer=conf, pk=pk)
+
+ return delete_object(request, model=models.GhostbusterRequest,
+ object_id=pk,
+ post_delete_redirect=reverse(ghostbuster_list),
+ template_name='app/ghostbusterrequest_detail.html',
+ extra_context={'confirm_delete': True})
def _ghostbuster_edit(request, obj=None):
"""
Common code for create/edit.
+
"""
conf = request.session['handle']
form_class = forms.GhostbusterRequestForm
@@ -578,8 +608,8 @@ def _ghostbuster_edit(request, obj=None):
return http.HttpResponseRedirect(obj.get_absolute_url())
else:
form = form_class(conf, instance=obj)
- return render('app/ghostbuster_form.html', {'form': form, 'object': obj},
- request)
+ return render(request, 'app/ghostbuster_form.html',
+ {'form': form, 'object': obj})
@handle_required
@@ -599,8 +629,12 @@ def ghostbuster_create(request):
@handle_required
def refresh(request):
- "Query rpkid, update the db, and redirect back to the dashboard."
- glue.list_received_resources(request.META['wsgi.errors'], request.session['handle'])
+ """
+ Query rpkid, update the db, and redirect back to the dashboard.
+
+ """
+ glue.list_received_resources(request.META['wsgi.errors'],
+ request.session['handle'])
return http.HttpResponseRedirect(reverse(dashboard))
@@ -608,6 +642,7 @@ def refresh(request):
def initialize(request):
"""
Initialize a new user account.
+
"""
if request.method == 'POST':
form = forms.GenericConfirmationForm(request.POST)
@@ -618,13 +653,14 @@ def initialize(request):
else:
form = forms.GenericConfirmationForm()
- return render('app/initialize_form.html', {'form': form}, request)
+ return render(request, 'app/initialize_form.html', {'form': form})
@handle_required
def child_wizard(request):
"""
Wizard mode to create a new locally hosted child.
+
"""
conf = request.session['handle']
log = request.META['wsgi.errors']
@@ -639,7 +675,7 @@ def child_wizard(request):
else:
form = forms.ChildWizardForm(conf)
- return render('app/child_wizard_form.html', {'form': form}, request)
+ return render(request, 'app/child_wizard_form.html', {'form': form})
@handle_required
@@ -647,10 +683,12 @@ def export_child_response(request, child_handle):
"""
Export the XML file containing the output of the configure_child
to send back to the client.
+
"""
conf = request.session['handle']
log = request.META['wsgi.errors']
- return serve_xml(glue.read_child_response(log, conf, child_handle), child_handle)
+ return serve_xml(glue.read_child_response(log, conf, child_handle),
+ child_handle)
@handle_required
@@ -658,26 +696,27 @@ def export_child_repo_response(request, child_handle):
"""
Export the XML file containing the output of the configure_child
to send back to the client.
+
"""
conf = request.session['handle']
log = request.META['wsgi.errors']
- return serve_xml(glue.read_child_repo_response(log, conf, child_handle), child_handle)
+ return serve_xml(glue.read_child_repo_response(log, conf, child_handle),
+ child_handle)
@handle_required
def update_bpki(request):
conf = request.session['handle']
- log = request.META['wsgi.errors']
if request.method == 'POST':
form = forms.GenericConfirmationForm(request.POST, request.FILES)
if form.is_valid():
- glue.update_bpki(log, conf)
+ Zookeeper(handle=conf.handle).update_bpki()
return http.HttpResponseRedirect(reverse(dashboard))
else:
form = forms.GenericConfirmationForm()
- return render('app/update_bpki_form.html', {'form': form}, request)
+ return render(request, 'app/update_bpki_form.html', {'form': form})
@handle_required
@@ -693,41 +732,45 @@ def child_delete(request, pk):
else:
form = forms.GenericConfirmationForm()
- return render('app/child_delete_form.html', {'form': form, 'object': child}, request)
+ return render(request, 'app/child_delete_form.html',
+ {'form': form, 'object': child})
@login_required
def destroy_handle(request, handle):
"""
Completely remove a hosted resource handle.
- """
+ """
log = request.META['wsgi.errors']
if not request.user.is_superuser:
return http.HttpResponseForbidden()
- conf = get_object_or_404(models.Conf, handle=handle)
+ get_object_or_404(models.Conf, handle=handle)
if request.method == 'POST':
form = forms.GenericConfirmationForm(request.POST, request.FILES)
if form.is_valid():
glue.destroy_handle(log, handle)
- return render('app/generic_result.html',
+ return render(request, 'app/generic_result.html',
{'operation': 'Destroy ' + handle,
- 'result': 'Succeeded'}, request)
+ 'result': 'Succeeded'})
else:
form = forms.GenericConfirmationForm()
- return render('app/destroy_handle_form.html', {'form': form,
- 'handle': handle}, request)
+ return render(request, 'app/destroy_handle_form.html',
+ {'form': form, 'handle': handle})
def roa_match(rng):
- "Return a list of tuples of matching routes and roas."
+ """
+ Return a list of tuples of matching routes and roas.
+
+ """
object_accepted = rpki.gui.cacheview.models.ValidationLabel.objects.get(label='object_accepted')
- if isinstance(rng, rpki.resource_set.resource_range_ipv6):
+ if isinstance(rng, resource_range_ipv6):
route_manager = rpki.gui.routeview.models.RouteOriginV6.objects
pfx = 'prefixes_v6'
else:
@@ -751,7 +794,9 @@ def roa_match(rng):
def validate_route(route, roas):
"""Annotate the route object with its validation status.
- `roas` is a queryset containing ROAs which cover `route`. """
+ `roas` is a queryset containing ROAs which cover `route`.
+
+ """
pfx = 'prefixes' if isinstance(route, rpki.gui.routeview.models.RouteOrigin) else 'prefixes_v6'
args = {'asid': route.asn,
'%s__prefix_min__lte' % pfx: route.prefix_min,
@@ -762,7 +807,8 @@ def validate_route(route, roas):
if not roas.exists():
route.status = 'unknown'
route.status_label = 'warning'
- # 3. if any candidate roa matches the origin AS and max_length, end with valid
+ # 3. if any candidate roa matches the origin AS and max_length, end with
+ # valid
#
# AS0 is always invalid.
elif route.asn != 0 and roas.filter(**args).exists():
@@ -779,10 +825,10 @@ def validate_route(route, roas):
@handle_required
def route_view(request):
"""
- Display a list of global routing table entries which match resources listed
- in received certificates.
- """
+ Display a list of global routing table entries which match resources
+ listed in received certificates.
+ """
conf = request.session['handle']
log = request.META['wsgi.errors']
@@ -797,58 +843,47 @@ def route_view(request):
routes.extend([validate_route(*x) for x in roa_match(r)])
ts = dict((attr['name'], attr['ts']) for attr in models.Timestamp.objects.values())
- return render('app/routes_view.html', {'routes': routes, 'timestamp': ts}, request)
+ return render(request, 'app/routes_view.html',
+ {'routes': routes, 'timestamp': ts})
@handle_required
def repository_list(request):
conf = request.session['handle']
qs = models.Repository.objects.filter(issuer=conf)
- return object_list(request, queryset=qs, template_name='app/repository_list.html',
- extra_context={
- 'create_url': reverse(repository_import),
- 'create_label': u'Import'})
+ return object_list(request, queryset=qs,
+ template_name='app/repository_list.html',
+ extra_context={
+ 'create_url': reverse(repository_import),
+ 'create_label': u'Import'})
@handle_required
def repository_detail(request, pk):
conf = request.session['handle']
qs = models.Repository.objects.filter(issuer=conf)
- return object_detail(request, queryset=qs, object_id=pk, template_name='app/repository_detail.html')
+ return object_detail(request, queryset=qs, object_id=pk,
+ template_name='app/repository_detail.html')
@handle_required
def repository_delete(request, pk):
conf = request.session['handle']
- get_object_or_404(models.Repository, issuer=conf, pk=pk) # permission check
+ # Ensure the repository being deleted belongs to the current user.
+ get_object_or_404(models.Repository, issuer=conf, pk=pk)
return delete_object(request, model=models.Repository, object_id=pk,
post_delete_redirect=reverse(repository_list),
template_name='app/repository_detail.html',
extra_context={'confirm_delete': True})
-@handle_required
+@superuser_required
def repository_import(request):
- conf = request.session['handle']
- log = request.META['wsgi.errors']
-
- if request.method == 'POST':
- form = forms.ImportRepositoryForm(request.POST, request.FILES)
- if form.is_valid():
- tmpf = tempfile.NamedTemporaryFile(prefix='repository', suffix='.xml', delete=False)
- f = tmpf.name
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
-
- glue.import_repository(log, conf, f)
-
- os.remove(tmpf.name)
-
- return http.HttpResponseRedirect(reverse(dashboard))
- else:
- form = forms.ImportRepositoryForm()
-
- return render('app/import_repository_form.html', {'form': form}, request)
+ return generic_import(request,
+ models.Repository.objects,
+ Zookeeper.configure_repository,
+ form_class=forms.ImportClientForm,
+ post_import_redirect=reverse(repository_list))
@superuser_required
@@ -872,25 +907,7 @@ def client_delete(request, pk):
@superuser_required
def client_import(request):
- conf = request.session['handle']
- log = request.META['wsgi.errors']
-
- if request.method == 'POST':
- form = forms.ImportPubClientForm(request.POST, request.FILES)
- if form.is_valid():
- tmpf = tempfile.NamedTemporaryFile(prefix='pubclient', suffix='.xml', delete=False)
- f = tmpf.name
- tmpf.write(form.cleaned_data['xml'].read())
- tmpf.close()
-
- glue.import_pubclient(log, conf, f)
-
- os.remove(tmpf.name)
-
- return http.HttpResponseRedirect(reverse(dashboard))
- else:
- form = forms.ImportPubClientForm()
-
- return render('app/import_pubclient_form.html', {'form': form}, request)
-
-# vim:sw=4 ts=8 expandtab
+ return generic_import(request, models.Client.objects,
+ Zookeeper.configure_publication_client,
+ form_class=forms.ImportClientForm,
+ post_import_redirect=reverse(client_list))