Package list python-castellan / 3d031cb
Update the key manager API Includes changes to the base API class to support managed objects and creation of asymmetric key pairs. The current implementations of the key manager only support symmetric keys for retrieval, and raise NotImplementedErrors for generation of asymmetric key pairs. Full functionality coming in later commits. Change-Id: I69e0c22729413e95808f9419df59017011f14d99 Kaitlin Farr 6 years ago
7 changed file(s) with 198 addition(s) and 167 deletion(s). Raw diff Collapse all Expand all
106106 return base_url
107107
108108 def create_key(self, context, algorithm, length, expiration=None):
109 """Creates a key.
109 """Creates a symmetric key.
110110
111111 :param context: contains information of the user and the environment
112112 for the request (castellan/context.py)
134134 with excutils.save_and_reraise_exception():
135135 LOG.error(u._LE("Error creating key: %s"), e)
136136
137 def store_key(self, context, key, expiration=None):
138 """Stores (i.e., registers) a key with the key manager.
139
140 :param context: contains information of the user and the environment
141 for the request (castellan/context.py)
142 :param key: the unencrypted secret data. Known as "payload" to the
143 barbicanclient api
144 :param expiration: the expiration time of the secret in ISO 8601
145 format
146 :returns: the UUID of the stored key
137 def create_key_pair(self, context, algorithm, length, expiration=None):
138 """Creates an asymmetric key pair.
139
140 Not implemented yet.
141
142 :param context: contains information of the user and the environment
143 for the request (castellan/context.py)
144 :param algorithm: the algorithm associated with the secret
145 :param length: the bit length of the secret
146 :param expiration: the date the key will expire
147 :return: TODO: the UUIDs of the new key, in the order (private, public)
148 :raises NotImplementedError: until implemented
147149 :raises HTTPAuthError: if key creation fails with 401
148150 :raises HTTPClientError: if key creation failes with 4xx
149151 :raises HTTPServerError: if key creation fails with 5xx
150152 """
153 raise NotImplementedError()
154
155 def store(self, context, managed_object, expiration=None):
156 """Stores (i.e., registers) an object with the key manager.
157
158 :param context: contains information of the user and the environment
159 for the request (castellan/context.py)
160 :param managed_object: the unencrypted secret data. Known as "payload"
161 to the barbicanclient api
162 :param expiration: the expiration time of the secret in ISO 8601
163 format
164 :returns: the UUID of the stored object
165 :raises HTTPAuthError: if object creation fails with 401
166 :raises HTTPClientError: if object creation failes with 4xx
167 :raises HTTPServerError: if object creation fails with 5xx
168 """
151169 barbican_client = self._get_barbican_client(context)
152170
153171 try:
154 if key.algorithm:
155 algorithm = key.algorithm
156 encoded_key = key.get_encoded()
172 if managed_object.algorithm:
173 algorithm = managed_object.algorithm
174 else:
175 algorithm = None
176 encoded_object = managed_object.get_encoded()
157177 # TODO(kfarr) add support for objects other than symmetric keys
158 secret = barbican_client.secrets.create(payload=encoded_key,
178 secret = barbican_client.secrets.create(payload=encoded_object,
159179 algorithm=algorithm,
160180 expiration=expiration)
161181 secret_ref = secret.store()
164184 barbican_exceptions.HTTPClientError,
165185 barbican_exceptions.HTTPServerError) as e:
166186 with excutils.save_and_reraise_exception():
167 LOG.error(u._LE("Error storing key: %s"), e)
168
169 def copy_key(self, context, key_id):
170 """Copies (i.e., clones) a key stored by barbican.
171
172 :param context: contains information of the user and the environment
173 for the request (castellan/context.py)
174 :param key_id: the UUID of the key to copy
175 :return: the UUID of the key copy
176 :raises HTTPAuthError: if key creation fails with 401
177 :raises HTTPClientError: if key creation failes with 4xx
178 :raises HTTPServerError: if key creation fails with 5xx
179 """
180
181 try:
182 secret = self._get_secret(context, key_id)
187 LOG.error(u._LE("Error storing object: %s"), e)
188
189 def copy(self, context, managed_object_id):
190 """Copies (i.e., clones) a managed object stored by barbican.
191
192 :param context: contains information of the user and the environment
193 for the request (castellan/context.py)
194 :param managed_object_id: the UUID of the object to copy
195 :return: the UUID of the object copy
196 :raises HTTPAuthError: if object creation fails with 401
197 :raises HTTPClientError: if object creation failes with 4xx
198 :raises HTTPServerError: if object creation fails with 5xx
199 """
200
201 try:
202 secret = self._get_secret(context, managed_object_id)
183203 secret_data = self._get_secret_data(secret)
184204 # TODO(kfarr) modify to support other types of keys
185 key = sym_key.SymmetricKey(secret.algorithm, secret_data)
186 copy_uuid = self.store_key(context, key, secret.expiration)
205 key = sym_key.SymmetricKey(secret.algorithm,
206 secret.bit_length,
207 secret_data)
208 copy_uuid = self.store(context, key, secret.expiration)
187209 return copy_uuid
188210 except (barbican_exceptions.HTTPAuthError,
189211 barbican_exceptions.HTTPClientError,
190212 barbican_exceptions.HTTPServerError) as e:
191213 with excutils.save_and_reraise_exception():
192 LOG.error(u._LE("Error copying key: %s"), e)
214 LOG.error(u._LE("Error copying object: %s"), e)
193215
194216 def _create_secret_ref(self, key_id):
195217 """Creates the URL required for accessing a secret.
234256 for the request (castellan/context.py)
235257 :param key_id: UUID of the secret
236258 :return: the secret's metadata
237 :raises HTTPAuthError: if key creation fails with 401
238 :raises HTTPClientError: if key creation failes with 4xx
239 :raises HTTPServerError: if key creation fails with 5xx
259 :raises HTTPAuthError: if object retrieval fails with 401
260 :raises HTTPClientError: if object retrieval fails with 4xx
261 :raises HTTPServerError: if object retrieval fails with 5xx
240262 """
241263
242264 barbican_client = self._get_barbican_client(context)
250272 with excutils.save_and_reraise_exception():
251273 LOG.error(u._LE("Error getting secret metadata: %s"), e)
252274
253 def get_key(self, context, key_id):
254 """Retrieves the specified key.
255
256 :param context: contains information of the user and the environment
257 for the request (castellan/context.py)
258 :param key_id: the UUID of the key to retrieve
275 def get(self, context, managed_object_id):
276 """Retrieves the specified managed object.
277
278 Currently only supports retrieving symmetric keys.
279
280 :param context: contains information of the user and the environment
281 for the request (castellan/context.py)
282 :param managed_object_id: the UUID of the object to retrieve
259283 :return: SymmetricKey representation of the key
260 :raises HTTPAuthError: if key creation fails with 401
261 :raises HTTPClientError: if key creation failes with 4xx
262 :raises HTTPServerError: if key creation fails with 5xx
263 """
264 try:
265 secret = self._get_secret(context, key_id)
284 :raises HTTPAuthError: if object retrieval fails with 401
285 :raises HTTPClientError: if object retrieval fails with 4xx
286 :raises HTTPServerError: if object retrieval fails with 5xx
287 """
288 try:
289 secret = self._get_secret(context, managed_object_id)
266290 secret_data = self._get_secret_data(secret)
267291 # TODO(kfarr) add support for other objects
268292 key = sym_key.SymmetricKey(secret.algorithm,
273297 barbican_exceptions.HTTPClientError,
274298 barbican_exceptions.HTTPServerError) as e:
275299 with excutils.save_and_reraise_exception():
276 LOG.error(u._LE("Error getting key: %s"), e)
277
278 def delete_key(self, context, key_id):
279 """Deletes the specified key.
280
281 :param context: contains information of the user and the environment
282 for the request (castellan/context.py)
283 :param key_id: the UUID of the key to delete
284 :raises HTTPAuthError: if key creation fails with 401
285 :raises HTTPClientError: if key creation failes with 4xx
286 :raises HTTPServerError: if key creation fails with 5xx
300 LOG.error(u._LE("Error getting object: %s"), e)
301
302 def delete(self, context, managed_object_id):
303 """Deletes the specified managed object.
304
305 :param context: contains information of the user and the environment
306 for the request (castellan/context.py)
307 :param managed_object_id: the UUID of the object to delete
308 :raises HTTPAuthError: if key deletion fails with 401
309 :raises HTTPClientError: if key deletion fails with 4xx
310 :raises HTTPServerError: if key deletion fails with 5xx
287311 """
288312 barbican_client = self._get_barbican_client(context)
289313
290314 try:
291 secret_ref = self._create_secret_ref(key_id)
315 secret_ref = self._create_secret_ref(managed_object_id)
292316 barbican_client.secrets.delete(secret_ref)
293317 except (barbican_exceptions.HTTPAuthError,
294318 barbican_exceptions.HTTPClientError,
295319 barbican_exceptions.HTTPServerError) as e:
296320 with excutils.save_and_reraise_exception():
297 LOG.error(u._LE("Error deleting key: %s"), e)
321 LOG.error(u._LE("Error deleting object: %s"), e)
3030 """
3131
3232 @abc.abstractmethod
33 def create_key(self, context, algorithm, length,
34 expiration=None):
35 """Creates a key.
33 def create_key(self, context, algorithm, length, expiration=None):
34 """Creates a symmetric key.
3635
37 This method creates a key and returns the key's UUID. If the specified
38 context does not permit the creation of keys, then a NotAuthorized
39 exception should be raised.
36 This method creates a symmetric key and returns the key's UUID. If the
37 specified context does not permit the creation of keys, then a
38 NotAuthorized exception should be raised.
4039 """
4140 pass
4241
4342 @abc.abstractmethod
44 def store_key(self, context, key, expiration=None):
45 """Stores (i.e., registers) a key with the key manager.
43 def create_key_pair(self, context, algorithm, length, expiration=None):
44 """Creates an asymmetric key pair.
4645
47 This method stores the specified key and returns its UUID that
48 identifies it within the key manager. If the specified context does
49 not permit the creation of keys, then a NotAuthorized exception should
50 be raised.
46 This method creates an asymmetric key pair and returns the pair of key
47 UUIDs. If the specified context does not permit the creation of keys,
48 then a NotAuthorized exception should be raised. The order of the UUIDs
49 will be (private, public).
5150 """
5251 pass
5352
5453 @abc.abstractmethod
55 def copy_key(self, context, key_id):
56 """Copies (i.e., clones) a key stored by the key manager.
54 def store(self, context, managed_object, expiration=None):
55 """Stores a managed object with the key manager.
5756
58 This method copies the specified key and returns the copy's UUID. If
59 the specified context does not permit copying keys, then a
60 NotAuthorized error should be raised.
61
62 Implementation note: This method should behave identically to
63 store_key(context, get_key(context, <encryption key UUID>))
64 although it is preferable to perform this operation within the key
65 manager to avoid unnecessary handling of the key material.
57 This method stores the specified managed object and returns its UUID
58 that identifies it within the key manager. If the specified context
59 does not permit the creation of keys, then a NotAuthorized exception
60 should be raised.
6661 """
6762 pass
6863
6964 @abc.abstractmethod
70 def get_key(self, context, key_id):
71 """Retrieves the specified key.
65 def copy(self, context, managed_object_id):
66 """Copies (i.e., clones) a managed object stored by the key manager.
7267
73 Implementations should verify that the caller has permissions to
74 retrieve the key by checking the context object passed in as context.
75 If the user lacks permission then a NotAuthorized exception is raised.
68 This method copies the specified managed object and returns the copy's
69 UUID. If the specified context does not permit copying objects, then a
70 NotAuthorized error should be raised.
7671
77 If the specified key does not exist, then a KeyError should be raised.
78 Implementations should preclude users from discerning the UUIDs of
79 keys that belong to other users by repeatedly calling this method.
80 That is, keys that belong to other users should be considered "non-
81 existent" and completely invisible.
72 Implementation note: This method should behave identically to
73 store(context, get(context, <object UUID>))
74 although it is preferable to perform this operation within the key
75 manager to avoid unnecessary handling of the object material.
8276 """
8377 pass
8478
8579 @abc.abstractmethod
86 def delete_key(self, context, key_id):
87 """Deletes the specified key.
80 def get(self, context, managed_object_id):
81 """Retrieves the specified managed object.
82
83 Implementations should verify that the caller has permissions to
84 retrieve the managed object by checking the context object passed in
85 as context. If the user lacks permission then a NotAuthorized
86 exception is raised.
87
88 If the specified object does not exist, then a KeyError should be
89 raised. Implementations should preclude users from discerning the
90 UUIDs of objects that belong to other users by repeatedly calling
91 this method. That is, objects that belong to other users should be
92 considered "non-existent" and completely invisible.
93 """
94 pass
95
96 @abc.abstractmethod
97 def delete(self, context, managed_object_id):
98 """Deletes the specified managed object.
8899
89100 Implementations should verify that the caller has permission to delete
90 the key by checking the context object (context). A NotAuthorized
91 exception should be raised if the caller lacks permission.
101 the managed object by checking the context object (context). A
102 NotAuthorized exception should be raised if the caller lacks
103 permission.
92104
93 If the specified key does not exist, then a KeyError should be raised.
94 Implementations should preclude users from discerning the UUIDs of
95 keys that belong to other users by repeatedly calling this method.
96 That is, keys that belong to other users should be considered "non-
97 existent" and completely invisible.
105 If the specified object does not exist, then a KeyError should be
106 raised. Implementations should preclude users from discerning the
107 UUIDs of objects that belong to other users by repeatedly calling this
108 method. That is, objects that belong to other users should be
109 considered "non-existent" and completely invisible.
98110 """
99111 pass
2828 expiration=None, **kwargs):
2929 raise NotImplementedError()
3030
31 def store_key(self, context, key, expiration=None, **kwargs):
31 def create_key_pair(self, context, algorithm, lengthm, expiration=None):
3232 raise NotImplementedError()
3333
34 def copy_key(self, context, key_id, **kwargs):
34 def store(self, context, managed_object, expiration=None, **kwargs):
3535 raise NotImplementedError()
3636
37 def get_key(self, context, key_id, **kwargs):
37 def copy(self, context, managed_object_id, **kwargs):
3838 raise NotImplementedError()
3939
40 def delete_key(self, context, key_id, **kwargs):
40 def get(self, context, managed_object_id, **kwargs):
4141 raise NotImplementedError()
42
43 def delete(self, context, managed_object_id, **kwargs):
44 raise NotImplementedError()
3535
3636
3737 class MockKeyManager(key_manager.KeyManager):
38
3938 """Mocking manager for integration tests.
4039
4140 This mock key manager implementation supports all the methods specified
6665 bytes(binascii.unhexlify(_hex)))
6766
6867 def create_key(self, context, **kwargs):
69 """Creates a key.
68 """Creates a symmetric key.
7069
7170 This implementation returns a UUID for the created key. A
7271 Forbidden exception is raised if the specified context is None.
7574 raise exception.Forbidden()
7675
7776 key = self._generate_key(**kwargs)
78 return self.store_key(context, key)
77 return self.store(context, key)
78
79 def create_key_pair(self, context, algorithm, length, expiration=None):
80 raise NotImplementedError()
7981
8082 def _generate_key_id(self):
8183 key_id = str(uuid.uuid4())
8486
8587 return key_id
8688
87 def store_key(self, context, key, **kwargs):
89 def store(self, context, managed_object, **kwargs):
8890 """Stores (i.e., registers) a key with the key manager."""
8991 if context is None:
9092 raise exception.Forbidden()
9193
9294 key_id = self._generate_key_id()
93 self.keys[key_id] = key
95 self.keys[key_id] = managed_object
9496
9597 return key_id
9698
97 def copy_key(self, context, key_id, **kwargs):
99 def copy(self, context, managed_object_id, **kwargs):
98100 if context is None:
99101 raise exception.Forbidden()
100102
101103 copied_key_id = self._generate_key_id()
102 self.keys[copied_key_id] = self.keys[key_id]
104 self.keys[copied_key_id] = self.keys[managed_object_id]
103105
104106 return copied_key_id
105107
106 def get_key(self, context, key_id, **kwargs):
108 def get(self, context, managed_object_id, **kwargs):
107109 """Retrieves the key identified by the specified id.
108110
109111 This implementation returns the key that is associated with the
113115 if context is None:
114116 raise exception.Forbidden()
115117
116 return self.keys[key_id]
118 return self.keys[managed_object_id]
117119
118 def delete_key(self, context, key_id, **kwargs):
119 """Deletes the key identified by the specified id.
120 def delete(self, context, managed_object_id, **kwargs):
121 """Deletes the object identified by the specified id.
120122
121123 A Forbidden exception is raised if the context is None and a
122124 KeyError is raised if the UUID is invalid.
124126 if context is None:
125127 raise exception.Forbidden()
126128
127 del self.keys[key_id]
129 del self.keys[managed_object_id]
128130
129131 def _generate_password(self, length, symbolgroups):
130132 """Generate a random password from the supplied symbol groups.
6767 self.key_mgr._barbican_client = self.mock_barbican
6868 self.key_mgr._current_context = self.ctxt
6969
70 def _build_mock_symKey(self):
71 self.mock_symKey = mock.Mock()
72
73 def fake_sym_key(alg, key):
74 self.mock_symKey.get_encoded.return_value = key
75 p = mock.PropertyMock(return_value=alg)
76 type(self.mock_symKey).algorithm = p
77 return self.mock_symKey
78 self.original_key = key_manager_key.SymmetricKey
79 key_manager_key.SymmetricKey = fake_sym_key
80
8170 def test_copy_key(self):
8271 # Create metadata for original secret
8372 original_secret_metadata = mock.Mock()
10089 self.get.return_value = original_secret_metadata
10190 self.create.return_value = copied_secret
10291
103 # Create the mock key
104 self._build_mock_symKey()
105
10692 # Copy the original
107 self.key_mgr.copy_key(self.ctxt, self.key_id)
93 self.key_mgr.copy(self.ctxt, self.key_id)
10894
10995 # Assert proper methods were called
11096 self.get.assert_called_once_with(self.secret_ref)
11197 self.create.assert_called_once_with(
112 payload=self.mock_symKey.get_encoded(),
98 payload=original_secret_metadata.payload,
11399 algorithm=mock.sentinel.alg,
114100 expiration=mock.sentinel.expiration)
115101 copied_secret.store.assert_called_once_with()
117103 def test_copy_null_context(self):
118104 self.key_mgr._barbican_client = None
119105 self.assertRaises(exception.Forbidden,
120 self.key_mgr.copy_key, None, self.key_id)
106 self.key_mgr.copy, None, self.key_id)
121107
122108 def test_create_key(self):
123109 # Create order_ref_url and assign return value
148134 def test_delete_null_context(self):
149135 self.key_mgr._barbican_client = None
150136 self.assertRaises(exception.Forbidden,
151 self.key_mgr.delete_key, None, self.key_id)
137 self.key_mgr.delete, None, self.key_id)
152138
153139 def test_delete_key(self):
154 self.key_mgr.delete_key(self.ctxt, self.key_id)
140 self.key_mgr.delete(self.ctxt, self.key_id)
155141 self.delete.assert_called_once_with(self.secret_ref)
156142
157143 def test_delete_unknown_key(self):
158144 self.assertRaises(exception.KeyManagerError,
159 self.key_mgr.delete_key, self.ctxt, None)
145 self.key_mgr.delete, self.ctxt, None)
160146
161147 def test_get_key(self):
162148 original_secret_metadata = mock.Mock()
166152 original_secret_metadata.payload = original_secret_data
167153
168154 self.mock_barbican.secrets.get.return_value = original_secret_metadata
169 key = self.key_mgr.get_key(self.ctxt, self.key_id)
155 key = self.key_mgr.get(self.ctxt, self.key_id)
170156
171157 self.get.assert_called_once_with(self.secret_ref)
172158 self.assertEqual(key.get_encoded(), original_secret_data)
174160 def test_get_null_context(self):
175161 self.key_mgr._barbican_client = None
176162 self.assertRaises(exception.Forbidden,
177 self.key_mgr.get_key, None, self.key_id)
163 self.key_mgr.get, None, self.key_id)
178164
179165 def test_get_unknown_key(self):
180166 self.assertRaises(exception.KeyManagerError,
181 self.key_mgr.get_key, self.ctxt, None)
167 self.key_mgr.get, self.ctxt, None)
182168
183169 def test_store_key_base64(self):
184170 # Create Key to store
193179 secret.store.return_value = self.secret_ref
194180
195181 # Store the Key
196 returned_uuid = self.key_mgr.store_key(self.ctxt, _key)
182 returned_uuid = self.key_mgr.store(self.ctxt, _key)
197183
198184 self.create.assert_called_once_with(algorithm='AES',
199185 payload=secret_key,
208194 secret_key_text)
209195
210196 # Store the Key
211 self.key_mgr.store_key(self.ctxt, _key)
197 self.key_mgr.store(self.ctxt, _key)
212198 self.create.assert_called_once_with(algorithm='AES',
213199 payload=secret_key_text,
214200 expiration=None)
217203 def test_store_null_context(self):
218204 self.key_mgr._barbican_client = None
219205 self.assertRaises(exception.Forbidden,
220 self.key_mgr.store_key, None, None)
206 self.key_mgr.store, None, None)
4343 def test_create_key_with_length(self):
4444 for length in [64, 128, 256]:
4545 key_id = self.key_mgr.create_key(self.context, key_length=length)
46 key = self.key_mgr.get_key(self.context, key_id)
46 key = self.key_mgr.get(self.context, key_id)
4747 self.assertEqual(length / 8, len(key.get_encoded()))
4848
4949 def test_create_null_context(self):
5353 def test_store_and_get_key(self):
5454 secret_key = bytes(b'0' * 64)
5555 _key = sym_key.SymmetricKey('AES', 64 * 8, secret_key)
56 key_id = self.key_mgr.store_key(self.context, _key)
56 key_id = self.key_mgr.store(self.context, _key)
5757
58 actual_key = self.key_mgr.get_key(self.context, key_id)
58 actual_key = self.key_mgr.get(self.context, key_id)
5959 self.assertEqual(_key, actual_key)
6060
6161 def test_store_null_context(self):
6262 self.assertRaises(exception.Forbidden,
63 self.key_mgr.store_key, None, None)
63 self.key_mgr.store, None, None)
6464
6565 def test_copy_key(self):
6666 key_id = self.key_mgr.create_key(self.context)
67 key = self.key_mgr.get_key(self.context, key_id)
67 key = self.key_mgr.get(self.context, key_id)
6868
69 copied_key_id = self.key_mgr.copy_key(self.context, key_id)
70 copied_key = self.key_mgr.get_key(self.context, copied_key_id)
69 copied_key_id = self.key_mgr.copy(self.context, key_id)
70 copied_key = self.key_mgr.get(self.context, copied_key_id)
7171
7272 self.assertNotEqual(key_id, copied_key_id)
7373 self.assertEqual(key, copied_key)
7474
7575 def test_copy_null_context(self):
7676 self.assertRaises(exception.Forbidden,
77 self.key_mgr.copy_key, None, None)
77 self.key_mgr.copy, None, None)
7878
7979 def test_get_null_context(self):
8080 self.assertRaises(exception.Forbidden,
81 self.key_mgr.get_key, None, None)
81 self.key_mgr.get, None, None)
8282
8383 def test_get_unknown_key(self):
84 self.assertRaises(KeyError, self.key_mgr.get_key, self.context, None)
84 self.assertRaises(KeyError, self.key_mgr.get, self.context, None)
8585
8686 def test_delete_key(self):
8787 key_id = self.key_mgr.create_key(self.context)
88 self.key_mgr.delete_key(self.context, key_id)
88 self.key_mgr.delete(self.context, key_id)
8989
90 self.assertRaises(KeyError, self.key_mgr.get_key, self.context,
90 self.assertRaises(KeyError, self.key_mgr.get, self.context,
9191 key_id)
9292
9393 def test_delete_null_context(self):
9494 self.assertRaises(exception.Forbidden,
95 self.key_mgr.delete_key, None, None)
95 self.key_mgr.delete, None, None)
9696
9797 def test_delete_unknown_key(self):
98 self.assertRaises(KeyError, self.key_mgr.delete_key, self.context,
98 self.assertRaises(KeyError, self.key_mgr.delete, self.context,
9999 None)
2929 self.assertRaises(NotImplementedError,
3030 self.key_mgr.create_key, None)
3131
32 def test_store_key(self):
32 def test_create_key_pair(self):
3333 self.assertRaises(NotImplementedError,
34 self.key_mgr.store_key, None, None)
34 self.key_mgr.create_key_pair, None, None, None)
3535
36 def test_copy_key(self):
36 def test_store(self):
3737 self.assertRaises(NotImplementedError,
38 self.key_mgr.copy_key, None, None)
38 self.key_mgr.store, None, None)
3939
40 def test_get_key(self):
40 def test_copy(self):
4141 self.assertRaises(NotImplementedError,
42 self.key_mgr.get_key, None, None)
42 self.key_mgr.copy, None, None)
4343
44 def test_delete_key(self):
44 def test_get(self):
4545 self.assertRaises(NotImplementedError,
46 self.key_mgr.delete_key, None, None)
46 self.key_mgr.get, None, None)
47
48 def test_delete(self):
49 self.assertRaises(NotImplementedError,
50 self.key_mgr.delete, None, None)