diff options
author | Michael Elkins <melkins@tislabs.com> | 2012-01-30 19:13:35 +0000 |
---|---|---|
committer | Michael Elkins <melkins@tislabs.com> | 2012-01-30 19:13:35 +0000 |
commit | 8867b8d0d247ae898e749d06a16f4fb593fe90be (patch) | |
tree | cc232520763f186401e4378161e66bcc2ea9aa57 /rpkid | |
parent | cb3e16840ce29c8e46789abd083803f4f9cfd2b5 (diff) |
remove dead code that is deprecated by the changes in tk100.
reformat code according to pep8
move copyright notices into comments out of docstrings
svn path=/branches/tk161/; revision=4271
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/rpki/gui/app/forms.py | 85 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/glue.py | 357 | ||||
-rwxr-xr-x | rpkid/rpki/gui/app/range_list.py | 16 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/app/bootstrap_form.html | 2 | ||||
-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.py | 495 |
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)) |