Ejemplo n.º 1
0
    def test_symlink_target(self):
        if 'symlink' not in self.cluster_info:
            raise unittest.SkipTest("Symlink not enabled in proxy; can't test "
                                    "symlink to reserved name")
        int_client = self.make_internal_client()

        # create link container first, ensure account gets created too
        client.put_container(self.url, self.token, 'c1')

        # Create reserve named container
        tgt_cont = get_reserved_name('container-%s' % uuid4())
        int_client.create_container(self.account, tgt_cont)

        # sanity, user can't get to reserved name
        with self.assertRaises(ClientException) as cm:
            client.head_container(self.url, self.token, tgt_cont)
        self.assertEqual(412, cm.exception.http_status)

        tgt_obj = get_reserved_name('obj-%s' % uuid4())
        int_client.upload_object(BytesIO(b'target object'), self.account,
                                 tgt_cont, tgt_obj)
        metadata = int_client.get_object_metadata(self.account, tgt_cont,
                                                  tgt_obj)
        etag = metadata['etag']

        # users can write a dynamic symlink that targets a reserved
        # name object
        client.put_object(self.url,
                          self.token,
                          'c1',
                          'symlink',
                          headers={
                              'X-Symlink-Target':
                              '%s/%s' % (tgt_cont, tgt_obj),
                              'Content-Type': 'application/symlink',
                          })

        # but can't read the symlink
        with self.assertRaises(ClientException) as cm:
            client.get_object(self.url, self.token, 'c1', 'symlink')
        self.assertEqual(412, cm.exception.http_status)

        # user's can't create static symlink to reserved name
        with self.assertRaises(ClientException) as cm:
            client.put_object(self.url,
                              self.token,
                              'c1',
                              'static-symlink',
                              headers={
                                  'X-Symlink-Target':
                                  '%s/%s' % (tgt_cont, tgt_obj),
                                  'X-Symlink-Target-Etag': etag,
                                  'Content-Type': 'application/symlink',
                              })
        self.assertEqual(412, cm.exception.http_status)

        # clean-up
        client.delete_object(self.url, self.token, 'c1', 'symlink')
        int_client.delete_object(self.account, tgt_cont, tgt_obj)
        int_client.delete_container(self.account, tgt_cont)
 def setUp(self):
     super(TestReservedNamespaceMergePolicyIndex, self).setUp()
     self.container_name = get_reserved_name('container', str(uuid.uuid4()))
     self.object_name = get_reserved_name('object', str(uuid.uuid4()))
     self.brain = InternalBrainSplitter('/etc/swift/internal-client.conf',
                                        self.container_name,
                                        self.object_name, 'container')
Ejemplo n.º 3
0
    def test_validate_internal_container(self):
        self.assertIsNone(rh.validate_internal_container('AUTH_foo', 'bar'))
        self.assertIsNone(
            rh.validate_internal_container(rh.get_reserved_name('AUTH_foo'),
                                           'bar'))
        self.assertIsNone(
            rh.validate_internal_container('foo', rh.get_reserved_name('bar')))
        self.assertIsNone(
            rh.validate_internal_container(rh.get_reserved_name('AUTH_foo'),
                                           rh.get_reserved_name('bar')))
        with self.assertRaises(HTTPException) as raised:
            rh.validate_internal_container('AUTH_foo' + rh.RESERVED, 'bar')
        e = raised.exception
        self.assertEqual(e.status_int, 400)
        self.assertEqual(str(e), '400 Bad Request')
        self.assertEqual(e.body, b"Invalid reserved-namespace account")
        with self.assertRaises(HTTPException) as raised:
            rh.validate_internal_container('AUTH_foo', 'bar' + rh.RESERVED)
        e = raised.exception
        self.assertEqual(e.status_int, 400)
        self.assertEqual(str(e), '400 Bad Request')
        self.assertEqual(e.body, b"Invalid reserved-namespace container")

        # These should always be operating on split_path outputs so this
        # shouldn't really be an issue, but just in case...
        for acct in ('', None):
            with self.assertRaises(ValueError) as raised:
                rh.validate_internal_container(acct, 'bar')
            self.assertEqual(raised.exception.args[0], 'Account is required')
