diff --git a/barbican/plugin/resources.py b/barbican/plugin/resources.py index 195145ce..d3998ab6 100644 --- a/barbican/plugin/resources.py +++ b/barbican/plugin/resources.py @@ -111,10 +111,15 @@ def store_secret(unencrypted_raw, content_type_raw, content_encoding, def get_secret(requesting_content_type, secret_model, project_model, twsk=None, transport_key=None): - tr.analyze_before_decryption(requesting_content_type) - secret_metadata = _get_secret_meta(secret_model) + # NOTE: */* is the pecan default meaning no content type sent in. In this + # case we should use the mime type stored in the metadata. + if requesting_content_type == '*/*': + requesting_content_type = secret_metadata['content_type'] + + tr.analyze_before_decryption(requesting_content_type) + if twsk is not None: secret_metadata['trans_wrapped_session_key'] = twsk secret_metadata['transport_key'] = transport_key diff --git a/barbican/tests/api/controllers/test_secrets.py b/barbican/tests/api/controllers/test_secrets.py index 62291c66..ee078e0d 100644 --- a/barbican/tests/api/controllers/test_secrets.py +++ b/barbican/tests/api/controllers/test_secrets.py @@ -280,6 +280,23 @@ class WhenGettingPuttingOrDeletingSecret(utils.BarbicanAPIBaseTestCase): self.assertEqual(200, get_resp.status_int) self.assertEqual(payload, get_resp.body) + def test_get_secret_payload_with_pecan_default_accept_header(self): + payload = 'a very interesting string' + resp, secret_uuid = create_secret( + self.app, + payload=payload, + content_type='text/plain' + ) + + self.assertEqual(201, resp.status_int) + + headers = {'Accept': '*/*'} + get_resp = self.app.get( + '/secrets/{0}/payload'.format(secret_uuid), headers=headers + ) + self.assertEqual(200, get_resp.status_int) + self.assertEqual(payload, get_resp.body) + def test_get_secret_payload_with_blank_accept_header(self): payload = 'a very interesting string' resp, secret_uuid = create_secret( diff --git a/functionaltests/api/v1/behaviors/secret_behaviors.py b/functionaltests/api/v1/behaviors/secret_behaviors.py index 9ac09fb7..066c973d 100644 --- a/functionaltests/api/v1/behaviors/secret_behaviors.py +++ b/functionaltests/api/v1/behaviors/secret_behaviors.py @@ -19,11 +19,13 @@ from functionaltests.api.v1.models import secret_models class SecretBehaviors(base_behaviors.BaseBehaviors): - def create_secret(self, model, headers=None, use_auth=True, - user_name=None, admin=None): + def create_secret(self, model, extra_headers=None, omit_headers=None, + use_auth=True, user_name=None, admin=None): """Create a secret from the data in the model. :param model: The metadata used to create the secret + :param extra_headers: Optional HTTP headers to add to the request + :param omit_headers: headers to delete before making the request :param use_auth: Boolean for whether to send authentication headers :param user_name: The user name used to create the secret :param admin: The user with permissions to delete the secrets @@ -32,7 +34,8 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): """ resp = self.client.post('secrets', request_model=model, - extra_headers=headers, use_auth=use_auth, + extra_headers=extra_headers, + omit_headers=omit_headers, use_auth=use_auth, user_name=user_name) # handle expected JSON parsing errors for unauthenticated requests @@ -49,8 +52,8 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): def update_secret_payload(self, secret_ref, payload, payload_content_type, payload_content_encoding=None, - extra_headers=None, use_auth=True, - user_name=None): + extra_headers=None, omit_headers=None, + use_auth=True, user_name=None): """Updates a secret's payload data. :param secret_ref: HATEOAS ref of the secret to be updated @@ -58,6 +61,7 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): :param payload_content_type: value for the Content-Type header :param payload_content_encoding: value for the Content-Encoding header :param extra_headers: Optional HTTP headers to add to the request + :param omit_headers: headers to delete before making the request :param use_auth: Boolean for whether to send authentication headers :param user_name: The user name used to update the secret :return: the response from the PUT update @@ -73,11 +77,12 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): headers.update(extra_headers) return self.client.put(secret_ref, data=payload, extra_headers=headers, + omit_headers=omit_headers, use_auth=use_auth, user_name=user_name) def get_secret(self, secret_ref, payload_content_type, payload_content_encoding=None, extra_headers=None, - use_auth=True, user_name=None): + omit_headers=None, use_auth=True, user_name=None): headers = {'Accept': payload_content_type, 'Accept-Encoding': payload_content_encoding} @@ -86,12 +91,15 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): headers.update(extra_headers) return self.client.get(secret_ref + '/payload', - extra_headers=headers, use_auth=use_auth, + extra_headers=headers, + omit_headers=omit_headers, use_auth=use_auth, user_name=user_name) def get_secret_based_on_content_type(self, secret_ref, payload_content_type, payload_content_encoding=None, + extra_headers=None, + omit_headers=None, user_name=None): """Retrieves a secret's payload based on the content type @@ -101,23 +109,31 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): headers = {'Accept': payload_content_type, 'Accept-Encoding': payload_content_encoding} - return self.client.get(secret_ref, extra_headers=headers, - user_name=user_name) + if extra_headers: + headers.update(extra_headers) - def get_secret_metadata(self, secret_ref, use_auth=True, user_name=None): + return self.client.get(secret_ref, extra_headers=headers, + omit_headers=omit_headers, user_name=user_name) + + def get_secret_metadata(self, secret_ref, extra_headers=None, + omit_headers=None, use_auth=True, user_name=None): """Retrieves a secret's metadata. :param secret_ref: HATEOAS ref of the secret to be retrieved + :param extra_headers: Optional HTTP headers to add to the request + :param omit_headers: headers to delete before making the request :param use_auth: Boolean for whether to send authentication headers :param user_name: The user name used to get the metadata :return: A request response object """ return self.client.get( - secret_ref, response_model_type=secret_models.SecretModel, + secret_ref, extra_headers=extra_headers, omit_headers=omit_headers, + response_model_type=secret_models.SecretModel, use_auth=use_auth, user_name=user_name) def get_secrets(self, limit=10, offset=0, filter=None, - extra_headers=None, use_auth=True, user_name=None): + extra_headers=None, omit_headers=None, use_auth=True, + user_name=None): """Handles getting a list of secrets. :param limit: limits number of returned secrets @@ -126,6 +142,7 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): :param filter: optional filter to limit the returned secrets to those whose name matches the filter. :param extra_headers: Optional HTTP headers to add to the request + :param omit_headers: headers to delete before making the request :param use_auth: Boolean for whether to send authentication headers :param user_name: The user name used to list the secrets """ @@ -134,6 +151,7 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): params['name'] = filter resp = self.client.get('secrets', params=params, extra_headers=extra_headers, + omit_headers=omit_headers, use_auth=use_auth, user_name=user_name) # handle expected JSON parsing errors for unauthenticated requests @@ -147,18 +165,20 @@ class SecretBehaviors(base_behaviors.BaseBehaviors): return resp, secrets, next_ref, prev_ref - def delete_secret(self, secret_ref, extra_headers=None, + def delete_secret(self, secret_ref, extra_headers=None, omit_headers=None, expected_fail=False, use_auth=True, user_name=None): """Delete a secret. :param secret_ref: HATEOAS ref of the secret to be deleted :param extra_headers: Optional HTTP headers to add to the request + :param omit_headers: headers to delete before making the request :param expected_fail: If test is expected to fail the deletion :param use_auth: Boolean for whether to send authentication headers :param user_name: The user name used to delete the secret :return A request response object """ resp = self.client.delete(secret_ref, extra_headers=extra_headers, + omit_headers=omit_headers, use_auth=use_auth, user_name=user_name) if not expected_fail: diff --git a/functionaltests/api/v1/functional/test_secrets.py b/functionaltests/api/v1/functional/test_secrets.py index 5b1e3e75..9544f5ff 100644 --- a/functionaltests/api/v1/functional/test_secrets.py +++ b/functionaltests/api/v1/functional/test_secrets.py @@ -195,7 +195,8 @@ class SecretsTestCase(base.TestCase): get_resp = self.behaviors.get_secret( secret_ref, - payload_content_type='') + payload_content_type='', + omit_headers=['Accept']) self.assertEqual(get_resp.status_code, 200) self.assertIn(test_model.payload, binascii.b2a_base64(get_resp.content)) @@ -928,7 +929,7 @@ class SecretsTestCase(base.TestCase): changed_host_header = {'Host': malicious_hostname} resp, secret_ref = self.behaviors.create_secret( - test_model, headers=changed_host_header) + test_model, extra_headers=changed_host_header) self.assertEqual(resp.status_code, 201) @@ -1108,7 +1109,7 @@ class SecretsUnauthedTestCase(base.TestCase): model = secret_models.SecretModel(self.default_secret_create_data) resp, secret_ref = self.behaviors.create_secret( - model, headers=self.dummy_project_id_header, use_auth=False + model, extra_headers=self.dummy_project_id_header, use_auth=False ) self.assertEqual(401, resp.status_code) @@ -1122,7 +1123,7 @@ class SecretsUnauthedTestCase(base.TestCase): model = secret_models.SecretModel(self.default_secret_create_data) resp, secret_ref = self.behaviors.create_secret( - model, headers=self.project_id_header, use_auth=False + model, extra_headers=self.project_id_header, use_auth=False ) self.assertEqual(401, resp.status_code) diff --git a/functionaltests/common/client.py b/functionaltests/common/client.py index cae15daf..bdfb6a03 100644 --- a/functionaltests/common/client.py +++ b/functionaltests/common/client.py @@ -235,6 +235,7 @@ class BarbicanClient(object): return models, next_ref, prev_ref def request(self, method, url, data=None, extra_headers=None, + omit_headers=None, use_auth=True, response_model_type=None, request_model=None, params=None, user_name=None): """Prepares and sends http request through Requests.""" @@ -247,6 +248,15 @@ class BarbicanClient(object): if extra_headers: headers.update(extra_headers) + if omit_headers: + for header in omit_headers: + try: + del headers[header] + except KeyError: + # key error means we tried to delete a nonexistent + # entry - we don't care about that + pass + # Attempt to serialize model if required if request_model: data = self.attempt_to_serialize(request_model)