diff options
author | Michael Elkins <melkins@tislabs.com> | 2010-07-01 00:09:36 +0000 |
---|---|---|
committer | Michael Elkins <melkins@tislabs.com> | 2010-07-01 00:09:36 +0000 |
commit | 44da2a8aaae8181269d35729ae3d165dec4a68d5 (patch) | |
tree | 168db92e1414e7a07c487f746e82050918eb7a58 | |
parent | e59901e2bbdea5d6a9e4c26f699499c9c7696abe (diff) |
commit recent rpki changes
svn path=/portal-gui/rpkigui/myrpki/admin.py; revision=3305
21 files changed, 1181 insertions, 236 deletions
diff --git a/portal-gui/rpkigui/myrpki/admin.py b/portal-gui/rpkigui/myrpki/admin.py index a5a91043..72d330a2 100644 --- a/portal-gui/rpkigui/myrpki/admin.py +++ b/portal-gui/rpkigui/myrpki/admin.py @@ -1,9 +1,9 @@ from django import forms from django.contrib import admin -from rpki.myrpki import models +from rpkigui.myrpki import models -class CertAdmin( admin.ModelAdmin ): - pass +#class CertAdmin( admin.ModelAdmin ): +# pass class ConfAdmin( admin.ModelAdmin ): pass @@ -23,7 +23,7 @@ class ParentAdmin( admin.ModelAdmin ): class RoaAdmin( admin.ModelAdmin ): pass -admin.site.register( models.Cert, CertAdmin ) +#admin.site.register( models.Cert, CertAdmin ) admin.site.register( models.Conf, ConfAdmin ) admin.site.register( models.Child, ChildAdmin ) admin.site.register( models.AddressRange, AddressRangeAdmin ) diff --git a/portal-gui/rpkigui/myrpki/forms.py b/portal-gui/rpkigui/myrpki/forms.py index b91f6acc..ae7bca23 100644 --- a/portal-gui/rpkigui/myrpki/forms.py +++ b/portal-gui/rpkigui/myrpki/forms.py @@ -1,17 +1,168 @@ from django import forms from myrpki import models -def ConfCertForm( request ): - class CertForm( forms.ModelForm ): - class Meta: - model = models.Cert - exclude = ( 'conf' ) - - def save( self ): - obj = forms.ModelForm.save( self, commit=False ) - obj.conf = request.session[ 'handle' ] - obj.save() - return obj +def ConfCertForm(request): + class CertForm(forms.ModelForm): + class Meta: + model = models.Cert + exclude = ('conf') + + def save(self): + obj = forms.ModelForm.save(self, commit=False) + obj.conf = request.session['handle'] + obj.save() + return obj return CertForm +class AddConfForm(forms.Form): + handle = forms.CharField(required=True, + help_text='your handle for your rpki instance') + run_rpkid = forms.BooleanField(required=False, initial=True, + label='Run rpkid?', + help_text='do you want to run your own instance of rpkid?') + rpkid_server_host = forms.CharField(initial='rpkid.example.org', + label='rpkid hostname', + help_text='publicly visible hostname for your rpkid instance') + rpkid_server_port = forms.IntegerField(initial=4404, + label='rpkid port') + run_pubd = forms.BooleanField(required=False, initial=False, + label='Run pubd?', + help_text='do you want to run your own instance of pubd?') + pubd_server_host = forms.CharField(initial='pubd.example.org', + label='pubd hostname', + help_text='publicly visible hostname for your pubd instance') + pubd_server_port = forms.IntegerField(initial=4402, label='pubd port') + pubd_contact_info = forms.CharField(initial='repo-man@rpki.example.org', + label='Pubd contact', + 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() + xml = forms.FileField() + +class ChildImportForm(ImportForm): + validity = forms.DateTimeField(help_text='YYYY-MM-DD') + +def RangeForm(field_type, *args, **kwargs): + '''Generic form with an upper and lower bound.''' + class wrapped(forms.Form): + lo = field_type(label='Lower Bound') + hi = field_type(label='Upper Bound') + + def __init__(self, *inargs, **inkwargs): + forms.Form.__init__(self, *inargs, **inkwargs) + + def clean(self): + lo = self.cleaned_data.get('lo') + hi = self.cleaned_data.get('hi') + if lo > hi: + # should we just fix it? + raise forms.ValidationError, 'Lower bound is higher than upper.' + return self.cleaned_data + + return wrapped(*args, **kwargs) + +def AddressRangeForm(*args, **kwargs): + '''Form used for entering address ranges.''' + return RangeForm(forms.IPAddressField, *args, **kwargs) + +class AsnField(forms.IntegerField): + def __init__(self, *args, **kwargs): + forms.IntegerField.__init__(self, *args, **kwargs) + + def clean(self, val): + val = super(AsnField, self).clean(val) + if val < 0: + raise forms.ValidationError, 'Value out of range.' + return val + +def AsnRangeForm(*args, **kwargs): + '''Form used for entering asn ranges.''' + return RangeForm(AsnField, *args, **kwargs) + +def get_pk(child): + '''return the primary key or None if child is None''' + return child.pk if child else None + +def SubOrAssignForm(handle, addr, field_type, *args, **kwargs): + '''Closure to select child of the specified handle.''' + class Wrapper(forms.Form): + '''Form for the address view to allow the user to subdivide or assign + the block to a child.''' + lo = field_type(required=False, label='Lower bound') + hi = field_type(required=False, label='Upper bound') + child = forms.ModelChoiceField(required=False, label='Assign to child', + initial=get_pk(addr.allocated), queryset=handle.children.all()) + + #def __init__(self, *inargs, **inkwargs): + # super(Wrapper, self).__init__(*inargs, **inkwargs) + # if isinstance(addr, models.AddressRange): + # self.asn = forms.ModelChoiceField(required=False, + # label='Issue ROA', queryset=models.Asn.objects.all()) + + def clean_lo(self): + '''validate the self.lo field to ensure it is within the + parent's range.''' + lo = self.cleaned_data['lo'] + if lo is not None: + if lo < addr.lo or lo > addr.hi: + raise forms.ValidationError, 'Value is out of range of parent.' + # ensure there is no overlap with other children + for c in addr.children.all(): + if lo >= c.lo and lo <= c.hi: + raise forms.ValidationError, \ + 'Value overlaps another suballocation.' + return lo + + def clean_hi(self): + '''validate the self.hi field to ensure it is within the + parent's range.''' + hi = self.cleaned_data['hi'] + if hi is not None: + if hi < addr.lo or hi > addr.hi: + raise forms.ValidationError, 'Value is out of range of parent.' + # ensure there is no overlap with other children + for c in addr.children.all(): + if hi >= c.lo and hi <= c.hi: + raise forms.ValidationError, \ + 'Value overlaps another suballocation.' + return hi + + def clean_child(self): + if self.cleaned_data['child'] and addr.children.count(): + raise forms.ValidationError, "Can't allocate a subdivided address." + return self.cleaned_data['child'] + + def clean(self): + clean_data = self.cleaned_data + child = clean_data.get('child') + lo = clean_data.get('lo') + hi = clean_data.get('hi') + if child and (lo or hi): + raise forms.ValidationError, \ + 'Either a range or a child must be set, but not both.' + elif (lo and not hi) or (hi and not lo): + raise forms.ValidationError, \ + 'Both a high and low range must be specified.' + return clean_data + + return Wrapper(*args, **kwargs) + +def SubOrAssignAddressForm(handle, addr, *args, **kwargs): + return SubOrAssignForm(handle, addr, forms.IPAddressField, *args, **kwargs) + +def SubOrAssignAsnForm(handle, asn, *args, **kwargs): + return SubOrAssignForm(handle, asn, forms.IntegerField, *args, **kwargs) + +def RoaForm(handle, pk=None, initcomment=None, initval=[], *args, **kwargs): + vals = models.AddressRange.objects.filter(from_parent__in=handle.parents.all()) + + class Wrapped(forms.Form): + asn = AsnField(initial=pk) + prefix = forms.ModelMultipleChoiceField(label='Prefixes', + queryset=vals, initial=initval) + comments = forms.CharField(required=False, initial=initcomment) + + return Wrapped(*args, **kwargs) diff --git a/portal-gui/rpkigui/myrpki/glue.py b/portal-gui/rpkigui/myrpki/glue.py new file mode 100644 index 00000000..23609131 --- /dev/null +++ b/portal-gui/rpkigui/myrpki/glue.py @@ -0,0 +1,152 @@ +from __future__ import with_statement + +import os +import os.path +import csv +import math +import rpki.myrpki +from rpki.resource_set import resource_range_ipv4 +from rpki.ipaddrs import v4addr +import settings + +def form_to_conf(data): + """Write out a myrpki.conf based on the given form data.""" + handle = data['handle'] + confdir = settings.MYRPKI_DATA_DIR + '/' + handle + if os.path.exists(confdir): + raise RuntimeError, '%s: directory already exists!' % (confdir, ) + os.makedirs(confdir) + template = open(settings.MYRPKI_DATA_DIR + '/examples/myrpki.conf', 'r').read() + # stuff the appropriate output directory into the dict + data['MYRPKI_DATA_DIR'] = confdir + with open(confdir + '/myrpki.conf', 'w') as conf: + print >>conf, template % data + invoke_rpki(handle, ['initialize']) + +def invoke_rpki(handle, args): + """Invoke the myrpki cli for the specified configuration.""" + config = settings.MYRPKI_DATA_DIR + '/' + handle + '/myrpki.conf' + os.chdir(settings.MYRPKI_DATA_DIR + '/' + handle) + cmd = 'python ' + settings.MYRPKI_SRC_DIR + '/myrpki.py ' + ' '.join(['--config=' + config] + args) + print 'invoking', cmd + os.system(cmd) + +def read_identity(handle): + fname = settings.MYRPKI_DATA_DIR + '/' + handle + '/entitydb/identity.xml' + with open(fname, 'r') as fp: + data = fp.read() + return data + +def read_child_response(handle, child): + fname = '%s/%s/entitydb/children/%s.xml' % (settings.MYRPKI_DATA_DIR, handle, child) + with open(fname, 'r') as fp: + data = fp.read() + return data + +def output_asns(handle): + '''Write out csv file containing resources delegated to my children.''' + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/asns.csv', 'w'), delimiter='\t') + for p in handle.children.all(): + for asn in p.asn.all(): + if asn.lo == asn.hi: + f.writerow([p.handle, asn.lo]) + +def output_prefixes(handle): + '''Write out csv file containing resources delegated to my children.''' + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/prefixes.csv', 'w'), delimiter='\t') + for p in handle.children.all(): + for prefix in p.address_range.all(): + f.writerow([p.handle, '%s-%s' % (prefix.lo, prefix.hi)]) + +def output_roas(handle): + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/roas.csv', 'w'), delimiter='\t') + for r in handle.roas.all(): + for addr in r.prefix.all(): + f.writerow([resource_range_ipv4(v4addr(str(addr.lo)), v4addr(str(addr.hi))), + r.asn, handle.handle]) + +def configure_resources(handle): + # write out the .csv files and invoke the myrpki command line tool + output_asns(handle) + output_prefixes(handle) + output_roas(handle) + #invoke_rpki(handle.handle, ['configure_daemons']) +from __future__ import with_statement + +import os +import os.path +import csv +import math +import rpki.myrpki +from rpki.resource_set import resource_range_ipv4 +from rpki.ipaddrs import v4addr +import settings + +def form_to_conf(data): + """Write out a myrpki.conf based on the given form data.""" + handle = data['handle'] + confdir = settings.MYRPKI_DATA_DIR + '/' + handle + if os.path.exists(confdir): + raise RuntimeError, '%s: directory already exists!' % (confdir, ) + os.makedirs(confdir) + template = open(settings.MYRPKI_DATA_DIR + '/examples/myrpki.conf', 'r').read() + # stuff the appropriate output directory into the dict + data['MYRPKI_DATA_DIR'] = confdir + with open(confdir + '/myrpki.conf', 'w') as conf: + print >>conf, template % data + invoke_rpki(handle, ['initialize']) + +def invoke_rpki(handle, args): + """Invoke the myrpki cli for the specified configuration.""" + config = settings.MYRPKI_DATA_DIR + '/' + handle + '/myrpki.conf' + os.chdir(settings.MYRPKI_DATA_DIR + '/' + handle) + cmd = 'python ' + settings.MYRPKI_SRC_DIR + '/myrpki.py ' + ' '.join(['--config=' + config] + args) + print 'invoking', cmd + os.system(cmd) + +def read_identity(handle): + fname = settings.MYRPKI_DATA_DIR + '/' + handle + '/entitydb/identity.xml' + with open(fname, 'r') as fp: + data = fp.read() + return data + +def read_child_response(handle, child): + fname = '%s/%s/entitydb/children/%s.xml' % (settings.MYRPKI_DATA_DIR, handle, child) + with open(fname, 'r') as fp: + data = fp.read() + return data + +def output_asns(handle): + '''Write out csv file containing resources delegated to my children.''' + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/asns.csv', 'w'), delimiter='\t') + for p in handle.children.all(): + for asn in p.asn.all(): + if asn.lo == asn.hi: + f.writerow([p.handle, asn.lo]) + +def output_prefixes(handle): + '''Write out csv file containing resources delegated to my children.''' + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/prefixes.csv', 'w'), delimiter='\t') + for p in handle.children.all(): + for prefix in p.address_range.all(): + f.writerow([p.handle, '%s-%s' % (prefix.lo, prefix.hi)]) + +def output_roas(handle): + confdir = settings.MYRPKI_DATA_DIR + '/' + handle.handle + f = csv.writer(open(confdir + '/roas.csv', 'w'), delimiter='\t') + for r in handle.roas.all(): + for addr in r.prefix.all(): + f.writerow([resource_range_ipv4(v4addr(str(addr.lo)), v4addr(str(addr.hi))), + r.asn, handle.handle]) + +def configure_resources(handle): + # write out the .csv files and invoke the myrpki command line tool + output_asns(handle) + output_prefixes(handle) + output_roas(handle) + #invoke_rpki(handle.handle, ['configure_daemons']) diff --git a/portal-gui/rpkigui/myrpki/models.py b/portal-gui/rpkigui/myrpki/models.py index 85e23228..d062698e 100644 --- a/portal-gui/rpkigui/myrpki/models.py +++ b/portal-gui/rpkigui/myrpki/models.py @@ -1,104 +1,108 @@ from django.db import models from django.contrib.auth.models import Group -# TO DO: -# URL: text? -class HandleField( models.CharField ): - def __init__( self, **kwargs ): - models.CharField.__init__( self, max_length=255, **kwargs ) +class HandleField(models.CharField): + def __init__(self, **kwargs): + models.CharField.__init__(self, max_length=255, **kwargs) class IPAddressField( models.CharField ): def __init__( self, **kwargs ): - models.CharField.__init__( self, max_length=40, **kwargs ) - -class Cert( models.Model ): - '''A certificate, relating to a single configuration.''' - conf = models.ForeignKey( 'Conf', related_name='certs' ) - name = models.CharField( unique=True, max_length=255 ) - data = models.TextField() - def __unicode__( self ): - return "%s's %s" % ( self.conf, self.name ) + models.CharField.__init__(self, max_length=40, **kwargs) -class Conf( models.Model ): +class Conf(models.Model): '''This is the center of the universe, also known as a place to have a handle on a resource-holding entity. It's the <self> - in the rpkid schema. - ''' - handle = HandleField( unique=True, db_index=True ) - repository_bpki_cert = models.ForeignKey( Cert, - related_name='conf_bpki_cert', - null=True, blank=True ) - my_bpki_ta = models.ForeignKey( Cert, related_name='conf_my_ta', - null=True, blank=True ) - repository_handle = HandleField() - owner = models.OneToOneField( Group ) - def __unicode__( self ): + in the rpkid schema.''' + handle = HandleField(unique=True, db_index=True) + owner = models.OneToOneField(Group) + + def __unicode__(self): return self.handle -class AddressRange( models.Model ): - '''An address range / prefix.''' - lo = IPAddressField() - hi = IPAddressField() - parent = models.ForeignKey( 'AddressRange', related_name='children', - blank=True, null=True ) - def __unicode__( self ): - return u"address range %s-%s" % ( self.lo, self.hi ) +class AddressRange(models.Model): + '''An address range/prefix.''' + lo = IPAddressField(blank=False) + hi = IPAddressField(blank=False) + # parent address range + parent = models.ForeignKey('AddressRange', related_name='children', + blank=True, null=True) + allocated = models.ForeignKey('Child', related_name='address_range', + blank=True, null=True) + + def __unicode__(self): + if self.lo == self.hi: + return u"address %s" % (self.lo,) + else: + return u"address range %s-%s" % (self.lo, self.hi) + + def get_absolute_url(self): + return u'/myrpki/address/%d' % (self.pk,) -class Asn( models.Model ): +class Asn(models.Model): '''An ASN or range thereof.''' - min = models.IntegerField() - max = models.IntegerField() - parent = models.ForeignKey( 'Asn', related_name='children', - blank=True, null=True ) - def __unicode__( self ): - if self.min == self.max: - return u"ASN %d" % ( self.min ) + lo = models.IntegerField(blank=False) + hi = models.IntegerField(blank=False) + # parent asn range + parent = models.ForeignKey('Asn', related_name='children', + blank=True, null=True) + allocated = models.ForeignKey('Child', related_name='asn', + blank=True, null=True) + + def __unicode__(self): + if self.lo == self.hi: + return u"ASN %d" % (self.lo,) else: - return u"ASNs %d-%d" % ( self.min, self.max ) + return u"ASNs %d-%d" % (self.lo, self.hi) -class Child( models.Model ): - conf = models.ForeignKey( Conf, related_name='children' ) - handle = HandleField() + def get_absolute_url(self): + return u'/myrpki/asn/%d' % (self.pk,) + +class Child(models.Model): + conf = models.ForeignKey(Conf, related_name='children') + handle = HandleField() # parent's name for child validity = models.DateTimeField() - bpki_cert = models.ForeignKey( Cert, related_name='child_bpki' ) - # It may seem strange that the address_range and asn fields - # are ManyToManyFields. Why not simply a ForeignKey from an Asn - # and an AddressRange? This is for transition. When a resource - # is moving from one child to another, we may have to be delegating - # it to both children at once. - address_range = models.ManyToManyField( AddressRange, blank=True ) - asn = models.ManyToManyField( Asn, blank=True ) - def __unicode__( self ): - return u"%s's child %s" % ( self.conf, self.handle ) + + def __unicode__(self): + return u"%s's child %s" % (self.conf, self.handle) + + def get_absolute_url(self): + return u'/myrpki/child/%s' % (self.handle,) + class Meta: verbose_name_plural = "children" + # children of a specific configuration should be unique + unique_together = ('conf', 'handle') + +class Parent(models.Model): + conf = models.ForeignKey(Conf, related_name='parents') + handle = HandleField() +# service_uri = models.URLField( verify_exists=False ) +# sia_base = models.URLField( verify_exists=False ) + + # resources granted from my parent + address_range = models.ManyToManyField(AddressRange, blank=True, + related_name='from_parent') + asn = models.ManyToManyField(Asn, related_name='from_parent', blank=True) + + def __unicode__(self): + return u"%s's parent %s" % (self.conf, self.handle) + def get_absolute_url(self): + return u'/myrpki/parent/%s' % (self.handle,) -class Parent( models.Model ): - conf = models.ForeignKey( Conf, related_name='parents' ) - handle = HandleField( unique=True ) - service_uri = models.URLField( verify_exists=False ) - cms_bpki_cert = models.ForeignKey( Cert, related_name='parent_cms' ) - https_bpki_cert = models.ForeignKey( Cert, related_name='parent_https' ) - my_handle = HandleField() - sia_base = models.URLField( verify_exists=False ) - # It may seem strange that the address_range and asn fields - # are ManyToManyFields. Why not simply a ForeignKey from an Asn - # and an AddressRange? This is for transition. When a resource - # is moving from one parent to another, we may be receiving the - # resource from both parents at once. - address_range = models.ManyToManyField( AddressRange, - related_name='from_parent' ) - asn = models.ManyToManyField( Asn, related_name='from_parent' ) - def __unicode__( self ): - return u"%s's parent %s" % ( self.conf, self.handle ) - -class Roa( models.Model ): - conf = models.ForeignKey( Conf, related_name='roas' ) - prefix = models.ManyToManyField( AddressRange ) + class Meta: + # parents of a specific configuration should be unique + unique_together = ('conf', 'handle') + +class Roa(models.Model): + conf = models.ForeignKey(Conf, related_name='roas') + prefix = models.ManyToManyField(AddressRange) max_len = models.IntegerField() asn = models.IntegerField() - active = models.BooleanField() comments = models.TextField() - def __unicode__( self ): - return u"%s's ROA for %d" % ( self.conf, self.asn ) + + def __unicode__(self): + return u"%s's ROA for %d" % (self.conf, self.asn) + + def get_absolute_url(self): + return u'/myrpki/roa/%d' % (self.pk, ) diff --git a/portal-gui/rpkigui/myrpki/urls.py b/portal-gui/rpkigui/myrpki/urls.py index f5ba7c90..2a8e2a96 100644 --- a/portal-gui/rpkigui/myrpki/urls.py +++ b/portal-gui/rpkigui/myrpki/urls.py @@ -3,9 +3,23 @@ from django.views.generic.list_detail import object_list import views urlpatterns = patterns('', - (r'^cert/$', views.cert_list ), - (r'^cert/add/$', views.cert_add ), - (r'^cert/(?P<id>\d+)/$', views.cert_view ), - (r'^cert/(?P<id>\d+)/edit/$', views.cert_edit ), - (r'^cert/(?P<id>\d+)/delete/$', views.cert_delete ), +# (r'^cert/$', views.cert_list ), +# (r'^cert/add/$', views.cert_add ), +# (r'^cert/(?P<id>\d+)/$', views.cert_view ), +# (r'^cert/(?P<id>\d+)/edit/$', views.cert_edit ), +# (r'^cert/(?P<id>\d+)/delete/$', views.cert_delete ), + (r'^$', views.dashboard), +# (r'^conf/add$', views.conf_add), + (r'^conf/export$', views.conf_export), + (r'^conf/list$', views.conf_list), + (r'^conf/select$', views.conf_select), + (r'^import/parent$', views.parent_import), + (r'^import/child$', views.child_import), + (r'^parent/(?P<parent_handle>[^/]+)$', views.parent_view), +# (r'^parent/(?P<parent_handle>[^/]+)/address$', views.parent_address), +# (r'^parent/(?P<parent_handle>[^/]+)/asn$', views.parent_asn), + (r'^address/(?P<pk>\d+)$', views.address_view), + (r'^asn/(?P<pk>\d+)$', views.asn_view), + (r'^roa/$', views.roa_edit ), + (r'^roa/(?P<pk>\d+)$', views.roa_edit ), ) diff --git a/portal-gui/rpkigui/myrpki/views.py b/portal-gui/rpkigui/myrpki/views.py index 68cf7eba..1789fd91 100644 --- a/portal-gui/rpkigui/myrpki/views.py +++ b/portal-gui/rpkigui/myrpki/views.py @@ -1,3 +1,5 @@ +import os +import tempfile from django.views.generic.create_update import create_object, update_object, \ delete_object from django.views.generic.list_detail import object_detail, object_list @@ -5,92 +7,401 @@ from django.contrib.auth.decorators import login_required from django.shortcuts import get_object_or_404, render_to_response from django.utils.http import urlquote from django.template import RequestContext +from django.db import IntegrityError +from django.db.models import Q from django import http from functools import update_wrapper import models import forms - +import settings +import glue # 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. -class handle_required(object): - '''A decorator to require picking a configuration. __call__ is - decorated with login_required so that we can be sure that the - request has a user. - - We don't support picking the configuration yet -- if multiple - configurations match, we redirect to handle_picker, which should - allow a staff member to pick any handle. - ''' +def handle_required(f): + @login_required + def wrapped_fn(request, *args, **kwargs): + if 'handle' not in request.session: + conf = models.Conf.objects.all().filter(owner__in=request.user.groups.all()) + if conf.count() == 1: + handle = conf[ 0 ] + elif conf.count() == 0: + return http.HttpResponseRedirect('/myrpki/conf/add') + else: + # Should reverse the view for this instead of hardcoding + # the URL. + return http.HttpResponseRedirect( + '/myrpki/conf/list?next=%s' % + urlquote(request.get_full_path())) + request.session[ 'handle' ] = handle + return f(request, *args, **kwargs) + return wrapped_fn - def __init__(self, f): - self.f = f - update_wrapper( self, f ) +#class handle_required(object): +# '''A decorator to require picking a configuration. __call__ is +# decorated with login_required so that we can be sure that the +# request has a user. +# +# We don't support picking the configuration yet -- if multiple +# configurations match, we redirect to handle_picker, which should +# allow a staff member to pick any handle. +# ''' +# +# def __init__(self, f): +# self.f = f +# update_wrapper( self, f ) +# +# @login_required +# def __call__(self, request, *args, **kwargs): +# if 'handle' not in request.session: +# conf = models.Conf.objects.all().filter( +# owner__in=request.user.groups.all()) +# if conf.count() == 1: +# handle = conf[ 0 ] +# elif conf.count() == 0: +# return http.HttpResponseRedirect('/myrpki/conf/add') +# else: +# # Should reverse the view for this instead of hardcoding +# # the URL. +# return http.HttpResponseRedirect( +# '/myrpki/conf/select?next=%s' % +# urlquote(request.get_full_path())) +# request.session[ 'handle' ] = handle +# return self.f(request, *args, **kwargs) - @login_required - def __call__(self, request, *args, **kwargs): - if 'handle' not in request.session: - conf = models.Conf.objects.all().filter( - owner__in=request.user.groups.all() ) - if conf.count() == 1: - handle = conf[ 0 ] - else: - # Should reverse the view for this instead of hardcoding - # the URL. - return http.HttpResponseRedirect( '/handle_picker/?next=%s' % - urlquote(request.get_full_path()) ) - request.session[ 'handle' ] = handle - return self.f(request, *args, **kwargs) - -def render( template, context, request ): - return render_to_response( template, context, - context_instance=RequestContext(request) ) +def render(template, context, request): + return render_to_response(template, context, + context_instance=RequestContext(request)) @handle_required -def dashboard( request ): +def dashboard(request): '''The user's dashboard.''' handle = request.session[ 'handle' ] # ... pick out data for the dashboard and return it # my parents # the resources that my parents have given me - # the reousrces that I have accepted from my parents + # the resources that I have accepted from my parents # my children # the resources that I have given my children # my roas - return render( 'myrpki/dashboard.html', { 'conf': handle }, request ) + + # get list of ASNs used in my ROAs + roa_asns = [r.asn for r in handle.roas.all()] + # get list of unallocated asns + asns = [o for p in handle.parents.all() + for o in p.asn.filter(parent__isnull=True, allocated__isnull=True).exclude(lo__in=roa_asns) + if (o.hi == o.lo)] + + # get list of address ranges included in ROAs + roa_addrs = [p for r in handle.roas.all() for p in r.prefix.all()] + # get list of unallocated address ranges + ars = [o for p in handle.parents.all() + for o in p.address_range.filter(parent__isnull=True, allocated__isnull=True) + if (not o in roa_addrs)] + + return render('myrpki/dashboard.html', { 'conf': handle, 'asns': asns, + 'ars': ars }, request) + +#@handle_required +#def cert_add( request ): +# return create_object( request, form_class=forms.ConfCertForm( request ), +# post_save_redirect='/myrpki/cert/' ) + +#@handle_required +#def cert_view( request, id ): +# handle = request.session[ 'handle' ] +# queryset = models.Cert.objects.filter( conf=handle ) +# return object_detail( request, queryset=queryset, object_id=id, +# template_object_name='cert' ) +# +#@handle_required +#def cert_list( request ): +# handle = request.session[ 'handle' ] +# queryset = models.Cert.objects.filter( conf=handle ) +# return object_list( request, queryset=queryset, +# template_object_name='cert' ) +# +#@handle_required +#def cert_edit( request, id ): +# handle = request.session[ 'handle' ] +# cert = get_object_or_404( models.Cert, pk=id, conf=handle ) +# return update_object( request, form_class=forms.ConfCertForm( request ), +# object_id=id, +# post_save_redirect='/myrpki/cert/' ) +# +#@handle_required +#def cert_delete( request, id ): +# handle = request.session[ 'handle' ] +# cert = get_object_or_404( models.Cert, pk=id, conf=handle ) +# return delete_object( request, model=models.Cert, object_id=id, +# post_delete_redirect='/dashboard/' ) + +@login_required +def conf_add(request): + '''Allow the user to create a new configuration.''' + errors = [] + if request.method == 'POST': + form = forms.AddConfForm(request.POST) + if form.is_valid(): + try: + handle = form.cleaned_data['handle'] + # ensure this user is in the group for this handle + grps = request.user.groups.filter(name=handle) + if len(grps) == 0: + errors.append( + 'You are not in the proper group for that handle.') + else: + conf = models.Conf.objects.create( + handle=form.cleaned_data['handle'], owner=grps[0]) + conf.save() + glue.form_to_conf(form.cleaned_data) + return http.HttpResponseRedirect('/myrpki/') + # data model will ensure the handle is unique + except IntegrityError, e: + print e + errors.append('That handle already exists.') + else: + errors.append("The form wasn't valid.") + else: + form = forms.AddConfForm() + return render_to_response('myrpki/add_conf.html', + { 'form': form, 'errors': errors }) + +@login_required +def conf_list(request): + """Allow the user to select a handle.""" + queryset = models.Conf.objects.filter(owner__in=request.user.groups.all()) + return object_list(request, queryset, + template_name='myrpki/conf_list.html', template_object_name='conf') + +@login_required +def conf_select(request): + '''Change the handle for the current session.''' + if not 'handle' in request.GET: + return http.HttpResponseRedirect('/myrpki/conf/select') + handle = request.GET['handle'] + next_url = request.GET.get('next', '/myrpki/') + if next_url == '': + next_url = '/myrpki/' + + # since the handle is passed in as a parameter, need to verify that + # the user is actually in the group + conf = models.Conf.objects.filter( + Q(handle__exact=handle) & Q(owner__in=request.user.groups.all())) + if conf.count() > 0: + request.session['handle'] = conf[0] + return http.HttpResponseRedirect(next_url) + + return http.HttpResponseRedirect('/myrpki/conf/list?next=' + next_url) + +def serve_xml(content, basename): + resp = http.HttpResponse(content , mimetype='application/xml') + resp['Content-Disposition'] = 'attachment; filename=%s.xml' % (basename, ) + return resp @handle_required -def cert_add( request ): - return create_object( request, form_class=forms.ConfCertForm( request ), - post_save_redirect='/myrpki/cert/' ) +def conf_export(request): + """Return the identity.xml for the current handle.""" + handle = request.session['handle'] + return serve_xml(glue.read_identity(handle.handle), 'identity') @handle_required -def cert_view( request, id ): - handle = request.session[ 'handle' ] - queryset = models.Cert.objects.filter( conf=handle ) - return object_detail( request, queryset=queryset, object_id=id, - template_object_name='cert' ) +def parent_import(request): + handle = request.session['handle'].handle + errs = [] + if request.method == 'POST': + form = forms.ImportForm(request.POST, request.FILES) + if form.is_valid(): + input_file = tempfile.NamedTemporaryFile(delete=False) + try: + parent_handle = form.cleaned_data['handle'] + parent = models.Parent( + conf=request.session['handle'], handle=parent_handle) + parent.save() + + input_file.write(request.FILES['xml'].read()) + input_file.close() + + args = ['configure_parent', '--parent_handle=' + parent_handle, + input_file.name] + glue.invoke_rpki(handle, args) + + return http.HttpResponseRedirect('/myrpki/') + except IntegrityError, e: + print e + errs.append('A parent with that handle already exists.') + finally: + os.remove(input_file.name) + else: + print 'invalid form' + errs.append('The form was invalid.') + else: + form = forms.ImportForm() + return render('myrpki/xml_import.html', { 'form': form, + 'kind': 'parent', 'post_url': '/myrpki/import/parent', + 'errors': errs }, request) @handle_required -def cert_list( request ): - handle = request.session[ 'handle' ] - queryset = models.Cert.objects.filter( conf=handle ) - return object_list( request, queryset=queryset, - template_object_name='cert' ) +def parent_view(request, parent_handle): + """Detail view for a particular parent.""" + handle = request.session['handle'] + parent = get_object_or_404(handle.parents.all(), + handle__exact=parent_handle) + return render('myrpki/parent_view.html', { 'parent': parent }, request) + +#def parent_resource(request, parent_handle, obj_type, form_type): +# """Add an resource range to a parent.""" +# handle = request.session['handle'] +# parent = get_object_or_404(handle.parents.all(), +# handle__exact=parent_handle) +# if request.method == 'POST': +# form = form_type(request.POST) +# if form.is_valid(): +# obj = obj_type(parent).create( +# lo=form.cleaned_data['lo'], hi=form.cleaned_data['hi']) +# +# glue.configure_resources(handle) +# +# return http.HttpResponseRedirect('/myrpki/parent/' + parent_handle) +# else: +# form = form_type() +# return render('myrpki/parent_resource.html', +# { 'parent': parent, 'form': form }, request) + +#@handle_required +#def parent_address(request, parent_handle): +# return parent_resource(request, parent_handle, +# lambda p: p.address_range, forms.AddressRangeForm) + +#@handle_required +#def parent_asn(request, parent_handle): +# return parent_resource(request, parent_handle, +# lambda p: p.asn, forms.AsnRangeForm) @handle_required -def cert_edit( request, id ): - handle = request.session[ 'handle' ] - cert = get_object_or_404( models.Cert, pk=id, conf=handle ) - return update_object( request, form_class=forms.ConfCertForm( request ), - object_id=id, - post_save_redirect='/myrpki/cert/' ) +def child_import(request): + handle = request.session['handle'].handle + if request.method == 'POST': + form = forms.ChildImportForm(request.POST, request.FILES) + if form.is_valid(): + input_file = tempfile.NamedTemporaryFile(delete=False) + try: + child_handle = form.cleaned_data['handle'] + child = models.Child( + conf=request.session['handle'], handle=child_handle, + validity=form.cleaned_data['validity']) + child.save() + + input_file.write(request.FILES['xml'].read()) + input_file.close() + args = ['configure_child', '--child_handle=' + child_handle, + input_file.name] + glue.invoke_rpki(handle, args) + + # send response back to user + return serve_xml( + glue.read_child_response(handle, child_handle), + child_handle) + finally: + os.remove(input_file.name) + else: + print 'invalid form' + else: + form = forms.ChildImportForm() + return render('myrpki/xml_import.html', + { 'form': form, 'kind': 'child', + 'post_url': '/myrpki/import/child'}, request) + +def get_parent_or_404(handle, obj): + '''Return the Parent object that the given address range derives + from, or raise a 404 error.''' + while obj.parent: obj = obj.parent + + if isinstance(obj, models.AddressRange): + fn = lambda x: x.address_range.all() + else: + fn = lambda x: x.asn.all() + + for p in handle.parents.all(): + if obj in fn(p): return p + raise http.Http404 + +def resource_view(request, object_type, form_type, pk): + '''view/subdivide an address range.''' + handle = request.session['handle'] + obj = get_object_or_404(object_type, pk=pk) + # ensure this resource range belongs to a parent of the current conf + parent = get_parent_or_404(handle, obj) + + if request.method == 'POST': + form = form_type(handle, obj, request.POST) + if form.is_valid(): + if form.cleaned_data['child'] is None: + hi = form.cleaned_data['hi'] + lo = form.cleaned_data['lo'] + # if a range is given, create a new object + if hi != '' and lo != '': + subobj = object_type.objects.create( + lo=lo, hi=hi, parent=obj, allocated=None) + subobj.save() + if obj.allocated: + obj.allocated = None + obj.save() + else: + obj.allocated = form.cleaned_data['child'] + obj.save() + + glue.configure_resources(handle) + else: + form = form_type(handle, obj) + return render('myrpki/resource_view.html', { 'addr': obj, 'form': form, + 'parent': parent }, request) @handle_required -def cert_delete( request, id ): - handle = request.session[ 'handle' ] - cert = get_object_or_404( models.Cert, pk=id, conf=handle ) - return delete_object( request, model=models.Cert, object_id=id, - post_delete_redirect='/dashboard/' ) +def address_view(request, pk): + '''view/subdivide an address range.''' + return resource_view(request, models.AddressRange, + forms.SubOrAssignAddressForm, pk) + +@handle_required +def asn_view(request, pk): + '''view/subdivide an asn range.''' + return resource_view(request, models.Asn, forms.SubOrAssignAsnForm, pk) + +@handle_required +def roa_edit(request, pk=None): + '''Create or edit a ROA.''' + + handle = request.session['handle'] + + if not pk is None: + obj = get_object_or_404(models.Roa, pk=pk) + if obj.conf != handle: + raise http.Http404 + else: + obj = None + + if request.method == 'POST': + form = forms.RoaForm(handle, None, None, None, request.POST) + if form.is_valid(): + if not obj: + obj = models.Roa(conf=handle, asn=form.cleaned_data['asn'], + comments=form.cleaned_data['comments'], max_len=0) + else: + obj.asn = form.cleaned_data['asn'] + obj.comments = form.cleaned_data['comments'] + obj.save() + obj.prefix.clear() + obj.prefix.add(*form.cleaned_data['prefix']) + + glue.configure_resources(handle) + + return http.HttpResponseRedirect('/myrpki/') + else: + asn = obj.asn if obj else None + comments = obj.comments if obj else None + prefix = [o.pk for o in obj.prefix.all()] if obj else [] + form = forms.RoaForm(handle, asn, comments, prefix) + return render('myrpki/roaform.html', { 'form': form }, request) diff --git a/portal-gui/rpkigui/settings.py b/portal-gui/rpkigui/settings.py index af8417e8..529b0235 100644 --- a/portal-gui/rpkigui/settings.py +++ b/portal-gui/rpkigui/settings.py @@ -10,7 +10,7 @@ ADMINS = ( MANAGERS = ADMINS DATABASE_ENGINE = 'sqlite3' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -DATABASE_NAME = '/tmp/rpkiop' # Or path to database file if using sqlite3. +DATABASE_NAME = '/home/me/myrpki/rpkiop' # Or path to database file if using sqlite3. DATABASE_USER = '' # Not used with sqlite3. DATABASE_PASSWORD = '' # Not used with sqlite3. DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3. @@ -21,7 +21,7 @@ DATABASE_PORT = '' # Set to empty string for default. Not used with # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'America/Chicago' +TIME_ZONE = 'America/Los_Angeles' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html @@ -61,16 +61,18 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', + # for django 1.2 +# 'django.middleware.csrf.CsrfMiddleware' ) -ROOT_URLCONF = 'rpki.urls' +ROOT_URLCONF = 'rpkigui.urls' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. #XXX - '/Users/fenner/src/portal-gui/rpki/templates', + '/home/me/src/rpki/portal-gui/rpkigui/templates', ) INSTALLED_APPS = ( @@ -80,8 +82,8 @@ INSTALLED_APPS = ( 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', - 'rpki.myrpki', - 'rpki.django_extensions', + 'rpkigui.myrpki', +# 'rpki.django_extensions', ) TEMPLATE_CONTEXT_PROCESSORS = ( @@ -91,3 +93,13 @@ TEMPLATE_CONTEXT_PROCESSORS = ( "django.core.context_processors.media", "django.core.context_processors.request", ) + +# +# RPKI GUI specific +# + +# Top of directory tree where myrpki.conf, etc are stored for each resource holder +MYRPKI_DATA_DIR = '/home/me/myrpki' + +# where to find the myrpki.py command line tool +MYRPKI_SRC_DIR = '/home/me/src/rpki/rpkid' diff --git a/portal-gui/rpkigui/templates/base.html b/portal-gui/rpkigui/templates/base.html index 1f6018bf..aa5f924a 100644 --- a/portal-gui/rpkigui/templates/base.html +++ b/portal-gui/rpkigui/templates/base.html @@ -1,10 +1,10 @@ <html> <head> - <title>{% block title %}MyRPKI{% endblock %} + <title>{% block title %}MyRPKI{% endblock %}</title> {% block head %}{% endblock %} <style type="text/css"><!-- {% block css %}{% endblock %} - // --> + --> </style> </head> <body> diff --git a/portal-gui/rpkigui/templates/myrpki/add_conf.html b/portal-gui/rpkigui/templates/myrpki/add_conf.html new file mode 100644 index 00000000..b0994138 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/add_conf.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} + +{% block content %} +<h1>Add Configuration</h1> +<p>Please enter a unique handle for your RPKI instance. + +{% if errors %} +<ul> +{% for m in errors %} +<li class="errmsg">{{ m }} +{% endfor %} +</ul> +{% endif %} + +<form action="/myrpki/conf/add" method="post"> +<table> +{{ form.as_table }} +<tr><td><input type="submit" value="Submit" /></td></tr> +</table> +</form> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<h1>Add Configuration</h1> +<p>Please enter a unique handle for your RPKI instance. + +{% if errors %} +<ul> +{% for m in errors %} +<li class="errmsg">{{ m }} +{% endfor %} +</ul> +{% endif %} + +<form action="/myrpki/conf/add" method="post"> +<table> +{{ form.as_table }} +<tr><td><input type="submit" value="Submit" /></td></tr> +</table> +</form> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/cert_confirm_delete.html b/portal-gui/rpkigui/templates/myrpki/cert_confirm_delete.html deleted file mode 100644 index 661f4cc0..00000000 --- a/portal-gui/rpkigui/templates/myrpki/cert_confirm_delete.html +++ /dev/null @@ -1,10 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -<h1>Confirm Delete Cert</h1> - -<p>You would like to delete the certificate named {{ object.name }}. -<form action="" method="post"> -<input type="submit" value="Delete" /> -</form> -{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/cert_detail.html b/portal-gui/rpkigui/templates/myrpki/cert_detail.html deleted file mode 100644 index e9d11074..00000000 --- a/portal-gui/rpkigui/templates/myrpki/cert_detail.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -<h1>Cert {{ cert.name }}</h1> - -<pre> -{{ cert.data }} -</pre> - -<p><a href="edit/">[edit]</a> | <a href="delete/">[delete]</a> - -{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/cert_form.html b/portal-gui/rpkigui/templates/myrpki/cert_form.html deleted file mode 100644 index 2ff95fb0..00000000 --- a/portal-gui/rpkigui/templates/myrpki/cert_form.html +++ /dev/null @@ -1,13 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -<h1>Add/Edit Cert</h1> - -<form action="" method="post"> -<table> -{{ form }} -</table> -<input type="submit" value="Submit" /> -</form> - -{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/cert_list.html b/portal-gui/rpkigui/templates/myrpki/cert_list.html deleted file mode 100644 index 882a1301..00000000 --- a/portal-gui/rpkigui/templates/myrpki/cert_list.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -<h1>Certificates for {{ request.session.handle }}</h1> - -<p> -<a href="add/">[add]</a> -<p> -<ul> -{% for cert in cert_list %} -<li><a href="{{ cert.id }}/">{{ cert.name }}</a> -{% endfor %} -</ul> -{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/conf_list.html b/portal-gui/rpkigui/templates/myrpki/conf_list.html new file mode 100644 index 00000000..827994f4 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/conf_list.html @@ -0,0 +1,34 @@ +{% extends "base.html" %} + +{% block content %} +<h1>Handle List</h1> +<p>Please select a handle. + +<ul> +{% for c in conf_list %} +<li><a href="/myrpki/conf/select?handle={{ c.handle }}&next={{ next_url }}">{{ c.handle }}</a> +{% endfor %} +</ul> + +<!-- +<p><a href="/myrpki/conf/add?next={{ next_url }}">[create]</a> +--> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<h1>Handle List</h1> +<p>Please select a handle. + +<ul> +{% for c in conf_list %} +<li><a href="/myrpki/conf/select?handle={{ c.handle }}&next={{ next_url }}">{{ c.handle }}</a> +{% endfor %} +</ul> + +<!-- +<p><a href="/myrpki/conf/add?next={{ next_url }}">[create]</a> +--> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/dashboard.html b/portal-gui/rpkigui/templates/myrpki/dashboard.html index aef2b6cd..12fee303 100644 --- a/portal-gui/rpkigui/templates/myrpki/dashboard.html +++ b/portal-gui/rpkigui/templates/myrpki/dashboard.html @@ -1,48 +1,54 @@ {% extends "base.html" %} {% block content %} +<p>Handle: {{ request.session.handle }} +| <a href="/myrpki/conf/export">export</a> +| <a href="/myrpki/conf/list">change</a> + <div style="border: inset"> <h1 style="text-align: center">Parents</h1> -<a href="#">[add]</a> <ul> {% for parent in request.session.handle.parents.all %} -<li>{{ parent.handle }} (knows me as {{ parent.my_handle }}) +<li><a href="{{ parent.get_absolute_url }}">{{ parent.handle }}</a> {% if parent.asn.count or parent.address_range.count %} <p>Accepted resources: <ul> {% for asn in parent.asn.all %} -<li>{{ asn }} +<li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a> {% endfor %} {% for address in parent.address_range.all %} -<li>{{ address }} +<li><a href="{{ address.get_absolute_url }}">{{ address }}</a> {% endfor %} </ul> {% endif %} {% endfor %} </ul> +<a href="/myrpki/import/parent">[add]</a> </div> + <span> <div style="border: outset"> <h1 style="text-align: center">Children</h1> -<a href="#">[add]</a> <p> <ul> {% for child in request.session.handle.children.all %} -<li>{{ child.handle }} +<li><a href="/myrpki/child/{{ child.handle }}/">{{ child.handle }}</a> {% if child.address_range.count or child.asn.count %} <p>Delegated resources: <ul> {% for asn in child.asn.all %} -<li>{{ asn }} +<li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a> {% endfor %} {% for address in child.address_range.all %} -<li>{{ address }} +<li><a href="{{ address.get_absolute_url}}">{{ address }}</a> {% endfor %} </ul> {% endif %} {% endfor %} </ul> +<a href="/myrpki/import/child">[add]</a> </div> + <div style="border: outset"> <h1 style="text-align: center">My ROA [request]s</h1> {% if request.session.handle.roas.count %} @@ -51,8 +57,8 @@ <th>Prefix</th> <th>ASN</th> <th>Max Len</th> -<th>Active</th> <th>Comments</th> +<th></th> </tr> {% for roa in request.session.handle.roas.all %} {% for address_range in roa.prefix.all %} @@ -60,8 +66,8 @@ <td>{{ address_range }}</td> <td>{{ roa.asn }}</td> <td>{{ roa.max_len }}</td> -<td>{{ roa.active }}</td> <td>{{ roa.comments }}</td> +<td><a href="{{ roa.get_absolute_url }}">edit</a></td> </tr> {% endfor %} {% endfor %} @@ -70,17 +76,32 @@ <p> -- none -- {% endif %} +<p><a href="/myrpki/roa/">[add]</a> </div> + <div style="border: outset"> -<h1 style="text-align: center">Un<something> Resources</h1> +<h1 style="text-align: center">Unallocated Resources</h1> +{% if asns or ars %} <ul> + {% for asn in asns %} + <li><a href="{{ asn.get_absolute_url }}">{{ asn }}</a> + {% endfor %} + {% for addr in ars %} + <li><a href="{{ addr.get_absolute_url }}">{{ addr }}</a> + {% endfor %} +{% else %} +<p>-- none -- +{% endif %} + + <!-- <li>Address range 172.17.2.0-172.17.255.255 - <a href="#">[subdivide]</a> | - <a href="#">[give to child]</a> | - <a href="#">[issue roa]</a> -<li>ASN 1 <a href="#">[give to child]</a> -<li>ASNs 3-100 <a href="#">[subdivide]</a> | - <a href="#">[give to child]</a> + | <a href="#">subdivide</a> | + <a href="#">give to child</a> | + <a href="#">issue roa</a> +<li>ASN 1 | <a href="#">give to child</a> +<li>ASNs 3-100 | <a href="#">subdivide</a> | + <a href="#">give to child</a> + --> </ul> </div> </span> diff --git a/portal-gui/rpkigui/templates/myrpki/parent_resource.html b/portal-gui/rpkigui/templates/myrpki/parent_resource.html new file mode 100644 index 00000000..b0b61a86 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/parent_resource.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% block content %} + +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<h1>Add Resource Range</h1> +<form action="{{ request.url }}" method="post"> +<table> +{{ form.as_table }} +</table> +<input type="submit" value="Submit" /> +</form> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} + +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<h1>Add Resource Range</h1> +<form action="{{ request.url }}" method="post"> +<table> +{{ form.as_table }} +</table> +<input type="submit" value="Submit" /> +</form> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/parent_view.html b/portal-gui/rpkigui/templates/myrpki/parent_view.html new file mode 100644 index 00000000..5da574a2 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/parent_view.html @@ -0,0 +1,68 @@ +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle.handle }}</a> +<h1>Parent View</h1> +<p>Parent: {{ parent.handle }} +<h2>Delegated Addresses</h2> +{% if parent.address_range.count %} +<table> + <tr><td>Low</td><td>High</td><td></td></tr> +{% for a in parent.address_range.all %} +<tr> + <td>{{ a.lo }}</td> + <td>{{ a.hi }}</td> + <td><a href="{{ a.get_absolute_url }}">view</a></td> +</tr> +{% endfor %} +</table> +{% else %} +<p>--none-- +{% endif %} + +<h2>Delegated ASNs</h2> +{% if parent.asn.count %} +<ul> +{% for a in parent.asn.all %} +<li><a href="{{ a.get_absolute_url }}">{{ a }}</a> +{% endfor %} +</ul> +{% else %} +<p>--none-- +{% endif %} + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle.handle }}</a> +<h1>Parent View</h1> +<p>Parent: {{ parent.handle }} +<h2>Delegated Addresses</h2> +{% if parent.address_range.count %} +<table> + <tr><td>Low</td><td>High</td><td></td></tr> +{% for a in parent.address_range.all %} +<tr> + <td>{{ a.lo }}</td> + <td>{{ a.hi }}</td> + <td><a href="{{ a.get_absolute_url }}">view</a></td> +</tr> +{% endfor %} +</table> +{% else %} +<p>--none-- +{% endif %} + +<h2>Delegated ASNs</h2> +{% if parent.asn.count %} +<ul> +{% for a in parent.asn.all %} +<li><a href="{{ a.get_absolute_url }}">{{ a }}</a> +{% endfor %} +</ul> +{% else %} +<p>--none-- +{% endif %} + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/resource_view.html b/portal-gui/rpkigui/templates/myrpki/resource_view.html new file mode 100644 index 00000000..55dc1b46 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/resource_view.html @@ -0,0 +1,86 @@ +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> +<h1>Resource Range View</h1> +<table> + <tr> + <td>Range:</td><td>{{ addr }}</td> + </tr> + <tr> + <td>Parent:</td> + <td><a href="{{ parent.get_absolute_url }}">{{ parent.handle }}</a></td> + </tr> + {% if addr.parent %} + <tr> + <td>Suballocated from:</td> + <td><a href="{{ addr.parent.get_absolute_url }}">{{ addr.parent }}</a></td> + </tr> + {% endif %} +</table> + +<h2>Suballocations</h2> + +{% if addr.children.count %} + +<ul> +{% for subaddr in addr.children.all %} +<li><a href="{{ subaddr.get_absolute_url }}">{{ subaddr }}</a> +{% endfor %} +</ul> + +{% else %} + +<p>--none-- + +{% endif %} + +<form action="{{ request.url }}" method="post"> + {{ form.as_p }} + <p><input type="submit" value="Submit"> +</form> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> +<h1>Resource Range View</h1> +<table> + <tr> + <td>Range:</td><td>{{ addr }}</td> + </tr> + <tr> + <td>Parent:</td> + <td><a href="{{ parent.get_absolute_url }}">{{ parent.handle }}</a></td> + </tr> + {% if addr.parent %} + <tr> + <td>Suballocated from:</td> + <td><a href="{{ addr.parent.get_absolute_url }}">{{ addr.parent }}</a></td> + </tr> + {% endif %} +</table> + +<h2>Suballocations</h2> + +{% if addr.children.count %} + +<ul> +{% for subaddr in addr.children.all %} +<li><a href="{{ subaddr.get_absolute_url }}">{{ subaddr }}</a> +{% endfor %} +</ul> + +{% else %} + +<p>--none-- + +{% endif %} + +<form action="{{ request.url }}" method="post"> + {{ form.as_p }} + <p><input type="submit" value="Submit"> +</form> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/roaform.html b/portal-gui/rpkigui/templates/myrpki/roaform.html new file mode 100644 index 00000000..2678ed12 --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/roaform.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} + +{% block content %} +<h1>Edit ROA</h1> + +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<form action="{{ request.url }}" method="POST"> +{{ form.as_p }} +<p><input type="submit" value="Submit"> +</form> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<h1>Edit ROA</h1> + +<p>Handle: <a href="/myrpki/">{{ request.session.handle }}</a> + +<form action="{{ request.url }}" method="POST"> +{{ form.as_p }} +<p><input type="submit" value="Submit"> +</form> + +{% endblock %} diff --git a/portal-gui/rpkigui/templates/myrpki/xml_import.html b/portal-gui/rpkigui/templates/myrpki/xml_import.html new file mode 100644 index 00000000..bdd449ad --- /dev/null +++ b/portal-gui/rpkigui/templates/myrpki/xml_import.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% block content %} +<h1>Import {{ kind }}</h1> +<p>Handle: {{ request.session.handle }} +<p>Please select the xml file containing the identity of the <strong>{{ kind }}</strong> to import. +{% if errors %} +<ul> +{% for e in errors %} +<li>{{ e }} +{% endfor %} +</ul> +{% endif %} + +<form enctype="multipart/form-data" action="{{ post_url }}" method="post"> +{{ form.as_p }} +<input type="submit" value="Submit" /> +</form> + +{% endblock %} +{% extends "base.html" %} + +{% block content %} +<h1>Import {{ kind }}</h1> +<p>Handle: {{ request.session.handle }} +<p>Please select the xml file containing the identity of the <strong>{{ kind }}</strong> to import. +{% if errors %} +<ul> +{% for e in errors %} +<li>{{ e }} +{% endfor %} +</ul> +{% endif %} + +<form enctype="multipart/form-data" action="{{ post_url }}" method="post"> +{{ form.as_p }} +<input type="submit" value="Submit" /> +</form> + +{% endblock %} diff --git a/portal-gui/rpkigui/urls.py b/portal-gui/rpkigui/urls.py index f754fb23..b5947c91 100644 --- a/portal-gui/rpkigui/urls.py +++ b/portal-gui/rpkigui/urls.py @@ -14,14 +14,15 @@ urlpatterns = patterns('', # Uncomment the next line to enable the admin: (r'^admin/', include(admin.site.urls)), - (r'^dashboard/', include('myrpki.dashboardurls')), - (r'^myrpki/', include('myrpki.urls')), + #(r'^dashboard/', include('myrpki.dashboardurls')), + (r'^myrpki/', include('rpkigui.myrpki.urls')), (r'^accounts/login/$', 'django.contrib.auth.views.login'), (r'^accounts/logout/$', 'django.contrib.auth.views.logout'), #XXX (r'^site_media/(?P<path>.*)$', 'django.views.static.serve', - {'document_root': '/Users/fenner/src/portal-gui/media/'}), + #{'document_root': '/Users/fenner/src/portal-gui/media/'}), + {'document_root': '/home/me/src/rpki/portal-gui/media/'}), ) |