Ejemplo n.º 4
0
 def test_validate_internal_name(self):
     self.assertIsNone(rh._validate_internal_name('foo'))
     self.assertIsNone(
         rh._validate_internal_name(rh.get_reserved_name('foo')))
     self.assertIsNone(
         rh._validate_internal_name(rh.get_reserved_name('foo', 'bar')))
     self.assertIsNone(rh._validate_internal_name(''))
     self.assertIsNone(rh._validate_internal_name(rh.RESERVED))
Ejemplo n.º 5
0
    def test_account_listing_reserved_names(self):
        broker = backend.AccountBroker(':memory:', account='a')
        put_timestamp = next(self.ts)
        now = time.time()
        with mock.patch('time.time', new=lambda: now):
            broker.initialize(put_timestamp.internal)
        container_timestamp = next(self.ts)
        broker.put_container(get_reserved_name('foo'),
                             container_timestamp.internal, 0, 10, 100, 0)

        req = Request.blank('')
        resp = utils.account_listing_response(
            'a', req, 'application/json', broker)
        self.assertEqual(resp.status_int, 200)
        expected = HeaderKeyDict({
            'Content-Type': 'application/json; charset=utf-8',
            'Content-Length': 2,
            'X-Account-Container-Count': 1,
            'X-Account-Object-Count': 10,
            'X-Account-Bytes-Used': 100,
            'X-Timestamp': Timestamp(now).normal,
            'X-PUT-Timestamp': put_timestamp.normal,
            'X-Account-Storage-Policy-Zero-Container-Count': 1,
            'X-Account-Storage-Policy-Zero-Object-Count': 10,
            'X-Account-Storage-Policy-Zero-Bytes-Used': 100,
        })
        self.assertEqual(expected, resp.headers)
        self.assertEqual(b'[]', resp.body)

        req = Request.blank('', headers={
            'X-Backend-Allow-Reserved-Names': 'true'})
        resp = utils.account_listing_response(
            'a', req, 'application/json', broker)
        self.assertEqual(resp.status_int, 200)
        expected = HeaderKeyDict({
            'Content-Type': 'application/json; charset=utf-8',
            'Content-Length': 97,
            'X-Account-Container-Count': 1,
            'X-Account-Object-Count': 10,
            'X-Account-Bytes-Used': 100,
            'X-Timestamp': Timestamp(now).normal,
            'X-PUT-Timestamp': put_timestamp.normal,
            'X-Account-Storage-Policy-Zero-Container-Count': 1,
            'X-Account-Storage-Policy-Zero-Object-Count': 10,
            'X-Account-Storage-Policy-Zero-Bytes-Used': 100,
        })
        self.assertEqual(expected, resp.headers)
        expected = [{
            "last_modified": container_timestamp.isoformat,
            "count": 10,
            "bytes": 100,
            "name": get_reserved_name('foo'),
        }]
        self.assertEqual(sorted(json.dumps(expected).encode('ascii')),
                         sorted(resp.body))
Ejemplo n.º 6
0
    def put_data(self):
        int_client = self.make_internal_client()
        int_client.create_account(self.account)
        container = get_reserved_name('container', str(uuid4()))
        int_client.create_container(self.account, container,
                                    headers={'X-Storage-Policy':
                                             self.policy.name})

        obj = get_reserved_name('object', str(uuid4()))
        int_client.upload_object(
            BytesIO(b'VERIFY'), self.account, container, obj)
