Merge "Object write/read ACL and few security testcases"
diff --git a/tempest/clients.py b/tempest/clients.py
index 0bb1752..245f852 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -64,6 +64,8 @@
from tempest.services.volume.xml.volumes_client import VolumesClientXML
from tempest.services.object_storage.object_client import \
ObjectClientCustomizedHeader
+from tempest.services.object_storage.account_client import \
+ AccountClientCustomizedHeader
LOG = logging.getLogger(__name__)
@@ -202,6 +204,8 @@
self.ec2api_client = APIClientEC2(*client_args)
self.s3_client = ObjectClientS3(*client_args)
self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
+ self.custom_account_client = \
+ AccountClientCustomizedHeader(*client_args)
class AltManager(Manager):
diff --git a/tempest/services/object_storage/account_client.py b/tempest/services/object_storage/account_client.py
index 26f8329..734307c 100644
--- a/tempest/services/object_storage/account_client.py
+++ b/tempest/services/object_storage/account_client.py
@@ -15,10 +15,12 @@
# License for the specific language governing permissions and limitations
# under the License.
+import httplib2
import json
import urllib
from tempest.common.rest_client import RestClient
+from tempest import exceptions
class AccountClient(RestClient):
@@ -89,3 +91,66 @@
resp, body = self.get(url)
body = json.loads(body)
return resp, body
+
+
+class AccountClientCustomizedHeader(RestClient):
+
+ def __init__(self, config, username, password, auth_url, tenant_name=None):
+ super(AccountClientCustomizedHeader, self).__init__(config, username,
+ password, auth_url,
+ tenant_name)
+ #Overwrites json-specific header encoding in RestClient
+ self.service = self.config.object_storage.catalog_type
+ self.format = 'json'
+
+ def request(self, method, url, headers=None, body=None, wait=None):
+ """A simple HTTP request interface."""
+ self.http_obj = httplib2.Http()
+ if headers is None:
+ headers = {}
+ if self.base_url is None:
+ self._set_auth()
+
+ req_url = "%s/%s" % (self.base_url, url)
+ resp, resp_body = self.http_obj.request(req_url, method,
+ headers=headers, body=body)
+
+ if resp.status == 401 or resp.status == 403:
+ self._log(req_url, body, resp, resp_body)
+ raise exceptions.Unauthorized()
+
+ return resp, resp_body
+
+ def list_account_containers(self, params=None, metadata=None):
+ """
+ GET on the (base) storage URL
+ Given the X-Storage-URL and a valid X-Auth-Token, returns
+ a list of all containers for the account.
+
+ Optional Arguments:
+ limit=[integer value N]
+ Limits the number of results to at most N values
+ DEFAULT: 10,000
+
+ marker=[string value X]
+ Given string value X, return object names greater in value
+ than the specified marker.
+ DEFAULT: No Marker
+
+ format=[string value, either 'json' or 'xml']
+ Specify either json or xml to return the respective serialized
+ response.
+ DEFAULT: Python-List returned in response body
+ """
+
+ url = '?format=%s' % self.format
+ if params:
+ url += '&%s' + urllib.urlencode(params)
+
+ headers = {}
+ if metadata:
+ for key in metadata:
+ headers[str(key)] = metadata[key]
+
+ resp, body = self.get(url, headers=headers)
+ return resp, body
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index c05c905..b0f75d4 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -172,9 +172,14 @@
resp, body = self.put(url, data, headers=headers)
return resp, body
- def delete_object(self, container, object_name):
+ def delete_object(self, container, object_name, metadata=None):
"""Delete storage object."""
+ headers = {}
+ if metadata:
+ for key in metadata:
+ headers[str(key)] = metadata[key]
+
url = "%s/%s" % (str(container), str(object_name))
- resp, body = self.delete(url)
+ resp, body = self.delete(url, headers=headers)
return resp, body
diff --git a/tempest/tests/object_storage/base.py b/tempest/tests/object_storage/base.py
index 3992b13..10e2269 100644
--- a/tempest/tests/object_storage/base.py
+++ b/tempest/tests/object_storage/base.py
@@ -21,6 +21,7 @@
from tempest import clients
import tempest.config
from tempest import exceptions
+from tempest.tests.identity.base import DataGenerator
class BaseObjectTest(unittest.TestCase):
@@ -33,6 +34,16 @@
cls.account_client = cls.os.account_client
cls.config = cls.os.config
cls.custom_object_client = cls.os.custom_object_client
+ cls.os_admin = clients.IdentityManager()
+ cls.token_client = cls.os_admin.token_client
+ cls.admin_client = cls.os_admin.admin_client
+ cls.custom_account_client = cls.os.custom_account_client
+ cls.os_alt = clients.AltManager()
+ cls.object_client_alt = cls.os_alt.object_client
+ cls.container_client_alt = cls.os_alt.container_client
+ cls.admin_client_alt = cls.os_alt.admin_client
+
+ cls.data = DataGenerator(cls.admin_client)
try:
cls.account_client.list_account_containers()
diff --git a/tempest/tests/object_storage/test_account_services.py b/tempest/tests/object_storage/test_account_services.py
index cae2da1..e34e349 100644
--- a/tempest/tests/object_storage/test_account_services.py
+++ b/tempest/tests/object_storage/test_account_services.py
@@ -17,6 +17,7 @@
from nose.plugins.attrib import attr
from tempest.common.utils.data_utils import rand_name
+from tempest import exceptions
from tempest.tests.object_storage import base
@@ -80,3 +81,30 @@
resp, metadata = self.account_client.list_account_metadata()
self.assertNotIn('x-account-meta-test-account-meta', resp)
+
+ @attr(type='negative')
+ def test_list_containers_with_non_authorized_user(self):
+ #Listing containers with using non authorized user
+
+ # Randomly creating user
+ self.data.setup_test_user()
+
+ resp, body = \
+ self.token_client.auth(self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+ new_token = \
+ self.token_client.get_token(self.data.test_user,
+ self.data.test_password,
+ self.data.test_tenant)
+
+ custom_headers = {'X-Auth-Token': new_token}
+
+ params = {'format': 'json'}
+ # Trying to list containers with non authorized user token
+ self.assertRaises(exceptions.Unauthorized,
+ self.custom_account_client.list_account_containers,
+ params=params, metadata=custom_headers)
+
+ #Attempt to the delete the user setup created
+ self.data.teardown_all()
diff --git a/tempest/tests/object_storage/test_object_services.py b/tempest/tests/object_storage/test_object_services.py
index 8b87ad6..d380da6 100644
--- a/tempest/tests/object_storage/test_object_services.py
+++ b/tempest/tests/object_storage/test_object_services.py
@@ -21,6 +21,7 @@
from tempest.common.utils.data_utils import rand_name
from tempest import exceptions
from tempest.tests.object_storage import base
+import unittest2 as unittest
class ObjectTest(base.BaseObjectTest):
@@ -33,6 +34,20 @@
cls.container_name = rand_name(name='TestContainer')
cls.container_client.create_container(cls.container_name)
+ # Randomly creating user
+ cls.data.setup_test_user()
+
+ resp, body = \
+ cls.token_client.auth(cls.data.test_user,
+ cls.data.test_password,
+ cls.data.test_tenant)
+ cls.new_token = \
+ cls.token_client.get_token(cls.data.test_user,
+ cls.data.test_password,
+ cls.data.test_tenant)
+
+ cls.custom_headers = {'X-Auth-Token': cls.new_token}
+
@classmethod
def tearDownClass(cls):
#Get list of all object in the container
@@ -47,6 +62,9 @@
#Attempt to delete the container
resp, _ = cls.container_client.delete_container(cls.container_name)
+ #Attempt to the delete the user setup created
+ cls.data.teardown_all()
+
@attr(type='smoke')
def test_create_object(self):
# Create storage object, test response
@@ -316,10 +334,184 @@
self.assertIn('x-container-read', resp)
self.assertEqual(resp['x-container-read'], 'x')
+ @unittest.skip('Until Bug 1091669 is resolved.')
+ @attr(type='smoke')
+ def test_access_public_object_with_another_user_creds(self):
+ #Make container public-readable, and access the object
+ #anonymously, e.g. using another user credentials
+
+ try:
+ resp_meta = None
+ cont_headers = {'X-Container-Read': '.r:*,.rlistings'}
+ resp_meta, body = \
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+ self.assertEqual(resp_meta['status'], '204')
+ # Create Object
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name) * 1,
+ base_text=object_name)
+ resp, _ = self.object_client.create_object(self.container_name,
+ object_name, data)
+ self.assertEqual(resp['status'], '201')
+
+ # List container metadata
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-read', resp)
+ self.assertEqual(resp['x-container-read'], '.r:*,.rlistings')
+
+ # Trying to GET Auth Token of Alternate user
+ token = self.admin_client_alt.get_auth()
+ headers = {'X-Auth-Token': token}
+
+ # Trying to create object with Alternate user creds
+ resp, body = \
+ self.custom_object_client.get_object(
+ self.container_name, object_name, metadata=headers)
+ self.assertEqual(body, data)
+
+ except Exception as e:
+ self.fail("Failed to get public readable object with another"
+ " user creds raised exception is %s" % e)
+
+ finally:
+ if resp_meta['status'] == '204':
+ # Delete updated container metadata, to revert back.
+ resp, body = \
+ self.container_client.delete_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-read', resp)
+ self.assertEqual(resp['x-container-read'], 'x')
+
+ @unittest.skip('Until Bug #1020722 is resolved.')
+ @attr(type='smoke')
+ def test_write_public_object_without_using_creds(self):
+ #Make container public-writable, and create object
+ #anonymously, e.g. without using credentials
+ try:
+ resp_meta = None
+ # Update Container Metadata to make public readable
+ cont_headers = {'X-Container-Write': '-*'}
+ resp_meta, body = \
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+ self.assertEqual(resp_meta['status'], '204')
+ # List container metadata
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-write', resp)
+ self.assertEqual(resp['x-container-write'], '-*')
+
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name),
+ base_text=object_name)
+
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json'}
+
+ #Trying to Create object without using creds
+ resp, body = \
+ self.custom_object_client.create_object(self.container_name,
+ object_name, data,
+ metadata=headers)
+ self.assertEqual(resp['status'], '201')
+
+ except Exception as e:
+ self.fail("Failed to create public writable object without using"
+ " creds raised exception is %s" % e)
+
+ finally:
+ if resp_meta['status'] == '204':
+ # Delete updated container metadata, to revert back.
+ resp, body = \
+ self.container_client.delete_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-write', resp)
+ self.assertEqual(resp['x-container-write'], 'x')
+
+ @unittest.skip('Until Bug #1020722 is resolved.')
+ @attr(type='smoke')
+ def test_write_public_with_another_user_creds(self):
+ #Make container public-writable, and create object
+ #anonymously, e.g. with another user credentials
+
+ try:
+ resp_meta = None
+ # Update Container Metadata to make public readable
+ cont_headers = {'X-Container-Write': '-*'}
+ resp_meta, body = \
+ self.container_client.update_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+ self.assertEqual(resp_meta['status'], '204')
+ # List container metadata
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-write', resp)
+ self.assertEqual(resp['x-container-write'], '-*')
+
+ #Trying to GET auth token of Alternate user
+ token = self.admin_client_alt.get_auth()
+
+ headers = {'Content-Type': 'application/json',
+ 'Accept': 'application/json',
+ 'X-Auth-Token': token}
+
+ #Trying to Create an object with another user creds
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name),
+ base_text=object_name)
+ resp, body = \
+ self.custom_object_client.create_object(
+ self.container_name, object_name, data, metadata=headers)
+ self.assertEqual(resp['status'], '201')
+
+ except Exception as e:
+ self.fail("Failed to create public writable object with another"
+ " user creds raised exception is %s" % e)
+
+ finally:
+ if resp_meta['status'] == '204':
+ # Delete updated container metadata, to revert back.
+ resp, body = \
+ self.container_client.delete_container_metadata(
+ self.container_name, metadata=cont_headers,
+ metadata_prefix='')
+
+ resp, _ = \
+ self.container_client.list_container_metadata(
+ self.container_name)
+ self.assertEqual(resp['status'], '204')
+ self.assertIn('x-container-write', resp)
+ self.assertEqual(resp['x-container-write'], 'x')
+
@attr(type='negative')
def test_access_object_without_using_creds(self):
# Attempt to access the object anonymously, e.g.
- # not using any credentials
+ # not using any credentials
# Create Object
object_name = rand_name(name='Object')
@@ -354,7 +546,7 @@
@attr(type='negative')
def test_delete_object_without_using_creds(self):
# Attempt to delete the object anonymously,
- # e.g. not using any credentials
+ # e.g. not using any credentials
# Create Object
object_name = rand_name(name='Object')
@@ -367,3 +559,55 @@
self.assertRaises(exceptions.Unauthorized,
self.custom_object_client.delete_object,
self.container_name, object_name)
+
+ @attr(type='negative')
+ def test_write_object_with_non_authorized_user(self):
+ #Attempt to upload another file using non authorized user
+
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name) * 5,
+ base_text=object_name)
+
+ # Trying to Create Object with non authorized user token
+ self.assertRaises(exceptions.Unauthorized,
+ self.custom_object_client.create_object,
+ self.container_name, object_name, data,
+ metadata=self.custom_headers)
+
+ @attr(type='negative')
+ def test_read_object_with_non_authorized_user(self):
+ #Attempt to download the file using non authorized user
+
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name) * 5,
+ base_text=object_name)
+
+ resp, body = \
+ self.object_client.create_object(self.container_name,
+ object_name, data)
+ self.assertEqual(resp['status'], '201')
+
+ # Trying to Get Object with non authorized user token
+ self.assertRaises(exceptions.Unauthorized,
+ self.custom_object_client.get_object,
+ self.container_name, object_name,
+ metadata=self.custom_headers)
+
+ @attr(type='negative')
+ def test_delete_object_with_non_authorized_user(self):
+ #Attempt to delete container using non authorized user
+
+ object_name = rand_name(name='Object')
+ data = arbitrary_string(size=len(object_name) * 5,
+ base_text=object_name)
+
+ resp, body = \
+ self.object_client.create_object(self.container_name,
+ object_name, data)
+ self.assertEqual(resp['status'], '201')
+
+ # Trying to Delete Object with non authorized user token
+ self.assertRaises(exceptions.Unauthorized,
+ self.custom_object_client.delete_object,
+ self.container_name, object_name,
+ metadata=self.custom_headers)