Ejemplo n.º 1
0
    def test_swift3_with_only_s3_token_v3(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.s3_token = S3Token(self.keystone_auth,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_PROJECT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_PROJECT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            mock_resp = requests.Response()
            mock_resp._content = json.dumps(GOOD_RESPONSE_V3)
            mock_resp.status_code = 200
            mock_req.return_value = mock_resp

            status, headers, body = self.call_swift3(req)
            self.assertEqual(body, '')
            self.assertEqual(1, mock_req.call_count)
Ejemplo n.º 2
0
    def test_swift3_with_s3_token_no_pass_token_to_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(self.keystone_auth,
                                       {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(self.auth_token,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                no_token_id_good_resp = copy.deepcopy(GOOD_RESPONSE_V2)
                # delete token id
                del no_token_id_good_resp['access']['token']['id']
                mock_resp._content = json.dumps(no_token_id_good_resp)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE_V2)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                # No token provided from keystone result in 401 Unauthorized
                # at `swift.common.middleware.keystoneauth` because auth_token
                # will remove all auth headers including 'X-Identity-Status'[1]
                # and then, set X-Identity-Status: Invalid at [2]
                #
                # 1: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L620
                # 2: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L627-L629

                self.assertEqual('403 Forbidden', status)
                self.assertEqual(1, mock_req.call_count)
                # if no token provided from keystone, we can skip the call to
                # fetch the token
                self.assertEqual(0, mock_fetch.call_count)
Ejemplo n.º 3
0
    def test_swift3_with_only_s3_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.s3_token = S3Token(
            self.keystone_auth, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            mock_resp = requests.Response()
            mock_resp._content = json.dumps(GOOD_RESPONSE)
            mock_resp.status_code = 201
            mock_req.return_value = mock_resp

            status, headers, body = self.call_swift3(req)
            self.assertEqual(body, '')
            self.assertEqual(1, mock_req.call_count)
Ejemplo n.º 4
0
    def setUp(self):
        self.app = FakeSwift()
        self.swift3 = swift3.filter_factory(self.conf)(self.app)

        # container '.' will be accessed to resolve a tenant name
        self.app.register('HEAD', '/v1/AUTH_test/.', swob.HTTPNotFound,
                          {}, None)
        self.app.register('HEAD', '/v1/AUTH_X/.', swob.HTTPNotFound,
                          {}, None)
        self.app.register('PUT', '/v1/.swift3/acl',
                          swob.HTTPAccepted, {}, None)
        self.app.register('PUT', '/v1/.swift3/lifecycle_rules',
                          swob.HTTPAccepted, {}, None)
Ejemplo n.º 5
0
    def test_swift3_with_s3_token_and_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(self.keystone_auth,
                                       {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(self.auth_token,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                mock_resp._content = json.dumps(GOOD_RESPONSE_V2)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE_V2)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                self.assertEqual(body, '')
                self.assertEqual(1, mock_req.call_count)
                # With X-Auth-Token, auth_token will call _do_fetch_token to
                # connect to keystone in auth_token, again
                self.assertEqual(1, mock_fetch.call_count)
Ejemplo n.º 6
0
    def test_swift3_with_s3_token_no_pass_token_to_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(
            self.keystone_auth, {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(
            self.auth_token, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                no_token_id_good_resp = copy.deepcopy(GOOD_RESPONSE)
                # delete token id
                del no_token_id_good_resp['access']['token']['id']
                mock_resp._content = json.dumps(no_token_id_good_resp)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                # No token provided from keystone result in 401 Unauthorized
                # at `swift.common.middleware.keystoneauth` because auth_token
                # will remove all auth headers including 'X-Identity-Status'[1]
                # and then, set X-Identity-Status: Invalid at [2]
                #
                # 1: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L620
                # 2: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L627-L629

                self.assertEqual('403 Forbidden', status)
                self.assertEqual(1, mock_req.call_count)
                # if no token provided from keystone, we can skip the call to
                # fetch the token
                self.assertEqual(0, mock_fetch.call_count)
Ejemplo n.º 7
0
    def test_fake_swift_sysmeta(self):
        swift = FakeSwift()
        orig_headers = HeaderKeyDict()
        orig_headers.update({sysmeta_header('container', 'acl'): 'test',
                             'x-container-meta-foo': 'bar'})

        swift.register(self.method, self.path, MagicMock(), orig_headers, None)

        self._check_headers(swift, self.method, self.path, orig_headers)

        new_headers = orig_headers.copy()
        del new_headers[sysmeta_header('container', 'acl').title()]
        swift.register(self.method, self.path, MagicMock(), new_headers, None)

        self._check_headers(swift, self.method, self.path, orig_headers)
Ejemplo n.º 8
0
    def test_swift3_with_s3_token_and_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(
            self.keystone_auth, {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(
            self.auth_token, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                mock_resp._content = json.dumps(GOOD_RESPONSE)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                self.assertEqual(body, '')
                self.assertEqual(1, mock_req.call_count)
                # With X-Auth-Token, auth_token will call _do_fetch_token to
                # connect to keystone in auth_token, again
                self.assertEqual(1, mock_fetch.call_count)
Ejemplo n.º 9
0
class TestSwift3Middleware(Swift3TestCase):
    def setUp(self):
        super(TestSwift3Middleware, self).setUp()

        self.swift.register('GET', '/something', swob.HTTPOk, {}, 'FAKE APP')

    def test_non_s3_request_passthrough(self):
        req = Request.blank('/something')
        status, headers, body = self.call_swift3(req)
        self.assertEqual(body, 'FAKE APP')

    def test_bad_format_authorization(self):
        req = Request.blank('/something',
                            headers={
                                'Authorization': 'hoge',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_bad_method(self):
        req = Request.blank('/',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS test:tester:hmac',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'MethodNotAllowed')

    def test_bad_method_but_method_exists_in_controller(self):
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': '_delete_segments_bucket'},
            headers={
                'Authorization': 'AWS test:tester:hmac',
                'Date': self.get_date_header()
            })
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'MethodNotAllowed')

    def test_path_info_encode(self):
        bucket_name = 'b%75cket'
        object_name = 'ob%6aect:1'
        self.swift.register('GET', '/v1/AUTH_test/bucket/object:1',
                            swob.HTTPOk, {}, None)
        req = Request.blank('/%s/%s' % (bucket_name, object_name),
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={
                                'Authorization': 'AWS test:tester:hmac',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        raw_path_info = "/%s/%s" % (bucket_name, object_name)
        path_info = req.environ['PATH_INFO']
        self.assertEqual(path_info, unquote(raw_path_info))
        self.assertEqual(req.path, quote(path_info))

    def test_canonical_string_v2(self):
        """
        The hashes here were generated by running the same requests against
        boto.utils.canonical_string
        """
        def canonical_string(path, headers):
            if '?' in path:
                path, query_string = path.split('?', 1)
            else:
                query_string = ''
            env = {
                'REQUEST_METHOD': 'GET',
                'PATH_INFO': path,
                'QUERY_STRING': query_string,
                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
            }
            for header, value in headers.items():
                header = 'HTTP_' + header.replace('-', '_').upper()
                if header in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
                    header = header[5:]
                env[header] = value

            with patch('swift3.request.Request._validate_headers'):
                req = S3Request(env)
            return req.environ['swift3.auth_details']['string_to_sign']

        def verify(hash, path, headers):
            s = canonical_string(path, headers)
            self.assertEqual(hash, hashlib.md5(s).hexdigest())

        verify(
            '6dd08c75e42190a1ce9468d1fd2eb787', '/bucket/object', {
                'Content-Type': 'text/plain',
                'X-Amz-Something': 'test',
                'Date': 'whatever'
            })

        verify('c8447135da232ae7517328f3429df481', '/bucket/object', {
            'Content-Type': 'text/plain',
            'X-Amz-Something': 'test'
        })

        verify('bf49304103a4de5c325dce6384f2a4a2', '/bucket/object',
               {'content-type': 'text/plain'})

        verify('be01bd15d8d47f9fe5e2d9248cc6f180', '/bucket/object', {})

        verify('e9ec7dca45eef3e2c7276af23135e896', '/bucket/object',
               {'Content-MD5': 'somestuff'})

        verify('a822deb31213ad09af37b5a7fe59e55e', '/bucket/object?acl', {})

        verify(
            'cce5dd1016595cb706c93f28d3eaa18f', '/bucket/object', {
                'Content-Type': 'text/plain',
                'X-Amz-A': 'test',
                'X-Amz-Z': 'whatever',
                'X-Amz-B': 'lalala',
                'X-Amz-Y': 'lalalalalalala'
            })

        verify('7506d97002c7d2de922cc0ec34af8846', '/bucket/object', {
            'Content-Type': None,
            'X-Amz-Something': 'test'
        })

        verify(
            '28f76d6162444a193b612cd6cb20e0be', '/bucket/object', {
                'Content-Type': None,
                'X-Amz-Date': 'Mon, 11 Jul 2011 10:52:57 +0000',
                'Date': 'Tue, 12 Jul 2011 10:52:57 +0000'
            })

        verify('ed6971e3eca5af4ee361f05d7c272e49', '/bucket/object', {
            'Content-Type': None,
            'Date': 'Tue, 12 Jul 2011 10:52:57 +0000'
        })

        verify('41ecd87e7329c33fea27826c1c9a6f91', '/bucket/object?cors', {})

        verify('d91b062f375d8fab407d6dab41fd154e', '/bucket/object?tagging',
               {})

        verify('ebab878a96814b30eb178e27efb3973f', '/bucket/object?restore',
               {})

        verify(
            'f6bf1b2d92b054350d3679d28739fc69', '/bucket/object?'
            'response-cache-control&response-content-disposition&'
            'response-content-encoding&response-content-language&'
            'response-content-type&response-expires', {})

        str1 = canonical_string('/',
                                headers={
                                    'Content-Type': None,
                                    'X-Amz-Something': 'test'
                                })
        str2 = canonical_string('/',
                                headers={
                                    'Content-Type': '',
                                    'X-Amz-Something': 'test'
                                })
        str3 = canonical_string('/', headers={'X-Amz-Something': 'test'})

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

        # Note that boto does not do proper stripping (as of 2.42.0).
        # These were determined by examining the StringToSignBytes element of
        # resulting SignatureDoesNotMatch errors from AWS.
        str1 = canonical_string('/', {
            'Content-Type': 'text/plain',
            'Content-MD5': '##'
        })
        str2 = canonical_string('/', {
            'Content-Type': '\x01\x02text/plain',
            'Content-MD5': '\x1f ##'
        })
        str3 = canonical_string('/', {
            'Content-Type': 'text/plain \x10',
            'Content-MD5': '##\x18'
        })

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

    def test_signed_urls_expired(self):
        expire = '1000000000'
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls(self):
        # Set expire to last 32b timestamp value
        # This number can't be higher, because it breaks tests on 32b systems
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        utc_date = datetime.utcnow()
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester&Timestamp=%s' %
                            (expire, utc_date.isoformat().rsplit('.')[0]),
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual(headers['Authorization'], 'AWS test:tester:X')

    def test_signed_urls_no_timestamp(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        # Curious! But actually S3 doesn't verify any x-amz-date/date headers
        # for signed_url access and it also doesn't check timestamp
        self.assertEqual(status.split()[0], '200')
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual(headers['Authorization'], 'AWS test:tester:X')

    def test_signed_urls_invalid_expire(self):
        expire = 'invalid'
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_no_sign(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_no_access(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Expires=%s&'
                            'AWSAccessKeyId=' % expire,
                            environ={'REQUEST_METHOD': 'GET'})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4(self):
        req = Request.blank(
            '/bucket/object'
            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
            '&X-Amz-Credential=test:tester/20T20Z/US/s3/aws4_request'
            '&X-Amz-Date=%s'
            '&X-Amz-Expires=1000'
            '&X-Amz-SignedHeaders=host'
            '&X-Amz-Signature=X' % self.get_v4_amz_date_header(),
            headers={'Date': self.get_date_header()},
            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200', body)
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual('AWS test:tester:X', headers['Authorization'])
            self.assertIn('X-Auth-Token', headers)

    def test_signed_urls_v4_missing_x_amz_date(self):
        req = Request.blank(
            '/bucket/object'
            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
            '&X-Amz-Credential=test:tester/20T20Z/US/s3/aws4_request'
            '&X-Amz-Expires=1000'
            '&X-Amz-SignedHeaders=host'
            '&X-Amz-Signature=X',
            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4_invalid_algorithm(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=FAKE'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidArgument')

    def test_signed_urls_v4_missing_signed_headers(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body),
                         'AuthorizationHeaderMalformed')

    def test_signed_urls_v4_invalid_credentials(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4_missing_signature(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_bucket_virtual_hosted_style(self):
        req = Request.blank('/',
                            environ={
                                'HTTP_HOST': 'bucket.localhost:80',
                                'REQUEST_METHOD': 'HEAD',
                                'HTTP_AUTHORIZATION': 'AWS test:tester:hmac'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')

    def test_object_virtual_hosted_style(self):
        req = Request.blank('/object',
                            environ={
                                'HTTP_HOST': 'bucket.localhost:80',
                                'REQUEST_METHOD': 'HEAD',
                                'HTTP_AUTHORIZATION': 'AWS test:tester:hmac'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')

    def test_token_generation(self):
        self.swift.register(
            'HEAD', '/v1/AUTH_test/bucket+segments/'
            'object/123456789abcdef', swob.HTTPOk, {}, None)
        self.swift.register(
            'PUT', '/v1/AUTH_test/bucket+segments/'
            'object/123456789abcdef/1', swob.HTTPCreated, {}, None)
        req = Request.blank(
            '/bucket/object?uploadId=123456789abcdef'
            '&partNumber=1',
            environ={'REQUEST_METHOD': 'PUT'})
        req.headers['Authorization'] = 'AWS test:tester:hmac'
        date_header = self.get_date_header()
        req.headers['Date'] = date_header
        with mock.patch('swift3.request.Request.check_signature') as mock_cs:
            status, headers, body = self.call_swift3(req)
        _, _, headers = self.swift.calls_with_headers[-1]
        self.assertEqual(
            req.environ['swift3.auth_details'], {
                'access_key':
                'test:tester',
                'signature':
                'hmac',
                'string_to_sign':
                '\n'.join([
                    'PUT', '', '', date_header,
                    '/bucket/object?partNumber=1&uploadId=123456789abcdef'
                ]),
                'check_signature':
                mock_cs
            })

    def test_invalid_uri(self):
        req = Request.blank('/bucket/invalid\xffname',
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={
                                'Authorization': 'AWS test:tester:hmac',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidURI')

    def test_object_create_bad_md5_unreadable(self):
        req = Request.blank('/bucket/object',
                            environ={
                                'REQUEST_METHOD': 'PUT',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                'HTTP_CONTENT_MD5': '#'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_object_create_bad_md5_too_short(self):
        too_short_digest = hashlib.md5('hey').hexdigest()[:-1]
        md5_str = too_short_digest.encode('base64').strip()
        req = Request.blank('/bucket/object',
                            environ={
                                'REQUEST_METHOD': 'PUT',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                'HTTP_CONTENT_MD5': md5_str
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_object_create_bad_md5_too_long(self):
        too_long_digest = hashlib.md5('hey').hexdigest() + 'suffix'
        md5_str = too_long_digest.encode('base64').strip()
        req = Request.blank('/bucket/object',
                            environ={
                                'REQUEST_METHOD': 'PUT',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                'HTTP_CONTENT_MD5': md5_str
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_invalid_metadata_directive(self):
        req = Request.blank('/',
                            environ={
                                'REQUEST_METHOD': 'GET',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                'HTTP_X_AMZ_METADATA_DIRECTIVE': 'invalid'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidArgument')

    def test_invalid_storage_class(self):
        req = Request.blank('/',
                            environ={
                                'REQUEST_METHOD': 'GET',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                'HTTP_X_AMZ_STORAGE_CLASS': 'INVALID'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidStorageClass')

    def _test_unsupported_header(self, header):
        req = Request.blank('/error',
                            environ={
                                'REQUEST_METHOD': 'GET',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z'
                            },
                            headers={
                                'x-amz-' + header: 'value',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'NotImplemented')

    def test_mfa(self):
        self._test_unsupported_header('mfa')

    def test_server_side_encryption(self):
        self._test_unsupported_header('server-side-encryption')

    def test_website_redirect_location(self):
        self._test_unsupported_header('website-redirect-location')

    def _test_unsupported_resource(self, resource):
        req = Request.blank('/error?' + resource,
                            environ={
                                'REQUEST_METHOD': 'GET',
                                'HTTP_AUTHORIZATION': 'AWS X:Y:Z'
                            },
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'NotImplemented')

    def test_notification(self):
        self._test_unsupported_resource('notification')

    def test_policy(self):
        self._test_unsupported_resource('policy')

    def test_request_payment(self):
        self._test_unsupported_resource('requestPayment')

    def test_torrent(self):
        self._test_unsupported_resource('torrent')

    def test_website(self):
        self._test_unsupported_resource('website')

    def test_cors(self):
        self._test_unsupported_resource('cors')

    def test_tagging(self):
        self._test_unsupported_resource('tagging')

    def test_restore(self):
        self._test_unsupported_resource('restore')

    def test_unsupported_method(self):
        req = Request.blank('/bucket?acl',
                            environ={'REQUEST_METHOD': 'POST'},
                            headers={
                                'Authorization': 'AWS test:tester:hmac',
                                'Date': self.get_date_header()
                            })
        status, headers, body = self.call_swift3(req)
        elem = fromstring(body, 'Error')
        self.assertEqual(elem.find('./Code').text, 'MethodNotAllowed')
        self.assertEqual(elem.find('./Method').text, 'POST')
        self.assertEqual(elem.find('./ResourceType').text, 'ACL')

    def test_registered_defaults(self):
        filter_factory(CONF)
        swift_info = utils.get_swift_info()
        self.assertTrue('swift3' in swift_info)
        self.assertEqual(swift_info['swift3'].get('version'),
                         swift3.__version__)
        self.assertEqual(swift_info['swift3'].get('max_bucket_listing'),
                         CONF.max_bucket_listing)
        self.assertEqual(swift_info['swift3'].get('max_parts_listing'),
                         CONF.max_parts_listing)
        self.assertEqual(swift_info['swift3'].get('max_upload_part_num'),
                         CONF.max_upload_part_num)
        self.assertEqual(swift_info['swift3'].get('max_multi_delete_objects'),
                         CONF.max_multi_delete_objects)

    def test_check_pipeline(self):
        with nested(patch("swift3.middleware.CONF"),
                    patch("swift3.middleware.PipelineWrapper"),
                    patch("swift3.middleware.loadcontext")) as \
                (conf, pipeline, _):
            conf.auth_pipeline_check = True
            conf.__file__ = ''

            pipeline.return_value = 'swift3 tempauth proxy-server'
            self.swift3.check_pipeline(conf)

            # This *should* still work; authtoken will remove our auth details,
            # but the X-Auth-Token we drop in will remain
            # if we found one in the response
            pipeline.return_value = 'swift3 s3token authtoken keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            # This should work now; no more doubled-up requests to keystone!
            pipeline.return_value = 'swift3 s3token keystoneauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 swauth proxy-server'
            self.swift3.check_pipeline(conf)

            # Note that authtoken would need to have delay_auth_decision=True
            pipeline.return_value = 'swift3 authtoken s3token keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 proxy-server'
            with self.assertRaises(ValueError) as cm:
                self.swift3.check_pipeline(conf)
            self.assertIn('expected auth between swift3 and proxy-server',
                          cm.exception.message)

            pipeline.return_value = 'proxy-server'
            with self.assertRaises(ValueError) as cm:
                self.swift3.check_pipeline(conf)
            self.assertIn("missing filters ['swift3']", cm.exception.message)

    def test_swift3_initialization_with_disabled_pipeline_check(self):
        with nested(patch("swift3.middleware.CONF"),
                    patch("swift3.middleware.PipelineWrapper"),
                    patch("swift3.middleware.loadcontext")) as \
                (conf, pipeline, _):
            # Disable pipeline check
            conf.auth_pipeline_check = False
            conf.__file__ = ''

            pipeline.return_value = 'swift3 tempauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 s3token authtoken keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 swauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 authtoken s3token keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'proxy-server'
            with self.assertRaises(ValueError):
                self.swift3.check_pipeline(conf)

    def test_signature_v4(self):
        environ = {'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
            'AWS4-HMAC-SHA256 '
            'Credential=test:tester/20130524/US/s3/aws4_request, '
            'SignedHeaders=host;x-amz-date,'
            'Signature=X',
            'X-Amz-Date':
            self.get_v4_amz_date_header(),
            'X-Amz-Content-SHA256':
            '0123456789'
        }
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200', body)
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual('AWS test:tester:X', headers['Authorization'])
            self.assertIn('X-Auth-Token', headers)

    def test_signature_v4_no_date(self):
        environ = {'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
            'AWS4-HMAC-SHA256 '
            'Credential=test:tester/20130524/US/s3/aws4_request, '
            'SignedHeaders=host;range;x-amz-date,'
            'Signature=X',
            'X-Amz-Content-SHA256':
            '0123456789'
        }
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '403')
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signature_v4_no_payload(self):
        environ = {'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
            'AWS4-HMAC-SHA256 '
            'Credential=test:tester/20130524/US/s3/aws4_request, '
            'SignedHeaders=host;x-amz-date,'
            'Signature=X',
            'X-Amz-Date':
            self.get_v4_amz_date_header()
        }
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '400')
        self.assertEqual(self._get_error_code(body), 'InvalidRequest')
        self.assertEqual(
            self._get_error_message(body),
            'Missing required header for this request: x-amz-content-sha256')

    def test_signature_v4_bad_authorization_string(self):
        def test(auth_str, error, msg):
            environ = {'REQUEST_METHOD': 'GET'}
            headers = {
                'Authorization': auth_str,
                'X-Amz-Date': self.get_v4_amz_date_header(),
                'X-Amz-Content-SHA256': '0123456789'
            }
            req = Request.blank('/bucket/object',
                                environ=environ,
                                headers=headers)
            req.content_type = 'text/plain'
            status, headers, body = self.call_swift3(req)
            self.assertEqual(self._get_error_code(body), error)
            self.assertEqual(self._get_error_message(body), msg)

        auth_str = ('AWS4-HMAC-SHA256 '
                    'SignedHeaders=host;x-amz-date,'
                    'Signature=X')
        test(auth_str, 'AccessDenied', 'Access Denied.')

        auth_str = ('AWS4-HMAC-SHA256 '
                    'Credential=test:tester/20130524/US/s3/aws4_request, '
                    'Signature=X')
        test(
            auth_str, 'AuthorizationHeaderMalformed',
            'The authorization header is malformed; the authorization '
            'header requires three components: Credential, SignedHeaders, '
            'and Signature.')

        auth_str = ('AWS4-HMAC-SHA256 '
                    'Credential=test:tester/20130524/US/s3/aws4_request, '
                    'SignedHeaders=host;x-amz-date')
        test(auth_str, 'AccessDenied', 'Access Denied.')

    def test_canonical_string_v4(self):
        def _get_req(path, environ):
            if '?' in path:
                path, query_string = path.split('?', 1)
            else:
                query_string = ''

            env = {
                'REQUEST_METHOD':
                'GET',
                'PATH_INFO':
                path,
                'QUERY_STRING':
                query_string,
                'HTTP_DATE':
                'Mon, 09 Sep 2011 23:36:00 GMT',
                'HTTP_X_AMZ_CONTENT_SHA256':
                'e3b0c44298fc1c149afbf4c8996fb924'
                '27ae41e4649b934ca495991b7852b855',
                'HTTP_AUTHORIZATION':
                'AWS4-HMAC-SHA256 '
                'Credential=X:Y/dt/reg/host/blah, '
                'SignedHeaders=content-md5;content-type;date, '
                'Signature=x',
            }
            env.update(environ)
            with patch('swift3.request.Request._validate_headers'):
                req = SigV4Request(env)
            return req

        def canonical_string(path, environ):
            return _get_req(path, environ)._canonical_request()

        def verify(hash_val, path, environ):
            # See http://docs.aws.amazon.com/general/latest/gr
            # /signature-v4-test-suite.html for where location, service, and
            # signing key came from
            with patch.object(CONF, 'location', 'us-east-1'), \
                    patch.object(swift3.request, 'SERVICE', 'host'):
                req = _get_req(path, environ)
                hash_in_sts = req._string_to_sign().split('\n')[3]
                self.assertEqual(hash_val, hash_in_sts)
                self.assertTrue(
                    req.check_signature(
                        'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'))

        # all next data got from aws4_testsuite from Amazon
        # http://docs.aws.amazon.com/general/latest/gr/samples
        # /aws4_testsuite.zip
        # Each *expected* hash value is the 4th line in <test-name>.sts in the
        # test suite.

        # get-vanilla
        env = {
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host, '
             'Signature=b27ccfbfa7df52a200ff74193ca6e32d'
             '4b48b8856fab7ebf1c595d0670a7e470'),
            'HTTP_HOST':
            'host.foo.com'
        }
        verify(
            '366b91fb121d72a00f46bbe8d395f53a'
            '102b06dfb7e79636515208ed3fa606b1', '/', env)

        # get-header-value-trim
        env = {
            'REQUEST_METHOD':
            'POST',
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host;p, '
             'Signature=debf546796015d6f6ded8626f5ce9859'
             '7c33b47b9164cf6b17b4642036fcb592'),
            'HTTP_HOST':
            'host.foo.com',
            'HTTP_P':
            'phfft'
        }
        verify(
            'dddd1902add08da1ac94782b05f9278c'
            '08dc7468db178a84f8950d93b30b1f35', '/', env)

        # get-utf8 (not exact)
        env = {
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host, '
             'Signature=8d6634c189aa8c75c2e51e106b6b5121'
             'bed103fdb351f7d7d4381c738823af74'),
            'HTTP_HOST':
            'host.foo.com',
            'RAW_PATH_INFO':
            '/%E1%88%B4'
        }

        # This might look weird because actually S3 doesn't care about utf-8
        # encoded multi-byte bucket name from bucket-in-host name constraint.
        # However, aws4_testsuite has only a sample hash with utf-8 *bucket*
        # name to make sure the correctness (probably it can be used in other
        # aws resource except s3) so, to test also utf-8, skip the bucket name
        # validation in the following test.

        # NOTE: eventlet's PATH_INFO is unquoted
        with patch('swift3.request.validate_bucket_name'):
            verify(
                '27ba31df5dbc6e063d8f87d62eb07143'
                'f7f271c5330a917840586ac1c85b6f6b', unquote('/%E1%88%B4'), env)

        # get-vanilla-query-order-key
        env = {
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host, '
             'Signature=0dc122f3b28b831ab48ba65cb47300de'
             '53fbe91b577fe113edac383730254a3b'),
            'HTTP_HOST':
            'host.foo.com'
        }
        verify(
            '2f23d14fe13caebf6dfda346285c6d9c'
            '14f49eaca8f5ec55c627dd7404f7a727', '/?a=foo&b=foo', env)

        # post-header-value-case
        env = {
            'REQUEST_METHOD':
            'POST',
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host;zoo, '
             'Signature=273313af9d0c265c531e11db70bbd653'
             'f3ba074c1009239e8559d3987039cad7'),
            'HTTP_HOST':
            'host.foo.com',
            'HTTP_ZOO':
            'ZOOBAR'
        }
        verify(
            '3aae6d8274b8c03e2cc96fc7d6bda4b9'
            'bd7a0a184309344470b2c96953e124aa', '/', env)

        # post-x-www-form-urlencoded-parameters
        env = {
            'REQUEST_METHOD':
            'POST',
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host;content-type, '
             'Signature=b105eb10c6d318d2294de9d49dd8b031'
             'b55e3c3fe139f2e637da70511e9e7b71'),
            'HTTP_HOST':
            'host.foo.com',
            'HTTP_X_AMZ_CONTENT_SHA256':
            '3ba8907e7a252327488df390ed517c45'
            'b96dead033600219bdca7107d1d3f88a',
            'CONTENT_TYPE':
            'application/x-www-form-urlencoded; charset=utf8'
        }
        verify(
            'c4115f9e54b5cecf192b1eaa23b8e88e'
            'd8dc5391bd4fde7b3fff3d9c9fe0af1f', '/', env)

        # post-x-www-form-urlencoded
        env = {
            'REQUEST_METHOD':
            'POST',
            'HTTP_AUTHORIZATION':
            ('AWS4-HMAC-SHA256 '
             'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
             'SignedHeaders=date;host;content-type, '
             'Signature=5a15b22cf462f047318703b92e6f4f38'
             '884e4a7ab7b1d6426ca46a8bd1c26cbc'),
            'HTTP_HOST':
            'host.foo.com',
            'HTTP_X_AMZ_CONTENT_SHA256':
            '3ba8907e7a252327488df390ed517c45'
            'b96dead033600219bdca7107d1d3f88a',
            'CONTENT_TYPE':
            'application/x-www-form-urlencoded'
        }
        verify(
            '4c5c6e4b52fb5fb947a8733982a8a5a6'
            '1b14f04345cbfe6e739236c76dd48f74', '/', env)

        # Note that boto does not do proper stripping (as of 2.42.0).
        # These were determined by examining the StringToSignBytes element of
        # resulting SignatureDoesNotMatch errors from AWS.
        str1 = canonical_string('/', {
            'CONTENT_TYPE': 'text/plain',
            'HTTP_CONTENT_MD5': '##'
        })
        str2 = canonical_string('/', {
            'CONTENT_TYPE': '\x01\x02text/plain',
            'HTTP_CONTENT_MD5': '\x1f ##'
        })
        str3 = canonical_string('/', {
            'CONTENT_TYPE': 'text/plain \x10',
            'HTTP_CONTENT_MD5': '##\x18'
        })

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

    def test_mixture_param_v4(self):
        # now we have an Authorization header
        headers = {
            'Authorization':
            'AWS4-HMAC-SHA256 '
            'Credential=test/20130524/US/s3/aws4_request_A, '
            'SignedHeaders=hostA;rangeA;x-amz-dateA,'
            'Signature=X',
            'X-Amz-Date':
            self.get_v4_amz_date_header(),
            'X-Amz-Content-SHA256':
            '0123456789'
        }

        # and then, different auth info (Credential, SignedHeaders, Signature)
        # in query
        req = Request.blank(
            '/bucket/object'
            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_requestB'
            '&X-Amz-SignedHeaders=hostB'
            '&X-Amz-Signature=Y',
            environ={'REQUEST_METHOD': 'GET'},
            headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        # FIXME: should this failed as 400 or pass via query auth?
        # for now, 403 forbidden for safety
        self.assertEqual(status.split()[0], '403', body)

        # But if we are missing Signature in query param
        req = Request.blank(
            '/bucket/object'
            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_requestB'
            '&X-Amz-SignedHeaders=hostB',
            environ={'REQUEST_METHOD': 'GET'},
            headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '403', body)

    def test_swift3_with_only_s3_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.s3_token = S3Token(self.keystone_auth,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            mock_resp = requests.Response()
            mock_resp._content = json.dumps(GOOD_RESPONSE_V2)
            mock_resp.status_code = 201
            mock_req.return_value = mock_resp

            status, headers, body = self.call_swift3(req)
            self.assertEqual(body, '')
            self.assertEqual(1, mock_req.call_count)

    def test_swift3_with_only_s3_token_v3(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.s3_token = S3Token(self.keystone_auth,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_PROJECT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_PROJECT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            mock_resp = requests.Response()
            mock_resp._content = json.dumps(GOOD_RESPONSE_V3)
            mock_resp.status_code = 200
            mock_req.return_value = mock_resp

            status, headers, body = self.call_swift3(req)
            self.assertEqual(body, '')
            self.assertEqual(1, mock_req.call_count)

    def test_swift3_with_s3_token_and_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(self.keystone_auth,
                                       {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(self.auth_token,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                mock_resp._content = json.dumps(GOOD_RESPONSE_V2)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE_V2)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                self.assertEqual(body, '')
                self.assertEqual(1, mock_req.call_count)
                # With X-Auth-Token, auth_token will call _do_fetch_token to
                # connect to keystone in auth_token, again
                self.assertEqual(1, mock_fetch.call_count)

    def test_swift3_with_s3_token_no_pass_token_to_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(self.swift,
                                          {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(self.keystone_auth,
                                       {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(self.auth_token,
                                {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank('/bucket',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={
                                'Authorization': 'AWS access:signature',
                                'Date': self.get_date_header()
                            })
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID', swob.HTTPOk, {},
                            None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                no_token_id_good_resp = copy.deepcopy(GOOD_RESPONSE_V2)
                # delete token id
                del no_token_id_good_resp['access']['token']['id']
                mock_resp._content = json.dumps(no_token_id_good_resp)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE_V2)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                # No token provided from keystone result in 401 Unauthorized
                # at `swift.common.middleware.keystoneauth` because auth_token
                # will remove all auth headers including 'X-Identity-Status'[1]
                # and then, set X-Identity-Status: Invalid at [2]
                #
                # 1: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L620
                # 2: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L627-L629

                self.assertEqual('403 Forbidden', status)
                self.assertEqual(1, mock_req.call_count)
                # if no token provided from keystone, we can skip the call to
                # fetch the token
                self.assertEqual(0, mock_fetch.call_count)
Ejemplo n.º 10
0
 def __init__(self):
     self.swift = FakeSwift()
Ejemplo n.º 11
0
class TestSwift3Middleware(Swift3TestCase):
    def setUp(self):
        super(TestSwift3Middleware, self).setUp()

        self.swift.register('GET', '/something', swob.HTTPOk, {}, 'FAKE APP')

    def test_non_s3_request_passthrough(self):
        req = Request.blank('/something')
        status, headers, body = self.call_swift3(req)
        self.assertEqual(body, 'FAKE APP')

    def test_bad_format_authorization(self):
        req = Request.blank('/something',
                            headers={'Authorization': 'hoge',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_bad_method(self):
        req = Request.blank('/',
                            environ={'REQUEST_METHOD': 'PUT'},
                            headers={'Authorization': 'AWS test:tester:hmac',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'MethodNotAllowed')

    def test_bad_method_but_method_exists_in_controller(self):
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': '_delete_segments_bucket'},
            headers={'Authorization': 'AWS test:tester:hmac',
                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'MethodNotAllowed')

    def test_path_info_encode(self):
        bucket_name = 'b%75cket'
        object_name = 'ob%6aect:1'
        self.swift.register('GET', '/v1/AUTH_test/bucket/object:1',
                            swob.HTTPOk, {}, None)
        req = Request.blank('/%s/%s' % (bucket_name, object_name),
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Authorization': 'AWS test:tester:hmac',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        raw_path_info = "/%s/%s" % (bucket_name, object_name)
        path_info = req.environ['PATH_INFO']
        self.assertEqual(path_info, unquote(raw_path_info))
        self.assertEqual(req.path, quote(path_info))

    def test_canonical_string_v2(self):
        """
        The hashes here were generated by running the same requests against
        boto.utils.canonical_string
        """
        def canonical_string(path, headers):
            if '?' in path:
                path, query_string = path.split('?', 1)
            else:
                query_string = ''

            with patch('swift3.request.Request._validate_headers'):
                req = S3Request({
                    'REQUEST_METHOD': 'GET',
                    'PATH_INFO': path,
                    'QUERY_STRING': query_string,
                    'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                })
            req.headers.update(headers)
            return req._string_to_sign()

        def verify(hash, path, headers):
            s = canonical_string(path, headers)
            self.assertEqual(hash, hashlib.md5(s).hexdigest())

        verify('6dd08c75e42190a1ce9468d1fd2eb787', '/bucket/object',
               {'Content-Type': 'text/plain', 'X-Amz-Something': 'test',
                'Date': 'whatever'})

        verify('c8447135da232ae7517328f3429df481', '/bucket/object',
               {'Content-Type': 'text/plain', 'X-Amz-Something': 'test'})

        verify('bf49304103a4de5c325dce6384f2a4a2', '/bucket/object',
               {'content-type': 'text/plain'})

        verify('be01bd15d8d47f9fe5e2d9248cc6f180', '/bucket/object', {})

        verify('e9ec7dca45eef3e2c7276af23135e896', '/bucket/object',
               {'Content-MD5': 'somestuff'})

        verify('a822deb31213ad09af37b5a7fe59e55e', '/bucket/object?acl', {})

        verify('cce5dd1016595cb706c93f28d3eaa18f', '/bucket/object',
               {'Content-Type': 'text/plain', 'X-Amz-A': 'test',
                'X-Amz-Z': 'whatever', 'X-Amz-B': 'lalala',
                'X-Amz-Y': 'lalalalalalala'})

        verify('7506d97002c7d2de922cc0ec34af8846', '/bucket/object',
               {'Content-Type': None, 'X-Amz-Something': 'test'})

        verify('28f76d6162444a193b612cd6cb20e0be', '/bucket/object',
               {'Content-Type': None,
                'X-Amz-Date': 'Mon, 11 Jul 2011 10:52:57 +0000',
                'Date': 'Tue, 12 Jul 2011 10:52:57 +0000'})

        verify('ed6971e3eca5af4ee361f05d7c272e49', '/bucket/object',
               {'Content-Type': None,
                'Date': 'Tue, 12 Jul 2011 10:52:57 +0000'})

        verify('41ecd87e7329c33fea27826c1c9a6f91', '/bucket/object?cors', {})

        verify('d91b062f375d8fab407d6dab41fd154e', '/bucket/object?tagging',
               {})

        verify('ebab878a96814b30eb178e27efb3973f', '/bucket/object?restore',
               {})

        verify('f6bf1b2d92b054350d3679d28739fc69', '/bucket/object?'
               'response-cache-control&response-content-disposition&'
               'response-content-encoding&response-content-language&'
               'response-content-type&response-expires', {})

        str1 = canonical_string('/', headers={'Content-Type': None,
                                              'X-Amz-Something': 'test'})
        str2 = canonical_string('/', headers={'Content-Type': '',
                                              'X-Amz-Something': 'test'})
        str3 = canonical_string('/', headers={'X-Amz-Something': 'test'})

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

        # Note that boto does not do proper stripping (as of 2.42.0).
        # These were determined by examining the StringToSignBytes element of
        # resulting SignatureDoesNotMatch errors from AWS.
        str1 = canonical_string('/', {'Content-Type': 'text/plain',
                                      'Content-MD5': '##'})
        str2 = canonical_string('/', {'Content-Type': '\x01\x02text/plain',
                                      'Content-MD5': '\x1f ##'})
        str3 = canonical_string('/', {'Content-Type': 'text/plain \x10',
                                      'Content-MD5': '##\x18'})

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

    def test_signed_urls_expired(self):
        expire = '1000000000'
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls(self):
        # Set expire to last 32b timestamp value
        # This number can't be higher, because it breaks tests on 32b systems
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        utc_date = datetime.utcnow()
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester&Timestamp=%s' %
                            (expire, utc_date.isoformat().rsplit('.')[0]),
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual(headers['Authorization'], 'AWS test:tester:X')

    def test_signed_urls_no_timestamp(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        # Curious! But actually S3 doesn't verify any x-amz-date/date headers
        # for signed_url access and it also doesn't check timestamp
        self.assertEqual(status.split()[0], '200')
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual(headers['Authorization'], 'AWS test:tester:X')

    def test_signed_urls_invalid_expire(self):
        expire = 'invalid'
        req = Request.blank('/bucket/object?Signature=X&Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_no_sign(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Expires=%s&'
                            'AWSAccessKeyId=test:tester' % expire,
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Date': self.get_date_header()})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_no_access(self):
        expire = '2147483647'  # 19 Jan 2038 03:14:07
        req = Request.blank('/bucket/object?Expires=%s&'
                            'AWSAccessKeyId=' % expire,
                            environ={'REQUEST_METHOD': 'GET'})
        req.headers['Date'] = datetime.utcnow()
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4(self):
        req = Request.blank(
            '/bucket/object'
            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
            '&X-Amz-Credential=test:tester/20T20Z/US/s3/aws4_request'
            '&X-Amz-Date=%s'
            '&X-Amz-Expires=1000'
            '&X-Amz-SignedHeaders=host'
            '&X-Amz-Signature=X' %
            self.get_v4_amz_date_header(),
            headers={'Date': self.get_date_header()},
            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200', body)
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual('AWS test:tester:X', headers['Authorization'])
            self.assertIn('X-Auth-Token', headers)

    def test_signed_urls_v4_missing_x_amz_date(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host'
                            '&X-Amz-Signature=X',
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4_invalid_algorithm(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=FAKE'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidArgument')

    def test_signed_urls_v4_missing_signed_headers(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body),
                         'AuthorizationHeaderMalformed')

    def test_signed_urls_v4_invalid_credentials(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host'
                            '&X-Amz-Signature=X' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signed_urls_v4_missing_signature(self):
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_request'
                            '&X-Amz-Date=%s'
                            '&X-Amz-Expires=1000'
                            '&X-Amz-SignedHeaders=host' %
                            self.get_v4_amz_date_header(),
                            environ={'REQUEST_METHOD': 'GET'})
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_bucket_virtual_hosted_style(self):
        req = Request.blank('/',
                            environ={'HTTP_HOST': 'bucket.localhost:80',
                                     'REQUEST_METHOD': 'HEAD',
                                     'HTTP_AUTHORIZATION':
                                     'AWS test:tester:hmac'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')

    def test_object_virtual_hosted_style(self):
        req = Request.blank('/object',
                            environ={'HTTP_HOST': 'bucket.localhost:80',
                                     'REQUEST_METHOD': 'HEAD',
                                     'HTTP_AUTHORIZATION':
                                     'AWS test:tester:hmac'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200')

    def test_token_generation(self):
        self.swift.register('HEAD', '/v1/AUTH_test/bucket+segments/'
                                    'object/123456789abcdef',
                            swob.HTTPOk, {}, None)
        self.swift.register('PUT', '/v1/AUTH_test/bucket+segments/'
                                   'object/123456789abcdef/1',
                            swob.HTTPCreated, {}, None)
        req = Request.blank('/bucket/object?uploadId=123456789abcdef'
                            '&partNumber=1',
                            environ={'REQUEST_METHOD': 'PUT'})
        req.headers['Authorization'] = 'AWS test:tester:hmac'
        date_header = self.get_date_header()
        req.headers['Date'] = date_header
        status, headers, body = self.call_swift3(req)
        _, _, headers = self.swift.calls_with_headers[-1]
        self.assertEqual(base64.urlsafe_b64decode(
            headers['X-Auth-Token']),
            'PUT\n\n\n%s\n/bucket/object?partNumber=1&uploadId=123456789abcdef'
            % date_header)

    def test_invalid_uri(self):
        req = Request.blank('/bucket/invalid\xffname',
                            environ={'REQUEST_METHOD': 'GET'},
                            headers={'Authorization': 'AWS test:tester:hmac',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidURI')

    def test_object_create_bad_md5_unreadable(self):
        req = Request.blank('/bucket/object',
                            environ={'REQUEST_METHOD': 'PUT',
                                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                     'HTTP_CONTENT_MD5': '#'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_object_create_bad_md5_too_short(self):
        too_short_digest = hashlib.md5('hey').hexdigest()[:-1]
        md5_str = too_short_digest.encode('base64').strip()
        req = Request.blank(
            '/bucket/object',
            environ={'REQUEST_METHOD': 'PUT',
                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                     'HTTP_CONTENT_MD5': md5_str},
            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_object_create_bad_md5_too_long(self):
        too_long_digest = hashlib.md5('hey').hexdigest() + 'suffix'
        md5_str = too_long_digest.encode('base64').strip()
        req = Request.blank(
            '/bucket/object',
            environ={'REQUEST_METHOD': 'PUT',
                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                     'HTTP_CONTENT_MD5': md5_str},
            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidDigest')

    def test_invalid_metadata_directive(self):
        req = Request.blank('/',
                            environ={'REQUEST_METHOD': 'GET',
                                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                     'HTTP_X_AMZ_METADATA_DIRECTIVE':
                                     'invalid'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidArgument')

    def test_invalid_storage_class(self):
        req = Request.blank('/',
                            environ={'REQUEST_METHOD': 'GET',
                                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z',
                                     'HTTP_X_AMZ_STORAGE_CLASS': 'INVALID'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'InvalidStorageClass')

    def _test_unsupported_header(self, header):
        req = Request.blank('/error',
                            environ={'REQUEST_METHOD': 'GET',
                                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z'},
                            headers={'x-amz-' + header: 'value',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'NotImplemented')

    def test_mfa(self):
        self._test_unsupported_header('mfa')

    def test_server_side_encryption(self):
        self._test_unsupported_header('server-side-encryption')

    def test_website_redirect_location(self):
        self._test_unsupported_header('website-redirect-location')

    def _test_unsupported_resource(self, resource):
        req = Request.blank('/error?' + resource,
                            environ={'REQUEST_METHOD': 'GET',
                                     'HTTP_AUTHORIZATION': 'AWS X:Y:Z'},
                            headers={'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        self.assertEqual(self._get_error_code(body), 'NotImplemented')

    def test_notification(self):
        self._test_unsupported_resource('notification')

    def test_policy(self):
        self._test_unsupported_resource('policy')

    def test_request_payment(self):
        self._test_unsupported_resource('requestPayment')

    def test_torrent(self):
        self._test_unsupported_resource('torrent')

    def test_website(self):
        self._test_unsupported_resource('website')

    def test_cors(self):
        self._test_unsupported_resource('cors')

    def test_tagging(self):
        self._test_unsupported_resource('tagging')

    def test_restore(self):
        self._test_unsupported_resource('restore')

    def test_unsupported_method(self):
        req = Request.blank('/bucket?acl',
                            environ={'REQUEST_METHOD': 'POST'},
                            headers={'Authorization': 'AWS test:tester:hmac',
                                     'Date': self.get_date_header()})
        status, headers, body = self.call_swift3(req)
        elem = fromstring(body, 'Error')
        self.assertEqual(elem.find('./Code').text, 'MethodNotAllowed')
        self.assertEqual(elem.find('./Method').text, 'POST')
        self.assertEqual(elem.find('./ResourceType').text, 'ACL')

    def test_registered_defaults(self):
        filter_factory(CONF)
        swift_info = utils.get_swift_info()
        self.assertTrue('swift3' in swift_info)
        self.assertEqual(swift_info['swift3'].get('version'),
                         swift3.__version__)
        self.assertEqual(swift_info['swift3'].get('max_bucket_listing'),
                         CONF.max_bucket_listing)
        self.assertEqual(swift_info['swift3'].get('max_parts_listing'),
                         CONF.max_parts_listing)
        self.assertEqual(swift_info['swift3'].get('max_upload_part_num'),
                         CONF.max_upload_part_num)
        self.assertEqual(swift_info['swift3'].get('max_multi_delete_objects'),
                         CONF.max_multi_delete_objects)

    def test_check_pipeline(self):
        with nested(patch("swift3.middleware.CONF"),
                    patch("swift3.middleware.PipelineWrapper"),
                    patch("swift3.middleware.loadcontext")) as \
                (conf, pipeline, _):
            conf.auth_pipeline_check = True
            conf.__file__ = ''

            pipeline.return_value = 'swift3 tempauth proxy-server'
            self.swift3.check_pipeline(conf)

            # This *should* still work; authtoken will remove our auth details,
            # but the X-Auth-Token we drop in will remain
            # if we found one in the response
            pipeline.return_value = 'swift3 s3token authtoken keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            # This should work now; no more doubled-up requests to keystone!
            pipeline.return_value = 'swift3 s3token keystoneauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 swauth proxy-server'
            self.swift3.check_pipeline(conf)

            # Note that authtoken would need to have delay_auth_decision=True
            pipeline.return_value = 'swift3 authtoken s3token keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 proxy-server'
            with self.assertRaises(ValueError) as cm:
                self.swift3.check_pipeline(conf)
            self.assertIn('expected auth between swift3 and proxy-server',
                          cm.exception.message)

            pipeline.return_value = 'proxy-server'
            with self.assertRaises(ValueError) as cm:
                self.swift3.check_pipeline(conf)
            self.assertIn("missing filters ['swift3']",
                          cm.exception.message)

    def test_swift3_initialization_with_disabled_pipeline_check(self):
        with nested(patch("swift3.middleware.CONF"),
                    patch("swift3.middleware.PipelineWrapper"),
                    patch("swift3.middleware.loadcontext")) as \
                (conf, pipeline, _):
            # Disable pipeline check
            conf.auth_pipeline_check = False
            conf.__file__ = ''

            pipeline.return_value = 'swift3 tempauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 s3token authtoken keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 swauth proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 authtoken s3token keystoneauth ' \
                'proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'swift3 proxy-server'
            self.swift3.check_pipeline(conf)

            pipeline.return_value = 'proxy-server'
            with self.assertRaises(ValueError):
                self.swift3.check_pipeline(conf)

    def test_signature_v4(self):
        environ = {
            'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
                'AWS4-HMAC-SHA256 '
                'Credential=test:tester/20130524/US/s3/aws4_request, '
                'SignedHeaders=host;x-amz-date,'
                'Signature=X',
            'X-Amz-Date': self.get_v4_amz_date_header(),
            'X-Amz-Content-SHA256': '0123456789'}
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '200', body)
        for _, _, headers in self.swift.calls_with_headers:
            self.assertEqual('AWS test:tester:X', headers['Authorization'])
            self.assertIn('X-Auth-Token', headers)

    def test_signature_v4_no_date(self):
        environ = {
            'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
                'AWS4-HMAC-SHA256 '
                'Credential=test:tester/20130524/US/s3/aws4_request, '
                'SignedHeaders=host;range;x-amz-date,'
                'Signature=X',
            'X-Amz-Content-SHA256': '0123456789'}
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '403')
        self.assertEqual(self._get_error_code(body), 'AccessDenied')

    def test_signature_v4_no_payload(self):
        environ = {
            'REQUEST_METHOD': 'GET'}
        headers = {
            'Authorization':
                'AWS4-HMAC-SHA256 '
                'Credential=test:tester/20130524/US/s3/aws4_request, '
                'SignedHeaders=host;x-amz-date,'
                'Signature=X',
            'X-Amz-Date': self.get_v4_amz_date_header()}
        req = Request.blank('/bucket/object', environ=environ, headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '400')
        self.assertEqual(self._get_error_code(body), 'InvalidRequest')
        self.assertEqual(
            self._get_error_message(body),
            'Missing required header for this request: x-amz-content-sha256')

    def test_signature_v4_bad_authorization_string(self):
        def test(auth_str, error, msg):
            environ = {
                'REQUEST_METHOD': 'GET'}
            headers = {
                'Authorization': auth_str,
                'X-Amz-Date': self.get_v4_amz_date_header(),
                'X-Amz-Content-SHA256': '0123456789'}
            req = Request.blank('/bucket/object', environ=environ,
                                headers=headers)
            req.content_type = 'text/plain'
            status, headers, body = self.call_swift3(req)
            self.assertEqual(self._get_error_code(body), error)
            self.assertEqual(self._get_error_message(body), msg)

        auth_str = ('AWS4-HMAC-SHA256 '
                    'SignedHeaders=host;x-amz-date,'
                    'Signature=X')
        test(auth_str, 'AccessDenied', 'Access Denied.')

        auth_str = ('AWS4-HMAC-SHA256 '
                    'Credential=test:tester/20130524/US/s3/aws4_request, '
                    'Signature=X')
        test(auth_str, 'AuthorizationHeaderMalformed',
             'The authorization header is malformed; the authorization '
             'header requires three components: Credential, SignedHeaders, '
             'and Signature.')

        auth_str = ('AWS4-HMAC-SHA256 '
                    'Credential=test:tester/20130524/US/s3/aws4_request, '
                    'SignedHeaders=host;x-amz-date')
        test(auth_str, 'AccessDenied', 'Access Denied.')

    def test_canonical_string_v4(self):
        def _get_req(path, environ):
            if '?' in path:
                path, query_string = path.split('?', 1)
            else:
                query_string = ''

            env = {
                'REQUEST_METHOD': 'GET',
                'PATH_INFO': path,
                'QUERY_STRING': query_string,
                'HTTP_DATE': 'Mon, 09 Sep 2011 23:36:00 GMT',
                'HTTP_X_AMZ_CONTENT_SHA256':
                    'e3b0c44298fc1c149afbf4c8996fb924'
                    '27ae41e4649b934ca495991b7852b855',
                'HTTP_AUTHORIZATION':
                    'AWS4-HMAC-SHA256 '
                    'Credential=X:Y/dt/reg/host/blah, '
                    'SignedHeaders=content-md5;content-type;date, '
                    'Signature=x',
            }
            env.update(environ)
            with patch('swift3.request.Request._validate_headers'):
                req = SigV4Request(env)
            return req

        def string_to_sign(path, environ):
            return _get_req(path, environ)._string_to_sign()

        def canonical_string(path, environ):
            return _get_req(path, environ)._canonical_request()

        def verify(hash_val, path, environ):
            s = string_to_sign(path, environ)
            s = s.split('\n')[3]
            self.assertEqual(hash_val, s)

        # all next data got from aws4_testsuite from Amazon
        # http://docs.aws.amazon.com/general/latest/gr/samples
        # /aws4_testsuite.zip
        # Each *expected* hash value is the 4th line in <test-name>.sts in the
        # test suite.

        # get-vanilla
        env = {
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host, Signature=X'),
            'HTTP_HOST': 'host.foo.com'}
        verify('366b91fb121d72a00f46bbe8d395f53a'
               '102b06dfb7e79636515208ed3fa606b1',
               '/', env)

        # get-header-value-trim
        env = {
            'REQUEST_METHOD': 'POST',
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host;p, Signature=X'),
            'HTTP_HOST': 'host.foo.com',
            'HTTP_P': 'phfft'}
        verify('dddd1902add08da1ac94782b05f9278c'
               '08dc7468db178a84f8950d93b30b1f35',
               '/', env)

        # get-utf8 (not exact)
        env = {
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host, Signature=X'),
            'HTTP_HOST': 'host.foo.com',
            'RAW_PATH_INFO': '/%E1%88%B4'}

        # This might look weird because actually S3 doesn't care about utf-8
        # encoded multi-byte bucket name from bucket-in-host name constraint.
        # However, aws4_testsuite has only a sample hash with utf-8 *bucket*
        # name to make sure the correctness (probably it can be used in other
        # aws resource except s3) so, to test also utf-8, skip the bucket name
        # validation in the following test.

        # NOTE: eventlet's PATH_INFO is unquoted
        with patch('swift3.request.validate_bucket_name'):
            verify('27ba31df5dbc6e063d8f87d62eb07143'
                   'f7f271c5330a917840586ac1c85b6f6b',
                   unquote('/%E1%88%B4'), env)

        # get-vanilla-query-order-key
        env = {
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host, Signature=X'),
            'HTTP_HOST': 'host.foo.com'}
        verify('2f23d14fe13caebf6dfda346285c6d9c'
               '14f49eaca8f5ec55c627dd7404f7a727',
               '/?a=foo&b=foo', env)

        # post-header-value-case
        env = {
            'REQUEST_METHOD': 'POST',
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host;zoo, Signature=X'),
            'HTTP_HOST': 'host.foo.com',
            'HTTP_ZOO': 'ZOOBAR'}
        verify('3aae6d8274b8c03e2cc96fc7d6bda4b9'
               'bd7a0a184309344470b2c96953e124aa',
               '/', env)

        # post-x-www-form-urlencoded-parameters
        env = {
            'REQUEST_METHOD': 'POST',
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host;content-type, Signature=X'),
            'HTTP_HOST': 'host.foo.com',
            'HTTP_X_AMZ_CONTENT_SHA256':
                '3ba8907e7a252327488df390ed517c45'
                'b96dead033600219bdca7107d1d3f88a',
            'CONTENT_TYPE':
                'application/x-www-form-urlencoded; charset=utf8'}
        verify('c4115f9e54b5cecf192b1eaa23b8e88e'
               'd8dc5391bd4fde7b3fff3d9c9fe0af1f',
               '/', env)

        # post-x-www-form-urlencoded
        env = {
            'REQUEST_METHOD': 'POST',
            'HTTP_AUTHORIZATION': (
                'AWS4-HMAC-SHA256 '
                'Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, '
                'SignedHeaders=date;host;content-type, Signature=X'),
            'HTTP_HOST': 'host.foo.com',
            'HTTP_X_AMZ_CONTENT_SHA256':
                '3ba8907e7a252327488df390ed517c45'
                'b96dead033600219bdca7107d1d3f88a',
            'CONTENT_TYPE':
                'application/x-www-form-urlencoded'}
        verify('4c5c6e4b52fb5fb947a8733982a8a5a6'
               '1b14f04345cbfe6e739236c76dd48f74',
               '/', env)

        # Note that boto does not do proper stripping (as of 2.42.0).
        # These were determined by examining the StringToSignBytes element of
        # resulting SignatureDoesNotMatch errors from AWS.
        str1 = canonical_string('/', {'CONTENT_TYPE': 'text/plain',
                                      'HTTP_CONTENT_MD5': '##'})
        str2 = canonical_string('/', {'CONTENT_TYPE': '\x01\x02text/plain',
                                      'HTTP_CONTENT_MD5': '\x1f ##'})
        str3 = canonical_string('/', {'CONTENT_TYPE': 'text/plain \x10',
                                      'HTTP_CONTENT_MD5': '##\x18'})

        self.assertEqual(str1, str2)
        self.assertEqual(str2, str3)

    def test_mixture_param_v4(self):
        # now we have an Authorization header
        headers = {
            'Authorization':
                'AWS4-HMAC-SHA256 '
                'Credential=test/20130524/US/s3/aws4_request_A, '
                'SignedHeaders=hostA;rangeA;x-amz-dateA,'
                'Signature=X',
            'X-Amz-Date': self.get_v4_amz_date_header(),
            'X-Amz-Content-SHA256': '0123456789'}

        # and then, different auth info (Credential, SignedHeaders, Signature)
        # in query
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_requestB'
                            '&X-Amz-SignedHeaders=hostB'
                            '&X-Amz-Signature=Y',
                            environ={'REQUEST_METHOD': 'GET'},
                            headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        # FIXME: should this failed as 400 or pass via query auth?
        # for now, 403 forbidden for safety
        self.assertEqual(status.split()[0], '403', body)

        # But if we are missing Signature in query param
        req = Request.blank('/bucket/object'
                            '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
                            '&X-Amz-Credential=test/20T20Z/US/s3/aws4_requestB'
                            '&X-Amz-SignedHeaders=hostB',
                            environ={'REQUEST_METHOD': 'GET'},
                            headers=headers)
        req.content_type = 'text/plain'
        status, headers, body = self.call_swift3(req)
        self.assertEqual(status.split()[0], '403', body)

    def test_swift3_with_only_s3_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.s3_token = S3Token(
            self.keystone_auth, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            mock_resp = requests.Response()
            mock_resp._content = json.dumps(GOOD_RESPONSE)
            mock_resp.status_code = 201
            mock_req.return_value = mock_resp

            status, headers, body = self.call_swift3(req)
            self.assertEqual(body, '')
            self.assertEqual(1, mock_req.call_count)

    def test_swift3_with_s3_token_and_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(
            self.keystone_auth, {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(
            self.auth_token, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                mock_resp._content = json.dumps(GOOD_RESPONSE)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                self.assertEqual(body, '')
                self.assertEqual(1, mock_req.call_count)
                # With X-Auth-Token, auth_token will call _do_fetch_token to
                # connect to keystone in auth_token, again
                self.assertEqual(1, mock_fetch.call_count)

    def test_swift3_with_s3_token_no_pass_token_to_auth_token(self):
        self.swift = FakeSwift()
        self.keystone_auth = KeystoneAuth(
            self.swift, {'operator_roles': 'swift-user'})
        self.auth_token = AuthProtocol(
            self.keystone_auth, {'delay_auth_decision': 'True'})
        self.s3_token = S3Token(
            self.auth_token, {'auth_uri': 'https://fakehost/identity'})
        self.swift3 = Swift3Middleware(self.s3_token, CONF)
        req = Request.blank(
            '/bucket',
            environ={'REQUEST_METHOD': 'PUT'},
            headers={'Authorization': 'AWS access:signature',
                     'Date': self.get_date_header()})
        self.swift.register('PUT', '/v1/AUTH_TENANT_ID/bucket',
                            swob.HTTPCreated, {}, None)
        self.swift.register('HEAD', '/v1/AUTH_TENANT_ID',
                            swob.HTTPOk, {}, None)
        with patch.object(self.s3_token, '_json_request') as mock_req:
            with patch.object(self.auth_token,
                              '_do_fetch_token') as mock_fetch:
                mock_resp = requests.Response()
                no_token_id_good_resp = copy.deepcopy(GOOD_RESPONSE)
                # delete token id
                del no_token_id_good_resp['access']['token']['id']
                mock_resp._content = json.dumps(no_token_id_good_resp)
                mock_resp.status_code = 201
                mock_req.return_value = mock_resp

                mock_access_info = AccessInfoV2(GOOD_RESPONSE)
                mock_access_info.will_expire_soon = \
                    lambda stale_duration: False
                mock_fetch.return_value = (MagicMock(), mock_access_info)

                status, headers, body = self.call_swift3(req)
                # No token provided from keystone result in 401 Unauthorized
                # at `swift.common.middleware.keystoneauth` because auth_token
                # will remove all auth headers including 'X-Identity-Status'[1]
                # and then, set X-Identity-Status: Invalid at [2]
                #
                # 1: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L620
                # 2: https://github.com/openstack/keystonemiddleware/blob/
                #    master/keystonemiddleware/auth_token/__init__.py#L627-L629

                self.assertEqual('403 Forbidden', status)
                self.assertEqual(1, mock_req.call_count)
                # if no token provided from keystone, we can skip the call to
                # fetch the token
                self.assertEqual(0, mock_fetch.call_count)
Ejemplo n.º 12
0
class Swift3TestCase(unittest.TestCase):
    def __init__(self, name):
        unittest.TestCase.__init__(self, name)
        self.conf = {
            'log_level': 'debug',
            'pretty_print_xml': True,
            'storage_domain': 'localhost',
        }

    def setUp(self):
        self.app = FakeSwift()
        self.swift3 = swift3.filter_factory(self.conf)(self.app)

        # container '.' will be accessed to resolve a tenant name
        self.app.register('HEAD', '/v1/AUTH_test/.', swob.HTTPNotFound,
                          {}, None)
        self.app.register('HEAD', '/v1/AUTH_X/.', swob.HTTPNotFound,
                          {}, None)
        self.app.register('PUT', '/v1/.swift3/acl',
                          swob.HTTPAccepted, {}, None)
        self.app.register('PUT', '/v1/.swift3/lifecycle_rules',
                          swob.HTTPAccepted, {}, None)

    def _get_error_code(self, body):
        elem = fromstring(body, 'Error')
        self.assertEquals(elem.tag, 'Error')
        return elem.find('./Code').text

    def _test_method_error(self, method, path, response_class, headers=None,
                           body=None):
        if headers is None:
            headers = {}
        headers.update({'x-container-meta-[swift3]-timestamp': 0,
                        'X-Container-Meta-[Swift3]-Owner': 'test:tester',
                        'X-Object-Meta-[Swift3]-Owner': 'test:tester'})
        uri = '/v1/AUTH_test' + path
        if uri == '/v1/AUTH_test/':
            uri = '/v1/AUTH_test'
        self.app.register(method, uri, response_class,
                          headers, body)
        headers.update({'Authorization': 'AWS test:tester:hmac'})
        req = Request.blank(path, environ={'REQUEST_METHOD': method},
                            headers=headers)
        status, headers, body = self.call_swift3(req)
        #print body
        if body == '':
            return status
        return self._get_error_code(body)

    def call_app(self, req, app=None, expect_exception=False):
        if app is None:
            app = self.app

        req.headers.setdefault("User-Agent", "Mozzarella Foxfire")

        status = [None]
        headers = [None]

        def start_response(s, h, ei=None):
            status[0] = s
            headers[0] = swob.HeaderKeyDict(h)

        body_iter = app(req.environ, start_response)
        body = ''
        caught_exc = None
        try:
            for chunk in body_iter:
                body += chunk
        except Exception as exc:
            if expect_exception:
                caught_exc = exc
            else:
                raise

        if expect_exception:
            return status[0], headers[0], body, caught_exc
        else:
            return status[0], headers[0], body

    def call_swift3(self, req, **kwargs):
        return self.call_app(req, app=self.swift3, **kwargs)
Ejemplo n.º 13
0
 def test_keystone_auth(self):
     app = FakeSwift(auth='keystone')
     app.register('HEAD', '/v1/AUTH_X/.', swob.HTTPNotFound,
                  {'X-Timestamp': 0}, None)
     app.register('HEAD', '/v1/AUTH_X/bucket', swob.HTTPNoContent,
                  {'X-Timestamp': 0}, None)
     app.register('PUT', '/v1/AUTH_X/bucket', swob.HTTPCreated,
                  {'X-Timestamp': 0}, None)
     app.register('POST', '/v1/AUTH_X/bucket', swob.HTTPNoContent,
                  {'X-Timestamp': 0}, None)
     app.register('PUT', '/v1/.swift3/acl',
                  swob.HTTPAccepted, {}, None)
     app.register('PUT', '/v1/.swift3/acl/AUTH_X/bucket/0',
                  swob.HTTPCreated, {}, None)
     req = Request.blank('/bucket',
                         environ={'REQUEST_METHOD': 'PUT'},
                         headers={'Authorization': 'AWS X:Y:Z'})
     s3_app = swift3.filter_factory(self.conf)(app)
     status, headers, body = self.call_app(req, app=s3_app)
     print body
     self.assertEquals(status.split()[0], '200')