Ejemplo n.º 7
0
    def test_simple_crud(self):
        int_client = self.make_internal_client()

        # Create reserve named container
        user_cont = 'container-%s' % uuid4()
        reserved_cont = get_reserved_name('container-%s' % uuid4())
        client.put_container(self.url, self.token, user_cont)
        int_client.create_container(self.account, reserved_cont)

        # Check that we can list both reserved and non-reserved containers
        self.assertEqual(
            [reserved_cont, user_cont],
            [c['name'] for c in int_client.iter_containers(self.account)])

        # sanity, user can't get to reserved name
        with self.assertRaises(ClientException) as cm:
            client.head_container(self.url, self.token, reserved_cont)
        self.assertEqual(412, cm.exception.http_status)

        user_obj = 'obj-%s' % uuid4()
        reserved_obj = get_reserved_name('obj-%s' % uuid4())

        # InternalClient can write & read reserved names fine
        int_client.upload_object(BytesIO(b'data'), self.account, reserved_cont,
                                 reserved_obj)
        int_client.get_object_metadata(self.account, reserved_cont,
                                       reserved_obj)
        _, _, app_iter = int_client.get_object(self.account, reserved_cont,
                                               reserved_obj)
        self.assertEqual(b''.join(app_iter), b'data')
        self.assertEqual([reserved_obj], [
            o['name']
            for o in int_client.iter_objects(self.account, reserved_cont)
        ])

        # But reserved objects must be in reserved containers, and
        # user objects must be in user containers (at least for now)
        int_client.upload_object(BytesIO(b'data'),
                                 self.account,
                                 reserved_cont,
                                 user_obj,
                                 acceptable_statuses=(400, ))

        int_client.upload_object(BytesIO(b'data'),
                                 self.account,
                                 user_cont,
                                 reserved_obj,
                                 acceptable_statuses=(400, ))

        # Make sure we can clean up, too
        int_client.delete_object(self.account, reserved_cont, reserved_obj)
        int_client.delete_container(self.account, reserved_cont)
Ejemplo n.º 8
0
 def get_metadata_resp_headers(self, meta):
     headers = {}
     system = meta.get('system') or {}
     # sys.m2.ctime is microseconds
     ctime = float(system.get('sys.m2.ctime', 0)) / 1000000.0
     headers.update({
         'X-Container-Object-Count':
         system.get('sys.m2.objects', 0),
         'X-Container-Bytes-Used':
         system.get('sys.m2.usage', 0),
         'X-Timestamp':
         Timestamp(ctime).normal,
         # FIXME: save modification time somewhere
         'X-PUT-Timestamp':
         Timestamp(ctime).normal,
     })
     for (k, v) in meta['properties'].items():
         if v and (k.lower() in self.pass_through_headers
                   or is_sys_or_user_meta('container', k)):
             headers[k] = v
     # HACK: oio-sds always sets version numbers, so let some middlewares
     # think that versioning is always enabled.
     if SYSMETA_VERSIONS_CONT not in headers and 'sys.user.name' in system:
         try:
             v_con = get_reserved_name('versions', system['sys.user.name'])
             headers[SYSMETA_VERSIONS_CONT] = v_con
         except ValueError:
             # sys.user.name contains reserved characters
             # -> this is probably a versioning container.
             pass
     return headers
Ejemplo n.º 9
0
    def setUp(self):
        super(TestAccountReaper, self).setUp()
        self.all_objects = []
        int_client = self.make_internal_client()
        # upload some containers
        body = b'test-body'
        for policy in ENABLED_POLICIES:
            container = 'container-%s-%s' % (policy.name, uuid.uuid4())
            client.put_container(self.url,
                                 self.token,
                                 container,
                                 headers={'X-Storage-Policy': policy.name})
            obj = 'object-%s' % uuid.uuid4()
            client.put_object(self.url, self.token, container, obj, body)
            self.all_objects.append((policy, container, obj))

            # Also create some reserved names
            container = get_reserved_name('reserved', policy.name,
                                          str(uuid.uuid4()))
            int_client.create_container(
                self.account,
                container,
                headers={'X-Storage-Policy': policy.name})
            obj = get_reserved_name('object', str(uuid.uuid4()))
            int_client.upload_object(BytesIO(body), self.account, container,
                                     obj)
            self.all_objects.append((policy, container, obj))

            policy.load_ring('/etc/swift')

        Manager(['container-updater']).once()

        headers = client.head_account(self.url, self.token)

        self.assertEqual(int(headers['x-account-container-count']),
                         len(self.all_objects))
        self.assertEqual(int(headers['x-account-object-count']),
                         len(self.all_objects))
        self.assertEqual(int(headers['x-account-bytes-used']),
                         len(self.all_objects) * len(body))

        part, nodes = self.account_ring.get_nodes(self.account)

        for node in nodes:
            direct_delete_account(node, part, self.account)
