From 2eccc33f19a1c3ae79e56aede48230964d160b94 Mon Sep 17 00:00:00 2001 From: "Joe H. Rahme" Date: Thu, 29 Aug 2013 15:50:35 +0200 Subject: [PATCH] Adds more test to cover Swift tempURL middleware The patch moves away the single test existing from test_object_services.py to its own file. It then adds more tests to validate other HTTP verbs like PUT and HEAD. blueprint test-swift-tempurl-middleware Change-Id: I1d7b021e8e3749b58a9fa657c709fb7154bd104e --- .../object_storage/test_object_services.py | 39 ----- .../object_storage/test_object_temp_url.py | 157 ++++++++++++++++++ tempest/common/rest_client.py | 4 +- .../services/object_storage/object_client.py | 29 +--- 4 files changed, 167 insertions(+), 62 deletions(-) create mode 100644 tempest/api/object_storage/test_object_temp_url.py diff --git a/tempest/api/object_storage/test_object_services.py b/tempest/api/object_storage/test_object_services.py index 7c10138a53..407c3ec1ba 100644 --- a/tempest/api/object_storage/test_object_services.py +++ b/tempest/api/object_storage/test_object_services.py @@ -16,7 +16,6 @@ # under the License. import hashlib -import time from tempest.api.object_storage import base from tempest.common.utils.data_utils import arbitrary_string @@ -224,44 +223,6 @@ class ObjectTest(base.BaseObjectTest): self.assertTrue(actual_meta_key in resp) self.assertEqual(resp[actual_meta_key], meta_value) - @attr(type='gate') - def test_get_object_using_temp_url(self): - # access object using temporary URL within expiration time - - try: - # update account metadata - # flag to check if account metadata got updated - flag = False - key = 'Meta' - metadata = {'Temp-URL-Key': key} - resp, _ = self.account_client.create_account_metadata( - metadata=metadata) - self.assertIn(int(resp['status']), HTTP_SUCCESS) - flag = True - resp, _ = self.account_client.list_account_metadata() - self.assertIn('x-account-meta-temp-url-key', resp) - self.assertEqual(resp['x-account-meta-temp-url-key'], key) - - # create object - object_name = rand_name(name='ObjectTemp') - data = arbitrary_string(size=len(object_name), - base_text=object_name) - self.object_client.create_object(self.container_name, - object_name, data) - expires = int(time.time() + 10) - - # trying to get object using temp url with in expiry time - _, body = self.object_client.get_object_using_temp_url( - self.container_name, object_name, - expires, key) - self.assertEqual(body, data) - finally: - if flag: - resp, _ = self.account_client.delete_account_metadata( - metadata=metadata) - resp, _ = self.account_client.list_account_metadata() - self.assertNotIn('x-account-meta-temp-url-key', resp) - @attr(type='gate') def test_object_upload_in_segments(self): # create object diff --git a/tempest/api/object_storage/test_object_temp_url.py b/tempest/api/object_storage/test_object_temp_url.py new file mode 100644 index 0000000000..0fd5499b1b --- /dev/null +++ b/tempest/api/object_storage/test_object_temp_url.py @@ -0,0 +1,157 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (C) 2013 eNovance SAS +# +# Author: Joe H. Rahme +# +# 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. + +import hashlib +import hmac +import time +import urlparse + +from tempest.api.object_storage import base +from tempest.common.utils.data_utils import arbitrary_string +from tempest.common.utils.data_utils import rand_name +from tempest import exceptions +from tempest.test import attr +from tempest.test import HTTP_SUCCESS + + +class ObjectTempUrlTest(base.BaseObjectTest): + + @classmethod + def setUpClass(cls): + super(ObjectTempUrlTest, cls).setUpClass() + cls.container_name = rand_name(name='TestContainer') + cls.container_client.create_container(cls.container_name) + cls.containers = [cls.container_name] + + # update account metadata + cls.key = 'Meta' + cls.metadata = {'Temp-URL-Key': cls.key} + cls.account_client.create_account_metadata(metadata=cls.metadata) + cls.account_client_metadata, _ = \ + cls.account_client.list_account_metadata() + + @classmethod + def tearDownClass(cls): + resp, _ = cls.account_client.delete_account_metadata( + metadata=cls.metadata) + resp, _ = cls.account_client.list_account_metadata() + + cls.delete_containers(cls.containers) + # delete the user setup created + cls.data.teardown_all() + super(ObjectTempUrlTest, cls).tearDownClass() + + def setUp(self): + super(ObjectTempUrlTest, self).setUp() + # make sure the metadata has been set + self.assertIn('x-account-meta-temp-url-key', + self.account_client_metadata) + + self.assertEqual( + self.account_client_metadata['x-account-meta-temp-url-key'], + self.key) + + # create object + self.object_name = rand_name(name='ObjectTemp') + self.data = arbitrary_string(size=len(self.object_name), + base_text=self.object_name) + self.object_client.create_object(self.container_name, + self.object_name, self.data) + + def get_temp_url(self, container, object_name, method, expires, + key): + """Create the temporary URL.""" + + path = "%s/%s/%s" % ( + urlparse.urlparse(self.object_client.base_url).path, + container, object_name) + + hmac_body = '%s\n%s\n%s' % (method, expires, path) + sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest() + + url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container, + object_name, + sig, expires) + + return url + + @attr(type='gate') + def test_get_object_using_temp_url(self): + EXPIRATION_TIME = 10000 # high to ensure the test finishes. + expires = int(time.time() + EXPIRATION_TIME) + + # get a temp URL for the created object + url = self.get_temp_url(self.container_name, + self.object_name, "GET", + expires, self.key) + + # trying to get object using temp url within expiry time + _, body = self.object_client.get_object_using_temp_url(url) + + self.assertEqual(body, self.data) + + # Testing a HEAD on this Temp URL + resp, body = self.object_client.head(url) + self.assertIn(int(resp['status']), HTTP_SUCCESS) + + @attr(type='gate') + def test_put_object_using_temp_url(self): + # make sure the metadata has been set + new_data = arbitrary_string(size=len(self.object_name), + base_text=rand_name(name="random")) + + EXPIRATION_TIME = 10000 # high to ensure the test finishes. + expires = int(time.time() + EXPIRATION_TIME) + + url = self.get_temp_url(self.container_name, + self.object_name, "PUT", + expires, self.key) + + # trying to put random data in the object using temp url + resp, body = self.object_client.put_object_using_temp_url( + url, new_data) + + self.assertIn(int(resp['status']), HTTP_SUCCESS) + + # Testing a HEAD on this Temp URL + resp, body = self.object_client.head(url) + self.assertIn(int(resp['status']), HTTP_SUCCESS) + + # Validate that the content of the object has been modified + url = self.get_temp_url(self.container_name, + self.object_name, "GET", + expires, self.key) + + _, body = self.object_client.get_object_using_temp_url(url) + self.assertEqual(body, new_data) + + @attr(type=['gate', 'negative']) + def test_get_object_after_expiration_time(self): + EXPIRATION_TIME = 1 + expires = int(time.time() + EXPIRATION_TIME) + + # get a temp URL for the created object + url = self.get_temp_url(self.container_name, + self.object_name, "GET", + expires, self.key) + + # temp URL is valid for 1 seconds, let's wait 3 + time.sleep(EXPIRATION_TIME + 2) + + self.assertRaises(exceptions.Unauthorized, + self.object_client.get_object_using_temp_url, + url) diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py index a867a127a7..e864ae3b17 100644 --- a/tempest/common/rest_client.py +++ b/tempest/common/rest_client.py @@ -448,8 +448,8 @@ class RestClient(object): # NOTE(mtreinish): This is for compatibility with Glance and swift # APIs. These are the return content types that Glance api v1 # (and occasionally swift) are using. - TXT_ENC = ['text/plain; charset=UTF-8', 'text/html; charset=UTF-8', - 'text/plain; charset=utf-8'] + TXT_ENC = ['text/plain', 'text/plain; charset=UTF-8', + 'text/html; charset=UTF-8', 'text/plain; charset=utf-8'] XML_ENC = ['application/xml', 'application/xml; charset=UTF-8'] if ctype in JSON_ENC or ctype in XML_ENC: diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py index d338d4555c..2fee042f0d 100644 --- a/tempest/services/object_storage/object_client.py +++ b/tempest/services/object_storage/object_client.py @@ -15,10 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import hashlib -import hmac -import urlparse - from tempest.common import http from tempest.common.rest_client import RestClient from tempest import exceptions @@ -125,29 +121,20 @@ class ObjectClient(RestClient): resp, body = self.copy(url, headers=headers) return resp, body - def get_object_using_temp_url(self, container, object_name, expires, key): - """Retrieve object's data using temporary URL.""" - - self._set_auth() - method = 'GET' - path = "%s/%s/%s" % (urlparse.urlparse(self.base_url).path, container, - object_name) - hmac_body = '%s\n%s\n%s' % (method, expires, path) - sig = hmac.new(key, hmac_body, hashlib.sha1).hexdigest() - - url = "%s/%s?temp_url_sig=%s&temp_url_expires=%s" % (container, - object_name, - sig, expires) - - resp, body = self.get(url) - return resp, body - def create_object_segments(self, container, object_name, segment, data): """Creates object segments.""" url = "{0}/{1}/{2}".format(container, object_name, segment) resp, body = self.put(url, data, self.headers) return resp, body + def get_object_using_temp_url(self, url): + """Retrieve object's data using temp URL.""" + return self.get(url) + + def put_object_using_temp_url(self, url, data): + """Put data in an object using temp URL.""" + return self.put(url, data, None) + class ObjectClientCustomizedHeader(RestClient):