aboutsummaryrefslogtreecommitdiff
path: root/rpki/rcynicdb
diff options
context:
space:
mode:
Diffstat (limited to 'rpki/rcynicdb')
-rw-r--r--rpki/rcynicdb/__init__.py0
-rw-r--r--rpki/rcynicdb/iterator.py49
-rw-r--r--rpki/rcynicdb/migrations/0001_initial.py58
-rw-r--r--rpki/rcynicdb/migrations/0002_auto_20160227_2003.py29
-rw-r--r--rpki/rcynicdb/migrations/0003_auto_20160301_0333.py24
-rw-r--r--rpki/rcynicdb/migrations/__init__.py0
-rw-r--r--rpki/rcynicdb/models.py81
7 files changed, 241 insertions, 0 deletions
diff --git a/rpki/rcynicdb/__init__.py b/rpki/rcynicdb/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rpki/rcynicdb/__init__.py
diff --git a/rpki/rcynicdb/iterator.py b/rpki/rcynicdb/iterator.py
new file mode 100644
index 00000000..a754ed72
--- /dev/null
+++ b/rpki/rcynicdb/iterator.py
@@ -0,0 +1,49 @@
+"""
+rcynic database iterator.
+
+At least for the moment, we attempt to provide an iterator that works
+with both old-style (directory tree of file objects with names similar
+to what wget would use) and new style (Django ORM) databases.
+"""
+
+import os
+
+initialized_django = False
+
+def _uri_to_class(uri, class_map):
+ return class_map[uri[uri.rindex(".")+1:]]
+
+def authenticated_objects(directory_tree = None, uri_suffix = None, class_map = None):
+
+ if class_map is None:
+ import rpki.POW
+ class_map = dict(cer = rpki.POW.X509,
+ crl = rpki.POW.CRL,
+ gbr = rpki.POW.CMS,
+ mft = rpki.POW.Manifest,
+ roa = rpki.POW.ROA)
+
+ if directory_tree:
+ for head, dirs, files in os.walk(directory_tree):
+ for fn in files:
+ if uri_suffix is None or fn.endswith(uri_suffix):
+ fn = os.path.join(head, fn)
+ uri = "rsync://" + fn[len(directory_tree):].lstrip("/")
+ yield uri, _uri_to_class(uri, class_map).derReadFile(fn)
+ return
+
+ global initialized_django
+ if not initialized_django:
+ os.environ.update(DJANGO_SETTINGS_MODULE = "rpki.django_settings.rcynic")
+ import django
+ django.setup()
+ initialized_django = True
+
+ import rpki.rcynicdb
+ auth = rpki.rcynicdb.models.Authenticated.objects.order_by("-started").first()
+ if auth is None:
+ return
+
+ q = auth.rpkiobject_set
+ for obj in q.filter(uri__endswith = uri_suffix) if uri_suffix else q.all():
+ yield obj.uri, _uri_to_class(obj.uri, class_map).derRead(obj.der)
diff --git a/rpki/rcynicdb/migrations/0001_initial.py b/rpki/rcynicdb/migrations/0001_initial.py
new file mode 100644
index 00000000..5f60253b
--- /dev/null
+++ b/rpki/rcynicdb/migrations/0001_initial.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Authenticated',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('started', models.DateTimeField()),
+ ('finished', models.DateTimeField(null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='Retrieval',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('uri', models.TextField()),
+ ('started', models.DateTimeField()),
+ ('finished', models.DateTimeField()),
+ ('successful', models.BooleanField()),
+ ],
+ ),
+ migrations.CreateModel(
+ name='RPKIObject',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('der', models.BinaryField()),
+ ('uri', models.TextField()),
+ ('aki', models.SlugField(max_length=40)),
+ ('ski', models.SlugField(max_length=40)),
+ ('sha256', models.SlugField(unique=True, max_length=64)),
+ ('authenticated', models.ManyToManyField(to='rcynicdb.Authenticated')),
+ ('retrieved', models.ForeignKey(to='rcynicdb.Retrieval')),
+ ],
+ ),
+ migrations.CreateModel(
+ name='RRDPSnapshot',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('session_id', models.UUIDField()),
+ ('serial', models.BigIntegerField()),
+ ('retrieved', models.OneToOneField(to='rcynicdb.Retrieval')),
+ ],
+ ),
+ migrations.AddField(
+ model_name='rpkiobject',
+ name='snapshot',
+ field=models.ManyToManyField(to='rcynicdb.RRDPSnapshot'),
+ ),
+ ]
diff --git a/rpki/rcynicdb/migrations/0002_auto_20160227_2003.py b/rpki/rcynicdb/migrations/0002_auto_20160227_2003.py
new file mode 100644
index 00000000..9c3acecb
--- /dev/null
+++ b/rpki/rcynicdb/migrations/0002_auto_20160227_2003.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rcynicdb', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='retrieval',
+ name='finished',
+ field=models.DateTimeField(null=True),
+ ),
+ migrations.AlterField(
+ model_name='retrieval',
+ name='successful',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='rrdpsnapshot',
+ name='retrieved',
+ field=models.OneToOneField(null=True, to='rcynicdb.Retrieval'),
+ ),
+ ]
diff --git a/rpki/rcynicdb/migrations/0003_auto_20160301_0333.py b/rpki/rcynicdb/migrations/0003_auto_20160301_0333.py
new file mode 100644
index 00000000..ea6e5499
--- /dev/null
+++ b/rpki/rcynicdb/migrations/0003_auto_20160301_0333.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('rcynicdb', '0002_auto_20160227_2003'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='retrieval',
+ name='finished',
+ field=models.DateTimeField(),
+ ),
+ migrations.AlterField(
+ model_name='retrieval',
+ name='successful',
+ field=models.BooleanField(),
+ ),
+ ]
diff --git a/rpki/rcynicdb/migrations/__init__.py b/rpki/rcynicdb/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/rpki/rcynicdb/migrations/__init__.py
diff --git a/rpki/rcynicdb/models.py b/rpki/rcynicdb/models.py
new file mode 100644
index 00000000..9a790230
--- /dev/null
+++ b/rpki/rcynicdb/models.py
@@ -0,0 +1,81 @@
+# First cut at ORM models for rcynicng.
+
+from django.db import models
+
+# HTTP/HTTPS/RSYNC fetch event.
+
+class Retrieval(models.Model):
+ uri = models.TextField()
+ started = models.DateTimeField()
+ finished = models.DateTimeField()
+ successful = models.BooleanField()
+
+ def __repr__(self):
+ try:
+ return "<Retrieval: {0.uri} started {0.started} finished {0.finished} successful {0.successful}>".format(self)
+ except:
+ return "<Retrieval: {}>".format(id(self))
+
+# Collection of validated objects.
+
+class Authenticated(models.Model):
+ started = models.DateTimeField()
+ finished = models.DateTimeField(null = True)
+
+ def __repr__(self):
+ try:
+ return "<Authenticated: started {0.started} finished {0.finished}>".format(self)
+ except:
+ return "<Authenticated: {}>".format(id(self))
+
+# One instance of an RRDP snapshot.
+
+class RRDPSnapshot(models.Model):
+ session_id = models.UUIDField()
+ serial = models.BigIntegerField()
+ retrieved = models.OneToOneField(Retrieval, null = True)
+
+ def __repr__(self):
+ try:
+ return "<RRDPSnapshot: serial {0.serial} session_id {0.session_id} retrieved {0.retrieved!r}>".format(self)
+ except:
+ return "<RRDPSnapshot: {}>".format(id(self))
+
+
+# RPKI objects.
+#
+# Might need to add an on_delete argument to the ForeignKey for the
+# retrieved field: the default behavior is CASCADE, which is may not
+# what we want in this case.
+#
+# https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.ForeignKey.on_delete
+#
+# Might also want to provide names for the reverse relationships, code
+# uses blah_set for now.
+
+# Setting unique = True on the der field breaks with PostgreSQL, see
+# https://code.djangoproject.com/ticket/14904
+#
+# In theory collisions on sha256 are possible, but in practice they're
+# not going to occur by accident. Setting unique = True on the sha256
+# field risks deliberate collisions, defending against that would
+# require detecting the collision and figuring out which is the
+# attacking object (easy in theory, as it probably won't validate),
+# then figuring out what to do about it (possibly harder -- do we drop
+# an entire RRDP zone because of one evil object?).
+
+class RPKIObject(models.Model):
+ der = models.BinaryField() # unique = True
+ uri = models.TextField()
+ aki = models.SlugField(max_length = 40) # hex SHA-1
+ ski = models.SlugField(max_length = 40) # hex SHA-1
+ sha256 = models.SlugField(max_length = 64, unique = True) # hex SHA-256
+ retrieved = models.ForeignKey(Retrieval)
+ authenticated = models.ManyToManyField(Authenticated)
+ snapshot = models.ManyToManyField(RRDPSnapshot)
+
+ def __repr__(self):
+ try:
+ return "<RPKIObject: uri {0.uri} sha256 {0.sha256} ski {0.ski} aki {0.aki} retrieved {0.retrieved!r}>".format(self)
+ except:
+ return "<RPKIObject: {}>".format(id(self))