Ejemplo n.º 10
0
 def test_validate_internal_account(self):
     self.assertIsNone(rh.validate_internal_account('AUTH_foo'))
     self.assertIsNone(
         rh.validate_internal_account(rh.get_reserved_name('AUTH_foo')))
     with self.assertRaises(HTTPException) as raised:
         rh.validate_internal_account('AUTH_foo' + rh.RESERVED)
     e = raised.exception
     self.assertEqual(e.status_int, 400)
     self.assertEqual(str(e), '400 Bad Request')
     self.assertEqual(e.body, b"Invalid reserved-namespace account")
Ejemplo n.º 11
0
 def test_get_reserved_name(self):
     expectations = {
         tuple(): rh.RESERVED,
         ('', ): rh.RESERVED,
         ('foo', ): rh.RESERVED + 'foo',
         ('foo', 'bar'): rh.RESERVED + 'foo' + rh.RESERVED + 'bar',
         ('foo', ''): rh.RESERVED + 'foo' + rh.RESERVED,
         ('', ''): rh.RESERVED * 2,
     }
     failures = []
     for parts, expected in expectations.items():
         name = rh.get_reserved_name(*parts)
         if name != expected:
             failures.append('get given %r expected %r != %r' %
                             (parts, expected, name))
     if failures:
         self.fail('Unexpected reults:\n' + '\n'.join(failures))
Ejemplo n.º 12
0
 def test_invalid_get_reserved_name(self):
     self.assertRaises(ValueError)
     with self.assertRaises(ValueError) as ctx:
         rh.get_reserved_name('foo', rh.RESERVED + 'bar', 'baz')
     self.assertEqual(str(ctx.exception),
                      'Invalid reserved part in components')
Ejemplo n.º 13
0
    def handle_versioned_request(self, req, versions_cont, api_version,
                                 account, container, obj, is_enabled, version):
        """
        Handle 'version-id' request for object resource. When a request
        contains a ``version-id=<id>`` parameter, the request is acted upon
        the actual version of that object. Version-aware operations
        require that the container is versioned, but do not require that
        the versioning is currently enabled. Users should be able to
        operate on older versions of an object even if versioning is
        currently suspended.

        PUT and POST requests are not allowed as that would overwrite
        the contents of the versioned object.

        :param req: The original request
        :param versions_cont: container holding versions of the requested obj
        :param api_version: should be v1 unless swift bumps api version
        :param account: account name string
        :param container: container name string
        :param object: object name string
        :param is_enabled: is versioning currently enabled
        :param version: version of the object to act on
        """
        if not versions_cont and version != 'null':
            raise HTTPBadRequest(
                'version-aware operations require that the container is '
                'versioned',
                request=req)
        req.environ['oio.query'] = {'version': version}
        if version != 'null':
            try:
                int(version)
            except ValueError:
                raise HTTPBadRequest('Invalid version parameter', request=req)

        if req.method == 'DELETE':
            return self.handle_delete_version(req, versions_cont, api_version,
                                              account, container, obj,
                                              is_enabled, version)
        elif req.method == 'PUT':
            return self.handle_put_version(req, versions_cont, api_version,
                                           account, container, obj, is_enabled,
                                           version)
        if version == 'null':
            resp = req.get_response(self.app)
            if resp.is_success:
                if get_reserved_name('versions', '') in wsgi_unquote(
                        resp.headers.get('Content-Location', '')):
                    # Have a latest version, but it's got a real version-id.
                    # Since the user specifically asked for null, return 404
                    close_if_possible(resp.app_iter)
                    raise HTTPNotFound(request=req)
                resp.headers['X-Object-Version-Id'] = 'null'
                if req.method == 'HEAD':
                    drain_and_close(resp)
            return resp
        else:
            resp = req.get_response(self.app)
            if resp.is_success:
                resp.headers['X-Object-Version-Id'] = version

            # Well, except for some delete marker business...
            is_del_marker = DELETE_MARKER_CONTENT_TYPE == resp.headers.get(
                'X-Backend-Content-Type', resp.headers['Content-Type'])

            if req.method == 'HEAD':
                drain_and_close(resp)

            if is_del_marker:
                hdrs = {
                    'X-Object-Version-Id': version,
                    'Content-Type': DELETE_MARKER_CONTENT_TYPE
                }
                raise HTTPNotFound(request=req, headers=hdrs)
            return resp
