aboutsummaryrefslogtreecommitdiff
path: root/rpki/left_right.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/left_right.py')
-rw-r--r--rpki/left_right.py414
1 files changed, 195 insertions, 219 deletions
diff --git a/rpki/left_right.py b/rpki/left_right.py
index fff3404d..1b3eb581 100644
--- a/rpki/left_right.py
+++ b/rpki/left_right.py
@@ -28,7 +28,6 @@ import rpki.resource_set
import rpki.x509
import rpki.sql
import rpki.exceptions
-import rpki.xml_utils
import rpki.http
import rpki.up_down
import rpki.relaxng
@@ -38,7 +37,7 @@ import rpki.publication
import rpki.async
import rpki.rpkid_tasks
-from lxml.etree import Element, SubElement
+from lxml.etree import Element, SubElement, tostring as ElementToString
logger = logging.getLogger(__name__)
@@ -76,40 +75,208 @@ tag_signing_cert_crl = xmlns + "signing_cert_crl"
enforce_strict_up_down_xml_sender = False
-class left_right_namespace(object):
+class base_elt(rpki.sql.sql_persistent):
"""
- XML namespace parameters for left-right protocol.
+ Virtual class for persistent left-right protocol elements.
+ These classes are being phased out in favor of Django ORM models.
"""
xmlns = rpki.relaxng.left_right.xmlns
nsmap = rpki.relaxng.left_right.nsmap
-class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistent, left_right_namespace):
- """
- Virtual class for top-level left-right protocol data elements.
- """
-
handles = ()
+ attributes = ()
+ elements = ()
+ booleans = ()
+ text_attribute = None
self_id = None
self_handle = None
- @property
- @rpki.sql.cache_reference
- def self(self):
+ def __str__(self):
+ return ElementToString(self.toXML(), pretty_print = True, encoding = "us-ascii")
+
+ @classmethod
+ def fromXML(cls, elt):
+
+ self = cls()
+
+ for key in self.attributes:
+ val = elt.get(key, None)
+ if val is not None:
+ val = val.encode("ascii")
+ if isinstance(self.attributes, dict) and self.attributes[key] is not None:
+ val = self.attributes[key](val)
+ elif val.isdigit() and not key.endswith("_handle"):
+ val = long(val)
+ setattr(self, key, val)
+ for key in self.booleans:
+ setattr(self, key, elt.get(key, False))
+
+ if self.text_attribute is not None:
+ setattr(self, self.text_attribute, elt.text)
+
+ # In the long run, we probably want the key for that to include
+ # the namespace, but that would break the current .toXML() code,
+ # so kludge it for now.
+
+ for b64 in elt:
+ assert b64.tag.startswith(self.xmlns)
+ ename = b64.tag[len(self.xmlns):]
+ etype = self.elements[ename]
+ setattr(self, ename, etype(Base64 = b64.text))
+
+ return self
+
+ def toXML(self):
+ """
+ Default element generator for SQL-based objects. This assumes
+ that sub-elements are Base64-encoded DER objects.
+ """
+
+ elt = Element(self.xmlns + self.element_name, nsmap = self.nsmap)
+ for key in self.attributes:
+ val = getattr(self, key, None)
+ if val is not None:
+ elt.set(key, str(val))
+ for key in self.booleans:
+ if getattr(self, key, False):
+ elt.set(key, "yes")
+ for name in self.elements:
+ value = getattr(self, name, None)
+ if value is not None and not value.empty():
+ SubElement(elt, self.xmlns + name, nsmap = self.nsmap).text = value.get_Base64()
+ return elt
+
+ def make_reply(self, r_pdu = None):
+ """
+ Construct a reply PDU.
+ """
+
+ if r_pdu is None:
+ r_pdu = self.__class__()
+ self.make_reply_clone_hook(r_pdu)
+ handle_name = self.element_name + "_handle"
+ setattr(r_pdu, handle_name, getattr(self, handle_name, None))
+ else:
+ self.make_reply_clone_hook(r_pdu)
+ for b in r_pdu.booleans:
+ setattr(r_pdu, b, False)
+ r_pdu.action = self.action
+ r_pdu.tag = self.tag
+ return r_pdu
+
+ def serve_fetch_one(self):
"""
- Fetch self object to which this object links.
+ Find the object on which a get, set, or destroy method should
+ operate.
"""
+ r = self.serve_fetch_one_maybe()
+ if r is None:
+ raise rpki.exceptions.NotFound
+ return r
+
+ def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
+ cb()
+
+ 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 + "_handle"),
+ oops, getattr(oops, oops.element_name + "_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:]:
+ v = getattr(self, a, None)
+ 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):
+ r_pdu = self.serve_fetch_one()
+ self.make_reply(r_pdu)
+ r_msg.append(r_pdu)
+ cb()
+
+ def serve_list(self, r_msg, cb, eb):
+ for r_pdu in self.serve_fetch_all():
+ self.make_reply(r_pdu)
+ r_msg.append(r_pdu)
+ cb()
+
+ def serve_destroy_hook(self, cb, eb):
+ cb()
+
+ def serve_destroy(self, r_msg, cb, eb):
+ def done():
+ db_pdu.sql_delete()
+ r_msg.append(self.make_reply())
+ cb()
+ db_pdu = self.serve_fetch_one()
+ db_pdu.serve_destroy_hook(done, eb)
+
+ 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)
+ method(fake_r_msg, fake_cb, fake_eb)
+
+ def unimplemented_control(self, *controls):
+ unimplemented = [x for x in controls if getattr(self, x, False)]
+ if unimplemented:
+ raise rpki.exceptions.NotImplementedYet("Unimplemented control %s" % ", ".join(unimplemented))
+
+ @property
+ @rpki.sql.cache_reference
+ def self(self):
return self_elt.sql_fetch(self.gctx, self.self_id)
@property
@rpki.sql.cache_reference
def bsc(self):
- """
- Return BSC object to which this object links.
- """
-
return bsc_elt.sql_fetch(self.gctx, self.bsc_id)
def make_reply_clone_hook(self, r_pdu):
@@ -130,10 +297,6 @@ class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistent, left_right_name
@classmethod
def serve_fetch_handle(cls, gctx, self_id, handle):
- """
- Find an object based on its handle.
- """
-
return cls.sql_fetch_where1(gctx, cls.element_name + "_handle = %s AND self_id = %s", (handle, self_id))
def serve_fetch_one_maybe(self):
@@ -173,7 +336,8 @@ class data_elt(rpki.xml_utils.data_elt, rpki.sql.sql_persistent, left_right_name
setattr(self, id_name, getattr(x, id_name))
cb()
-class self_elt(data_elt):
+
+class self_elt(base_elt):
"""
<self/> element.
"""
@@ -211,66 +375,33 @@ class self_elt(data_elt):
@property
def bscs(self):
- """
- Fetch all BSC objects that link to this self object.
- """
-
return bsc_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def repositories(self):
- """
- Fetch all repository objects that link to this self object.
- """
-
return repository_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def parents(self):
- """
- Fetch all parent objects that link to this self object.
- """
-
return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def children(self):
- """
- Fetch all child objects that link to this self object.
- """
-
return child_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def roas(self):
- """
- Fetch all ROA objects that link to this self object.
- """
-
return rpki.rpkid.roa_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def ghostbusters(self):
- """
- Fetch all Ghostbuster record objects that link to this self object.
- """
-
return rpki.rpkid.ghostbuster_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
@property
def ee_certificates(self):
- """
- Fetch all EE certificate objects that link to this self object.
- """
-
return rpki.rpkid.ee_cert_obj.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
-
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
- """
- Extra server actions for self_elt.
- """
-
actions = []
if q_pdu.rekey:
actions.append(self.serve_rekey)
@@ -291,65 +422,37 @@ class self_elt(data_elt):
rpki.async.iterator(actions, loop, cb)
def serve_rekey(self, cb, eb):
- """
- Handle a left-right rekey action for this self.
- """
-
def loop(iterator, parent):
parent.serve_rekey(iterator, eb)
rpki.async.iterator(self.parents, loop, cb)
def serve_revoke(self, cb, eb):
- """
- Handle a left-right revoke action for this self.
- """
-
def loop(iterator, parent):
parent.serve_revoke(iterator, eb)
rpki.async.iterator(self.parents, loop, cb)
def serve_reissue(self, cb, eb):
- """
- Handle a left-right reissue action for this self.
- """
-
def loop(iterator, parent):
parent.serve_reissue(iterator, eb)
rpki.async.iterator(self.parents, loop, cb)
def serve_revoke_forgotten(self, cb, eb):
- """
- Handle a left-right revoke_forgotten action for this self.
- """
-
def loop(iterator, parent):
parent.serve_revoke_forgotten(iterator, eb)
rpki.async.iterator(self.parents, loop, cb)
def serve_clear_replay_protection(self, cb, eb):
- """
- Handle a left-right clear_replay_protection action for this self.
- """
-
def loop(iterator, obj):
obj.serve_clear_replay_protection(iterator, eb)
rpki.async.iterator(self.parents + self.children + self.repositories, loop, cb)
def serve_destroy_hook(self, cb, eb):
- """
- Extra cleanup actions when destroying a self_elt.
- """
-
def loop(iterator, parent):
parent.delete(iterator)
rpki.async.iterator(self.parents, loop, cb)
def serve_publish_world_now(self, cb, eb):
- """
- Handle a left-right publish_world_now action for this self.
- """
-
publisher = rpki.rpkid.publication_queue()
repositories = set()
objects = dict()
@@ -417,10 +520,6 @@ class self_elt(data_elt):
rpki.async.iterator(self.parents, loop, done)
def serve_run_now(self, cb, eb):
- """
- Handle a left-right run_now action for this self.
- """
-
logger.debug("Forced immediate run of periodic actions for self %s[%d]",
self.self_handle, self.self_id)
completion = rpki.rpkid_tasks.CompletionHandler(cb)
@@ -438,10 +537,6 @@ class self_elt(data_elt):
@classmethod
def serve_fetch_handle(cls, gctx, self_id, self_handle):
- """
- Find a self object based on its self_handle.
- """
-
return cls.sql_fetch_where1(gctx, "self_handle = %s", (self_handle,))
def serve_fetch_all(self):
@@ -454,13 +549,8 @@ class self_elt(data_elt):
return self.sql_fetch_all(self.gctx)
def schedule_cron_tasks(self, completion):
- """
- Schedule periodic tasks.
- """
-
if self.cron_tasks is None:
self.cron_tasks = tuple(task(self) for task in rpki.rpkid_tasks.task_classes)
-
for task in self.cron_tasks:
self.gctx.task_add(task)
completion.register(task)
@@ -486,7 +576,7 @@ class self_elt(data_elt):
return results
-class bsc_elt(data_elt):
+class bsc_elt(base_elt):
"""
<bsc/> (Business Signing Context) element.
"""
@@ -523,32 +613,19 @@ class bsc_elt(data_elt):
@property
def repositories(self):
- """
- Fetch all repository objects that link to this BSC object.
- """
-
return repository_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
@property
def parents(self):
- """
- Fetch all parent objects that link to this BSC object.
- """
-
return parent_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
@property
def children(self):
- """
- Fetch all child objects that link to this BSC object.
- """
-
return child_elt.sql_fetch_where(self.gctx, "bsc_id = %s", (self.bsc_id,))
def serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb):
"""
- Extra server actions for bsc_elt -- handle key generation. For
- now this only allows RSA with SHA-256.
+ Extra server actions -- handle key generation, only RSA with SHA-256 for now.
"""
if q_pdu.generate_keypair:
@@ -556,9 +633,10 @@ class bsc_elt(data_elt):
self.private_key_id = rpki.x509.RSA.generate(keylength = q_pdu.key_length or 2048)
self.pkcs10_request = rpki.x509.PKCS10.create(keypair = self.private_key_id)
r_pdu.pkcs10_request = self.pkcs10_request
- data_elt.serve_pre_save_hook(self, q_pdu, r_pdu, cb, eb)
+ super(bsc_elt, self).serve_pre_save_hook(q_pdu, r_pdu, cb, eb)
-class repository_elt(data_elt):
+
+class repository_elt(base_elt):
"""
<repository/> element.
"""
@@ -594,17 +672,9 @@ class repository_elt(data_elt):
@property
def parents(self):
- """
- Fetch all parent objects that link to this repository object.
- """
-
return parent_elt.sql_fetch_where(self.gctx, "repository_id = %s", (self.repository_id,))
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
- """
- Extra server actions for repository_elt.
- """
-
actions = []
if q_pdu.clear_replay_protection:
actions.append(self.serve_clear_replay_protection)
@@ -613,10 +683,6 @@ class repository_elt(data_elt):
rpki.async.iterator(actions, loop, cb)
def serve_clear_replay_protection(self, cb, eb):
- """
- Handle a left-right clear_replay_protection action for this repository.
- """
-
self.last_cms_timestamp = None
self.sql_mark_dirty()
cb()
@@ -680,7 +746,8 @@ class repository_elt(data_elt):
except Exception, e:
errback(e)
-class parent_elt(data_elt):
+
+class parent_elt(base_elt):
"""
<parent/> element.
"""
@@ -723,25 +790,13 @@ class parent_elt(data_elt):
@property
@rpki.sql.cache_reference
def repository(self):
- """
- Fetch repository object to which this parent object links.
- """
-
return repository_elt.sql_fetch(self.gctx, self.repository_id)
@property
def cas(self):
- """
- Fetch all CA objects that link to this parent object.
- """
-
return rpki.rpkid.ca_obj.sql_fetch_where(self.gctx, "parent_id = %s", (self.parent_id,))
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
- """
- Extra server actions for parent_elt.
- """
-
actions = []
if q_pdu.rekey:
actions.append(self.serve_rekey)
@@ -758,37 +813,21 @@ class parent_elt(data_elt):
rpki.async.iterator(actions, loop, cb)
def serve_rekey(self, cb, eb):
- """
- Handle a left-right rekey action for this parent.
- """
-
def loop(iterator, ca):
ca.rekey(iterator, eb)
rpki.async.iterator(self.cas, loop, cb)
def serve_revoke(self, cb, eb):
- """
- Handle a left-right revoke action for this parent.
- """
-
def loop(iterator, ca):
ca.revoke(cb = iterator, eb = eb)
rpki.async.iterator(self.cas, loop, cb)
def serve_reissue(self, cb, eb):
- """
- Handle a left-right reissue action for this parent.
- """
-
def loop(iterator, ca):
ca.reissue(cb = iterator, eb = eb)
rpki.async.iterator(self.cas, loop, cb)
def serve_clear_replay_protection(self, cb, eb):
- """
- Handle a left-right clear_replay_protection action for this parent.
- """
-
self.last_cms_timestamp = None
self.sql_mark_dirty()
cb()
@@ -883,36 +922,20 @@ class parent_elt(data_elt):
def serve_destroy_hook(self, cb, eb):
- """
- Extra server actions when destroying a parent_elt.
- """
-
self.delete(cb, delete_parent = False)
def _compose_up_down_query(self, query_type):
- """
- Compose top level element of an up-down query to this parent.
- """
-
return Element(rpki.up_down.tag_message, nsmap = rpki.up_down.nsmap, version = rpki.up_down.version,
sender = self.sender_name, recipient = self.recipient_name, type = query_type)
def up_down_list_query(self, cb, eb):
- """
- Send an up-down list query to this parent.
- """
-
q_msg = self._compose_up_down_query("list")
self.query_up_down(q_msg, cb, eb)
def up_down_issue_query(self, ca, ca_detail, cb, eb):
- """
- Send an up-down issue query to this parent.
- """
-
pkcs10 = rpki.x509.PKCS10.create(
keypair = ca_detail.private_key_id,
is_ca = True,
@@ -926,19 +949,12 @@ class parent_elt(data_elt):
def up_down_revoke_query(self, class_name, ski, cb, eb):
- """
- Send an up-down revoke query to this parent.
- """
-
q_msg = self._compose_up_down_query("revoke")
SubElement(q_msg, rpki.up_down.tag_key, class_name = class_name, ski = ski)
self.query_up_down(q_msg, cb, eb)
def query_up_down(self, q_msg, cb, eb):
- """
- Client code for sending one up-down query PDU to this parent.
- """
bsc = self.bsc
if bsc is None:
@@ -976,7 +992,8 @@ class parent_elt(data_elt):
errback = eb,
content_type = rpki.up_down.content_type)
-class child_elt(data_elt):
+
+class child_elt(base_elt):
"""
<child/> element.
"""
@@ -1010,33 +1027,17 @@ class child_elt(data_elt):
return rpki.log.log_repr(self, self.child_handle)
def fetch_child_certs(self, ca_detail = None, ski = None, unique = False):
- """
- Fetch all child_cert objects that link to this child object.
- """
-
return rpki.rpkid.child_cert_obj.fetch(self.gctx, self, ca_detail, ski, unique)
@property
def child_certs(self):
- """
- Fetch all child_cert objects that link to this child object.
- """
-
return self.fetch_child_certs()
@property
def parents(self):
- """
- Fetch all parent objects that link to self object to which this child object links.
- """
-
return parent_elt.sql_fetch_where(self.gctx, "self_id = %s", (self.self_id,))
def serve_post_save_hook(self, q_pdu, r_pdu, cb, eb):
- """
- Extra server actions for child_elt.
- """
-
actions = []
if q_pdu.reissue:
actions.append(self.serve_reissue)
@@ -1047,29 +1048,17 @@ class child_elt(data_elt):
rpki.async.iterator(actions, loop, cb)
def serve_reissue(self, cb, eb):
- """
- Handle a left-right reissue action for this child.
- """
-
publisher = rpki.rpkid.publication_queue()
for child_cert in self.child_certs:
child_cert.reissue(child_cert.ca_detail, publisher, force = True)
publisher.call_pubd(cb, eb)
def serve_clear_replay_protection(self, cb, eb):
- """
- Handle a left-right clear_replay_protection action for this child.
- """
-
self.last_cms_timestamp = None
self.sql_mark_dirty()
cb()
def ca_from_class_name(self, class_name):
- """
- Fetch the CA corresponding to an up-down class_name.
- """
-
if not class_name.isdigit():
raise rpki.exceptions.BadClassNameSyntax("Bad class name %s" % class_name)
ca = rpki.rpkid.ca_obj.sql_fetch(self.gctx, long(class_name))
@@ -1083,10 +1072,6 @@ class child_elt(data_elt):
return ca
def serve_destroy_hook(self, cb, eb):
- """
- Extra server actions when destroying a child_elt.
- """
-
publisher = rpki.rpkid.publication_queue()
for child_cert in self.child_certs:
child_cert.revoke(publisher = publisher,
@@ -1095,9 +1080,6 @@ class child_elt(data_elt):
def up_down_handle_list(self, q_msg, r_msg, callback, errback):
- """
- Serve one up-down "list" PDU.
- """
def got_resources(irdb_resources):
@@ -1132,9 +1114,6 @@ class child_elt(data_elt):
def up_down_handle_issue(self, q_msg, r_msg, callback, errback):
- """
- Serve one issue request PDU.
- """
def got_resources(irdb_resources):
@@ -1205,9 +1184,6 @@ class child_elt(data_elt):
def up_down_handle_revoke(self, q_msg, r_msg, callback, errback):
- """
- Serve one revoke request PDU.
- """
def done():
SubElement(r_msg, key.tag, class_name = class_name, ski = key.get("ski"))