Merge "Test to GET public-readable container's object"
diff --git a/tempest/openstack.py b/tempest/openstack.py
index e29ea8e..01b4cc1 100644
--- a/tempest/openstack.py
+++ b/tempest/openstack.py
@@ -22,33 +22,33 @@
 from tempest.services.boto.clients import APIClientEC2
 from tempest.services.boto.clients import ObjectClientS3
 from tempest.services.compute.json.extensions_client import \
-        ExtensionsClientJSON
+    ExtensionsClientJSON
 from tempest.services.compute.json.flavors_client import FlavorsClientJSON
 from tempest.services.compute.json.floating_ips_client import \
-        FloatingIPsClientJSON
+    FloatingIPsClientJSON
 from tempest.services.compute.json.images_client import ImagesClientJSON
 from tempest.services.compute.json.limits_client import LimitsClientJSON
 from tempest.services.compute.json.servers_client import ServersClientJSON
 from tempest.services.compute.json.security_groups_client import \
-        SecurityGroupsClientJSON
+    SecurityGroupsClientJSON
 from tempest.services.compute.json.keypairs_client import KeyPairsClientJSON
 from tempest.services.compute.json.quotas_client import QuotasClient
 from tempest.services.compute.json.volumes_extensions_client import \
-        VolumesExtensionsClientJSON
+    VolumesExtensionsClientJSON
 from tempest.services.compute.json.console_output_client import \
-        ConsoleOutputsClient
+    ConsoleOutputsClient
 from tempest.services.compute.xml.extensions_client import ExtensionsClientXML
 from tempest.services.compute.xml.flavors_client import FlavorsClientXML
 from tempest.services.compute.xml.floating_ips_client import \
-        FloatingIPsClientXML
+    FloatingIPsClientXML
 from tempest.services.compute.xml.images_client import ImagesClientXML
 from tempest.services.compute.xml.keypairs_client import KeyPairsClientXML
 from tempest.services.compute.xml.limits_client import LimitsClientXML
 from tempest.services.compute.xml.security_groups_client \
-import SecurityGroupsClientXML
+    import SecurityGroupsClientXML
 from tempest.services.compute.xml.servers_client import ServersClientXML
 from tempest.services.compute.xml.volumes_extensions_client import \
-        VolumesExtensionsClientXML
+    VolumesExtensionsClientXML
 from tempest.services.identity.json.admin_client import AdminClientJSON
 from tempest.services.identity.json.admin_client import TokenClientJSON
 from tempest.services.identity.xml.admin_client import AdminClientXML
@@ -60,6 +60,8 @@
 from tempest.services.object_storage.object_client import ObjectClient
 from tempest.services.volume.json.volumes_client import VolumesClientJSON
 from tempest.services.volume.xml.volumes_client import VolumesClientXML
+from tempest.services.object_storage.object_client import \
+    ObjectClientCustomizedHeader
 
 LOG = logging.getLogger(__name__)
 
@@ -191,6 +193,7 @@
         self.object_client = ObjectClient(*client_args)
         self.ec2api_client = APIClientEC2(*client_args)
         self.s3_client = ObjectClientS3(*client_args)
+        self.custom_object_client = ObjectClientCustomizedHeader(*client_args)
 
 
 class AltManager(Manager):
diff --git a/tempest/services/object_storage/object_client.py b/tempest/services/object_storage/object_client.py
index c8f63ef..5bf8de0 100644
--- a/tempest/services/object_storage/object_client.py
+++ b/tempest/services/object_storage/object_client.py
@@ -15,6 +15,8 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 
+import httplib2
+import json
 import re
 from tempest.common.rest_client import RestClient
 
@@ -116,3 +118,44 @@
 
         resp, body = self.copy(url, headers=headers)
         return resp, body
+
+
+class ObjectClientCustomizedHeader(RestClient):
+
+    def __init__(self, config, username, password, auth_url, tenant_name=None):
+        super(ObjectClientCustomizedHeader, 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 in (401, 403):
+            try:
+                resp_body = json.loads(resp_body)
+                raise exceptions.Unauthorized(resp_body['error']['message'])
+            except ValueError:
+                pass
+        return resp, resp_body
+
+    def get_object(self, container, object_name, metadata=None):
+        """Retrieve object's data."""
+        headers = {}
+        if metadata:
+            for key in metadata:
+                headers[str(key)] = metadata[key]
+
+        url = "{0}/{1}".format(container, object_name)
+        resp, body = self.get(url, headers=headers)
+        return resp, body
diff --git a/tempest/tests/object_storage/base.py b/tempest/tests/object_storage/base.py
index 8edb3d2..f29798f 100644
--- a/tempest/tests/object_storage/base.py
+++ b/tempest/tests/object_storage/base.py
@@ -32,6 +32,7 @@
         cls.container_client = cls.os.container_client
         cls.account_client = cls.os.account_client
         cls.config = cls.os.config
+        cls.custom_object_client = cls.os.custom_object_client
 
         try:
             cls.account_client.list_account_containers()
diff --git a/tempest/tests/object_storage/test_object_services.py b/tempest/tests/object_storage/test_object_services.py
index 3be2bee..807a99e 100644
--- a/tempest/tests/object_storage/test_object_services.py
+++ b/tempest/tests/object_storage/test_object_services.py
@@ -263,3 +263,36 @@
                 src_container_name)
             resp, _ = self.container_client.delete_container(
                 dst_container_name)
+
+    @attr(type='smoke')
+    def test_access_object_without_using_creds(self):
+        """Make container public-readable, and access the object
+           anonymously, e.g. without using credentials"""
+
+        # Update Container Metadata to make public readable
+        headers = {'X-Container-Read': '.r:*,.rlistings'}
+        resp, body = \
+            self.container_client.update_container_metadata(
+                self.container_name, metadata=headers, metadata_prefix='')
+        self.assertIn(resp['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 Object with empty Headers as it is public readable
+        resp, body = \
+            self.custom_object_client.get_object(self.container_name,
+                                                 object_name, metadata={})
+        self.assertEqual(body, data)