Merge "Synchronize the key manager interface with Cinder"
This commit is contained in:
@@ -977,7 +977,7 @@
|
|||||||
|
|
||||||
# The full class name of the key manager API class (string
|
# The full class name of the key manager API class (string
|
||||||
# value)
|
# value)
|
||||||
#keymgr_api_class=nova.keymgr.key_mgr.KeyManager
|
#keymgr_api_class=nova.keymgr.not_implemented_key_mgr.NotImplementedKeyManager
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@@ -21,7 +21,8 @@ from nova.openstack.common import log as logging
|
|||||||
|
|
||||||
keymgr_opts = [
|
keymgr_opts = [
|
||||||
cfg.StrOpt('keymgr_api_class',
|
cfg.StrOpt('keymgr_api_class',
|
||||||
default='nova.keymgr.key_mgr.KeyManager',
|
default='nova.keymgr.'
|
||||||
|
'not_implemented_key_mgr.NotImplementedKeyManager',
|
||||||
help='The full class name of the key manager API class'),
|
help='The full class name of the key manager API class'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@@ -33,15 +33,18 @@ class Key(object):
|
|||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_algorithm(self):
|
def get_algorithm(self):
|
||||||
"""Returns this key's algorithm. For example, "DSA" would indicate
|
"""Returns the key's algorithm.
|
||||||
that this key is a DSA key.
|
|
||||||
|
Returns the key's algorithm. For example, "DSA" indicates that this key
|
||||||
|
is a DSA key and "AES" indicates that this key is an AES key.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_format(self):
|
def get_format(self):
|
||||||
"""Returns the encoding format of this key or None if this key is not
|
"""Returns the encoding format.
|
||||||
encoded.
|
|
||||||
|
Returns the key's encoding format or None if this key is not encoded.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -57,8 +60,10 @@ class SymmetricKey(Key):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, alg, key):
|
def __init__(self, alg, key):
|
||||||
"""Create a new SymmetricKey object. This specifies the algorithm for
|
"""Create a new SymmetricKey object.
|
||||||
the symmetric encryption and the bytes for the key.
|
|
||||||
|
The arguments specify the algorithm for the symmetric encryption and
|
||||||
|
the bytes for the key.
|
||||||
"""
|
"""
|
||||||
self.alg = alg
|
self.alg = alg
|
||||||
self.key = key
|
self.key = key
|
||||||
@@ -68,7 +73,7 @@ class SymmetricKey(Key):
|
|||||||
return self.alg
|
return self.alg
|
||||||
|
|
||||||
def get_format(self):
|
def get_format(self):
|
||||||
"""This returns 'RAW'."""
|
"""This method returns 'RAW'."""
|
||||||
return "RAW"
|
return "RAW"
|
||||||
|
|
||||||
def get_encoded(self):
|
def get_encoded(self):
|
||||||
|
@@ -52,6 +52,21 @@ class KeyManager(object):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def copy_key(self, ctxt, key_id, **kwargs):
|
||||||
|
"""Copies (i.e., clones) a key stored by the key manager.
|
||||||
|
|
||||||
|
This method copies the specified key and returns the copy's UUID. If
|
||||||
|
the specified context does not permit copying keys, then a
|
||||||
|
NotAuthorized error should be raised.
|
||||||
|
|
||||||
|
Implementation note: This method should behave identically to
|
||||||
|
store_key(context, get_key(context, <encryption key UUID>))
|
||||||
|
although it is preferable to perform this operation within the key
|
||||||
|
manager to avoid unnecessary handling of the key material.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_key(self, ctxt, key_id, **kwargs):
|
def get_key(self, ctxt, key_id, **kwargs):
|
||||||
"""Retrieves the specified key.
|
"""Retrieves the specified key.
|
||||||
|
42
nova/keymgr/not_implemented_key_mgr.py
Normal file
42
nova/keymgr/not_implemented_key_mgr.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Key manager implementation that raises NotImplementedError
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nova.keymgr import key_mgr
|
||||||
|
|
||||||
|
|
||||||
|
class NotImplementedKeyManager(key_mgr.KeyManager):
|
||||||
|
"""Key Manager Interface that raises NotImplementedError for all operations
|
||||||
|
"""
|
||||||
|
|
||||||
|
def create_key(self, ctxt, algorithm='AES', length=256, expiration=None,
|
||||||
|
**kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def store_key(self, ctxt, key, expiration=None, **kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def copy_key(self, ctxt, key_id, **kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def get_key(self, ctxt, key_id, **kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def delete_key(self, ctxt, key_id, **kwargs):
|
||||||
|
raise NotImplementedError()
|
26
nova/tests/keymgr/fake.py
Normal file
26
nova/tests/keymgr/fake.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2011 Justin Santa Barbara
|
||||||
|
# Copyright 2012 OpenStack LLC
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""Implementation of a fake key manager."""
|
||||||
|
|
||||||
|
|
||||||
|
from nova.tests.keymgr import mock_key_mgr
|
||||||
|
|
||||||
|
|
||||||
|
def fake_api():
|
||||||
|
return mock_key_mgr.MockKeyManager()
|
@@ -20,12 +20,12 @@ anything but integration testing.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import array
|
import array
|
||||||
import uuid
|
|
||||||
|
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.keymgr import key
|
from nova.keymgr import key
|
||||||
from nova.keymgr import key_mgr
|
from nova.keymgr import key_mgr
|
||||||
from nova.openstack.common import log as logging
|
from nova.openstack.common import log as logging
|
||||||
|
from nova.openstack.common import uuidutils
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
|
||||||
|
|
||||||
@@ -35,15 +35,15 @@ LOG = logging.getLogger(__name__)
|
|||||||
class MockKeyManager(key_mgr.KeyManager):
|
class MockKeyManager(key_mgr.KeyManager):
|
||||||
"""
|
"""
|
||||||
This mock key manager implementation supports all the methods specified
|
This mock key manager implementation supports all the methods specified
|
||||||
by the key manager interface. This implementation creates a single key in
|
by the key manager interface. This implementation stores keys within a
|
||||||
response to all invocations of create_key. Side effects (e.g., raising
|
dictionary, and as a result, it is not acceptable for use across different
|
||||||
exceptions) for each method are handled as specified by the key manager
|
services. Side effects (e.g., raising exceptions) for each method are
|
||||||
interface.
|
handled as specified by the key manager interface.
|
||||||
|
|
||||||
This class should NOT be used for anything but integration testing because
|
This class should NOT be used for anything but integration testing because
|
||||||
the same key is created for all invocations of create_key and keys are not
|
keys are not stored persistently.
|
||||||
stored persistently.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.keys = {}
|
self.keys = {}
|
||||||
|
|
||||||
@@ -67,6 +67,13 @@ class MockKeyManager(key_mgr.KeyManager):
|
|||||||
|
|
||||||
return self.store_key(ctxt, _key)
|
return self.store_key(ctxt, _key)
|
||||||
|
|
||||||
|
def _generate_key_id(self):
|
||||||
|
key_id = uuidutils.generate_uuid()
|
||||||
|
while key_id in self.keys:
|
||||||
|
key_id = uuidutils.generate_uuid()
|
||||||
|
|
||||||
|
return key_id
|
||||||
|
|
||||||
def store_key(self, ctxt, key, **kwargs):
|
def store_key(self, ctxt, key, **kwargs):
|
||||||
"""Stores (i.e., registers) a key with the key manager.
|
"""Stores (i.e., registers) a key with the key manager.
|
||||||
|
|
||||||
@@ -76,22 +83,26 @@ class MockKeyManager(key_mgr.KeyManager):
|
|||||||
if ctxt is None:
|
if ctxt is None:
|
||||||
raise exception.NotAuthorized()
|
raise exception.NotAuthorized()
|
||||||
|
|
||||||
# generate UUID and ensure that it isn't in use
|
key_id = self._generate_key_id()
|
||||||
key_id = uuid.uuid4()
|
|
||||||
while key_id in self.keys:
|
|
||||||
key_id = uuid.uuid4()
|
|
||||||
|
|
||||||
self.keys[key_id] = key
|
self.keys[key_id] = key
|
||||||
|
|
||||||
return key_id
|
return key_id
|
||||||
|
|
||||||
|
def copy_key(self, ctxt, key_id, **kwargs):
|
||||||
|
if ctxt is None:
|
||||||
|
raise exception.NotAuthorized()
|
||||||
|
|
||||||
|
copied_key_id = self._generate_key_id()
|
||||||
|
self.keys[copied_key_id] = self.keys[key_id]
|
||||||
|
|
||||||
|
return copied_key_id
|
||||||
|
|
||||||
def get_key(self, ctxt, key_id, **kwargs):
|
def get_key(self, ctxt, key_id, **kwargs):
|
||||||
"""Retrieves the key identified by the specified id.
|
"""Retrieves the key identified by the specified id.
|
||||||
|
|
||||||
This implementation returns a fixed key that is associated with the
|
This implementation returns the key that is associated with the
|
||||||
UUID returned by the create_key method. A NotAuthorized exception is
|
specified UUID. A NotAuthorized exception is raised if the specified
|
||||||
raised if the specified context is None; a KeyError is raised if the
|
context is None; a KeyError is raised if the UUID is invalid.
|
||||||
UUID is invalid.
|
|
||||||
"""
|
"""
|
||||||
if ctxt is None:
|
if ctxt is None:
|
||||||
raise exception.NotAuthorized()
|
raise exception.NotAuthorized()
|
||||||
@@ -101,9 +112,8 @@ class MockKeyManager(key_mgr.KeyManager):
|
|||||||
def delete_key(self, ctxt, key_id, **kwargs):
|
def delete_key(self, ctxt, key_id, **kwargs):
|
||||||
"""Deletes the key identified by the specified id.
|
"""Deletes the key identified by the specified id.
|
||||||
|
|
||||||
This implementation intentionally does nothing except raise a
|
A NotAuthorized exception is raised if the context is None and a
|
||||||
NotAuthorized exception is the context is None or a KeyError if the
|
KeyError is raised if the UUID is invalid.
|
||||||
UUID is invalid.
|
|
||||||
"""
|
"""
|
||||||
if ctxt is None:
|
if ctxt is None:
|
||||||
raise exception.NotAuthorized()
|
raise exception.NotAuthorized()
|
||||||
|
@@ -22,7 +22,7 @@ import array
|
|||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.keymgr import key
|
from nova.keymgr import key as keymgr_key
|
||||||
from nova.tests.keymgr import mock_key_mgr
|
from nova.tests.keymgr import mock_key_mgr
|
||||||
from nova.tests.keymgr import test_key_mgr
|
from nova.tests.keymgr import test_key_mgr
|
||||||
|
|
||||||
@@ -54,8 +54,8 @@ class MockKeyManagerTestCase(test_key_mgr.KeyManagerTestCase):
|
|||||||
self.key_mgr.create_key, None)
|
self.key_mgr.create_key, None)
|
||||||
|
|
||||||
def test_store_key(self):
|
def test_store_key(self):
|
||||||
_key = key.SymmetricKey('AES',
|
secret_key = array.array('B', ('0' * 64).decode('hex')).tolist()
|
||||||
array.array('B', ('0' * 64).decode('hex')).tolist())
|
_key = keymgr_key.SymmetricKey('AES', secret_key)
|
||||||
key_id = self.key_mgr.store_key(self.ctxt, _key)
|
key_id = self.key_mgr.store_key(self.ctxt, _key)
|
||||||
|
|
||||||
actual_key = self.key_mgr.get_key(self.ctxt, key_id)
|
actual_key = self.key_mgr.get_key(self.ctxt, key_id)
|
||||||
@@ -65,6 +65,20 @@ class MockKeyManagerTestCase(test_key_mgr.KeyManagerTestCase):
|
|||||||
self.assertRaises(exception.NotAuthorized,
|
self.assertRaises(exception.NotAuthorized,
|
||||||
self.key_mgr.store_key, None, None)
|
self.key_mgr.store_key, None, None)
|
||||||
|
|
||||||
|
def test_copy_key(self):
|
||||||
|
key_id = self.key_mgr.create_key(self.ctxt)
|
||||||
|
key = self.key_mgr.get_key(self.ctxt, key_id)
|
||||||
|
|
||||||
|
copied_key_id = self.key_mgr.copy_key(self.ctxt, key_id)
|
||||||
|
copied_key = self.key_mgr.get_key(self.ctxt, copied_key_id)
|
||||||
|
|
||||||
|
self.assertNotEqual(key_id, copied_key_id)
|
||||||
|
self.assertEqual(key, copied_key)
|
||||||
|
|
||||||
|
def test_copy_null_context(self):
|
||||||
|
self.assertRaises(exception.NotAuthorized,
|
||||||
|
self.key_mgr.copy_key, None, None)
|
||||||
|
|
||||||
def test_get_key(self):
|
def test_get_key(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
51
nova/tests/keymgr/test_not_implemented_key_mgr.py
Normal file
51
nova/tests/keymgr/test_not_implemented_key_mgr.py
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
# Copyright (c) 2013 The Johns Hopkins University/Applied Physics Laboratory
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Test cases for the not implemented key manager.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nova.keymgr import not_implemented_key_mgr
|
||||||
|
from nova.tests.keymgr import test_key_mgr
|
||||||
|
|
||||||
|
|
||||||
|
class NotImplementedKeyManagerTestCase(test_key_mgr.KeyManagerTestCase):
|
||||||
|
|
||||||
|
def _create_key_manager(self):
|
||||||
|
return not_implemented_key_mgr.NotImplementedKeyManager()
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(NotImplementedKeyManagerTestCase, self).setUp()
|
||||||
|
|
||||||
|
def test_create_key(self):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.key_mgr.create_key, None)
|
||||||
|
|
||||||
|
def test_store_key(self):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.key_mgr.store_key, None, None)
|
||||||
|
|
||||||
|
def test_copy_key(self):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.key_mgr.copy_key, None, None)
|
||||||
|
|
||||||
|
def test_get_key(self):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.key_mgr.get_key, None, None)
|
||||||
|
|
||||||
|
def test_delete_key(self):
|
||||||
|
self.assertRaises(NotImplementedError,
|
||||||
|
self.key_mgr.delete_key, None, None)
|
Reference in New Issue
Block a user