summaryrefslogtreecommitdiffstats
path: root/keystone/contrib/kds/backends/sql.py
diff options
context:
space:
mode:
Diffstat (limited to 'keystone/contrib/kds/backends/sql.py')
-rw-r--r--keystone/contrib/kds/backends/sql.py111
1 files changed, 110 insertions, 1 deletions
diff --git a/keystone/contrib/kds/backends/sql.py b/keystone/contrib/kds/backends/sql.py
index b7878f2a..36dec215 100644
--- a/keystone/contrib/kds/backends/sql.py
+++ b/keystone/contrib/kds/backends/sql.py
@@ -15,10 +15,43 @@
# under the License.
import base64
+import copy
import hashlib
+import time
from keystone.common import sql
+'''
+There are 2 types of keys we save here.
+Individual keys and group keys.
+
+Individual keys are identified by an id string and in 'key' is a
+dictionary the contains only the base64-encoded encrypted key in a
+field named 'key_v1'.
+Example:
+ id = d22b35312b77798eef75f195c2ed8ea78d2c26a98dedfa1c87ff0d4660636cc9
+ name = 'compute.abc.example.com'
+ key = {'key_v1': 'ERWERewre.....'}
+
+Group keys use a more complex key payload, where multiple
+'generations' of keys are stored with expiration times, as well as a
+generation counter.
+Example:
+ id = a02ba163f3a02db22fdd14b310119f1a4c2f9e4a773a573f757904dd5433d4dd
+ name = 'scheduler'
+ key = {'generation': 10,
+ 'group_keys': {'9': {'key_v1': 'ERWERewre.....',
+ 'expiration': 1234567890
+ },
+ '10': {'key_v1': 'GFDSAFhjhkd.....',
+ 'expiration': 1234509876
+ }
+ }
+
+'''
+
+EXPIRATION_TIME = 600
+
class Keys(sql.ModelBase, sql.DictBase):
__tablename__ = 'kds_keys'
@@ -58,6 +91,82 @@ class KDS(sql.Base):
session.add(key_ref)
session.flush()
+ def set_group_key(self, kds_id, expiration=None, key=None):
+ """Creates a new group of keys or sets a new key
+
+ If key is empty this function will create a abre entry in the
+ database or fail if one exists, in this case we return 0 on
+ success or an exception.
+ When a key is provided the database entry is updated with the
+ new key. Additionally any expired keys are removed.
+ We return the generation number associated to the new key on
+ success or an exception.
+
+ :param kds_id: the name/id of the key
+ :param expiration: the requested expiration time for the key
+ :param key: the actual key material (will be base64 encoded
+ before being stored in the db)
+ :returns: 0 when crating a new key, or the generation number
+ """
+
+ session = self.get_session()
+
+ if key is None:
+ # Create new group
+ with session.begin():
+ new_group = {'id': self._id_from_name(kds_id),
+ 'name': kds_id,
+ 'key': {'generation': 0, 'group_keys': dict()}}
+ key_ref = Keys.from_dict(new_group)
+ session.add(key_ref)
+ session.flush()
+ return 0
+
+ key_ref = None
+ cur_val = None
+ new_val = None
+ gen = 0
+ with session.begin():
+ id = self._id_from_name(kds_id)
+ key_ref = session.query(Keys).filter_by(id=id).first()
+ cur_val = key_ref.to_dict()
+
+ # get current entry if any, parse it and retrieve each
+ # referenced generation key, then delete all the keys expired
+ # by more than 10 minutes in a deepcopy
+ k = cur_val['key']
+ if 'generation' in k:
+ gen = k['generation']
+ if 'group_keys' in k:
+ purge_time = time.time() - EXPIRATION_TIME
+ ks = k['group_keys']
+ for g in ks:
+ if ks[g]['expiration'] < purge_time:
+ if new_val is None:
+ new_val = copy.deepcopy(cur_val)
+ del new_val['key']['group_keys'][g]
+
+ if new_val is None:
+ new_val = copy.deepcopy(cur_val)
+
+ gen = gen + 1
+ bkey = base64.b64encode(key)
+ new_val['key']['generation'] = gen
+ new_val['key']['group_keys'][gen] = {'expiration': expiration,
+ 'key_v1': bkey}
+
+ # first delete old if any
+ if key_ref is not None:
+ session.delete(key_ref)
+
+ # now store new generation list
+ key_ref = Keys.from_dict(new_val)
+ session.add(key_ref)
+
+ session.flush()
+
+ return gen
+
def get_shared_key(self, kds_id):
session = self.get_session()
id = self._id_from_name(kds_id)
@@ -65,4 +174,4 @@ class KDS(sql.Base):
if not key_ref:
return None
d = key_ref.to_dict()
- return base64.b64decode(d['key']['key_v1'])
+ return d['key']