diff options
Diffstat (limited to 'rpki/gui/app')
-rw-r--r-- | rpki/gui/app/check_expired.py | 6 | ||||
-rw-r--r-- | rpki/gui/app/forms.py | 17 | ||||
-rw-r--r-- | rpki/gui/app/glue.py | 3 | ||||
-rw-r--r-- | rpki/gui/app/models.py | 14 | ||||
-rwxr-xr-x | rpki/gui/app/range_list.py | 2 | ||||
-rw-r--r-- | rpki/gui/app/views.py | 41 |
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(): |