Ejemplo n.º 14
0
    def test_missing_versions_container(self):
        versions_header_key = 'X-Versions-Enabled'

        # Create container1
        container_name = 'container1'
        obj_name = 'object1'
        client.put_container(self.url, self.token, container_name)

        # Write some data
        client.put_object(self.url, self.token, container_name, obj_name,
                          b'null version')

        # Enable versioning
        hdrs = {versions_header_key: 'True'}
        client.post_container(self.url, self.token, container_name, hdrs)

        # But directly delete hidden container to leave an orphan primary
        # container
        self.direct_delete_container(
            container=get_reserved_name('versions', container_name))

        # Could be worse; we can still list versions and GET data
        _headers, all_versions = client.get_container(self.url,
                                                      self.token,
                                                      container_name,
                                                      query_string='versions')
        self.assertEqual(len(all_versions), 1)
        self.assertEqual(all_versions[0]['name'], obj_name)
        self.assertEqual(all_versions[0]['version_id'], 'null')

        _headers, data = client.get_object(self.url, self.token,
                                           container_name, obj_name)
        self.assertEqual(data, b'null version')

        _headers, data = client.get_object(self.url,
                                           self.token,
                                           container_name,
                                           obj_name,
                                           query_string='version-id=null')
        self.assertEqual(data, b'null version')

        # But most any write is going to fail
        with self.assertRaises(client.ClientException) as caught:
            client.put_object(self.url, self.token, container_name, obj_name,
                              b'new version')
        self.assertEqual(caught.exception.http_status, 500)
        with self.assertRaises(client.ClientException) as caught:
            client.delete_object(self.url, self.token, container_name,
                                 obj_name)
        self.assertEqual(caught.exception.http_status, 500)

        # Version-aware delete can work, though!
        client.delete_object(self.url,
                             self.token,
                             container_name,
                             obj_name,
                             query_string='version-id=null')

        # Re-enabling versioning should square us
        hdrs = {versions_header_key: 'True'}
        client.post_container(self.url, self.token, container_name, hdrs)

        client.put_object(self.url, self.token, container_name, obj_name,
                          b'new version')

        _headers, all_versions = client.get_container(self.url,
                                                      self.token,
                                                      container_name,
                                                      query_string='versions')
        self.assertEqual(len(all_versions), 1)
        self.assertEqual(all_versions[0]['name'], obj_name)
        self.assertNotEqual(all_versions[0]['version_id'], 'null')

        _headers, data = client.get_object(self.url, self.token,
                                           container_name, obj_name)
        self.assertEqual(data, b'new version')
 def get_object_name(self, name):
     return get_reserved_name(name)
