Merge "Add name to Castellan Objects and Barbican Key Manager"
Jenkins authored 8 years ago
Gerrit Code Review committed 8 years ago
28 | 28 | class ManagedObject(object): |
29 | 29 | """Base class to represent all managed objects.""" |
30 | 30 | |
31 | def __init__(self, name=None): | |
32 | """Managed Object has a name, defaulted to None.""" | |
33 | self._name = name | |
34 | ||
35 | @property | |
36 | def name(self): | |
37 | """Returns the name. | |
38 | ||
39 | Returns the object's name or None if this object does not have one. | |
40 | """ | |
41 | return self._name | |
42 | ||
31 | 43 | @abc.abstractproperty |
32 | 44 | def format(self): |
33 | 45 | """Returns the encoding format. |
24 | 24 | class OpaqueData(managed_object.ManagedObject): |
25 | 25 | """This class represents opaque data.""" |
26 | 26 | |
27 | def __init__(self, data): | |
27 | def __init__(self, data, name=None): | |
28 | 28 | """Create a new OpaqueData object. |
29 | 29 | |
30 | 30 | Expected type for data is a bytestring. |
31 | 31 | """ |
32 | 32 | self._data = data |
33 | super(OpaqueData, self).__init__(name=name) | |
33 | 34 | |
34 | 35 | @property |
35 | 36 | def format(self): |
42 | 43 | |
43 | 44 | def __eq__(self, other): |
44 | 45 | if isinstance(other, OpaqueData): |
45 | return self._data == other._data | |
46 | return (self._data == other._data and | |
47 | self._name == other._name) | |
46 | 48 | else: |
47 | 49 | return False |
48 | 50 |
24 | 24 | class Passphrase(managed_object.ManagedObject): |
25 | 25 | """This class represents a passphrase.""" |
26 | 26 | |
27 | def __init__(self, passphrase): | |
27 | def __init__(self, passphrase, name=None): | |
28 | 28 | """Create a new Passphrase object. |
29 | 29 | |
30 | 30 | The expected type for the passphrase is a bytestring. |
31 | 31 | """ |
32 | 32 | self._passphrase = passphrase |
33 | super(Passphrase, self).__init__(name=name) | |
33 | 34 | |
34 | 35 | @property |
35 | 36 | def format(self): |
42 | 43 | |
43 | 44 | def __eq__(self, other): |
44 | 45 | if isinstance(other, Passphrase): |
45 | return self._passphrase == other._passphrase | |
46 | return (self._passphrase == other._passphrase and | |
47 | self._name == other._name) | |
46 | 48 | else: |
47 | 49 | return False |
48 | 50 |
24 | 24 | class PrivateKey(key.Key): |
25 | 25 | """This class represents private keys.""" |
26 | 26 | |
27 | def __init__(self, algorithm, bit_length, key): | |
27 | def __init__(self, algorithm, bit_length, key, name=None): | |
28 | 28 | """Create a new PrivateKey object. |
29 | 29 | |
30 | 30 | The arguments specify the algorithm and bit length for the asymmetric |
33 | 33 | self._alg = algorithm |
34 | 34 | self._bit_length = bit_length |
35 | 35 | self._key = key |
36 | super(PrivateKey, self).__init__(name=name) | |
36 | 37 | |
37 | 38 | @property |
38 | 39 | def algorithm(self): |
56 | 57 | def __eq__(self, other): |
57 | 58 | if isinstance(other, PrivateKey): |
58 | 59 | return (self._alg == other._alg and |
59 | self._key == other._key) | |
60 | self._key == other._key and | |
61 | self._name == other._name) | |
60 | 62 | else: |
61 | 63 | return False |
62 | 64 |
24 | 24 | class PublicKey(key.Key): |
25 | 25 | """This class represents public keys.""" |
26 | 26 | |
27 | def __init__(self, algorithm, bit_length, key): | |
27 | def __init__(self, algorithm, bit_length, key, name=None): | |
28 | 28 | """Create a new PublicKey object. |
29 | 29 | |
30 | 30 | The arguments specify the algorithm and bit length for the asymmetric |
34 | 34 | self._alg = algorithm |
35 | 35 | self._bit_length = bit_length |
36 | 36 | self._key = key |
37 | super(PublicKey, self).__init__(name=name) | |
37 | 38 | |
38 | 39 | @property |
39 | 40 | def algorithm(self): |
57 | 58 | def __eq__(self, other): |
58 | 59 | if isinstance(other, PublicKey): |
59 | 60 | return (self._alg == other._alg and |
60 | self._key == other._key) | |
61 | self._key == other._key and | |
62 | self._name == other._name) | |
61 | 63 | else: |
62 | 64 | return False |
63 | 65 |
24 | 24 | class SymmetricKey(key.Key): |
25 | 25 | """This class represents symmetric keys.""" |
26 | 26 | |
27 | def __init__(self, algorithm, bit_length, key): | |
27 | def __init__(self, algorithm, bit_length, key, name=None): | |
28 | 28 | """Create a new SymmetricKey object. |
29 | 29 | |
30 | 30 | The arguments specify the algorithm and bit length for the symmetric |
33 | 33 | self._alg = algorithm |
34 | 34 | self._bit_length = bit_length |
35 | 35 | self._key = key |
36 | super(SymmetricKey, self).__init__(name=name) | |
36 | 37 | |
37 | 38 | @property |
38 | 39 | def algorithm(self): |
57 | 58 | if isinstance(other, SymmetricKey): |
58 | 59 | return (self._alg == other._alg and |
59 | 60 | self._bit_length == other._bit_length and |
60 | self._key == other._key) | |
61 | self._key == other._key and | |
62 | self._name == other._name) | |
61 | 63 | else: |
62 | 64 | return False |
63 | 65 |
24 | 24 | class X509(certificate.Certificate): |
25 | 25 | """This class represents X.509 certificates.""" |
26 | 26 | |
27 | def __init__(self, data): | |
27 | def __init__(self, data, name=None): | |
28 | 28 | """Create a new X509 object. |
29 | 29 | |
30 | 30 | The data should be in a bytestring. |
31 | 31 | """ |
32 | 32 | self._data = data |
33 | super(X509, self).__init__(name=name) | |
33 | 34 | |
34 | 35 | @property |
35 | 36 | def format(self): |
42 | 43 | |
43 | 44 | def __eq__(self, other): |
44 | 45 | if isinstance(other, X509): |
45 | return (self._data == other._data) | |
46 | return (self._data == other._data and | |
47 | self._name == other._name) | |
46 | 48 | else: |
47 | 49 | return False |
48 | 50 |
165 | 165 | endpoint, api_version) |
166 | 166 | return base_url |
167 | 167 | |
168 | def create_key(self, context, algorithm, length, expiration=None): | |
168 | def create_key(self, context, algorithm, length, | |
169 | expiration=None, name=None): | |
169 | 170 | """Creates a symmetric key. |
170 | 171 | |
171 | 172 | :param context: contains information of the user and the environment |
172 | 173 | for the request (castellan/context.py) |
173 | 174 | :param algorithm: the algorithm associated with the secret |
174 | 175 | :param length: the bit length of the secret |
176 | :param name: the name of the key | |
175 | 177 | :param expiration: the date the key will expire |
176 | 178 | :return: the UUID of the new key |
177 | 179 | :raises KeyManagerError: if key creation fails |
180 | 182 | |
181 | 183 | try: |
182 | 184 | key_order = barbican_client.orders.create_key( |
185 | name=name, | |
183 | 186 | algorithm=algorithm, |
184 | 187 | bit_length=length, |
185 | 188 | expiration=expiration) |
192 | 195 | LOG.error(u._LE("Error creating key: %s"), e) |
193 | 196 | raise exception.KeyManagerError(reason=e) |
194 | 197 | |
195 | def create_key_pair(self, context, algorithm, length, expiration=None): | |
198 | def create_key_pair(self, context, algorithm, length, | |
199 | expiration=None, name=None): | |
196 | 200 | """Creates an asymmetric key pair. |
197 | 201 | |
198 | 202 | :param context: contains information of the user and the environment |
199 | 203 | for the request (castellan/context.py) |
200 | 204 | :param algorithm: the algorithm associated with the secret |
201 | 205 | :param length: the bit length of the secret |
206 | :param name: the name of the key | |
202 | 207 | :param expiration: the date the key will expire |
203 | 208 | :return: the UUIDs of the new key, in the order (private, public) |
204 | 209 | :raises NotImplementedError: until implemented |
210 | 215 | key_pair_order = barbican_client.orders.create_asymmetric( |
211 | 216 | algorithm=algorithm, |
212 | 217 | bit_length=length, |
218 | name=name, | |
213 | 219 | expiration=expiration) |
214 | 220 | |
215 | 221 | order_ref = key_pair_order.submit() |
229 | 235 | |
230 | 236 | def _get_barbican_object(self, barbican_client, managed_object): |
231 | 237 | """Converts the Castellan managed_object to a Barbican secret.""" |
238 | name = getattr(managed_object, 'name', None) | |
239 | ||
232 | 240 | try: |
233 | 241 | algorithm = managed_object.algorithm |
234 | 242 | bit_length = managed_object.bit_length |
243 | 251 | secret = barbican_client.secrets.create(payload=payload, |
244 | 252 | algorithm=algorithm, |
245 | 253 | bit_length=bit_length, |
254 | name=name, | |
246 | 255 | secret_type=secret_type) |
247 | 256 | return secret |
248 | 257 | |
282 | 291 | |
283 | 292 | :param context: contains information of the user and the environment |
284 | 293 | for the request (castellan/context.py) |
285 | :param managed_object: the unencrypted secret data. Known as "payload" | |
286 | to the barbicanclient api | |
294 | :param managed_object: a secret object with unencrypted payload. | |
295 | Known as "secret" to the barbicanclient api | |
287 | 296 | :param expiration: the expiration time of the secret in ISO 8601 |
288 | 297 | format |
289 | 298 | :returns: the UUID of the stored object |
414 | 423 | if issubclass(secret_type, key_base_class.Key): |
415 | 424 | return secret_type(secret.algorithm, |
416 | 425 | secret.bit_length, |
417 | secret_data) | |
426 | secret_data, | |
427 | secret.name) | |
418 | 428 | else: |
419 | return secret_type(secret_data) | |
429 | return secret_type(secret_data, | |
430 | secret.name) | |
420 | 431 | |
421 | 432 | def _get_secret(self, context, object_id): |
422 | 433 | """Returns the metadata of the secret. |
39 | 39 | pass |
40 | 40 | |
41 | 41 | @abc.abstractmethod |
42 | def create_key(self, context, algorithm, length, expiration=None): | |
42 | def create_key(self, context, algorithm, length, | |
43 | expiration=None, name=None): | |
43 | 44 | """Creates a symmetric key. |
44 | 45 | |
45 | 46 | This method creates a symmetric key and returns the key's UUID. If the |
49 | 50 | pass |
50 | 51 | |
51 | 52 | @abc.abstractmethod |
52 | def create_key_pair(self, context, algorithm, length, expiration=None): | |
53 | def create_key_pair(self, context, algorithm, length, | |
54 | expiration=None, name=None): | |
53 | 55 | """Creates an asymmetric key pair. |
54 | 56 | |
55 | 57 | This method creates an asymmetric key pair and returns the pair of key |
185 | 185 | original_secret_metadata.algorithm = mock.sentinel.alg |
186 | 186 | original_secret_metadata.bit_length = mock.sentinel.bit |
187 | 187 | original_secret_metadata.secret_type = 'symmetric' |
188 | ||
189 | key_name = 'my key' | |
190 | original_secret_metadata.name = key_name | |
191 | ||
188 | 192 | original_secret_data = b'test key' |
189 | 193 | original_secret_metadata.payload = original_secret_data |
190 | 194 | |
192 | 196 | key = self.key_mgr.get(self.ctxt, self.key_id) |
193 | 197 | |
194 | 198 | self.get.assert_called_once_with(self.secret_ref) |
199 | self.assertEqual(key_name, key.name) | |
195 | 200 | self.assertEqual(original_secret_data, key.get_encoded()) |
196 | 201 | |
197 | 202 | def test_get_null_context(self): |
227 | 232 | |
228 | 233 | self.create.assert_called_once_with(algorithm='AES', |
229 | 234 | bit_length=key_length, |
235 | name=None, | |
230 | 236 | payload=secret_key, |
237 | secret_type='symmetric') | |
238 | self.assertEqual(self.key_id, returned_uuid) | |
239 | ||
240 | def test_store_key_with_name(self): | |
241 | # Create Key to store | |
242 | secret_key = bytes(b'\x01\x02\xA0\xB3') | |
243 | key_length = len(secret_key) * 8 | |
244 | secret_name = 'My Secret' | |
245 | _key = sym_key.SymmetricKey('AES', | |
246 | key_length, | |
247 | secret_key, | |
248 | secret_name) | |
249 | ||
250 | # Define the return values | |
251 | secret = mock.Mock() | |
252 | self.create.return_value = secret | |
253 | secret.store.return_value = self.secret_ref | |
254 | ||
255 | # Store the Key | |
256 | returned_uuid = self.key_mgr.store(self.ctxt, _key) | |
257 | ||
258 | self.create.assert_called_once_with(algorithm='AES', | |
259 | bit_length=key_length, | |
260 | payload=secret_key, | |
261 | name=secret_name, | |
231 | 262 | secret_type='symmetric') |
232 | 263 | self.assertEqual(self.key_id, returned_uuid) |
233 | 264 |
23 | 23 | class OpaqueDataTestCase(base.TestCase): |
24 | 24 | |
25 | 25 | def _create_data(self): |
26 | return opaque_data.OpaqueData(self.data) | |
26 | return opaque_data.OpaqueData(self.data, self.name) | |
27 | 27 | |
28 | 28 | def setUp(self): |
29 | 29 | self.data = bytes(b"secret opaque data") |
30 | self.name = 'my opaque' | |
30 | 31 | self.opaque_data = self._create_data() |
31 | 32 | |
32 | 33 | super(OpaqueDataTestCase, self).setUp() |
37 | 38 | def test_get_encoded(self): |
38 | 39 | self.assertEqual(self.data, self.opaque_data.get_encoded()) |
39 | 40 | |
41 | def test_get_name(self): | |
42 | self.assertEqual(self.name, self.opaque_data.name) | |
43 | ||
40 | 44 | def test___eq__(self): |
41 | 45 | self.assertTrue(self.opaque_data == self.opaque_data) |
42 | 46 | |
45 | 49 | |
46 | 50 | def test___ne__(self): |
47 | 51 | self.assertFalse(self.opaque_data != self.opaque_data) |
52 | self.assertFalse(self.name != self.name) | |
48 | 53 | |
49 | 54 | self.assertTrue(self.opaque_data is not None) |
50 | 55 | self.assertTrue(None != self.opaque_data) |
56 | ||
57 | def test___ne__name(self): | |
58 | other_opaque = opaque_data.OpaqueData(self.data, "other opaque") | |
59 | self.assertTrue(self.opaque_data != other_opaque) |
23 | 23 | class PassphraseTestCase(base.TestCase): |
24 | 24 | |
25 | 25 | def _create_passphrase(self): |
26 | return passphrase.Passphrase(self.passphrase_data) | |
26 | return passphrase.Passphrase(self.passphrase_data, | |
27 | self.name) | |
27 | 28 | |
28 | 29 | def setUp(self): |
29 | 30 | self.passphrase_data = bytes(b"secret passphrase") |
31 | self.name = 'my phrase' | |
30 | 32 | self.passphrase = self._create_passphrase() |
31 | 33 | |
32 | 34 | super(PassphraseTestCase, self).setUp() |
37 | 39 | def test_get_encoded(self): |
38 | 40 | self.assertEqual(self.passphrase_data, self.passphrase.get_encoded()) |
39 | 41 | |
42 | def test_get_name(self): | |
43 | self.assertEqual(self.name, self.passphrase.name) | |
44 | ||
40 | 45 | def test___eq__(self): |
41 | 46 | self.assertTrue(self.passphrase == self.passphrase) |
42 | 47 | |
45 | 50 | |
46 | 51 | def test___ne__(self): |
47 | 52 | self.assertFalse(self.passphrase != self.passphrase) |
53 | self.assertFalse(self.name != self.name) | |
48 | 54 | |
49 | 55 | self.assertTrue(self.passphrase is not None) |
50 | 56 | self.assertTrue(None != self.passphrase) |
57 | ||
58 | def test___ne__name(self): | |
59 | other_phrase = passphrase.Passphrase(self.passphrase_data, | |
60 | "other phrase") | |
61 | self.assertTrue(self.passphrase_data != other_phrase) |
26 | 26 | def _create_key(self): |
27 | 27 | return private_key.PrivateKey(self.algorithm, |
28 | 28 | self.length, |
29 | self.encoded) | |
29 | self.encoded, | |
30 | self.name) | |
30 | 31 | |
31 | 32 | def setUp(self): |
32 | 33 | self.algorithm = 'RSA' |
33 | 34 | self.length = 2048 |
34 | 35 | self.encoded = bytes(utils.get_private_key_der()) |
36 | self.name = 'my key' | |
35 | 37 | |
36 | 38 | super(PrivateKeyTestCase, self).setUp() |
37 | 39 | |
40 | 42 | |
41 | 43 | def test_get_length(self): |
42 | 44 | self.assertEqual(self.length, self.key.bit_length) |
45 | ||
46 | def test_get_name(self): | |
47 | self.assertEqual(self.name, self.key.name) | |
43 | 48 | |
44 | 49 | def test_get_format(self): |
45 | 50 | self.assertEqual('PKCS8', self.key.format) |
55 | 60 | |
56 | 61 | def test___ne__(self): |
57 | 62 | self.assertFalse(self.key != self.key) |
63 | self.assertFalse(self.name != self.name) | |
58 | 64 | |
59 | 65 | self.assertTrue(self.key is not None) |
60 | 66 | self.assertTrue(None != self.key) |
67 | ||
68 | def test___ne__name(self): | |
69 | other_key = private_key.PrivateKey(self.algorithm, | |
70 | self.length, | |
71 | self.encoded, | |
72 | 'other key') | |
73 | self.assertTrue(self.key != other_key) |
24 | 24 | class PublicKeyTestCase(base.KeyTestCase): |
25 | 25 | |
26 | 26 | def _create_key(self): |
27 | return public_key.PublicKey(self.algorithm, self.length, self.encoded) | |
27 | return public_key.PublicKey(self.algorithm, | |
28 | self.length, | |
29 | self.encoded, | |
30 | self.name) | |
28 | 31 | |
29 | 32 | def setUp(self): |
30 | 33 | self.algorithm = 'RSA' |
31 | 34 | self.length = 2048 |
32 | 35 | self.encoded = bytes(utils.get_public_key_der()) |
36 | self.name = 'my key' | |
33 | 37 | |
34 | 38 | super(PublicKeyTestCase, self).setUp() |
35 | 39 | |
38 | 42 | |
39 | 43 | def test_get_length(self): |
40 | 44 | self.assertEqual(self.length, self.key.bit_length) |
45 | ||
46 | def test_get_name(self): | |
47 | self.assertEqual(self.name, self.key.name) | |
41 | 48 | |
42 | 49 | def test_get_format(self): |
43 | 50 | self.assertEqual('SubjectPublicKeyInfo', self.key.format) |
53 | 60 | |
54 | 61 | def test___ne__(self): |
55 | 62 | self.assertFalse(self.key != self.key) |
63 | self.assertFalse(self.name != self.name) | |
56 | 64 | |
57 | 65 | self.assertTrue(self.key is not None) |
58 | 66 | self.assertTrue(None != self.key) |
67 | ||
68 | def test___ne__name(self): | |
69 | other_key = public_key.PublicKey(self.algorithm, | |
70 | self.length, | |
71 | self.encoded, | |
72 | 'other key') | |
73 | self.assertTrue(self.key != other_key) |
25 | 25 | def _create_key(self): |
26 | 26 | return sym_key.SymmetricKey(self.algorithm, |
27 | 27 | self.bit_length, |
28 | self.encoded) | |
28 | self.encoded, | |
29 | self.name) | |
29 | 30 | |
30 | 31 | def setUp(self): |
31 | 32 | self.algorithm = 'AES' |
32 | 33 | self.encoded = bytes(b'0' * 64) |
33 | 34 | self.bit_length = len(self.encoded) * 8 |
35 | self.name = 'my key' | |
34 | 36 | |
35 | 37 | super(SymmetricKeyTestCase, self).setUp() |
36 | 38 | |
37 | 39 | def test_get_format(self): |
38 | 40 | self.assertEqual('RAW', self.key.format) |
41 | ||
42 | def test_get_name(self): | |
43 | self.assertEqual(self.name, self.key.name) | |
39 | 44 | |
40 | 45 | def test_get_encoded(self): |
41 | 46 | self.assertEqual(self.encoded, self.key.get_encoded()) |
54 | 59 | |
55 | 60 | def test___ne__(self): |
56 | 61 | self.assertFalse(self.key != self.key) |
62 | self.assertFalse(self.name != self.name) | |
57 | 63 | |
58 | 64 | self.assertTrue(self.key is not None) |
59 | 65 | self.assertTrue(None != self.key) |
66 | ||
67 | def test___ne__name(self): | |
68 | other_key = sym_key.SymmetricKey(self.algorithm, | |
69 | self.bit_length, | |
70 | self.encoded, | |
71 | 'other key') | |
72 | self.assertTrue(self.key != other_key) |
24 | 24 | class X509TestCase(base.CertificateTestCase): |
25 | 25 | |
26 | 26 | def _create_cert(self): |
27 | return x_509.X509(self.data) | |
27 | return x_509.X509(self.data, self.name) | |
28 | 28 | |
29 | 29 | def setUp(self): |
30 | 30 | self.data = utils.get_certificate_der() |
31 | self.name = 'my cert' | |
31 | 32 | |
32 | 33 | super(X509TestCase, self).setUp() |
33 | 34 | |
34 | 35 | def test_get_format(self): |
35 | 36 | self.assertEqual('X.509', self.cert.format) |
37 | ||
38 | def test_get_name(self): | |
39 | self.assertEqual(self.name, self.cert.name) | |
36 | 40 | |
37 | 41 | def test_get_encoded(self): |
38 | 42 | self.assertEqual(self.data, self.cert.get_encoded()) |
45 | 49 | |
46 | 50 | def test___ne__(self): |
47 | 51 | self.assertFalse(self.cert != self.cert) |
52 | self.assertFalse(self.name != self.name) | |
48 | 53 | |
49 | 54 | self.assertTrue(self.cert is not None) |
50 | 55 | self.assertTrue(None != self.cert) |
56 | ||
57 | def test___ne__name(self): | |
58 | other_x509 = x_509.X509(self.data, "other x509") | |
59 | self.assertTrue(self.cert != other_x509) |