diff options
Diffstat (limited to 'rpkid/rpki')
-rw-r--r-- | rpkid/rpki/gui/cacheview/admin.py | 14 | ||||
-rw-r--r-- | rpkid/rpki/gui/cacheview/models.py | 51 | ||||
-rw-r--r-- | rpkid/rpki/gui/cacheview/templates/cacheview/cert_detail.html | 23 | ||||
-rw-r--r-- | rpkid/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html | 19 | ||||
-rw-r--r-- | rpkid/rpki/rcynic.py | 49 |
5 files changed, 103 insertions, 53 deletions
diff --git a/rpkid/rpki/gui/cacheview/admin.py b/rpkid/rpki/gui/cacheview/admin.py index 2b88c1f3..05bab881 100644 --- a/rpkid/rpki/gui/cacheview/admin.py +++ b/rpkid/rpki/gui/cacheview/admin.py @@ -37,8 +37,13 @@ class ROAAdmin(admin.ModelAdmin): class GhostbusterAdmin(admin.ModelAdmin): pass -class ValidationStatusAdmin(admin.ModelAdmin): - pass +class ValidationLabelAdmin(admin.ModelAdmin): pass + +class ValidationStatus_CertAdmin(admin.ModelAdmin): pass + +class ValidationStatus_ROAAdmin(admin.ModelAdmin): pass + +class ValidationStatus_GhostbusterAdmin(admin.ModelAdmin): pass admin.site.register(models.AddressRange, AddressRangeAdmin) admin.site.register(models.ASRange, AddressRangeAdmin) @@ -46,6 +51,9 @@ admin.site.register(models.Cert, CertAdmin) admin.site.register(models.Ghostbuster, GhostbusterAdmin) admin.site.register(models.ROA, ROAAdmin) admin.site.register(models.ROAPrefix, ROAPrefixAdmin) -admin.site.register(models.ValidationStatus, ValidationStatusAdmin) +admin.site.register(models.ValidationLabel, ValidationLabelAdmin) +admin.site.register(models.ValidationStatus_Cert, ValidationStatus_CertAdmin) +admin.site.register(models.ValidationStatus_ROA, ValidationStatus_ROAAdmin) +admin.site.register(models.ValidationStatus_Ghostbuster, ValidationStatus_GhostbusterAdmin) # vim:sw=4 ts=8 diff --git a/rpkid/rpki/gui/cacheview/models.py b/rpkid/rpki/gui/cacheview/models.py index d68601fc..077a28ff 100644 --- a/rpkid/rpki/gui/cacheview/models.py +++ b/rpkid/rpki/gui/cacheview/models.py @@ -73,9 +73,10 @@ class ASRange(models.Model): def get_absolute_url(self): return ('rpki.gui.cacheview.views.asrange_detail', [str(self.pk)]) -kinds = ( (0, 'good'), (1, 'warn'), (2, 'bad') ) +kinds = list(enumerate(('good', 'warn', 'bad'))) +kinds_dict = dict((v,k) for k,v in kinds) -class ValidationStatus(models.Model): +class ValidationLabel(models.Model): """ Represents a specific error condition defined in the rcynic XML output file. @@ -87,11 +88,19 @@ class ValidationStatus(models.Model): def __unicode__(self): return self.label - def kind_as_str(self): - return kinds[self.kind][1] + class Meta: + verbose_name_plural = 'ValidationLabels' + +generations = list(enumerate(('current', 'backup'))) +generations_dict = dict((val, key) for (key, val) in generations) + +class ValidationStatus(models.Model): + timestamp = models.DateTimeField() + generation = models.PositiveSmallIntegerField(choices=generations, null=True) + status = models.ForeignKey('ValidationLabel') class Meta: - verbose_name_plural = 'ValidationStatuses' + abstract = True class SignedObject(models.Model): """ @@ -101,10 +110,8 @@ class SignedObject(models.Model): """ # attributes from rcynic's output XML file uri = models.URLField(unique=True, db_index=True) - timestamp = models.DateTimeField() - ok = models.BooleanField() - status = models.ForeignKey('ValidationStatus') + # on-disk file modification time mtime = models.PositiveIntegerField(default=0) # SubjectName @@ -126,6 +133,24 @@ class SignedObject(models.Model): """ return datetime.utcfromtimestamp(self.mtime + time.timezone) + def is_valid(self): + """ + Returns a boolean value indicating whether this object has passed + validation checks. + """ + return bool(self.statuses.filter(status=ValidationLabel.objects.get(label="object_accepted"))) + + def status_id(self): + """ + Returns a HTML class selector for the current object based on its validation status. + The selector is chosen based on the current generation only. If there is any bad status, + return bad, else if there are any warn status, return warn, else return good. + """ + for x in reversed(kinds): + if self.statuses.filter(generation=generations_dict['current'], status__kind=x[0]): + return x[1] + return None # should not happen + def __unicode__(self): return u'%s' % self.name @@ -136,11 +161,15 @@ class Cert(SignedObject): addresses = models.ManyToManyField(AddressRange, related_name='certs') asns = models.ManyToManyField(ASRange, related_name='certs') issuer = models.ForeignKey('Cert', related_name='children', null=True, blank=True) + sia = models.CharField(max_length=255) @models.permalink def get_absolute_url(self): return ('rpki.gui.cacheview.views.cert_detail', [str(self.pk)]) +class ValidationStatus_Cert(ValidationStatus): + cert = models.ForeignKey('Cert', related_name='statuses') + class ROAPrefix(models.Model): family = models.PositiveIntegerField() prefix = models.IPAddressField() @@ -175,6 +204,9 @@ class ROA(SignedObject): def get_absolute_url(self): return ('rpki.gui.cacheview.views.roa_detail', [str(self.pk)]) +class ValidationStatus_ROA(ValidationStatus): + roa = models.ForeignKey('ROA', related_name='statuses') + class Ghostbuster(SignedObject): full_name = models.CharField(max_length=40) email_address = models.EmailField(blank=True, null=True) @@ -195,4 +227,7 @@ class Ghostbuster(SignedObject): return self.email_address return self.telephone +class ValidationStatus_Ghostbuster(ValidationStatus): + gbr = models.ForeignKey('Ghostbuster', related_name='statuses') + # vim:sw=4 ts=8 expandtab diff --git a/rpkid/rpki/gui/cacheview/templates/cacheview/cert_detail.html b/rpkid/rpki/gui/cacheview/templates/cacheview/cert_detail.html index a0291d7b..9ff304a2 100644 --- a/rpkid/rpki/gui/cacheview/templates/cacheview/cert_detail.html +++ b/rpkid/rpki/gui/cacheview/templates/cacheview/cert_detail.html @@ -32,6 +32,7 @@ Resource Certificate Detail <h2>Issued Objects</h2> <ul> +{% if object.ghostbusters.all %} <li> <h3>Ghostbusters</h3> @@ -39,26 +40,27 @@ Resource Certificate Detail <tr><th>Name</th><th>Valid</th><th>Until</th></tr> {% for g in object.ghostbusters.all %} - <tr class='{{ g.status.kind_as_str }}'> + <tr class='{{ g.status_id }}'> <td><a href="{{ g.get_absolute_url }}">{{ g }}</a></td> - <td>{{ g.ok }}</td> + <td>{{ g.is_valid }}</td> <td>{{ g.not_after }}</td> </tr> {% endfor %} </table> +{% endif %} +{% if object.roas.all %} <li> <h3>ROAs</h3> -{% if object.roas.all %} <table> <tr><th>Prefix</th><th>AS</th><th>Valid</th><th>Until</th></tr> {% for roa in object.roas.all %} {% for pfx in roa.prefixes.all %} - <tr class='{{ roa.status.kind_as_str }}'> + <tr class='{{ roa.status_id }}'> <td>{{ pfx }}</td> <td>{{ roa.asid }}</td> - <td><a href="{{ roa.get_absolute_url }}">{{ roa.ok }}</a></td> + <td><a href="{{ roa.get_absolute_url }}">{{ roa.is_valid }}</a></td> <td>{{ roa.not_after }}</td> </tr> {% endfor %} @@ -66,21 +68,24 @@ Resource Certificate Detail </table> {% endif %} +{% if object.children.all %} <li> - <h3>Children</h3> <table> - <tr><th>Name</th><th>Valid</th><th>Until</th></tr> + <tr><th>Name</th><th>SIA</th><th>Valid</th><th>Until</th><th>Ghostbuster</th></tr> {% for child in object.children.all %} - <tr class='{{ child.status.kind_as_str }}'> + <tr class='{{ child.status_id }}'> <td><a href="{{ child.get_absolute_url }}">{{ child.name }}</a></td> - <td>{{ child.ok }}</td> + <td>{{ child.sia }}</td> + <td>{{ child.is_valid }}</td> <td>{{ child.not_after }}</td> + <td><a href="{{ child.ghostbusters.all.0.get_absolute_url }}">{{ child.ghostbusters.all.0 }}</a></td> </tr> {% endfor %} </table> +{% endif %} </ul> diff --git a/rpkid/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html b/rpkid/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html index 45dff81b..b5f629d8 100644 --- a/rpkid/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html +++ b/rpkid/rpki/gui/cacheview/templates/cacheview/signedobject_detail.html @@ -8,22 +8,29 @@ <table> <tr><td>URI</td><td>{{ object.uri }}</td></tr> <tr><td>Last Modified</td><td>{{ object.mtime_as_datetime|date:"DATETIME_FORMAT" }}</td></tr> - <tr><td>Timestamp</td><td>{{ object.timestamp }}</td></tr> - <tr><td>Valid</td><td>{{ object.ok }}</td></tr> - <tr><td>Status</td><td class='{{ object.status.kind_as_str }}'>{{ object.status.status }}</td></tr> +</table> + +<h3>Validation Status</h3> +<table> + <tr><th>Timestamp</th><th>Generation</th><th>Status</th></tr> + {% for status in object.statuses.all %} + <tr class="{{ status.status.get_kind_display }}"><td>{{ status.timestamp }}</td><td>{{ status.get_generation_display }}</td><td>{{ status.status.status }}</td></tr> + {% endfor %} </table> <h2>X.509 Certificate Chain</h2> <table> - <tr><th>Depth</th><th>Name</th><th>Valid</th><th>Until</th></tr> + <tr><th>Depth</th><th>Name</th><th>SIA</th><th>Valid</th><th>Until</th><th>Ghostbuster</th></tr> {% for cert in chain %} -<tr class='{{ cert.1.status.kind_as_str }}'> +<tr class='{{ cert.1.status_id }}'> <td>{{ cert.0 }}</td> <td><a href="{{ cert.1.get_absolute_url }}">{{ cert.1.name }}</a></td> - <td>{{ cert.1.ok }}</td> + <td>{{ cert.1.sia }}</td> + <td>{{ cert.1.is_valid }}</td> <td>{{ cert.1.not_after }}</td> + <td><a href="{{ cert.1.ghostbusters.all.0.get_absolute_url }}">{{ cert.1.ghostbusters.all.0 }}</a></td> </tr> {% endfor %} diff --git a/rpkid/rpki/rcynic.py b/rpkid/rpki/rcynic.py index c29f5605..c2562cbd 100644 --- a/rpkid/rpki/rcynic.py +++ b/rpkid/rpki/rcynic.py @@ -187,12 +187,25 @@ class rcynic_file_iterator(object): if ext in file_name_classes: yield file_name_classes[ext](filename) +class validation_status_element(object): + def __init__(self, *args, **kwargs): + for k,v in kwargs.iteritems(): + setattr(self, k, v) + self._obj = None + + def get_obj(self): + if not self._obj: + self._obj = self.file_class(filename=self.filename, uri=self.uri) + return self._obj + + obj = property(get_obj) + class rcynic_xml_iterator(object): """ Iterate over validation_status entries in the XML output from an rcynic run. Yields a tuple for each entry: - URI, OK, status, timestamp, object + timestamp, generation, status, object where URI, status, and timestamp are the corresponding values from the XML element, OK is a boolean indicating whether validation was @@ -209,12 +222,10 @@ class rcynic_xml_iterator(object): """ def __init__(self, rcynic_root, xml_file, - authenticated_subdir = "authenticated", authenticated_old_subdir = "authenticated.old", unauthenticated_subdir = "unauthenticated"): self.rcynic_root = rcynic_root self.xml_file = xml_file - self.authenticated_subdir = os.path.join(rcynic_root, authenticated_subdir) self.authenticated_old_subdir = os.path.join(rcynic_root, authenticated_old_subdir) self.unauthenticated_subdir = os.path.join(rcynic_root, unauthenticated_subdir) @@ -227,17 +238,20 @@ class rcynic_xml_iterator(object): raise NotRsyncURI, "Not an rsync URI %r" % uri def __iter__(self): - - for validation_status in ElementTree(file = self.xml_file).getroot().getiterator("validation_status"): + for validation_status in ElementTree(file=self.xml_file).getroot().getiterator("validation_status"): timestamp = validation_status.get("timestamp") status = validation_status.get("status") uri = validation_status.text.strip() generation = validation_status.get("generation") - ok = status == "object_accepted" - filename = os.path.join(self.authenticated_subdir if ok else self.unauthenticated_subdir, self.uri_to_filename(uri)) + + # determine the path to this object + filename = os.path.join(self.authenticated_old_subdir if generation == 'backup' else self.unauthenticated_subdir, + self.uri_to_filename(uri)) + ext = os.path.splitext(filename)[1] if ext in file_name_classes: - yield file_name_classes[ext](filename = filename, uri = uri, ok = ok, status = status, timestamp = timestamp, generation = generation) + yield validation_status_element(timestamp=timestamp, generation=generation, uri=uri, + status=status, filename=filename, file_class=file_name_classes[ext]) def label_iterator(xml_file): """ @@ -248,22 +262,3 @@ def label_iterator(xml_file): for label in ElementTree(file=xml_file).find("labels"): yield label.tag, label.get("kind"), label.text.strip() - - -if __name__ == "__main__": - rcynic_dir = os.path.normpath(os.path.join(sys.path[0], "..", "rcynic")) - if False: - try: - for i in rcynic_file_iterator(os.path.join(rcynic_dir, "rcynic-data")): - print i - except IOError: - pass - if True: - try: - for i in rcynic_xml_iterator(os.path.join(rcynic_dir, "rcynic-data"), - os.path.join(rcynic_dir, "rcynic.xml")): - #print i - i.show() - print - except IOError: - pass |