Ejemplo n.º 16
0
    def test_valid_account_with_reserved(self):
        body_len = len(self.fake_account_listing_with_reserved)
        self.fake_swift.register('GET', '/v1/a\xe2\x98\x83', HTTPOk, {
            'Content-Length': str(body_len),
            'Content-Type': 'application/json',
        }, self.fake_account_listing_with_reserved)

        req = Request.blank('/v1/a\xe2\x98\x83')
        resp = req.get_response(self.app)
        self.assertEqual(resp.body, b'bar\nfoo_\n')
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))
        self.assertEqual(self.logger.get_lines_for_level('warning'), [
            "Account listing for a%E2%98%83 had reserved byte in name: "
            "'\\x00bar\\x00versions'",
            "Account listing for a%E2%98%83 had reserved byte in subdir: "
            "'\\x00foo_'",
        ])

        req = Request.blank('/v1/a\xe2\x98\x83',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'bar\n%s\nfoo_\n%s\n' % (
                get_reserved_name('bar', 'versions').encode('ascii'),
                get_reserved_name('foo_').encode('ascii'),
            ))
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=txt')
        resp = req.get_response(self.app)
        self.assertEqual(resp.body, b'bar\nfoo_\n')
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=txt',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'bar\n%s\nfoo_\n%s\n' % (
                get_reserved_name('bar', 'versions').encode('ascii'),
                get_reserved_name('foo_').encode('ascii'),
            ))
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=json')
        resp = req.get_response(self.app)
        self.assertEqual(json.loads(resp.body),
                         json.loads(self.fake_account_listing))
        self.assertEqual(resp.headers['Content-Type'],
                         'application/json; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=json',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(json.loads(resp.body),
                         json.loads(self.fake_account_listing_with_reserved))
        self.assertEqual(resp.headers['Content-Type'],
                         'application/json; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=xml')
        resp = req.get_response(self.app)
        self.assertEqual(resp.body.split(b'\n'), [
            b'<?xml version="1.0" encoding="UTF-8"?>',
            b'<account name="a\xe2\x98\x83">',
            b'<container><name>bar</name><count>0</count><bytes>0</bytes>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</container>',
            b'<subdir name="foo_" />',
            b'</account>',
        ])
        self.assertEqual(resp.headers['Content-Type'],
                         'application/xml; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))

        req = Request.blank('/v1/a\xe2\x98\x83?format=xml',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(resp.body.split(b'\n'), [
            b'<?xml version="1.0" encoding="UTF-8"?>',
            b'<account name="a\xe2\x98\x83">',
            b'<container><name>bar</name><count>0</count><bytes>0</bytes>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</container>',
            b'<container><name>%s</name>'
            b'<count>0</count><bytes>0</bytes>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</container>' %
            get_reserved_name('bar', 'versions').encode('ascii'),
            b'<subdir name="foo_" />',
            b'<subdir name="%s" />' %
            get_reserved_name('foo_').encode('ascii'),
            b'</account>',
        ])
        self.assertEqual(resp.headers['Content-Type'],
                         'application/xml; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', '/v1/a\xe2\x98\x83?format=json'))
Ejemplo n.º 17
0
    def setUp(self):
        self.fake_swift = FakeSwift()
        self.logger = debug_logger('test-listing')
        self.app = listing_formats.ListingFilter(self.fake_swift, {},
                                                 logger=self.logger)
        self.fake_account_listing = json.dumps([
            {
                'name': 'bar',
                'bytes': 0,
                'count': 0,
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'subdir': 'foo_'
            },
        ]).encode('ascii')
        self.fake_container_listing = json.dumps([
            {
                'name': 'bar',
                'hash': 'etag',
                'bytes': 0,
                'content_type': 'text/plain',
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'subdir': 'foo/'
            },
        ]).encode('ascii')

        self.fake_account_listing_with_reserved = json.dumps([
            {
                'name': 'bar',
                'bytes': 0,
                'count': 0,
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'name': get_reserved_name('bar', 'versions'),
                'bytes': 0,
                'count': 0,
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'subdir': 'foo_'
            },
            {
                'subdir': get_reserved_name('foo_')
            },
        ]).encode('ascii')
        self.fake_container_listing_with_reserved = json.dumps([
            {
                'name': 'bar',
                'hash': 'etag',
                'bytes': 0,
                'content_type': 'text/plain',
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'name': get_reserved_name('bar', 'extra data'),
                'hash': 'etag',
                'bytes': 0,
                'content_type': 'text/plain',
                'last_modified': '1970-01-01T00:00:00.000000'
            },
            {
                'subdir': 'foo/'
            },
            {
                'subdir': get_reserved_name('foo/')
            },
        ]).encode('ascii')
Ejemplo n.º 18
0
    def test_valid_container_with_reserved(self):
        path = '/v1/a\xe2\x98\x83/c\xf0\x9f\x8c\xb4'
        body_len = len(self.fake_container_listing_with_reserved)
        self.fake_swift.register('GET', path, HTTPOk, {
            'Content-Length': str(body_len),
            'Content-Type': 'application/json',
        }, self.fake_container_listing_with_reserved)

        req = Request.blank(path)
        resp = req.get_response(self.app)
        self.assertEqual(resp.body, b'bar\nfoo/\n')
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))
        self.assertEqual(self.logger.get_lines_for_level('warning'), [
            "Container listing for a%E2%98%83/c%F0%9F%8C%B4 had reserved byte "
            "in name: '\\x00bar\\x00extra data'",
            "Container listing for a%E2%98%83/c%F0%9F%8C%B4 had reserved byte "
            "in subdir: '\\x00foo/'",
        ])

        req = Request.blank(path,
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'bar\n%s\nfoo/\n%s\n' % (
                get_reserved_name('bar', 'extra data').encode('ascii'),
                get_reserved_name('foo/').encode('ascii'),
            ))
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=txt')
        resp = req.get_response(self.app)
        self.assertEqual(resp.body, b'bar\nfoo/\n')
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=txt',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'bar\n%s\nfoo/\n%s\n' % (
                get_reserved_name('bar', 'extra data').encode('ascii'),
                get_reserved_name('foo/').encode('ascii'),
            ))
        self.assertEqual(resp.headers['Content-Type'],
                         'text/plain; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=json')
        resp = req.get_response(self.app)
        self.assertEqual(json.loads(resp.body),
                         json.loads(self.fake_container_listing))
        self.assertEqual(resp.headers['Content-Type'],
                         'application/json; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=json',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(json.loads(resp.body),
                         json.loads(self.fake_container_listing_with_reserved))
        self.assertEqual(resp.headers['Content-Type'],
                         'application/json; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=xml')
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'<?xml version="1.0" encoding="UTF-8"?>\n'
            b'<container name="c\xf0\x9f\x8c\xb4">'
            b'<object><name>bar</name><hash>etag</hash><bytes>0</bytes>'
            b'<content_type>text/plain</content_type>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</object>'
            b'<subdir name="foo/"><name>foo/</name></subdir>'
            b'</container>')
        self.assertEqual(resp.headers['Content-Type'],
                         'application/xml; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))

        req = Request.blank(path + '?format=xml',
                            headers={'X-Backend-Allow-Reserved-Names': 'true'})
        resp = req.get_response(self.app)
        self.assertEqual(
            resp.body, b'<?xml version="1.0" encoding="UTF-8"?>\n'
            b'<container name="c\xf0\x9f\x8c\xb4">'
            b'<object><name>bar</name><hash>etag</hash><bytes>0</bytes>'
            b'<content_type>text/plain</content_type>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</object>'
            b'<object><name>%s</name>'
            b'<hash>etag</hash><bytes>0</bytes>'
            b'<content_type>text/plain</content_type>'
            b'<last_modified>1970-01-01T00:00:00.000000</last_modified>'
            b'</object>'
            b'<subdir name="foo/"><name>foo/</name></subdir>'
            b'<subdir name="%s"><name>%s</name></subdir>'
            b'</container>' % (
                get_reserved_name('bar', 'extra data').encode('ascii'),
                get_reserved_name('foo/').encode('ascii'),
                get_reserved_name('foo/').encode('ascii'),
            ))
        self.assertEqual(resp.headers['Content-Type'],
                         'application/xml; charset=utf-8')
        self.assertEqual(self.fake_swift.calls[-1],
                         ('GET', path + '?format=json'))