aboutsummaryrefslogtreecommitdiff
path: root/rpki
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2015-10-12 02:29:26 +0000
committerRob Austein <sra@hactrn.net>2015-10-12 02:29:26 +0000
commit0714a72bf29150d269e0dc45c2a868459dc5d023 (patch)
treeee45616b84ee7d7efe8718b3bfafdb795c63cc40 /rpki
parentc67ce5844729e3d2a96b020ed18c953e29d44f75 (diff)
Checkpoint while porting old create/set actions to ORM models.
svn path=/branches/tk705/; revision=6113
Diffstat (limited to 'rpki')
-rw-r--r--rpki/left_right.py36
-rw-r--r--rpki/rpkid.py36
-rw-r--r--rpki/rpkidb/models.py174
3 files changed, 180 insertions, 66 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py
index 9704291f..3367d102 100644
--- a/rpki/left_right.py
+++ b/rpki/left_right.py
@@ -156,26 +156,21 @@ class base_elt(rpki.sql.sql_persistent):
def serve_create(self, r_msg, cb, eb):
r_pdu = self.make_reply()
-
def one():
self.sql_store()
setattr(r_pdu, self.sql_template.index, getattr(self, self.sql_template.index))
self.serve_post_save_hook(self, r_pdu, two, eb)
-
def two():
r_msg.append(r_pdu)
cb()
-
oops = self.serve_fetch_one_maybe()
if oops is not None:
raise rpki.exceptions.DuplicateObject("Object already exists: %r[%r] %r[%r]" % (
self, getattr(self, self.element_name[len(xmlns):] + "_handle"),
oops, getattr(oops, oops.element_name[len(xmlns):] + "_handle")))
-
self.serve_pre_save_hook(self, r_pdu, one, eb)
def serve_set(self, r_msg, cb, eb):
-
db_pdu = self.serve_fetch_one()
r_pdu = self.make_reply()
for a in db_pdu.sql_template.columns[1:]:
@@ -183,15 +178,12 @@ class base_elt(rpki.sql.sql_persistent):
if v is not None:
setattr(db_pdu, a, v)
db_pdu.sql_mark_dirty()
-
def one():
db_pdu.sql_store()
db_pdu.serve_post_save_hook(self, r_pdu, two, eb)
-
def two():
r_msg.append(r_pdu)
cb()
-
db_pdu.serve_pre_save_hook(self, r_pdu, one, eb)
def serve_get(self, r_msg, cb, eb):
@@ -219,21 +211,16 @@ class base_elt(rpki.sql.sql_persistent):
def serve_dispatch(self, r_msg, cb, eb):
# Transition hack: handle the .toXML() call for old handlers.
-
fake_r_msg = []
-
def fake_convert():
r_msg.extend(r_pdu.toXML() if isinstance(r_pdu, base_elt) else r_pdu
for r_pdu in fake_r_msg)
-
def fake_cb():
fake_convert()
cb()
-
def fake_eb(e):
fake_convert()
eb(e)
-
method = getattr(self, "serve_" + self.action, None)
if method is None:
raise rpki.exceptions.BadQuery("Unexpected query: action %s" % self.action)
@@ -255,10 +242,6 @@ class base_elt(rpki.sql.sql_persistent):
return bsc_elt.sql_fetch(self.gctx, self.bsc_id)
def make_reply_clone_hook(self, r_pdu):
- """
- Set handles when cloning, including _id -> _handle translation.
- """
-
if r_pdu.self_handle is None:
r_pdu.self_handle = self.self_handle
for tag, elt in self.handles:
@@ -276,11 +259,6 @@ class base_elt(rpki.sql.sql_persistent):
return cls.sql_fetch_where1(gctx, name + "_handle = %s AND self_id = %s", (handle, self_id))
def serve_fetch_one_maybe(self):
- """
- Find the object on which a get, set, or destroy method should
- operate, or which would conflict with a create method.
- """
-
name = self.element_name[len(xmlns):]
where = "%s.%s_handle = %%s AND %s.self_id = self.self_id AND self.self_handle = %%s" % (name, name, name)
args = (getattr(self, name + "_handle"), self.self_handle)
@@ -288,24 +266,12 @@ class base_elt(rpki.sql.sql_persistent):
return self.sql_fetch_where1(self.gctx, where, args, "self")
def serve_fetch_all(self):
- """
- Find the objects on which a list method should operate.
- """
-
name = self.element_name[len(xmlns):]
where = "%s.self_id = self.self_id and self.self_handle = %%s" % name
return self.sql_fetch_where(self.gctx, where, (self.self_handle,), "self")
def serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb):
- """
- Hook to do _handle => _id translation before saving.
-
- self is always the object to be saved to SQL. For create
- operations, self and q_pdu are be the same object; for set
- operations, self is the pre-existing object from SQL and q_pdu is
- the set request received from the the IRBE.
- """
-
+ # self is always the object to be saved to SQL.
for tag, elt in self.handles:
id_name = tag + "_id"
if getattr(self, id_name, None) is None:
diff --git a/rpki/rpkid.py b/rpki/rpkid.py
index a5c0ddd7..c3b1ccb2 100644
--- a/rpki/rpkid.py
+++ b/rpki/rpkid.py
@@ -427,6 +427,7 @@ class main(object):
iterator()
elif True: # Old-style handlers
+
q_map = { rpki.left_right.tag_self : rpki.left_right.self_elt,
rpki.left_right.tag_bsc : rpki.left_right.bsc_elt,
rpki.left_right.tag_parent : rpki.left_right.parent_elt,
@@ -438,8 +439,24 @@ class main(object):
else: # New-style handlers
- # This will all need to go under an @atomic or equivalent
- # with statement, just not quite sure where to put it yet.
+ # Notes on hooks in old code
+ #
+ # .serve_pre_save_hook(): used by all classes to do some
+ # kind of handle fixup which I think is now OBE. Also
+ # used by BSC for key generation, because schema (and
+ # corresponding new model) don't allow NULL for private
+ # key or PKCS10 request, so either we have to relax the
+ # schema constraint or generate key before saving.
+ # (bsc)
+ #
+ # .serve_destroy_hook(): used by several objects to
+ # trigger revocation of related objects. Will probably
+ # need to preserve this behavior.
+ # (self, parent, child)
+ #
+ # .serve_post_save_hook(): used to trigger various actions
+ # based on boolean attributes in XML.
+ # (self, repository, parent, child)
action = q_pdu.get("action")
model = self.left_right_models[q_pdu.tag]
@@ -449,19 +466,22 @@ class main(object):
obj.xml_template.encode(obj, r_msg)
elif action == "destroy":
- model.objects.xml_get_for_delete(q_pdu).delete()
+ obj = model.objects.xml_get_for_delete(q_pdu)
+ obj.xml_pre_delete_hook()
+ obj.delete()
obj.xml_template.acknowledge(obj, q_pdu, r_msg)
- else:
- assert action in ("create", "set")
+ elif action in ("create", "set"):
obj = model.objects.xml_get_or_create(q_pdu)
obj.xml_template.decode(obj, q_pdu)
-
- # Handle special actions here.
-
+ obj.xml_pre_save_hook(q_pdu)
obj.save()
+ obj.xml_post_save_hook(q_pdu)
obj.xml_template.acknowledge(obj, q_pdu, r_msg)
+ else:
+ raise BadQuery
+
except (rpki.async.ExitNow, SystemExit):
raise
except Exception, e:
diff --git a/rpki/rpkidb/models.py b/rpki/rpkidb/models.py
index 5ba743de..154d759b 100644
--- a/rpki/rpkidb/models.py
+++ b/rpki/rpkidb/models.py
@@ -50,12 +50,13 @@ class XMLTemplate(object):
signing_cert = rpki.x509.X509,
signing_cert_crl = rpki.x509.CRL)
- def __init__(self, name, attributes = (), booleans = (), elements = (), handles = ()):
+ def __init__(self, name, attributes = (), booleans = (), elements = (), readonly = (), handles = ()):
self.name = name
self.handles = handles
self.attributes = attributes
self.booleans = booleans
self.elements = elements
+ self.readonly = readonly
def encode(self, obj, r_msg):
"""
@@ -78,7 +79,7 @@ class XMLTemplate(object):
for k in self.booleans:
if getattr(obj, k):
r_pdu.set(k, "yes")
- for k in self.elements:
+ for k in self.elements + self.readonly:
v = getattr(obj, k)
if v is not None and not v.empty():
SubElement(r_pdu, rpki.left_right.xmlns + k).text = v.get_Base64()
@@ -188,6 +189,104 @@ class Self(models.Model):
booleans = ("use_hsm",),
elements = ("bpki_cert", "bpki_glue"))
+ def xml_pre_delete_hook(self):
+ raise NotImplementedError
+
+ def xml_pre_save_hook(self, q_pdu):
+ pass
+
+ def xml_post_save_hook(self, q_pdu, cb, eb):
+ actions = []
+ rekey = q_pdu.get("rekey")
+ revoke = q_pdu.get("revoke")
+ reissue = q_pdu.get("reissue")
+ revoke_forgotten = q_pdu.get("revoke_forgotten")
+ publish_world_now = q_pdu.get("publish_world_now")
+ run_now = q_pdu.get("run_now")
+ clear_replay_protection = q_pdu.get("clear_replay_protection")
+ if rekey or revoke or reissue or revoke_forgotten or clear_replay_protection:
+ for parent in self.parents.all():
+ if rekey:
+ actions.append(parent.serve_rekey)
+ if revoke:
+ actions.append(parent.serve_revoke)
+ if reissue:
+ actions.append(parent.serve_reissue)
+ if revoke_forgotten:
+ actions.append(parent.serve_revoke_forgotten)
+ if clear_replay_protection:
+ actions.append(parent.serve_clear_replay_protection)
+ if clear_replay_protection:
+ for child in self.children.all():
+ actions.append(child.serve_clear_replay_protection)
+ for repository in self.repositories.all():
+ actions.append(repository.serve_clear_replay_protection)
+ if publish_world_now:
+ actions.append(self.serve_publish_world_now)
+ if run_now:
+ actions.append(self.serve_run_now)
+ def loop(iterator, action):
+ action(iterator, eb)
+ rpki.async.iterator(actions, loop, cb)
+
+ def serve_publish_world_now(self, cb, eb):
+ publisher = rpki.rpkid.publication_queue()
+ repositories = set()
+ objects = dict()
+
+ def loop(iterator, parent):
+ repository = parent.repository
+ if repository.peer_contact_uri in repositories:
+ return iterator()
+ repositories.add(repository.peer_contact_uri)
+ q_msg = Element(rpki.publication.tag_msg, nsmap = rpki.publication.nsmap,
+ type = "query", version = rpki.publication.version)
+ SubElement(q_msg, rpki.publication.tag_list, tag = "list")
+ def list_handler(r_pdu):
+ rpki.publication.raise_if_error(r_pdu)
+ assert r_pdu.tag == rpki.publication.tag_list
+ assert r_pdu.get("uri") not in objects
+ objects[r_pdu.get("uri")] = (r_pdu.get("hash"), repository)
+ repository.call_pubd(iterator, eb, q_msg, length_check = False, handlers = dict(list = list_handler))
+
+ def reconcile(uri, obj, repository):
+ h, r = objects.pop(uri, (None, None))
+ if h is not None:
+ assert r == repository
+ publisher.queue(uri = uri, new_obj = obj, old_hash = h, repository = repository)
+
+ def done():
+ for parent in self.parents.all():
+ repository = parent.repository
+ for ca in parent.cas.all():
+ ca_detail = ca.active_ca_detail
+ if ca_detail is not None:
+ reconcile(uri = ca_detail.crl_uri, obj = ca_detail.latest_crl, repository = repository)
+ reconcile(uri = ca_detail.manifest_uri, obj = ca_detail.latest_manifest, repository = repository)
+ for c in ca_detail.child_certs.all():
+ reconcile(uri = c.uri, obj = c.cert, repository = repository)
+ for r in ca_detail.roas.all():
+ if r.roa is not None:
+ reconcile(uri = r.uri, obj = r.roa, repository = repository)
+ for g in ca_detail.ghostbusters.all():
+ reconcile(uri = g.uri, obj = g.ghostbuster, repository = repository)
+ for c in ca_detail.ee_certificates.all():
+ reconcile(uri = c.uri, obj = c.cert, repository = repository)
+ for u in objects:
+ h, r = objects[h]
+ publisher.queue(uri = u, old_hash = h, repository = r)
+ publisher.call_pubd(cb, eb)
+
+ rpki.async.iterator(self.parents.all(), loop, done)
+
+ def serve_run_now(self, cb, eb):
+ logger.debug("Forced immediate run of periodic actions for self %s[%d]", self.self_handle, self.self_id)
+ completion = rpki.rpkid_tasks.CompletionHandler(cb)
+ self.schedule_cron_tasks(completion)
+ assert completion.count > 0
+ self.gctx.task_run()
+
+
class BSC(models.Model):
bsc_handle = models.SlugField(max_length = 255)
private_key_id = KeyField()
@@ -195,7 +294,7 @@ class BSC(models.Model):
hash_alg = EnumField(choices = ("sha256",))
signing_cert = CertificateField(null = True)
signing_cert_crl = CRLField(null = True)
- self = models.ForeignKey(Self)
+ self = models.ForeignKey(Self, related_name = "bscs")
objects = XMLManager()
class Meta:
@@ -203,7 +302,22 @@ class BSC(models.Model):
xml_template = XMLTemplate(
name = "bsc",
- elements = ("signing_cert", "signing_cert_crl", "pkcs10_request"))
+ elements = ("signing_cert", "signing_cert_crl"),
+ readonly = ("pkcs10_request",))
+
+ def xml_pre_delete_hook(self):
+ pass
+
+ def xml_pre_save_hook(self, q_pdu):
+ # Handle key generation, only supports RSA with SHA-256 for now.
+ if q_pdu.get("generate_keypair"):
+ assert q_pdu.get("key_type") in (None, "rsa") and q_pdu.get("hash_alg") in (None, "sha256")
+ self.private_key_id = rpki.x509.RSA.generate(keylength = int(q_pdu.get("key_length", 2048)))
+ self.pkcs10_request = rpki.x509.PKCS10.create(keypair = self.private_key_id)
+
+ def xml_post_save_hook(self, q_pdu):
+ pass
+
class Repository(models.Model):
repository_handle = models.SlugField(max_length = 255)
@@ -211,8 +325,8 @@ class Repository(models.Model):
bpki_cert = CertificateField(null = True)
bpki_glue = CertificateField(null = True)
last_cms_timestamp = SundialField(null = True)
- bsc = models.ForeignKey(BSC)
- self = models.ForeignKey(Self)
+ bsc = models.ForeignKey(BSC, related_name = "repositories")
+ self = models.ForeignKey(Self, related_name = "repositories")
objects = XMLManager()
class Meta:
@@ -224,6 +338,10 @@ class Repository(models.Model):
attributes = ("peer_contact_uri",),
elements = ("bpki_cert", "bpki_glue"))
+ def xml_pre_delete_hook(self): raise NotImplementedError
+ def xml_pre_save_hook(self, q_pdu): raise NotImplementedError
+ def xml_post_save_hook(self, q_pdu): raise NotImplementedError
+
class Parent(models.Model):
parent_handle = models.SlugField(max_length = 255)
@@ -234,9 +352,9 @@ class Parent(models.Model):
sender_name = models.TextField(null = True)
recipient_name = models.TextField(null = True)
last_cms_timestamp = SundialField(null = True)
- self = models.ForeignKey(Self)
- bsc = models.ForeignKey(BSC)
- repository = models.ForeignKey(Repository)
+ self = models.ForeignKey(Self, related_name = "parents")
+ bsc = models.ForeignKey(BSC, related_name = "parents")
+ repository = models.ForeignKey(Repository, related_name = "parents")
objects = XMLManager()
class Meta:
@@ -248,6 +366,11 @@ class Parent(models.Model):
attributes = ("peer_contact_uri", "sia_base", "sender_name", "recipient_name"),
elements = ("bpki_cms_cert", "bpki_cms_glue"))
+ def xml_pre_delete_hook(self): raise NotImplementedError
+ def xml_pre_save_hook(self, q_pdu): raise NotImplementedError
+ def xml_post_save_hook(self, q_pdu): raise NotImplementedError
+
+
class CA(models.Model):
last_crl_sn = models.BigIntegerField()
last_manifest_sn = models.BigIntegerField()
@@ -256,7 +379,7 @@ class CA(models.Model):
last_issued_sn = models.BigIntegerField()
sia_uri = models.TextField(null = True)
parent_resource_class = models.TextField(null = True)
- parent = models.ForeignKey(Parent)
+ parent = models.ForeignKey(Parent, related_name = "cas")
class CADetail(models.Model):
public_key = KeyField(null = True)
@@ -271,15 +394,15 @@ class CADetail(models.Model):
manifest_published = SundialField(null = True)
state = EnumField(choices = ("pending", "active", "deprecated", "revoked"))
ca_cert_uri = models.TextField(null = True)
- ca = models.ForeignKey(CA)
+ ca = models.ForeignKey(CA, related_name = "ca_details")
class Child(models.Model):
child_handle = models.SlugField(max_length = 255)
bpki_cert = CertificateField(null = True)
bpki_glue = CertificateField(null = True)
last_cms_timestamp = SundialField(null = True)
- self = models.ForeignKey(Self)
- bsc = models.ForeignKey(BSC)
+ self = models.ForeignKey(Self, related_name = "children")
+ bsc = models.ForeignKey(BSC, related_name = "children")
objects = XMLManager()
class Meta:
@@ -290,45 +413,50 @@ class Child(models.Model):
handles = (BSC,),
elements = ("bpki_cert", "bpki_glue"))
+ def xml_pre_delete_hook(self): raise NotImplementedError
+ def xml_pre_save_hook(self, q_pdu): raise NotImplementedError
+ def xml_post_save_hook(self, q_pdu): raise NotImplementedError
+
+
class ChildCert(models.Model):
cert = CertificateField()
published = SundialField(null = True)
ski = BlobField()
- child = models.ForeignKey(Child)
- ca_detail = models.ForeignKey(CADetail)
+ child = models.ForeignKey(Child, related_name = "child_certs")
+ ca_detail = models.ForeignKey(CADetail, related_name = "child_certs")
class EECert(models.Model):
ski = BlobField()
cert = CertificateField()
published = SundialField(null = True)
- self = models.ForeignKey(Self)
- ca_detail = models.ForeignKey(CADetail)
+ self = models.ForeignKey(Self, related_name = "ee_certs")
+ ca_detail = models.ForeignKey(CADetail, related_name = "ee_certs")
class Ghostbuster(models.Model):
vcard = models.TextField()
cert = CertificateField()
ghostbuster = GhostbusterField()
published = SundialField(null = True)
- self = models.ForeignKey(Self)
- ca_detail = models.ForeignKey(CADetail)
+ self = models.ForeignKey(Self, related_name = "ghostbusters")
+ ca_detail = models.ForeignKey(CADetail, related_name = "ghostbusters")
class RevokedCert(models.Model):
serial = models.BigIntegerField()
revoked = SundialField()
expires = SundialField()
- ca_detail = models.ForeignKey(CADetail)
+ ca_detail = models.ForeignKey(CADetail, related_name = "revoked_certs")
class ROA(models.Model):
asn = models.BigIntegerField()
cert = CertificateField()
roa = ROAField()
published = SundialField(null = True)
- self = models.ForeignKey(Self)
- ca_detail = models.ForeignKey(CADetail)
+ self = models.ForeignKey(Self, related_name = "roas")
+ ca_detail = models.ForeignKey(CADetail, related_name = "roas")
class ROAPrefix(models.Model):
prefix = models.CharField(max_length = 40)
prefixlen = models.SmallIntegerField()
max_prefixlen = models.SmallIntegerField()
version = models.SmallIntegerField()
- roa = models.ForeignKey(ROA)
+ roa = models.ForeignKey(ROA, related_name = "roa_prefixes")