python-botocore/tests/functional/test_s3_control_redirects.py
2022-12-12 08:14:19 -08:00

481 lines
17 KiB
Python

# Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.
import re
from contextlib import contextmanager
import pytest
from botocore import exceptions
from botocore.compat import urlsplit
from botocore.config import Config
from botocore.exceptions import (
InvalidHostLabelError,
ParamValidationError,
UnsupportedS3ControlArnError,
UnsupportedS3ControlConfigurationError,
)
from botocore.session import Session
from tests import ClientHTTPStubber, unittest
ACCESSPOINT_ARN_TEST_CASES = [
# Outpost accesspoint arn test cases
{
'arn': (
'arn:aws:s3-outposts:us-west-2:123456789012:'
'outpost:op-01234567890123456:accesspoint:myaccesspoint'
),
'region': 'us-west-2',
'config': {},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-west-2.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': False}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws-cn:s3-outposts:cn-north-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-gov-east-1',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-gov-east-1-fips',
'config': {'s3': {'use_arn_region': False}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts-fips.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:fips-us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'fips-us-gov-east-1',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'region': 'us-gov-east-1-fips',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts-fips.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'config': {'s3': {'use_dualstack_endpoint': True}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:accesspoint:myaccesspoint',
'config': {'s3': {'use_accelerate_endpoint': True}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:myaccesspoint',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
]
BUCKET_ARN_TEST_CASES = [
# Outpost bucket arn test cases
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-west-2',
'config': {},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-west-2.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': False}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws-cn:s3-outposts:cn-north-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-west-2',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-gov-east-1',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-gov-east-1-fips',
'config': {'s3': {'use_arn_region': False}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts-fips.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:fips-us-gov-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'fips-us-gov-east-1',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws-us-gov:s3-outposts:us-gov-east-1:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-gov-east-1-fips',
'config': {'s3': {'use_arn_region': True}},
'assertions': {
'signing_name': 's3-outposts',
'netloc': 's3-outposts-fips.us-gov-east-1.amazonaws.com',
'headers': {
'x-amz-outpost-id': 'op-01234567890123456',
'x-amz-account-id': '123456789012',
},
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:bucket:mybucket',
'region': 'us-west-2',
'config': {'s3': {'use_dualstack_endpoint': True}},
'assertions': {
'exception': 'UnsupportedS3ControlConfigurationError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:bucket',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
{
'arn': 'arn:aws:s3-outposts:us-west-2:123456789012:outpost:op-01234567890123456:bucket',
'assertions': {
'exception': 'UnsupportedS3ControlArnError',
},
},
]
V4_AUTH_REGEX = re.compile(
r'AWS4-HMAC-SHA256 Credential=\w+/\d+/'
r'(?P<region>[a-z0-9-]+)/(?P<name>[a-z0-9-]+)/'
)
def _assert_signing_name(stubber, expected_name):
request = stubber.requests[0]
auth_header = request.headers['Authorization'].decode('utf-8')
actual_name = V4_AUTH_REGEX.match(auth_header).group('name')
assert expected_name == actual_name
def _assert_netloc(stubber, expected_netloc):
request = stubber.requests[0]
url_parts = urlsplit(request.url)
assert expected_netloc == url_parts.netloc
def _assert_header(stubber, key, value):
request = stubber.requests[0]
assert key in request.headers
actual_value = request.headers[key]
if isinstance(actual_value, bytes):
actual_value = actual_value.decode('utf-8')
assert value == actual_value
def _assert_headers(stubber, headers):
for key, value in headers.items():
_assert_header(stubber, key, value)
def _bootstrap_session():
session = Session()
session.set_credentials('access_key', 'secret_key')
return session
def _bootstrap_client(session, region, **kwargs):
client = session.create_client('s3control', region, **kwargs)
stubber = ClientHTTPStubber(client)
return client, stubber
def _bootstrap_test_case_client(session, test_case):
region = test_case.get('region', 'us-west-2')
config = test_case.get('config', {})
config = Config(**config)
return _bootstrap_client(session, region, config=config)
@pytest.mark.parametrize("test_case", ACCESSPOINT_ARN_TEST_CASES)
def test_accesspoint_arn_redirection(test_case):
session = _bootstrap_session()
client, stubber = _bootstrap_test_case_client(session, test_case)
with _assert_test_case(test_case, client, stubber):
client.get_access_point_policy(Name=test_case['arn'])
@pytest.mark.parametrize("test_case", BUCKET_ARN_TEST_CASES)
def test_bucket_arn_redirection(test_case):
session = _bootstrap_session()
client, stubber = _bootstrap_test_case_client(session, test_case)
with _assert_test_case(test_case, client, stubber):
client.get_bucket(Bucket=test_case['arn'])
@contextmanager
def _assert_test_case(test_case, client, stubber):
stubber.add_response()
assertions = test_case['assertions']
exception_raised = None
try:
with stubber:
yield
except Exception as e:
if 'exception' not in assertions:
raise
exception_raised = e
if 'exception' in assertions:
exception_cls = getattr(exceptions, assertions['exception'])
if exception_raised is None:
raise RuntimeError(
'Expected exception "%s" was not raised' % exception_cls
)
error_msg = ('Expected exception "%s", got "%s"') % (
exception_cls,
type(exception_raised),
)
assert isinstance(exception_raised, exception_cls), error_msg
else:
assert len(stubber.requests) == 1
if 'signing_name' in assertions:
_assert_signing_name(stubber, assertions['signing_name'])
if 'headers' in assertions:
_assert_headers(stubber, assertions['headers'])
if 'netloc' in assertions:
_assert_netloc(stubber, assertions['netloc'])
class TestS3ControlRedirection(unittest.TestCase):
def setUp(self):
self.session = _bootstrap_session()
self.region = 'us-west-2'
self._bootstrap_client()
def _bootstrap_client(self, **kwargs):
client, stubber = _bootstrap_client(
self.session, self.region, **kwargs
)
self.client = client
self.stubber = stubber
def test_outpost_id_redirection_dualstack(self):
config = Config(s3={'use_dualstack_endpoint': True})
self._bootstrap_client(config=config)
with self.assertRaises(UnsupportedS3ControlConfigurationError):
self.client.create_bucket(Bucket='foo', OutpostId='op-123')
def test_outpost_id_redirection_create_bucket(self):
self.stubber.add_response()
with self.stubber:
self.client.create_bucket(Bucket='foo', OutpostId='op-123')
_assert_netloc(self.stubber, 's3-outposts.us-west-2.amazonaws.com')
_assert_header(self.stubber, 'x-amz-outpost-id', 'op-123')
def test_outpost_id_redirection_list_regional_buckets(self):
self.stubber.add_response()
with self.stubber:
self.client.list_regional_buckets(
OutpostId='op-123',
AccountId='1234',
)
_assert_netloc(self.stubber, 's3-outposts.us-west-2.amazonaws.com')
_assert_header(self.stubber, 'x-amz-outpost-id', 'op-123')
def test_outpost_redirection_custom_endpoint(self):
self._bootstrap_client(endpoint_url='https://outpost.foo.com/')
self.stubber.add_response()
with self.stubber:
self.client.create_bucket(Bucket='foo', OutpostId='op-123')
_assert_netloc(self.stubber, 'outpost.foo.com')
_assert_header(self.stubber, 'x-amz-outpost-id', 'op-123')
def test_normal_ap_request_has_correct_endpoint(self):
self.stubber.add_response()
with self.stubber:
self.client.get_access_point_policy(Name='MyAp', AccountId='1234')
_assert_netloc(self.stubber, '1234.s3-control.us-west-2.amazonaws.com')
def test_normal_ap_request_custom_endpoint(self):
self._bootstrap_client(endpoint_url='https://example.com/')
self.stubber.add_response()
with self.stubber:
self.client.get_access_point_policy(Name='MyAp', AccountId='1234')
_assert_netloc(self.stubber, '1234.example.com')
def test_normal_bucket_request_has_correct_endpoint(self):
self.stubber.add_response()
with self.stubber:
self.client.create_bucket(Bucket='foo')
_assert_netloc(self.stubber, 's3-control.us-west-2.amazonaws.com')
def test_arn_account_id_mismatch(self):
arn = (
'arn:aws:s3-outposts:us-west-2:123456789012:outpost:'
'op-01234567890123456:accesspoint:myaccesspoint'
)
with self.assertRaises(UnsupportedS3ControlArnError):
self.client.get_access_point_policy(Name=arn, AccountId='1234')
def test_create_access_point_does_not_expand_name(self):
arn = (
'arn:aws:s3-outposts:us-west-2:123456789012:outpost:'
'op-01234567890123456:accesspoint:myaccesspoint'
)
# AccountId will not be backfilled by the arn expansion and thus
# fail parameter validation
with self.assertRaises(ParamValidationError):
self.client.create_access_point(Name=arn, Bucket='foo')
def test_arn_invalid_host_label(self):
config = Config(s3={'use_arn_region': True})
self._bootstrap_client(config=config)
arn = (
'arn:aws:s3-outposts:us-we$t-2:123456789012:outpost:'
'op-01234567890123456:accesspoint:myaccesspoint'
)
with self.assertRaises(InvalidHostLabelError):
self.client.get_access_point_policy(Name=arn)
def test_unknown_arn_format(self):
arn = 'arn:aws:foo:us-west-2:123456789012:bar:myresource'
with self.assertRaises(UnsupportedS3ControlArnError):
self.client.get_access_point_policy(Name=arn)