Remove copy_key operation
This change removes the copy_key operation from the key manager. The
copy_key operation isn't ideal because few key managers support such
an operation natively. Lack of native support requires the key to be
retrieved and stored in separate steps, which increases the handling
of the key material.
It would be relatively trivial to add this operation back to the
key manager interface at a future point. Once Castellan becomes
widely used by other projects, removing this operation will not be
possible, as it would be a backward-incompatible change.
Change-Id: I1a1dfdb4d4268319f9277fc639027819e70d4a8b
Joel Coffman
8 years ago
184 | 184 | with excutils.save_and_reraise_exception(): |
185 | 185 | LOG.error(u._LE("Error storing object: %s"), e) |
186 | 186 | |
187 | def copy(self, context, managed_object_id): | |
188 | """Copies (i.e., clones) a managed object stored by barbican. | |
189 | ||
190 | :param context: contains information of the user and the environment | |
191 | for the request (castellan/context.py) | |
192 | :param managed_object_id: the UUID of the object to copy | |
193 | :return: the UUID of the object copy | |
194 | :raises HTTPAuthError: if object creation fails with 401 | |
195 | :raises HTTPClientError: if object creation failes with 4xx | |
196 | :raises HTTPServerError: if object creation fails with 5xx | |
197 | """ | |
198 | ||
199 | try: | |
200 | secret = self._get_secret(context, managed_object_id) | |
201 | secret_data = self._get_secret_data(secret) | |
202 | # TODO(kfarr) modify to support other types of keys | |
203 | key = sym_key.SymmetricKey(secret.algorithm, | |
204 | secret.bit_length, | |
205 | secret_data) | |
206 | copy_uuid = self.store(context, key, secret.expiration) | |
207 | return copy_uuid | |
208 | except (barbican_exceptions.HTTPAuthError, | |
209 | barbican_exceptions.HTTPClientError, | |
210 | barbican_exceptions.HTTPServerError) as e: | |
211 | with excutils.save_and_reraise_exception(): | |
212 | LOG.error(u._LE("Error copying object: %s"), e) | |
213 | ||
214 | 187 | def _create_secret_ref(self, key_id): |
215 | 188 | """Creates the URL required for accessing a secret. |
216 | 189 |
71 | 71 | pass |
72 | 72 | |
73 | 73 | @abc.abstractmethod |
74 | def copy(self, context, managed_object_id): | |
75 | """Copies (i.e., clones) a managed object stored by the key manager. | |
76 | ||
77 | This method copies the specified managed object and returns the copy's | |
78 | UUID. If the specified context does not permit copying objects, then a | |
79 | NotAuthorized error should be raised. | |
80 | ||
81 | Implementation note: This method should behave identically to | |
82 | store(context, get(context, <object UUID>)) | |
83 | although it is preferable to perform this operation within the key | |
84 | manager to avoid unnecessary handling of the object material. | |
85 | """ | |
86 | pass | |
87 | ||
88 | @abc.abstractmethod | |
89 | 74 | def get(self, context, managed_object_id): |
90 | 75 | """Retrieves the specified managed object. |
91 | 76 |
157 | 157 | |
158 | 158 | return key_id |
159 | 159 | |
160 | def copy(self, context, managed_object_id, **kwargs): | |
161 | if context is None: | |
162 | raise exception.Forbidden() | |
163 | ||
164 | copied_key_id = self._generate_key_id() | |
165 | self.keys[copied_key_id] = self.keys[managed_object_id] | |
166 | ||
167 | return copied_key_id | |
168 | ||
169 | 160 | def get(self, context, managed_object_id, **kwargs): |
170 | 161 | """Retrieves the key identified by the specified id. |
171 | 162 |
67 | 67 | |
68 | 68 | self.key_mgr._barbican_client = self.mock_barbican |
69 | 69 | self.key_mgr._current_context = self.ctxt |
70 | ||
71 | def test_copy_key(self): | |
72 | # Create metadata for original secret | |
73 | original_secret_metadata = mock.Mock() | |
74 | original_secret_metadata.algorithm = mock.sentinel.alg | |
75 | original_secret_metadata.bit_length = mock.sentinel.bit | |
76 | original_secret_metadata.name = mock.sentinel.name | |
77 | original_secret_metadata.expiration = mock.sentinel.expiration | |
78 | original_secret_metadata.mode = mock.sentinel.mode | |
79 | content_types = {'default': 'fake_type'} | |
80 | original_secret_metadata.content_types = content_types | |
81 | original_secret_data = mock.Mock() | |
82 | original_secret_metadata.payload = original_secret_data | |
83 | ||
84 | # Create href for copied secret | |
85 | copied_secret = mock.Mock() | |
86 | copied_secret.store.return_value = ( | |
87 | 'http://http://host:9311/v1/secrets/uuid') | |
88 | ||
89 | # Set get and create return values | |
90 | self.get.return_value = original_secret_metadata | |
91 | self.create.return_value = copied_secret | |
92 | ||
93 | # Copy the original | |
94 | self.key_mgr.copy(self.ctxt, self.key_id) | |
95 | ||
96 | # Assert proper methods were called | |
97 | self.get.assert_called_once_with(self.secret_ref) | |
98 | self.create.assert_called_once_with( | |
99 | payload=original_secret_metadata.payload, | |
100 | algorithm=mock.sentinel.alg, | |
101 | expiration=mock.sentinel.expiration) | |
102 | copied_secret.store.assert_called_once_with() | |
103 | ||
104 | def test_copy_null_context(self): | |
105 | self.key_mgr._barbican_client = None | |
106 | self.assertRaises(exception.Forbidden, | |
107 | self.key_mgr.copy, None, self.key_id) | |
108 | 70 | |
109 | 71 | def test_create_key(self): |
110 | 72 | # Create order_ref_url and assign return value |
135 | 135 | self.assertRaises(exception.Forbidden, |
136 | 136 | self.key_mgr.store, None, None) |
137 | 137 | |
138 | def test_copy_key(self): | |
139 | key_id = self.key_mgr.create_key(self.context) | |
140 | key = self.key_mgr.get(self.context, key_id) | |
141 | ||
142 | copied_key_id = self.key_mgr.copy(self.context, key_id) | |
143 | copied_key = self.key_mgr.get(self.context, copied_key_id) | |
144 | ||
145 | self.assertNotEqual(key_id, copied_key_id) | |
146 | self.assertEqual(key, copied_key) | |
147 | ||
148 | def test_copy_null_context(self): | |
149 | self.assertRaises(exception.Forbidden, | |
150 | self.key_mgr.copy, None, None) | |
151 | ||
152 | 138 | def test_get_null_context(self): |
153 | 139 | self.assertRaises(exception.Forbidden, |
154 | 140 | self.key_mgr.get, None, None) |