aboutsummaryrefslogtreecommitdiff
path: root/rpki/pubdb/models.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/pubdb/models.py')
-rw-r--r--rpki/pubdb/models.py476
1 files changed, 238 insertions, 238 deletions
diff --git a/rpki/pubdb/models.py b/rpki/pubdb/models.py
index 2b6d67e4..46dcf493 100644
--- a/rpki/pubdb/models.py
+++ b/rpki/pubdb/models.py
@@ -48,266 +48,266 @@ rrdp_tag_withdraw = rrdp_xmlns + "withdraw"
# sure quite where to put it at the moment.
def DERSubElement(elt, name, der, attrib = None, **kwargs):
- """
- Convenience wrapper around SubElement for use with Base64 text.
- """
+ """
+ Convenience wrapper around SubElement for use with Base64 text.
+ """
- se = SubElement(elt, name, attrib, **kwargs)
- se.text = rpki.x509.base64_with_linebreaks(der)
- se.tail = "\n"
- return se
+ se = SubElement(elt, name, attrib, **kwargs)
+ se.text = rpki.x509.base64_with_linebreaks(der)
+ se.tail = "\n"
+ return se
class Client(models.Model):
- client_handle = models.CharField(unique = True, max_length = 255)
- base_uri = models.TextField()
- bpki_cert = CertificateField()
- bpki_glue = CertificateField(null = True)
- last_cms_timestamp = SundialField(blank = True, null = True)
+ client_handle = models.CharField(unique = True, max_length = 255)
+ base_uri = models.TextField()
+ bpki_cert = CertificateField()
+ bpki_glue = CertificateField(null = True)
+ last_cms_timestamp = SundialField(blank = True, null = True)
- def check_allowed_uri(self, uri):
- """
- Make sure that a target URI is within this client's allowed URI space.
- """
+ def check_allowed_uri(self, uri):
+ """
+ Make sure that a target URI is within this client's allowed URI space.
+ """
- if not uri.startswith(self.base_uri):
- raise rpki.exceptions.ForbiddenURI
+ if not uri.startswith(self.base_uri):
+ raise rpki.exceptions.ForbiddenURI
class Session(models.Model):
- uuid = models.CharField(unique = True, max_length=36)
- serial = models.BigIntegerField()
- snapshot = models.TextField(blank = True)
- hash = models.CharField(max_length = 64, blank = True)
-
- ## @var keep_all_rrdp_files
- # Debugging flag to prevent expiration of old RRDP files.
- # This simplifies debugging delta code. Need for this
- # may go away once RRDP is fully integrated into rcynic.
- keep_all_rrdp_files = False
-
- def new_delta(self, expires):
- """
- Construct a new delta associated with this session.
- """
+ uuid = models.CharField(unique = True, max_length=36)
+ serial = models.BigIntegerField()
+ snapshot = models.TextField(blank = True)
+ hash = models.CharField(max_length = 64, blank = True)
+
+ ## @var keep_all_rrdp_files
+ # Debugging flag to prevent expiration of old RRDP files.
+ # This simplifies debugging delta code. Need for this
+ # may go away once RRDP is fully integrated into rcynic.
+ keep_all_rrdp_files = False
+
+ def new_delta(self, expires):
+ """
+ Construct a new delta associated with this session.
+ """
+
+ delta = Delta(session = self,
+ serial = self.serial + 1,
+ expires = expires)
+ delta.elt = Element(rrdp_tag_delta,
+ nsmap = rrdp_nsmap,
+ version = rrdp_version,
+ session_id = self.uuid,
+ serial = str(delta.serial))
+ return delta
+
+
+ def expire_deltas(self):
+ """
+ Delete deltas whose expiration date has passed.
+ """
+
+ self.delta_set.filter(expires__lt = rpki.sundial.now()).delete()
+
+
+ def generate_snapshot(self):
+ """
+ Generate an XML snapshot of this session.
+ """
+
+ xml = Element(rrdp_tag_snapshot, nsmap = rrdp_nsmap,
+ version = rrdp_version,
+ session_id = self.uuid,
+ serial = str(self.serial))
+ xml.text = "\n"
+ for obj in self.publishedobject_set.all():
+ DERSubElement(xml, rrdp_tag_publish,
+ der = obj.der,
+ uri = obj.uri)
+ rpki.relaxng.rrdp.assertValid(xml)
+ self.snapshot = ElementToString(xml, pretty_print = True)
+ self.hash = rpki.x509.sha256(self.snapshot).encode("hex")
+ self.save()
+
+
+ @property
+ def snapshot_fn(self):
+ return "%s/snapshot/%s.xml" % (self.uuid, self.serial)
+
+
+ @property
+ def notification_fn(self):
+ return "notify.xml"
+
+
+ @staticmethod
+ def _write_rrdp_file(fn, text, rrdp_publication_base, overwrite = False):
+ if overwrite or not os.path.exists(os.path.join(rrdp_publication_base, fn)):
+ tn = os.path.join(rrdp_publication_base, fn + ".%s.tmp" % os.getpid())
+ if not os.path.isdir(os.path.dirname(tn)):
+ os.makedirs(os.path.dirname(tn))
+ with open(tn, "w") as f:
+ f.write(text)
+ os.rename(tn, os.path.join(rrdp_publication_base, fn))
+
+
+ @staticmethod
+ def _rrdp_filename_to_uri(fn, rrdp_uri_base):
+ return "%s/%s" % (rrdp_uri_base.rstrip("/"), fn)
+
+
+ def _generate_update_xml(self, rrdp_uri_base):
+ xml = Element(rrdp_tag_notification, nsmap = rrdp_nsmap,
+ version = rrdp_version,
+ session_id = self.uuid,
+ serial = str(self.serial))
+ SubElement(xml, rrdp_tag_snapshot,
+ uri = self._rrdp_filename_to_uri(self.snapshot_fn, rrdp_uri_base),
+ hash = self.hash)
+ for delta in self.delta_set.all():
+ SubElement(xml, rrdp_tag_delta,
+ uri = self._rrdp_filename_to_uri(delta.fn, rrdp_uri_base),
+ hash = delta.hash,
+ serial = str(delta.serial))
+ rpki.relaxng.rrdp.assertValid(xml)
+ return ElementToString(xml, pretty_print = True)
+
+
+ def synchronize_rrdp_files(self, rrdp_publication_base, rrdp_uri_base):
+ """
+ Write current RRDP files to disk, clean up old files and directories.
+ """
+
+ current_filenames = set()
+
+ for delta in self.delta_set.all():
+ self._write_rrdp_file(delta.fn, delta.xml, rrdp_publication_base)
+ current_filenames.add(delta.fn)
+
+ self._write_rrdp_file(self.snapshot_fn, self.snapshot, rrdp_publication_base)
+ current_filenames.add(self.snapshot_fn)
+
+ self._write_rrdp_file(self.notification_fn, self._generate_update_xml(rrdp_uri_base),
+ rrdp_publication_base, overwrite = True)
+ current_filenames.add(self.notification_fn)
+
+ if not self.keep_all_rrdp_files:
+ for root, dirs, files in os.walk(rrdp_publication_base, topdown = False):
+ for fn in files:
+ fn = os.path.join(root, fn)
+ if fn[len(rrdp_publication_base):].lstrip("/") not in current_filenames:
+ os.remove(fn)
+ for dn in dirs:
+ try:
+ os.rmdir(os.path.join(root, dn))
+ except OSError:
+ pass
- delta = Delta(session = self,
- serial = self.serial + 1,
- expires = expires)
- delta.elt = Element(rrdp_tag_delta,
- nsmap = rrdp_nsmap,
- version = rrdp_version,
- session_id = self.uuid,
- serial = str(delta.serial))
- return delta
+class Delta(models.Model):
+ serial = models.BigIntegerField()
+ xml = models.TextField()
+ hash = models.CharField(max_length = 64)
+ expires = SundialField()
+ session = models.ForeignKey(Session)
- def expire_deltas(self):
- """
- Delete deltas whose expiration date has passed.
- """
- self.delta_set.filter(expires__lt = rpki.sundial.now()).delete()
+ @staticmethod
+ def _uri_to_filename(uri, publication_base):
+ if not uri.startswith("rsync://"):
+ raise rpki.exceptions.BadURISyntax(uri)
+ path = uri.split("/")[4:]
+ path.insert(0, publication_base.rstrip("/"))
+ filename = "/".join(path)
+ if "/../" in filename or filename.endswith("/.."):
+ raise rpki.exceptions.BadURISyntax(filename)
+ return filename
- def generate_snapshot(self):
- """
- Generate an XML snapshot of this session.
- """
+ @property
+ def fn(self):
+ return "%s/deltas/%s.xml" % (self.session.uuid, self.serial)
- xml = Element(rrdp_tag_snapshot, nsmap = rrdp_nsmap,
- version = rrdp_version,
- session_id = self.uuid,
- serial = str(self.serial))
- xml.text = "\n"
- for obj in self.publishedobject_set.all():
- DERSubElement(xml, rrdp_tag_publish,
- der = obj.der,
- uri = obj.uri)
- rpki.relaxng.rrdp.assertValid(xml)
- self.snapshot = ElementToString(xml, pretty_print = True)
- self.hash = rpki.x509.sha256(self.snapshot).encode("hex")
- self.save()
-
-
- @property
- def snapshot_fn(self):
- return "%s/snapshot/%s.xml" % (self.uuid, self.serial)
-
-
- @property
- def notification_fn(self):
- return "notify.xml"
-
-
- @staticmethod
- def _write_rrdp_file(fn, text, rrdp_publication_base, overwrite = False):
- if overwrite or not os.path.exists(os.path.join(rrdp_publication_base, fn)):
- tn = os.path.join(rrdp_publication_base, fn + ".%s.tmp" % os.getpid())
- if not os.path.isdir(os.path.dirname(tn)):
- os.makedirs(os.path.dirname(tn))
- with open(tn, "w") as f:
- f.write(text)
- os.rename(tn, os.path.join(rrdp_publication_base, fn))
-
-
- @staticmethod
- def _rrdp_filename_to_uri(fn, rrdp_uri_base):
- return "%s/%s" % (rrdp_uri_base.rstrip("/"), fn)
-
-
- def _generate_update_xml(self, rrdp_uri_base):
- xml = Element(rrdp_tag_notification, nsmap = rrdp_nsmap,
- version = rrdp_version,
- session_id = self.uuid,
- serial = str(self.serial))
- SubElement(xml, rrdp_tag_snapshot,
- uri = self._rrdp_filename_to_uri(self.snapshot_fn, rrdp_uri_base),
- hash = self.hash)
- for delta in self.delta_set.all():
- SubElement(xml, rrdp_tag_delta,
- uri = self._rrdp_filename_to_uri(delta.fn, rrdp_uri_base),
- hash = delta.hash,
- serial = str(delta.serial))
- rpki.relaxng.rrdp.assertValid(xml)
- return ElementToString(xml, pretty_print = True)
-
-
- def synchronize_rrdp_files(self, rrdp_publication_base, rrdp_uri_base):
- """
- Write current RRDP files to disk, clean up old files and directories.
- """
- current_filenames = set()
-
- for delta in self.delta_set.all():
- self._write_rrdp_file(delta.fn, delta.xml, rrdp_publication_base)
- current_filenames.add(delta.fn)
-
- self._write_rrdp_file(self.snapshot_fn, self.snapshot, rrdp_publication_base)
- current_filenames.add(self.snapshot_fn)
-
- self._write_rrdp_file(self.notification_fn, self._generate_update_xml(rrdp_uri_base),
- rrdp_publication_base, overwrite = True)
- current_filenames.add(self.notification_fn)
-
- if not self.keep_all_rrdp_files:
- for root, dirs, files in os.walk(rrdp_publication_base, topdown = False):
- for fn in files:
- fn = os.path.join(root, fn)
- if fn[len(rrdp_publication_base):].lstrip("/") not in current_filenames:
- os.remove(fn)
- for dn in dirs:
- try:
- os.rmdir(os.path.join(root, dn))
- except OSError:
- pass
+ def activate(self):
+ rpki.relaxng.rrdp.assertValid(self.elt)
+ self.xml = ElementToString(self.elt, pretty_print = True)
+ self.hash = rpki.x509.sha256(self.xml).encode("hex")
+ self.save()
+ self.session.serial += 1
+ self.session.save()
-class Delta(models.Model):
- serial = models.BigIntegerField()
- xml = models.TextField()
- hash = models.CharField(max_length = 64)
- expires = SundialField()
- session = models.ForeignKey(Session)
-
-
- @staticmethod
- def _uri_to_filename(uri, publication_base):
- if not uri.startswith("rsync://"):
- raise rpki.exceptions.BadURISyntax(uri)
- path = uri.split("/")[4:]
- path.insert(0, publication_base.rstrip("/"))
- filename = "/".join(path)
- if "/../" in filename or filename.endswith("/.."):
- raise rpki.exceptions.BadURISyntax(filename)
- return filename
-
-
- @property
- def fn(self):
- return "%s/deltas/%s.xml" % (self.session.uuid, self.serial)
-
-
- def activate(self):
- rpki.relaxng.rrdp.assertValid(self.elt)
- self.xml = ElementToString(self.elt, pretty_print = True)
- self.hash = rpki.x509.sha256(self.xml).encode("hex")
- self.save()
- self.session.serial += 1
- self.session.save()
-
-
- def publish(self, client, der, uri, obj_hash):
- try:
- obj = client.publishedobject_set.get(session = self.session, uri = uri)
- if obj.hash == obj_hash:
- obj.delete()
- elif obj_hash is None:
- raise rpki.exceptions.ExistingObjectAtURI("Object already published at %s" % uri)
- else:
- raise rpki.exceptions.DifferentObjectAtURI("Found different object at %s (old %s, new %s)" % (uri, obj.hash, obj_hash))
- except rpki.pubdb.models.PublishedObject.DoesNotExist:
- pass
- logger.debug("Publishing %s", uri)
- PublishedObject.objects.create(session = self.session, client = client, der = der, uri = uri,
- hash = rpki.x509.sha256(der).encode("hex"))
- se = DERSubElement(self.elt, rrdp_tag_publish, der = der, uri = uri)
- if obj_hash is not None:
- se.set("hash", obj_hash)
- rpki.relaxng.rrdp.assertValid(self.elt)
-
-
- def withdraw(self, client, uri, obj_hash):
- obj = client.publishedobject_set.get(session = self.session, uri = uri)
- if obj.hash != obj_hash:
- raise rpki.exceptions.DifferentObjectAtURI("Found different object at %s (old %s, new %s)" % (uri, obj.hash, obj_hash))
- logger.debug("Withdrawing %s", uri)
- obj.delete()
- SubElement(self.elt, rrdp_tag_withdraw, uri = uri, hash = obj_hash).tail = "\n"
- rpki.relaxng.rrdp.assertValid(self.elt)
-
-
- def update_rsync_files(self, publication_base):
- from errno import ENOENT
- min_path_len = len(publication_base.rstrip("/"))
- for pdu in self.elt:
- assert pdu.tag in (rrdp_tag_publish, rrdp_tag_withdraw)
- fn = self._uri_to_filename(pdu.get("uri"), publication_base)
- if pdu.tag == rrdp_tag_publish:
- tn = fn + ".tmp"
- dn = os.path.dirname(fn)
- if not os.path.isdir(dn):
- os.makedirs(dn)
- with open(tn, "wb") as f:
- f.write(pdu.text.decode("base64"))
- os.rename(tn, fn)
- else:
+ def publish(self, client, der, uri, obj_hash):
try:
- os.remove(fn)
- except OSError, e:
- if e.errno != ENOENT:
- raise
- dn = os.path.dirname(fn)
- while len(dn) > min_path_len:
- try:
- os.rmdir(dn)
- except OSError:
- break
- else:
- dn = os.path.dirname(dn)
- del self.elt
+ obj = client.publishedobject_set.get(session = self.session, uri = uri)
+ if obj.hash == obj_hash:
+ obj.delete()
+ elif obj_hash is None:
+ raise rpki.exceptions.ExistingObjectAtURI("Object already published at %s" % uri)
+ else:
+ raise rpki.exceptions.DifferentObjectAtURI("Found different object at %s (old %s, new %s)" % (uri, obj.hash, obj_hash))
+ except rpki.pubdb.models.PublishedObject.DoesNotExist:
+ pass
+ logger.debug("Publishing %s", uri)
+ PublishedObject.objects.create(session = self.session, client = client, der = der, uri = uri,
+ hash = rpki.x509.sha256(der).encode("hex"))
+ se = DERSubElement(self.elt, rrdp_tag_publish, der = der, uri = uri)
+ if obj_hash is not None:
+ se.set("hash", obj_hash)
+ rpki.relaxng.rrdp.assertValid(self.elt)
+
+
+ def withdraw(self, client, uri, obj_hash):
+ obj = client.publishedobject_set.get(session = self.session, uri = uri)
+ if obj.hash != obj_hash:
+ raise rpki.exceptions.DifferentObjectAtURI("Found different object at %s (old %s, new %s)" % (uri, obj.hash, obj_hash))
+ logger.debug("Withdrawing %s", uri)
+ obj.delete()
+ SubElement(self.elt, rrdp_tag_withdraw, uri = uri, hash = obj_hash).tail = "\n"
+ rpki.relaxng.rrdp.assertValid(self.elt)
+
+
+ def update_rsync_files(self, publication_base):
+ from errno import ENOENT
+ min_path_len = len(publication_base.rstrip("/"))
+ for pdu in self.elt:
+ assert pdu.tag in (rrdp_tag_publish, rrdp_tag_withdraw)
+ fn = self._uri_to_filename(pdu.get("uri"), publication_base)
+ if pdu.tag == rrdp_tag_publish:
+ tn = fn + ".tmp"
+ dn = os.path.dirname(fn)
+ if not os.path.isdir(dn):
+ os.makedirs(dn)
+ with open(tn, "wb") as f:
+ f.write(pdu.text.decode("base64"))
+ os.rename(tn, fn)
+ else:
+ try:
+ os.remove(fn)
+ except OSError, e:
+ if e.errno != ENOENT:
+ raise
+ dn = os.path.dirname(fn)
+ while len(dn) > min_path_len:
+ try:
+ os.rmdir(dn)
+ except OSError:
+ break
+ else:
+ dn = os.path.dirname(dn)
+ del self.elt
class PublishedObject(models.Model):
- uri = models.CharField(max_length = 255)
- der = models.BinaryField()
- hash = models.CharField(max_length = 64)
- client = models.ForeignKey(Client)
- session = models.ForeignKey(Session)
-
- class Meta: # pylint: disable=C1001,W0232
- unique_together = (("session", "hash"),
- ("session", "uri"))
+ uri = models.CharField(max_length = 255)
+ der = models.BinaryField()
+ hash = models.CharField(max_length = 64)
+ client = models.ForeignKey(Client)
+ session = models.ForeignKey(Session)
+
+ class Meta: # pylint: disable=C1001,W0232
+ unique_together = (("session", "hash"),
+ ("session", "uri"))