Addition of s3control support in server mode (#4885)
This commit is contained in:
parent
e84cc20abe
commit
8b81481d3e
@ -13,37 +13,38 @@ class S3ControlResponse(BaseResponse):
|
|||||||
def public_access_block(cls, request, full_url, headers):
|
def public_access_block(cls, request, full_url, headers):
|
||||||
response_instance = S3ControlResponse()
|
response_instance = S3ControlResponse()
|
||||||
try:
|
try:
|
||||||
return response_instance._public_access_block(request, headers)
|
return response_instance._public_access_block(request)
|
||||||
except S3ClientError as err:
|
except S3ClientError as err:
|
||||||
return err.code, {}, err.description
|
return err.code, {}, err.description
|
||||||
|
|
||||||
@amzn_request_id
|
@amzn_request_id
|
||||||
def _public_access_block(self, request, headers):
|
def _public_access_block(self, request):
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
return self.get_public_access_block(headers)
|
return self.get_public_access_block(request)
|
||||||
elif request.method == "PUT":
|
elif request.method == "PUT":
|
||||||
return self.put_public_access_block(request, headers)
|
return self.put_public_access_block(request)
|
||||||
elif request.method == "DELETE":
|
elif request.method == "DELETE":
|
||||||
return self.delete_public_access_block(headers)
|
return self.delete_public_access_block(request)
|
||||||
|
|
||||||
def get_public_access_block(self, headers):
|
def get_public_access_block(self, request):
|
||||||
account_id = headers["x-amz-account-id"]
|
account_id = request.headers.get("x-amz-account-id")
|
||||||
public_block_config = s3control_backend.get_public_access_block(
|
public_block_config = s3control_backend.get_public_access_block(
|
||||||
account_id=account_id,
|
account_id=account_id,
|
||||||
)
|
)
|
||||||
template = self.response_template(S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION)
|
template = self.response_template(S3_PUBLIC_ACCESS_BLOCK_CONFIGURATION)
|
||||||
return 200, {}, template.render(public_block_config=public_block_config)
|
return 200, {}, template.render(public_block_config=public_block_config)
|
||||||
|
|
||||||
def put_public_access_block(self, request, headers):
|
def put_public_access_block(self, request):
|
||||||
account_id = headers["x-amz-account-id"]
|
account_id = request.headers.get("x-amz-account-id")
|
||||||
pab_config = self._parse_pab_config(request.body)
|
data = request.body if hasattr(request, "body") else request.data
|
||||||
|
pab_config = self._parse_pab_config(data)
|
||||||
s3control_backend.put_public_access_block(
|
s3control_backend.put_public_access_block(
|
||||||
account_id, pab_config["PublicAccessBlockConfiguration"]
|
account_id, pab_config["PublicAccessBlockConfiguration"]
|
||||||
)
|
)
|
||||||
return 201, {}, json.dumps({})
|
return 201, {}, json.dumps({})
|
||||||
|
|
||||||
def delete_public_access_block(self, headers):
|
def delete_public_access_block(self, request):
|
||||||
account_id = headers["x-amz-account-id"]
|
account_id = request.headers.get("x-amz-account-id")
|
||||||
s3control_backend.delete_public_access_block(account_id=account_id,)
|
s3control_backend.delete_public_access_block(account_id=account_id,)
|
||||||
return 204, {}, json.dumps({})
|
return 204, {}, json.dumps({})
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ class DomainDispatcherApplication(object):
|
|||||||
# S3 is the last resort when the target is also unknown
|
# S3 is the last resort when the target is also unknown
|
||||||
service, region = DEFAULT_SERVICE_REGION
|
service, region = DEFAULT_SERVICE_REGION
|
||||||
|
|
||||||
|
path = environ.get("PATH_INFO", "")
|
||||||
if service in ["budgets", "cloudfront"]:
|
if service in ["budgets", "cloudfront"]:
|
||||||
# Global Services - they do not have/expect a region
|
# Global Services - they do not have/expect a region
|
||||||
host = f"{service}.amazonaws.com"
|
host = f"{service}.amazonaws.com"
|
||||||
@ -145,6 +146,10 @@ class DomainDispatcherApplication(object):
|
|||||||
host = "ingest.{service}.{region}.amazonaws.com".format(
|
host = "ingest.{service}.{region}.amazonaws.com".format(
|
||||||
service=service, region=region
|
service=service, region=region
|
||||||
)
|
)
|
||||||
|
elif service == "s3" and (
|
||||||
|
path.startswith("/v20180820/") or "s3-control" in environ["HTTP_HOST"]
|
||||||
|
):
|
||||||
|
host = "s3control"
|
||||||
else:
|
else:
|
||||||
host = "{service}.{region}.amazonaws.com".format(
|
host = "{service}.{region}.amazonaws.com".format(
|
||||||
service=service, region=region
|
service=service, region=region
|
||||||
|
@ -4,87 +4,76 @@ import sure # noqa # pylint: disable=unused-import
|
|||||||
|
|
||||||
from boto3 import Session
|
from boto3 import Session
|
||||||
from botocore.client import ClientError
|
from botocore.client import ClientError
|
||||||
from moto import settings, mock_s3control
|
from moto import mock_s3control
|
||||||
|
|
||||||
# All tests for s3-control cannot be run under the server without a modification of the
|
|
||||||
# hosts file on your system. This is due to the fact that the URL to the host is in the form of:
|
|
||||||
# ACCOUNT_ID.s3-control.amazonaws.com <-- That Account ID part is the problem. If you want to
|
|
||||||
# make use of the moto server, update your hosts file for `THE_ACCOUNT_ID_FOR_MOTO.localhost`
|
|
||||||
# and this will work fine.
|
|
||||||
|
|
||||||
if not settings.TEST_SERVER_MODE:
|
@mock_s3control
|
||||||
|
def test_get_public_access_block_for_account():
|
||||||
|
from moto.s3.models import ACCOUNT_ID
|
||||||
|
|
||||||
@mock_s3control
|
client = boto3.client("s3control", region_name="us-west-2")
|
||||||
def test_get_public_access_block_for_account():
|
|
||||||
from moto.s3.models import ACCOUNT_ID
|
|
||||||
|
|
||||||
client = boto3.client("s3control", region_name="us-west-2")
|
# With an invalid account ID:
|
||||||
|
with pytest.raises(ClientError) as ce:
|
||||||
|
client.get_public_access_block(AccountId="111111111111")
|
||||||
|
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
||||||
|
|
||||||
# With an invalid account ID:
|
# Without one defined:
|
||||||
with pytest.raises(ClientError) as ce:
|
with pytest.raises(ClientError) as ce:
|
||||||
client.get_public_access_block(AccountId="111111111111")
|
client.get_public_access_block(AccountId=ACCOUNT_ID)
|
||||||
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
assert ce.value.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
|
||||||
|
|
||||||
# Without one defined:
|
# Put a with an invalid account ID:
|
||||||
with pytest.raises(ClientError) as ce:
|
with pytest.raises(ClientError) as ce:
|
||||||
client.get_public_access_block(AccountId=ACCOUNT_ID)
|
|
||||||
assert (
|
|
||||||
ce.value.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Put a with an invalid account ID:
|
|
||||||
with pytest.raises(ClientError) as ce:
|
|
||||||
client.put_public_access_block(
|
|
||||||
AccountId="111111111111",
|
|
||||||
PublicAccessBlockConfiguration={"BlockPublicAcls": True},
|
|
||||||
)
|
|
||||||
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
|
||||||
|
|
||||||
# Put with an invalid PAB:
|
|
||||||
with pytest.raises(ClientError) as ce:
|
|
||||||
client.put_public_access_block(
|
|
||||||
AccountId=ACCOUNT_ID, PublicAccessBlockConfiguration={}
|
|
||||||
)
|
|
||||||
assert ce.value.response["Error"]["Code"] == "InvalidRequest"
|
|
||||||
assert (
|
|
||||||
"Must specify at least one configuration."
|
|
||||||
in ce.value.response["Error"]["Message"]
|
|
||||||
)
|
|
||||||
|
|
||||||
# Correct PAB:
|
|
||||||
client.put_public_access_block(
|
client.put_public_access_block(
|
||||||
AccountId=ACCOUNT_ID,
|
AccountId="111111111111",
|
||||||
PublicAccessBlockConfiguration={
|
PublicAccessBlockConfiguration={"BlockPublicAcls": True},
|
||||||
"BlockPublicAcls": True,
|
|
||||||
"IgnorePublicAcls": True,
|
|
||||||
"BlockPublicPolicy": True,
|
|
||||||
"RestrictPublicBuckets": True,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
|
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
||||||
|
|
||||||
# Get the correct PAB (for all regions):
|
# Put with an invalid PAB:
|
||||||
for region in Session().get_available_regions("s3control"):
|
with pytest.raises(ClientError) as ce:
|
||||||
region_client = boto3.client("s3control", region_name=region)
|
client.put_public_access_block(
|
||||||
assert region_client.get_public_access_block(AccountId=ACCOUNT_ID)[
|
AccountId=ACCOUNT_ID, PublicAccessBlockConfiguration={}
|
||||||
"PublicAccessBlockConfiguration"
|
|
||||||
] == {
|
|
||||||
"BlockPublicAcls": True,
|
|
||||||
"IgnorePublicAcls": True,
|
|
||||||
"BlockPublicPolicy": True,
|
|
||||||
"RestrictPublicBuckets": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Delete with an invalid account ID:
|
|
||||||
with pytest.raises(ClientError) as ce:
|
|
||||||
client.delete_public_access_block(AccountId="111111111111")
|
|
||||||
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
|
||||||
|
|
||||||
# Delete successfully:
|
|
||||||
client.delete_public_access_block(AccountId=ACCOUNT_ID)
|
|
||||||
|
|
||||||
# Confirm that it's deleted:
|
|
||||||
with pytest.raises(ClientError) as ce:
|
|
||||||
client.get_public_access_block(AccountId=ACCOUNT_ID)
|
|
||||||
assert (
|
|
||||||
ce.value.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
|
|
||||||
)
|
)
|
||||||
|
assert ce.value.response["Error"]["Code"] == "InvalidRequest"
|
||||||
|
assert (
|
||||||
|
"Must specify at least one configuration."
|
||||||
|
in ce.value.response["Error"]["Message"]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Correct PAB:
|
||||||
|
client.put_public_access_block(
|
||||||
|
AccountId=ACCOUNT_ID,
|
||||||
|
PublicAccessBlockConfiguration={
|
||||||
|
"BlockPublicAcls": True,
|
||||||
|
"IgnorePublicAcls": True,
|
||||||
|
"BlockPublicPolicy": True,
|
||||||
|
"RestrictPublicBuckets": True,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the correct PAB (for all regions):
|
||||||
|
for region in Session().get_available_regions("s3control"):
|
||||||
|
region_client = boto3.client("s3control", region_name=region)
|
||||||
|
assert region_client.get_public_access_block(AccountId=ACCOUNT_ID)[
|
||||||
|
"PublicAccessBlockConfiguration"
|
||||||
|
] == {
|
||||||
|
"BlockPublicAcls": True,
|
||||||
|
"IgnorePublicAcls": True,
|
||||||
|
"BlockPublicPolicy": True,
|
||||||
|
"RestrictPublicBuckets": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
# Delete with an invalid account ID:
|
||||||
|
with pytest.raises(ClientError) as ce:
|
||||||
|
client.delete_public_access_block(AccountId="111111111111")
|
||||||
|
assert ce.value.response["Error"]["Code"] == "AccessDenied"
|
||||||
|
|
||||||
|
# Delete successfully:
|
||||||
|
client.delete_public_access_block(AccountId=ACCOUNT_ID)
|
||||||
|
|
||||||
|
# Confirm that it's deleted:
|
||||||
|
with pytest.raises(ClientError) as ce:
|
||||||
|
client.get_public_access_block(AccountId=ACCOUNT_ID)
|
||||||
|
assert ce.value.response["Error"]["Code"] == "NoSuchPublicAccessBlockConfiguration"
|
||||||
|
Loading…
Reference in New Issue
Block a user