aboutsummaryrefslogtreecommitdiff
path: root/rpki/fields.py
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/fields.py')
-rw-r--r--rpki/fields.py192
1 files changed, 192 insertions, 0 deletions
diff --git a/rpki/fields.py b/rpki/fields.py
new file mode 100644
index 00000000..3d859aaa
--- /dev/null
+++ b/rpki/fields.py
@@ -0,0 +1,192 @@
+# $Id$
+#
+# Copyright (C) 2013--2014 Dragon Research Labs ("DRL")
+# Portions copyright (C) 2011--2012 Internet Systems Consortium ("ISC")
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notices and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND DRL AND ISC DISCLAIM ALL
+# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DRL OR
+# ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
+# OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+"""
+Common Django ORM field classes.
+
+Many of these are complex ASN.1 DER objects stored as SQL BLOBs, since
+the only sane text representation would just be the Base64 encoding of
+the DER and thus would add no value.
+"""
+
+from django.db import models
+from south.modelsinspector import add_introspection_rules
+
+import rpki.x509
+import rpki.sundial
+
+
+class EnumField(models.PositiveSmallIntegerField):
+ """
+ An enumeration type that uses strings in Python and small integers
+ in SQL.
+ """
+
+ description = "An enumeration type"
+
+ __metaclass__ = models.SubfieldBase
+
+ def __init__(self, *args, **kwargs):
+ if isinstance(kwargs.get("choices"), (tuple, list)) and isinstance(kwargs["choices"][0], (str, unicode)):
+ kwargs["choices"] = tuple(enumerate(kwargs["choices"], 1))
+ models.PositiveSmallIntegerField.__init__(self, *args, **kwargs)
+ self.enum_i2s = dict(self.flatchoices)
+ self.enum_s2i = dict((v, k) for k, v in self.flatchoices)
+
+ def to_python(self, value):
+ return self.enum_i2s.get(value, value)
+
+ def get_prep_value(self, value):
+ return self.enum_s2i.get(value, value)
+
+
+class SundialField(models.DateTimeField):
+ """
+ A field type for our customized datetime objects.
+ """
+ __metaclass__ = models.SubfieldBase
+
+ description = "A datetime type using our customized datetime objects"
+
+ def to_python(self, value):
+ if isinstance(value, rpki.sundial.pydatetime.datetime):
+ return rpki.sundial.datetime.from_datetime(
+ models.DateTimeField.to_python(self, value))
+ else:
+ return value
+
+ def get_prep_value(self, value):
+ if isinstance(value, rpki.sundial.datetime):
+ return value.to_datetime()
+ else:
+ return value
+
+
+class BlobField(models.Field):
+ """
+ Basic BLOB field, no type conversion, just an opaque byte string.
+
+ "BLOB" = "Binary Large OBject". Most SQL implementations seem to
+ have such a thing, but support appears to predate standardization,
+ so they all do it slightly differently and we have to cope.
+
+ In PostgreSQL, BLOBs are called "bytea".
+
+ In MySQL, there are different sizes of BLOBs and one must pick the
+ right one to avoid data truncation. RPKI manifests and CRLs can be
+ longer than 65535 octets, so in MySQL the only safe BLOB type for
+ general use is "LONGBLOB".
+
+ SQLite...is not like the other children: data types are more like
+ guidelines than actual rules. But "BLOB" works.
+
+ For anything else, we just use "BLOB" and hope for the best.
+ """
+
+ __metaclass__ = models.SubfieldBase
+ description = "Raw BLOB type without ASN.1 encoding/decoding"
+
+ def __init__(self, *args, **kwargs):
+ self.blob_type = kwargs.pop("blob_type", None)
+ kwargs["serialize"] = False
+ kwargs["blank"] = True
+ kwargs["default"] = None
+ models.Field.__init__(self, *args, **kwargs)
+
+ def db_type(self, connection):
+ if self.blob_type is not None:
+ return self.blob_type
+ elif connection.settings_dict['ENGINE'] == "django.db.backends.mysql":
+ return "LONGBLOB"
+ elif connection.settings_dict['ENGINE'] == "django.db.backends.posgresql":
+ return "bytea"
+ else:
+ return "BLOB"
+
+
+# For reasons which now escape me, I had a few fields in the old
+# hand-coded SQL which used MySQL type BINARY(20) to hold SKIs.
+# Presumably this was so that I could then use those SKIs in indexes
+# and searches, but apparently I never got around to that part.
+#
+# SKIs probably would be better stored as hex strings anyway, so not
+# bothering with a separate binary type model for this. Deal with
+# this if and when it ever becomes an issue.
+
+
+class DERField(BlobField):
+ """
+ Field class for DER objects. These are derived from BLOBs, but with
+ automatic translation between ASN.1 and Python types.
+
+ DERField itself is an abstract class, concrete field classes are
+ derived from it.
+ """
+
+ __metaclass__ = models.SubfieldBase
+
+ def to_python(self, value):
+ assert value is None or isinstance(value, (self.rpki_type, str))
+ if isinstance(value, str):
+ return self.rpki_type(DER = value)
+ else:
+ return value
+
+ def get_prep_value(self, value):
+ assert value is None or isinstance(value, (self.rpki_type, str))
+ if isinstance(value, self.rpki_type):
+ return value.get_DER()
+ else:
+ return value
+
+class CertificateField(DERField):
+ description = "X.509 certificate"
+ rpki_type = rpki.x509.X509
+
+class KeyField(DERField):
+ description = "RSA keypair"
+ rpki_type = rpki.x509.RSA
+
+class CRLField(DERField):
+ description = "Certificate Revocation List"
+ rpki_type = rpki.x509.CRL
+
+class PKCS10Field(DERField):
+ description = "PKCS #10 certificate request"
+ rpki_type = rpki.x509.PKCS10
+
+class ManifestField(DERField):
+ description = "RPKI Manifest"
+ rpki_type = rpki.x509.SignedManifest
+
+class ROAField(DERField):
+ description = "ROA"
+ rpki_type = rpki.x509.ROA
+
+class GhostbusterField(DERField):
+ description = "Ghostbuster Record"
+ rpki_type = rpki.x509.Ghostbuster
+
+
+field_classes = (EnumField, SundialField, BlobField, CertificateField, KeyField,
+ CRLField, PKCS10Field, ManifestField, ROAField, GhostbusterField)
+
+add_introspection_rules([(field_classes, [], {})],
+ [r"^rpki\.fields\." + cls.__name__ for cls in field_classes])
+
+del field_classes