aboutsummaryrefslogtreecommitdiff
path: root/rpki/gui/app
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/gui/app')
-rw-r--r--rpki/gui/app/check_expired.py6
-rw-r--r--rpki/gui/app/forms.py17
-rw-r--r--rpki/gui/app/glue.py3
-rw-r--r--rpki/gui/app/models.py14
-rwxr-xr-xrpki/gui/app/range_list.py2
-rw-r--r--rpki/gui/app/views.py41
6 files changed, 50 insertions, 33 deletions
diff --git a/rpki/gui/app/check_expired.py b/rpki/gui/app/check_expired.py
index a084af79..2907f071 100644
--- a/rpki/gui/app/check_expired.py
+++ b/rpki/gui/app/check_expired.py
@@ -41,8 +41,8 @@ def check_cert(handle, p, errs):
The displayed object name defaults to the class name, but can be overridden
using the `object_name` argument.
-
"""
+
t = p.certificate.getNotAfter()
if t <= expire_time:
e = 'expired' if t <= now else 'will expire'
@@ -102,8 +102,8 @@ def check_expire(conf, errs):
def check_child_certs(conf, errs):
"""Fetch the list of published objects from rpkid, and inspect the issued
resource certs (uri ending in .cer).
-
"""
+
z = Zookeeper(handle=conf.handle)
req = list_published_objects_elt.make_pdu(action="list",
tag="list_published_objects",
@@ -139,8 +139,8 @@ def notify_expired(expire_days=14, from_email=None):
expire_days: the number of days ahead of today to warn
from_email: set the From: address for the email
-
"""
+
global expire_time # so i don't have to pass it around
global now
diff --git a/rpki/gui/app/forms.py b/rpki/gui/app/forms.py
index 5394a804..02561303 100644
--- a/rpki/gui/app/forms.py
+++ b/rpki/gui/app/forms.py
@@ -52,6 +52,7 @@ class GhostbusterRequestForm(forms.ModelForm):
Generate a ModelForm with the subset of parents for the current
resource handle.
"""
+
# override default form field
parent = forms.ModelChoiceField(queryset=None, required=False,
help_text='Specify specific parent, or none for all parents')
@@ -86,6 +87,7 @@ class GhostbusterRequestForm(forms.ModelForm):
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')
@@ -101,6 +103,7 @@ class ImportRepositoryForm(forms.Form):
class ImportClientForm(forms.Form):
"""Form used for importing publication client requests."""
+
xml = forms.FileField(label='XML file')
@@ -137,6 +140,7 @@ class UserCreateForm(forms.Form):
class UserEditForm(forms.Form):
"""Form for editing a user."""
+
email = forms.CharField()
pw = forms.CharField(widget=forms.PasswordInput, label='Password',
required=False)
@@ -185,8 +189,8 @@ class ROARequest(forms.Form):
"""Takes an optional `conf` keyword argument specifying the user that
is creating the ROAs. It is used for validating that the prefix the
user entered is currently allocated to that user.
-
"""
+
conf = kwargs.pop('conf', None)
kwargs['auto_id'] = False
super(ROARequest, self).__init__(*args, **kwargs)
@@ -199,8 +203,8 @@ class ROARequest(forms.Form):
rpki.resource_set.resource_range_ip object.
If there is no mask provided, assume the closest classful mask.
-
"""
+
prefix = self.cleaned_data.get('prefix')
if '/' not in prefix:
p = IPAddress(prefix)
@@ -296,7 +300,6 @@ class AddASNForm(forms.Form):
Returns a forms.Form subclass which verifies that the entered ASN range
does not overlap with a previous allocation to the specified child, and
that the ASN range is within the range allocated to the parent.
-
"""
asns = forms.CharField(
@@ -335,8 +338,8 @@ class AddNetForm(forms.Form):
Returns a forms.Form subclass which validates that the entered address
range is within the resources allocated to the parent, and does not overlap
with what is already allocated to the specified child.
-
"""
+
address_range = forms.CharField(
help_text='CIDR or range',
widget=forms.TextInput(attrs={'autofocus': 'true'})
@@ -383,7 +386,6 @@ def ChildForm(instance):
This is roughly based on the equivalent ModelForm, but uses Form as a base
class so that selection boxes for the AS and Prefixes can be edited in a
single form.
-
"""
class _wrapped(forms.Form):
@@ -401,11 +403,13 @@ def ChildForm(instance):
class Empty(forms.Form):
"""Stub form for views requiring confirmation."""
+
pass
class ResourceHolderForm(forms.Form):
"""form for editing ACL on Conf objects."""
+
users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
help_text='users allowed to mange this resource holder'
@@ -413,7 +417,8 @@ class ResourceHolderForm(forms.Form):
class ResourceHolderCreateForm(forms.Form):
- """form for creating new resource holdres."""
+ """form for creating new resource holders."""
+
handle = forms.CharField(max_length=30)
parent = forms.ModelChoiceField(
required=False,
diff --git a/rpki/gui/app/glue.py b/rpki/gui/app/glue.py
index 0bf5f942..f17ba5ac 100644
--- a/rpki/gui/app/glue.py
+++ b/rpki/gui/app/glue.py
@@ -16,7 +16,6 @@
"""
This file contains code that interfaces between the django views implementing
the portal gui and the rpki.* modules.
-
"""
from __future__ import with_statement
@@ -39,6 +38,7 @@ from django.db.transaction import commit_on_success
def ghostbuster_to_vcard(gbr):
"""Convert a GhostbusterRequest object into a vCard object."""
+
import vobject
vcard = vobject.vCard()
@@ -86,7 +86,6 @@ def list_received_resources(log, conf):
The semantics are to clear the entire table and populate with the list of
certs received. Other models should not reference the table directly with
foreign keys.
-
"""
z = Zookeeper(handle=conf.handle)
diff --git a/rpki/gui/app/models.py b/rpki/gui/app/models.py
index 32a897c7..d6332796 100644
--- a/rpki/gui/app/models.py
+++ b/rpki/gui/app/models.py
@@ -120,16 +120,16 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
def parents(self):
"""Simulates irdb.models.Parent.objects, but returns app.models.Parent
proxy objects.
-
"""
+
return Parent.objects.filter(issuer=self)
@property
def children(self):
"""Simulates irdb.models.Child.objects, but returns app.models.Child
proxy objects.
-
"""
+
return Child.objects.filter(issuer=self)
@property
@@ -148,8 +148,8 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
def routes(self):
"""Return all IPv4 routes covered by RPKI certs issued to this resource
holder.
-
"""
+
# build a Q filter to select all RouteOrigin objects covered by
# prefixes in the resource holder's certificates
q = models.Q()
@@ -162,8 +162,8 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
def routes_v6(self):
"""Return all IPv6 routes covered by RPKI certs issued to this resource
holder.
-
"""
+
# build a Q filter to select all RouteOrigin objects covered by
# prefixes in the resource holder's certificates
q = models.Q()
@@ -174,6 +174,7 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
def send_alert(self, subject, message, from_email, severity=Alert.INFO):
"""Store an alert for this resource holder."""
+
self.alerts.create(subject=subject, text=message, severity=severity)
send_mail(
@@ -189,8 +190,8 @@ class Conf(rpki.irdb.models.ResourceHolderCA):
Contact emails are extract from any ghostbuster requests, and any
linked user accounts.
-
"""
+
notify_emails = [gbr.email_address for gbr in self.ghostbusters if gbr.email_address]
notify_emails.extend(
[acl.user.email for acl in ConfACL.objects.filter(conf=self) if acl.user.email]
@@ -209,7 +210,6 @@ class ResourceCert(models.Model):
"""Represents a resource certificate.
This model is used to cache the output of <list_received_resources/>.
-
"""
# Handle to which this cert was issued
@@ -237,6 +237,7 @@ class ResourceCert(models.Model):
def get_cert_chain(self):
"""Return a list containing the complete certificate chain for this
certificate."""
+
cert = self
x = [cert]
while cert.issuer:
@@ -410,7 +411,6 @@ class RouteOriginV6(rpki.gui.routeview.models.RouteOriginV6):
class ConfACL(models.Model):
"""Stores access control for which users are allowed to manage a given
resource handle.
-
"""
conf = models.ForeignKey(Conf)
diff --git a/rpki/gui/app/range_list.py b/rpki/gui/app/range_list.py
index 21fd1f29..5cb4f5e4 100755
--- a/rpki/gui/app/range_list.py
+++ b/rpki/gui/app/range_list.py
@@ -70,6 +70,7 @@ class RangeList(list):
def difference(self, other):
"""Return a RangeList object which contains ranges in this object which
are not in "other"."""
+
it = iter(other)
try:
@@ -85,6 +86,7 @@ class RangeList(list):
def V(v):
"""convert the integer value to the appropriate type for this
range"""
+
return x.__class__.datum_type(v)
try:
diff --git a/rpki/gui/app/views.py b/rpki/gui/app/views.py
index 9a1c4cfe..228d5c6c 100644
--- a/rpki/gui/app/views.py
+++ b/rpki/gui/app/views.py
@@ -71,6 +71,7 @@ def superuser_required(f):
def get_conf(user, handle):
"""return the Conf object for 'handle'.
user is a request.user object to use enforce ACLs."""
+
if user.is_superuser:
qs = models.Conf.objects.all()
else:
@@ -81,8 +82,8 @@ def get_conf(user, handle):
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
@tls_required
def wrapped_fn(request, *args, **kwargs):
@@ -126,8 +127,8 @@ def generic_import(request, queryset, configure, form_class=None,
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 = get_conf(request.user, request.session['handle'])
if form_class is None:
form_class = forms.ImportForm
@@ -251,6 +252,7 @@ def dashboard(request):
@login_required
def conf_list(request, **kwargs):
"""Allow the user to select a handle."""
+
log = request.META['wsgi.errors']
next_url = request.GET.get('next', reverse(dashboard))
if request.user.is_superuser:
@@ -266,6 +268,7 @@ def conf_list(request, **kwargs):
@login_required
def conf_select(request):
"""Change the handle for the current session."""
+
if not 'handle' in request.GET:
return redirect(conf_list)
handle = request.GET['handle']
@@ -288,8 +291,8 @@ def serve_xml(content, basename, ext='xml'):
`basename` is the prefix to specify for the XML filename.
`csv` is the type (default: xml)
-
"""
+
resp = http.HttpResponse(content, mimetype='application/%s' % ext)
resp['Content-Disposition'] = 'attachment; filename=%s.%s' % (basename, ext)
return resp
@@ -298,6 +301,7 @@ def serve_xml(content, basename, ext='xml'):
@handle_required
def conf_export(request):
"""Return the identity.xml for the current handle."""
+
conf = get_conf(request.user, request.session['handle'])
z = Zookeeper(handle=conf.handle)
xml = z.generate_identity()
@@ -307,6 +311,7 @@ def conf_export(request):
@handle_required
def export_asns(request):
"""Export CSV file containing ASN allocations to children."""
+
conf = get_conf(request.user, request.session['handle'])
s = cStringIO.StringIO()
csv_writer = csv.writer(s, delimiter=' ')
@@ -342,6 +347,7 @@ def import_asns(request):
@handle_required
def export_prefixes(request):
"""Export CSV file containing ASN allocations to children."""
+
conf = get_conf(request.user, request.session['handle'])
s = cStringIO.StringIO()
csv_writer = csv.writer(s, delimiter=' ')
@@ -411,6 +417,7 @@ def parent_delete(request, pk):
@handle_required
def parent_export(request, pk):
"""Export XML repository request for a given parent."""
+
conf = get_conf(request.user, request.session['handle'])
parent = get_object_or_404(conf.parents, pk=pk)
z = Zookeeper(handle=conf.handle)
@@ -474,6 +481,7 @@ def child_detail(request, pk):
@handle_required
def child_edit(request, pk):
"""Edit the end validity date for a resource handle's child."""
+
log = request.META['wsgi.errors']
conf = get_conf(request.user, request.session['handle'])
child = get_object_or_404(conf.children.all(), pk=pk)
@@ -505,8 +513,8 @@ def child_response(request, pk):
"""
Export the XML file containing the output of the configure_child
to send back to the client.
-
"""
+
conf = get_conf(request.user, request.session['handle'])
child = get_object_or_404(models.Child, issuer=conf, pk=pk)
z = Zookeeper(handle=conf.handle)
@@ -551,7 +559,6 @@ def get_covered_routes(rng, max_prefixlen, asn):
A "newstatus" attribute is monkey-patched on the RouteOrigin objects which
can be used in the template. "status" remains the current validation
status of the object.
-
"""
# find all routes that match or are completed covered by the proposed new roa
@@ -591,7 +598,6 @@ def roa_create(request):
Doesn't use the generic create_object() form because we need to
create both the ROARequest and ROARequestPrefix objects.
-
"""
conf = get_conf(request.user, request.session['handle'])
@@ -628,7 +634,6 @@ def roa_create(request):
class ROARequestFormSet(BaseFormSet):
"""There is no way to pass arbitrary keyword arguments to the form
constructor, so we have to override BaseFormSet to allow it.
-
"""
def __init__(self, *args, **kwargs):
self.conf = kwargs.pop('conf')
@@ -666,7 +671,6 @@ def roa_create_multi(request):
?roa=1.1.1.1-2.2.2.2,42
The ASN may optionally be omitted.
-
"""
conf = get_conf(request.user, request.session['handle'])
@@ -717,8 +721,8 @@ def roa_create_multi(request):
def roa_create_confirm(request):
"""This function is called when the user confirms the creation of a ROA
request. It is responsible for updating the IRDB.
-
"""
+
conf = get_conf(request.user, request.session['handle'])
log = request.META['wsgi.errors']
if request.method == 'POST':
@@ -746,8 +750,8 @@ def roa_create_confirm(request):
def roa_create_multi_confirm(request):
"""This function is called when the user confirms the creation of a ROA
request. It is responsible for updating the IRDB.
-
"""
+
conf = get_conf(request.user, request.session['handle'])
log = request.META['wsgi.errors']
if request.method == 'POST':
@@ -778,7 +782,6 @@ def roa_delete(request, pk):
Uses a form for double confirmation, displaying how the route
validation status may change as a result.
-
"""
conf = get_conf(request.user, request.session['handle'])
@@ -835,6 +838,7 @@ def roa_clone(request, pk):
@handle_required
def roa_import(request):
"""Import CSV containing ROA declarations."""
+
if request.method == 'POST':
form = forms.ImportCSVForm(request.POST, request.FILES)
if form.is_valid():
@@ -860,6 +864,7 @@ def roa_import(request):
@handle_required
def roa_export(request):
"""Export CSV containing ROA declarations."""
+
# FIXME: remove when Zookeeper can do this
f = cStringIO.StringIO()
csv_writer = csv.writer(f, delimiter=' ')
@@ -941,8 +946,8 @@ def ghostbuster_edit(request, pk):
def refresh(request):
"""
Query rpkid, update the db, and redirect back to the dashboard.
-
"""
+
conf = get_conf(request.user, request.session['handle'])
glue.list_received_resources(request.META['wsgi.errors'], conf)
return http.HttpResponseRedirect(reverse(dashboard))
@@ -953,8 +958,8 @@ def route_view(request):
"""
Display a list of global routing table entries which match resources
listed in received certificates.
-
"""
+
conf = get_conf(request.user, request.session['handle'])
count = request.GET.get('count', 25)
page = request.GET.get('page', 1)
@@ -972,6 +977,7 @@ def route_view(request):
def route_detail(request, pk):
"""Show a list of ROAs that match a given IPv4 route."""
+
route = get_object_or_404(models.RouteOrigin, pk=pk)
# when running rootd, viewing the 0.0.0.0/0 route will cause a fetch of all
# roas, so we paginate here, even though in the general case the number of
@@ -989,8 +995,8 @@ def route_suggest(request):
"""Handles POSTs from the route view and redirects to the ROA creation
page based on selected route objects. The form should contain elements of
the form "pk-NUM" where NUM is the RouteOrigin object id.
-
"""
+
if request.method == 'POST':
routes = []
for pk in request.POST.iterkeys():
@@ -1040,6 +1046,7 @@ def repository_delete(request, pk):
@handle_required
def repository_import(request):
"""Import XML response file from repository operator."""
+
return generic_import(request,
models.Repository.objects,
Zookeeper.configure_repository,
@@ -1094,8 +1101,8 @@ def client_import(request):
def client_export(request, pk):
"""Return the XML file resulting from a configure_publication_client
request.
-
"""
+
client = get_object_or_404(models.Client, pk=pk)
z = Zookeeper()
xml = z.generate_repository_response(client)
@@ -1107,6 +1114,7 @@ def client_export(request, pk):
@superuser_required
def resource_holder_list(request):
"""Display a list of all the RPKI handles managed by this server."""
+
return render(request, 'app/resource_holder_list.html', {
'object_list': models.Conf.objects.all()
})
@@ -1115,6 +1123,7 @@ def resource_holder_list(request):
@superuser_required
def resource_holder_edit(request, pk):
"""Display a list of all the RPKI handles managed by this server."""
+
conf = get_object_or_404(models.Conf, pk=pk)
if request.method == 'POST':
form = forms.ResourceHolderForm(request.POST, request.FILES)
@@ -1221,6 +1230,7 @@ def user_create(request):
@superuser_required
def user_list(request):
"""Display a list of all the RPKI handles managed by this server."""
+
return render(request, 'app/user_list.html', {
'object_list': User.objects.all()
})
@@ -1316,6 +1326,7 @@ class AlertDeleteView(DeleteView):
@handle_required
def alert_clear_all(request):
"""Clear all alerts associated with the current resource holder."""
+
if request.method == 'POST':
form = forms.Empty(request.POST, request.FILES)
if form.is_valid():