aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Elkins <melkins@tislabs.com>2010-07-01 00:09:36 +0000
committerMichael Elkins <melkins@tislabs.com>2010-07-01 00:09:36 +0000
commit44da2a8aaae8181269d35729ae3d165dec4a68d5 (patch)
tree168db92e1414e7a07c487f746e82050918eb7a58
parente59901e2bbdea5d6a9e4c26f699499c9c7696abe (diff)
commit recent rpki changes
svn path=/portal-gui/rpkigui/myrpki/admin.py; revision=3305
-rw-r--r--portal-gui/rpkigui/myrpki/admin.py8
-rw-r--r--portal-gui/rpkigui/myrpki/forms.py173
-rw-r--r--portal-gui/rpkigui/myrpki/glue.py152
-rw-r--r--portal-gui/rpkigui/myrpki/models.py166
-rw-r--r--portal-gui/rpkigui/myrpki/urls.py24
-rw-r--r--portal-gui/rpkigui/myrpki/views.py427
-rw-r--r--portal-gui/rpkigui/settings.py24
-rw-r--r--portal-gui/rpkigui/templates/base.html4
-rw-r--r--portal-gui/rpkigui/templates/myrpki/add_conf.html44
-rw-r--r--portal-gui/rpkigui/templates/myrpki/cert_confirm_delete.html10
-rw-r--r--portal-gui/rpkigui/templates/myrpki/cert_detail.html12
-rw-r--r--portal-gui/rpkigui/templates/myrpki/cert_form.html13
-rw-r--r--portal-gui/rpkigui/templates/myrpki/cert_list.html14
-rw-r--r--portal-gui/rpkigui/templates/myrpki/conf_list.html34
-rw-r--r--portal-gui/rpkigui/templates/myrpki/dashboard.html55
-rw-r--r--portal-gui/rpkigui/templates/myrpki/parent_resource.html30
-rw-r--r--portal-gui/rpkigui/templates/myrpki/parent_view.html68
-rw-r--r--portal-gui/rpkigui/templates/myrpki/resource_view.html86
-rw-r--r--portal-gui/rpkigui/templates/myrpki/roaform.html26
-rw-r--r--portal-gui/rpkigui/templates/myrpki/xml_import.html40
-rw-r--r--portal-gui/rpkigui/urls.py7
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&lt;something&gt; 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/'}),
)