diff options
author | Rob Austein <sra@hactrn.net> | 2015-10-12 02:29:26 +0000 |
---|---|---|
committer | Rob Austein <sra@hactrn.net> | 2015-10-12 02:29:26 +0000 |
commit | 0714a72bf29150d269e0dc45c2a868459dc5d023 (patch) | |
tree | ee45616b84ee7d7efe8718b3bfafdb795c63cc40 /rpki | |
parent | c67ce5844729e3d2a96b020ed18c953e29d44f75 (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.py | 36 | ||||
-rw-r--r-- | rpki/rpkid.py | 36 | ||||
-rw-r--r-- | rpki/rpkidb/models.py | 174 |
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") |