diff options
-rw-r--r-- | rpkid/rpki/gui/app/forms.py | 45 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/app/roa_request_confirm_delete.html | 50 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/app/roarequest_confirm_form.html | 58 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/templates/app/roarequest_form.html | 40 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/urls.py | 1 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/views.py | 121 |
6 files changed, 191 insertions, 124 deletions
diff --git a/rpkid/rpki/gui/app/forms.py b/rpkid/rpki/gui/app/forms.py index 2ddd1dbd..2fcceda2 100644 --- a/rpkid/rpki/gui/app/forms.py +++ b/rpkid/rpki/gui/app/forms.py @@ -22,6 +22,7 @@ from rpki.resource_set import (resource_range_as, resource_range_ipv4, resource_range_ipv6) from rpki.gui.app import models from rpki.exceptions import BadIPResource +from rpki.gui.app.glue import str_to_resource_range class AddConfForm(forms.Form): @@ -153,23 +154,19 @@ class ROARequest(forms.Form): def _as_resource_range(self): prefix = self.cleaned_data.get('prefix') - try: - r = resource_range_ipv4.parse_str(prefix) - except BadIPResource: - r = resource_range_ipv6.parse_str(prefix) - return r + return str_to_resource_range(prefix) def clean_asn(self): value = self.cleaned_data.get('asn') if value < 0: - raise forms.ValidationError, 'AS must be a positive value or 0' + raise forms.ValidationError('AS must be a positive value or 0') return value def clean_prefix(self): try: r = self._as_resource_range() except: - raise forms.ValidationError, 'invalid IP address' + raise forms.ValidationError('invalid IP address') return str(r) def clean_max_prefixlen(self): @@ -178,8 +175,7 @@ class ROARequest(forms.Form): if v[0] == '/': v = v[1:] # allow user to specify /24 if int(v) < 0: - raise forms.ValidationError, \ - 'max prefix length must be positive or 0' + raise forms.ValidationError('max prefix length must be positive or 0') return v def clean(self): @@ -188,8 +184,7 @@ class ROARequest(forms.Form): max_prefixlen = self.cleaned_data.get('max_prefixlen') max_prefixlen = int(max_prefixlen) if max_prefixlen else r.prefixlen() if max_prefixlen < r.prefixlen(): - raise forms.ValidationError, \ - 'max prefix length must be greater than or equal to the prefix length' + raise forms.ValidationError('max prefix length must be greater than or equal to the prefix length') if max_prefixlen > r.datum_type.bits: raise forms.ValidationError, \ 'max prefix length (%d) is out of range for IP version (%d)' % (max_prefixlen, r.datum_type.bits) @@ -198,6 +193,34 @@ class ROARequest(forms.Form): return self.cleaned_data +class ROARequestConfirm(forms.Form): + asn = forms.IntegerField(widget=forms.HiddenInput) + prefix = forms.CharField(widget=forms.HiddenInput) + max_prefixlen = forms.IntegerField(widget=forms.HiddenInput) + + def clean_asn(self): + value = self.cleaned_data.get('asn') + if value < 0: + raise forms.ValidationError('AS must be a positive value or 0') + return value + + def clean_prefix(self): + try: + r = str_to_resource_range(self.cleaned_data.get('prefix')) + except BadIPResource: + raise forms.ValidationError('invalid prefix') + return str(r) + + def clean(self): + try: + r =str_to_resource_range(self.cleaned_data.get('prefix')) + if r.prefixlen() > self.cleaned_data.get('max_prefixlen'): + raise forms.ValidationError('max length is smaller than mask') + except BadIPResource: + pass + return self.cleaned_data + + def AddASNForm(qs): """ Generate a form class which only allows specification of ASNs contained diff --git a/rpkid/rpki/gui/app/templates/app/roa_request_confirm_delete.html b/rpkid/rpki/gui/app/templates/app/roa_request_confirm_delete.html index a0e4b54d..4c8228b6 100644 --- a/rpkid/rpki/gui/app/templates/app/roa_request_confirm_delete.html +++ b/rpkid/rpki/gui/app/templates/app/roa_request_confirm_delete.html @@ -1,20 +1,19 @@ {% extends "app/app_base.html" %} {% block content %} - <div class='page-header'> <h1>Delete ROA Prefix</h1> </div> <div class='row'> <div class='span8'> - <div class='alert-message box-message warning'> - <p><strong>Please confirm</strong> that you would like to delete the following ROA Request: + <div class='alert-message block-message warning'> + <p><strong>Please confirm</strong> that you would like to delete the following ROA Request. The table to the right indicates how validation status for matching routes may change. <table style='condensed-table'> <tr> <th>Prefix</th> - <th>Max Len</th> + <th>Max Length</th> <th>AS</th> <tr> <td>{{ object.prefix }}/{{ object.prefixlen }}</td> @@ -30,31 +29,26 @@ <a class='btn' href="{% url rpki.gui.app.views.roa_list %}">Cancel</a> </div> </form> - </div> </div> -<div class='span8 offset8'> -<p>This table indicates what the validation status for the routing entries -that are covered by this ROA request will be if this ROA request is deleted: - -<table style='zebra-striped condensed-table'> - <tr> - <th>Prefix</th> - <th>Origin AS</th> - <th>Validation Status</th> - </tr> - {% for r in routes %} - <tr> - <td>{{ r.get_prefix_display }}</td> - <td>{{ r.asn }}</td> - <td><span class='label {{ r.status_label }}'>{{ r.status }}</span></td> - </tr> - {% endfor %} -</table> -</div><!-- /span8 --> + <div class='span8'> + <h2>Matching Routes</h2> + + <table style='zebra-striped condensed-table'> + <tr> + <th>Prefix</th> + <th>Origin AS</th> + <th>Validation Status</th> + </tr> + {% for r in routes %} + <tr> + <td>{{ r.get_prefix_display }}</td> + <td>{{ r.asn }}</td> + <td><span class='label {{ r.status_label }}'>{{ r.status }}</span></td> + </tr> + {% endfor %} + </table> + </div><!-- /span8 --> </div><!-- /row --> - -{% endblock %} - -<!-- vim:set sw=2: --> +{% endblock content %} diff --git a/rpkid/rpki/gui/app/templates/app/roarequest_confirm_form.html b/rpkid/rpki/gui/app/templates/app/roarequest_confirm_form.html new file mode 100644 index 00000000..99428875 --- /dev/null +++ b/rpkid/rpki/gui/app/templates/app/roarequest_confirm_form.html @@ -0,0 +1,58 @@ +{% extends "app/app_base.html" %} + +{% block content %} +<div class='page-title'> + <h1>Create ROA</h1> +</div> + +<div class='row'> + <div class='span8'> + <div class='alert-message block-message warning'> + <p><strong>Please confirm</strong> that you would like to create the following ROA. + The table on the right shows how the validation status may change as a result. + + <table class='condensed-table'> + <tr> + <th>AS</th> + <th>Prefix</th> + <th>Max Length</th> + </tr> + <tr> + <td>{{ asn }}</td> + <td>{{ prefix }}</td> + <td>{{ max_prefixlen }}</td> + </tr> + </table> + + <form method='POST' action='{% url rpki.gui.app.views.roa_create_confirm %}'> + {% csrf_token %} + {% include "app/bootstrap_form.html" %} + <div class='alert-actions'> + <input class='btn primary' type='submit' value='Create'/> + <a class='btn' href='{% url rpki.gui.app.views.roa_list %}'>Cancel</a> + </div> + </form> + </div><!-- /alert-message --> + </div> + + <div class='span8'> + <h2>Matched Routes</h2> + + <table style='zebra-striped condensed-table'> + <tr> + <th>Prefix</th> + <th>Origin AS</th> + <th>Validation Status</th> + </tr> + {% for r in routes %} + <tr> + <td>{{ r.get_prefix_display }}</td> + <td>{{ r.asn }}</td> + <td><span class='label {{ r.status_label }}'>{{ r.status }}</span></td> + </tr> + {% endfor %} + </table> + </div> + +</div> +{% endblock content %} diff --git a/rpkid/rpki/gui/app/templates/app/roarequest_form.html b/rpkid/rpki/gui/app/templates/app/roarequest_form.html index 83759120..5385cab0 100644 --- a/rpkid/rpki/gui/app/templates/app/roarequest_form.html +++ b/rpkid/rpki/gui/app/templates/app/roarequest_form.html @@ -1,42 +1,16 @@ {% extends "app/app_base.html" %} {% block content %} - <div class='page-title'> <h1>Create ROA</h1> </div> -<div class='row'> - <div class='span8'> - <form method='POST' action='{{ request.get_full_path }}'> - {% csrf_token %} - {% include "app/bootstrap_form.html" %} - <div class='actions'> - <input class='btn primary' type='submit' value='Create'/> - <a class='btn' href='{% url rpki.gui.app.views.roa_list %}'>Cancel</a> - </div> - </form> - </div> - <div class='span8 offset8'> - <p>Matched Routes - - <table style='zebra-striped condensed-table'> - <tr> - <th>Prefix</th> - <th>Origin AS</th> - <th>Validation Status</th> - </tr> - {% for r in routes %} - <tr> - <td>{{ r.get_prefix_display }}</td> - <td>{{ r.asn }}</td> - <td><span class='label {{ r.status_label }}'>{{ r.status }}</span></td> - </tr> - {% endfor %} - </table> +<form method='POST' action='{{ request.get_full_path }}'> + {% csrf_token %} + {% include "app/bootstrap_form.html" %} + <div class='actions'> + <input class='btn primary' type='submit' value='Create'/> + <a class='btn' href='{% url rpki.gui.app.views.roa_list %}'>Cancel</a> </div> -</div> - +</form> {% endblock content %} - -{# vim:set sw=2: #} diff --git a/rpkid/rpki/gui/app/urls.py b/rpkid/rpki/gui/app/urls.py index b2e30b12..cfe82777 100644 --- a/rpkid/rpki/gui/app/urls.py +++ b/rpkid/rpki/gui/app/urls.py @@ -54,6 +54,7 @@ urlpatterns = patterns('', (r'^repo/(?P<pk>\d+)/delete$', views.repository_delete), (r'^roa/$', views.roa_list), (r'^roa/create$', views.roa_create), + (r'^roa/confirm$', views.roa_create_confirm), (r'^roa/(?P<pk>\d+)$', views.roa_detail), (r'^roa/(?P<pk>\d+)/delete$', views.roa_delete), (r'^routes/$', views.route_view), diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py index 795080fc..b76495c0 100644 --- a/rpkid/rpki/gui/app/views.py +++ b/rpkid/rpki/gui/app/views.py @@ -428,7 +428,6 @@ def roa_create(request): """ - routes = [] if request.method == 'POST': form = forms.ROARequest(request.POST, request.FILES) if form.is_valid(): @@ -437,61 +436,79 @@ def roa_create(request): rng = form._as_resource_range() # FIXME calling "private" method max_prefixlen = int(form.cleaned_data.get('max_prefixlen')) - if form.cleaned_data.get('confirmed'): - 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. - roa = roarequests[0] - else: - roa = models.ROARequest.objects.create(issuer=conf, - asn=asn) - version = 'IPv4' if isinstance(rng, - 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)) - else: - form = forms.ROARequest(initial={ - 'asn': form.cleaned_data.get('asn'), - 'prefix': form.cleaned_data.get('prefix'), - 'max_prefixlen': form.cleaned_data.get('max_prefixlen'), - 'confirmed': True}) - - # find list of matching routes - match = roa_match(rng) - for route, roas in match: - validate_route(route, roas) - # tweak the validation status due to the presence of the - # new ROA. Don't need to check the prefix bounds here - # because all the matches routes will be covered by this - # new ROA - if route.status == 'unknown': - # if the route was previously unknown (no covering - # ROAs), then: - # if the AS matches, it is valid, otherwise invalid - if (route.asn != 0 and route.asn == asn and route.prefixlen() <= max_prefixlen): - route.status = 'valid' - route.status_label = 'success' - else: - route.status = 'invalid' - route.status_label = 'important' - elif route.status == 'invalid': - # if the route was previously invalid, but this new ROA - # matches the ASN, it is now valid - if route.asn != 0 and route.asn == asn and route.prefixlen() <= max_prefixlen: - route.status = 'valid' - route.status_label = 'success' - - routes.append(route) + # find list of matching routes + routes = [] + match = roa_match(rng) + for route, roas in match: + validate_route(route, roas) + # tweak the validation status due to the presence of the + # new ROA. Don't need to check the prefix bounds here + # because all the matches routes will be covered by this + # new ROA + if route.status == 'unknown': + # if the route was previously unknown (no covering + # ROAs), then: + # if the AS matches, it is valid, otherwise invalid + if (route.asn != 0 and route.asn == asn and route.prefixlen() <= max_prefixlen): + route.status = 'valid' + route.status_label = 'success' + else: + route.status = 'invalid' + route.status_label = 'important' + elif route.status == 'invalid': + # if the route was previously invalid, but this new ROA + # matches the ASN, it is now valid + if route.asn != 0 and route.asn == asn and route.prefixlen() <= max_prefixlen: + route.status = 'valid' + route.status_label = 'success' + + routes.append(route) + + prefix = str(rng) + form = forms.ROARequestConfirm(initial={'asn': asn, + 'prefix': prefix, + 'max_prefixlen': max_prefixlen}) + return render(request, 'app/roarequest_confirm_form.html', + {'form': form, + 'asn': asn, + 'prefix': prefix, + 'max_prefixlen': max_prefixlen, + 'routes': routes}) else: form = forms.ROARequest() - return render(request, 'app/roarequest_form.html', - {'form': form, 'routes': routes}) + return render(request, 'app/roarequest_form.html', {'form': form}) + + +@handle_required +def roa_create_confirm(request): + conf = request.session['handle'] + + if request.method == 'POST': + form = forms.ROARequestConfirm(request.POST, request.FILES) + if form.is_valid(): + asn = form.cleaned_data.get('asn') + prefix = form.cleaned_data.get('prefix') + rng = glue.str_to_resource_range(prefix) + max_prefixlen = form.cleaned_data.get('max_prefixlen') + 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. + roa = roarequests[0] + else: + roa = models.ROARequest.objects.create(issuer=conf, + asn=asn) + v = 'IPv4' if isinstance(rng, resource_range_ipv4) else 'IPv6' + roa.prefixes.create(version=v, prefix=str(rng.min), + prefixlen=rng.prefixlen(), + max_prefixlen=max_prefixlen) + return http.HttpResponseRedirect(reverse(roa_list)) + else: + return http.HttpResponseRedirect(reverse(roa_create)) @handle_required def roa_list(request): |