Merge pull request #7 from spulec/master

pull latest upstream changes
This commit is contained in:
Jon Beilke 2020-02-24 09:35:26 -06:00 committed by GitHub
commit 4c2667648a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 725 additions and 35 deletions

View File

@ -85,6 +85,15 @@ class NoMethodDefined(BadRequestException):
) )
class AuthorizerNotFoundException(RESTError):
code = 404
def __init__(self):
super(AuthorizerNotFoundException, self).__init__(
"NotFoundException", "Invalid Authorizer identifier specified"
)
class StageNotFoundException(RESTError): class StageNotFoundException(RESTError):
code = 404 code = 404

View File

@ -28,6 +28,7 @@ from .exceptions import (
InvalidHttpEndpoint, InvalidHttpEndpoint,
InvalidResourcePathException, InvalidResourcePathException,
InvalidRequestInput, InvalidRequestInput,
AuthorizerNotFoundException,
StageNotFoundException, StageNotFoundException,
RoleNotSpecified, RoleNotSpecified,
NoIntegrationDefined, NoIntegrationDefined,
@ -187,6 +188,54 @@ class Resource(BaseModel):
return self.resource_methods[method_type].pop("methodIntegration") return self.resource_methods[method_type].pop("methodIntegration")
class Authorizer(BaseModel, dict):
def __init__(self, id, name, authorizer_type, **kwargs):
super(Authorizer, self).__init__()
self["id"] = id
self["name"] = name
self["type"] = authorizer_type
if kwargs.get("provider_arns"):
self["providerARNs"] = kwargs.get("provider_arns")
if kwargs.get("auth_type"):
self["authType"] = kwargs.get("auth_type")
if kwargs.get("authorizer_uri"):
self["authorizerUri"] = kwargs.get("authorizer_uri")
if kwargs.get("authorizer_credentials"):
self["authorizerCredentials"] = kwargs.get("authorizer_credentials")
if kwargs.get("identity_source"):
self["identitySource"] = kwargs.get("identity_source")
if kwargs.get("identity_validation_expression"):
self["identityValidationExpression"] = kwargs.get(
"identity_validation_expression"
)
self["authorizerResultTtlInSeconds"] = kwargs.get("authorizer_result_ttl")
def apply_operations(self, patch_operations):
for op in patch_operations:
if "/authorizerUri" in op["path"]:
self["authorizerUri"] = op["value"]
elif "/authorizerCredentials" in op["path"]:
self["authorizerCredentials"] = op["value"]
elif "/authorizerResultTtlInSeconds" in op["path"]:
self["authorizerResultTtlInSeconds"] = int(op["value"])
elif "/authType" in op["path"]:
self["authType"] = op["value"]
elif "/identitySource" in op["path"]:
self["identitySource"] = op["value"]
elif "/identityValidationExpression" in op["path"]:
self["identityValidationExpression"] = op["value"]
elif "/name" in op["path"]:
self["name"] = op["value"]
elif "/providerARNs" in op["path"]:
# TODO: add and remove
raise Exception('Patch operation for "%s" not implemented' % op["path"])
elif "/type" in op["path"]:
self["type"] = op["value"]
else:
raise Exception('Patch operation "%s" not implemented' % op["op"])
return self
class Stage(BaseModel, dict): class Stage(BaseModel, dict):
def __init__( def __init__(
self, self,
@ -412,6 +461,7 @@ class RestAPI(BaseModel):
self.tags = kwargs.get("tags") or {} self.tags = kwargs.get("tags") or {}
self.deployments = {} self.deployments = {}
self.authorizers = {}
self.stages = {} self.stages = {}
self.resources = {} self.resources = {}
@ -479,6 +529,34 @@ class RestAPI(BaseModel):
), ),
) )
def create_authorizer(
self,
id,
name,
authorizer_type,
provider_arns=None,
auth_type=None,
authorizer_uri=None,
authorizer_credentials=None,
identity_source=None,
identiy_validation_expression=None,
authorizer_result_ttl=None,
):
authorizer = Authorizer(
id=id,
name=name,
authorizer_type=authorizer_type,
provider_arns=provider_arns,
auth_type=auth_type,
authorizer_uri=authorizer_uri,
authorizer_credentials=authorizer_credentials,
identity_source=identity_source,
identiy_validation_expression=identiy_validation_expression,
authorizer_result_ttl=authorizer_result_ttl,
)
self.authorizers[id] = authorizer
return authorizer
def create_stage( def create_stage(
self, self,
name, name,
@ -518,6 +596,9 @@ class RestAPI(BaseModel):
def get_deployment(self, deployment_id): def get_deployment(self, deployment_id):
return self.deployments[deployment_id] return self.deployments[deployment_id]
def get_authorizers(self):
return list(self.authorizers.values())
def get_stages(self): def get_stages(self):
return list(self.stages.values()) return list(self.stages.values())
@ -613,6 +694,46 @@ class APIGatewayBackend(BaseBackend):
) )
return method return method
def get_authorizer(self, restapi_id, authorizer_id):
api = self.get_rest_api(restapi_id)
authorizer = api.authorizers.get(authorizer_id)
if authorizer is None:
raise AuthorizerNotFoundException()
else:
return authorizer
def get_authorizers(self, restapi_id):
api = self.get_rest_api(restapi_id)
return api.get_authorizers()
def create_authorizer(self, restapi_id, name, authorizer_type, **kwargs):
api = self.get_rest_api(restapi_id)
authorizer_id = create_id()
authorizer = api.create_authorizer(
authorizer_id,
name,
authorizer_type,
provider_arns=kwargs.get("provider_arns"),
auth_type=kwargs.get("auth_type"),
authorizer_uri=kwargs.get("authorizer_uri"),
authorizer_credentials=kwargs.get("authorizer_credentials"),
identity_source=kwargs.get("identity_source"),
identiy_validation_expression=kwargs.get("identiy_validation_expression"),
authorizer_result_ttl=kwargs.get("authorizer_result_ttl"),
)
return api.authorizers.get(authorizer["id"])
def update_authorizer(self, restapi_id, authorizer_id, patch_operations):
authorizer = self.get_authorizer(restapi_id, authorizer_id)
if not authorizer:
api = self.get_rest_api(restapi_id)
authorizer = api.authorizers[authorizer_id] = Authorizer()
return authorizer.apply_operations(patch_operations)
def delete_authorizer(self, restapi_id, authorizer_id):
api = self.get_rest_api(restapi_id)
del api.authorizers[authorizer_id]
def get_stage(self, function_id, stage_name): def get_stage(self, function_id, stage_name):
api = self.get_rest_api(function_id) api = self.get_rest_api(function_id)
stage = api.stages.get(stage_name) stage = api.stages.get(stage_name)

View File

@ -8,11 +8,13 @@ from .exceptions import (
ApiKeyNotFoundException, ApiKeyNotFoundException,
BadRequestException, BadRequestException,
CrossAccountNotAllowed, CrossAccountNotAllowed,
AuthorizerNotFoundException,
StageNotFoundException, StageNotFoundException,
ApiKeyAlreadyExists, ApiKeyAlreadyExists,
) )
API_KEY_SOURCES = ["AUTHORIZER", "HEADER"] API_KEY_SOURCES = ["AUTHORIZER", "HEADER"]
AUTHORIZER_TYPES = ["TOKEN", "REQUEST", "COGNITO_USER_POOLS"]
ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"] ENDPOINT_CONFIGURATION_TYPES = ["PRIVATE", "EDGE", "REGIONAL"]
@ -177,6 +179,88 @@ class APIGatewayResponse(BaseResponse):
) )
return 200, {}, json.dumps(method_response) return 200, {}, json.dumps(method_response)
def restapis_authorizers(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
url_path_parts = self.path.split("/")
restapi_id = url_path_parts[2]
if self.method == "POST":
name = self._get_param("name")
authorizer_type = self._get_param("type")
provider_arns = self._get_param_with_default_value("providerARNs", None)
auth_type = self._get_param_with_default_value("authType", None)
authorizer_uri = self._get_param_with_default_value("authorizerUri", None)
authorizer_credentials = self._get_param_with_default_value(
"authorizerCredentials", None
)
identity_source = self._get_param_with_default_value("identitySource", None)
identiy_validation_expression = self._get_param_with_default_value(
"identityValidationExpression", None
)
authorizer_result_ttl = self._get_param_with_default_value(
"authorizerResultTtlInSeconds", 300
)
# Param validation
if authorizer_type and authorizer_type not in AUTHORIZER_TYPES:
return self.error(
"ValidationException",
(
"1 validation error detected: "
"Value '{authorizer_type}' at 'createAuthorizerInput.type' failed "
"to satisfy constraint: Member must satisfy enum value set: "
"[TOKEN, REQUEST, COGNITO_USER_POOLS]"
).format(authorizer_type=authorizer_type),
)
authorizer_response = self.backend.create_authorizer(
restapi_id,
name,
authorizer_type,
provider_arns=provider_arns,
auth_type=auth_type,
authorizer_uri=authorizer_uri,
authorizer_credentials=authorizer_credentials,
identity_source=identity_source,
identiy_validation_expression=identiy_validation_expression,
authorizer_result_ttl=authorizer_result_ttl,
)
elif self.method == "GET":
authorizers = self.backend.get_authorizers(restapi_id)
return 200, {}, json.dumps({"item": authorizers})
return 200, {}, json.dumps(authorizer_response)
def authorizers(self, request, full_url, headers):
self.setup_class(request, full_url, headers)
url_path_parts = self.path.split("/")
restapi_id = url_path_parts[2]
authorizer_id = url_path_parts[4]
if self.method == "GET":
try:
authorizer_response = self.backend.get_authorizer(
restapi_id, authorizer_id
)
except AuthorizerNotFoundException as error:
return (
error.code,
{},
'{{"message":"{0}","code":"{1}"}}'.format(
error.message, error.error_type
),
)
elif self.method == "PATCH":
patch_operations = self._get_param("patchOperations")
authorizer_response = self.backend.update_authorizer(
restapi_id, authorizer_id, patch_operations
)
elif self.method == "DELETE":
self.backend.delete_authorizer(restapi_id, authorizer_id)
return 202, {}, "{}"
return 200, {}, json.dumps(authorizer_response)
def restapis_stages(self, request, full_url, headers): def restapis_stages(self, request, full_url, headers):
self.setup_class(request, full_url, headers) self.setup_class(request, full_url, headers)
url_path_parts = self.path.split("/") url_path_parts = self.path.split("/")

View File

@ -7,6 +7,8 @@ url_paths = {
"{0}/restapis$": APIGatewayResponse().restapis, "{0}/restapis$": APIGatewayResponse().restapis,
"{0}/restapis/(?P<function_id>[^/]+)/?$": APIGatewayResponse().restapis_individual, "{0}/restapis/(?P<function_id>[^/]+)/?$": APIGatewayResponse().restapis_individual,
"{0}/restapis/(?P<function_id>[^/]+)/resources$": APIGatewayResponse().resources, "{0}/restapis/(?P<function_id>[^/]+)/resources$": APIGatewayResponse().resources,
"{0}/restapis/(?P<function_id>[^/]+)/authorizers$": APIGatewayResponse().restapis_authorizers,
"{0}/restapis/(?P<function_id>[^/]+)/authorizers/(?P<authorizer_id>[^/]+)/?$": APIGatewayResponse().authorizers,
"{0}/restapis/(?P<function_id>[^/]+)/stages$": APIGatewayResponse().restapis_stages, "{0}/restapis/(?P<function_id>[^/]+)/stages$": APIGatewayResponse().restapis_stages,
"{0}/restapis/(?P<function_id>[^/]+)/stages/(?P<stage_name>[^/]+)/?$": APIGatewayResponse().stages, "{0}/restapis/(?P<function_id>[^/]+)/stages/(?P<stage_name>[^/]+)/?$": APIGatewayResponse().stages,
"{0}/restapis/(?P<function_id>[^/]+)/deployments$": APIGatewayResponse().deployments, "{0}/restapis/(?P<function_id>[^/]+)/deployments$": APIGatewayResponse().deployments,

View File

@ -1406,6 +1406,7 @@ class DynamoDBBackend(BaseBackend):
range_value = None range_value = None
item = table.get_item(hash_value, range_value) item = table.get_item(hash_value, range_value)
orig_item = copy.deepcopy(item)
if not expected: if not expected:
expected = {} expected = {}
@ -1439,6 +1440,8 @@ class DynamoDBBackend(BaseBackend):
) )
else: else:
item.update_with_attribute_updates(attribute_updates) item.update_with_attribute_updates(attribute_updates)
if table.stream_shard is not None:
table.stream_shard.add(orig_item, item)
return item return item
def delete_item( def delete_item(

View File

@ -86,6 +86,9 @@ class FakeStep(BaseModel):
self.start_datetime = None self.start_datetime = None
self.state = state self.state = state
def start(self):
self.start_datetime = datetime.now(pytz.utc)
class FakeCluster(BaseModel): class FakeCluster(BaseModel):
def __init__( def __init__(
@ -204,6 +207,8 @@ class FakeCluster(BaseModel):
self.start_cluster() self.start_cluster()
self.run_bootstrap_actions() self.run_bootstrap_actions()
if self.steps:
self.steps[0].start()
@property @property
def instance_groups(self): def instance_groups(self):

View File

@ -835,7 +835,7 @@ LIST_STEPS_TEMPLATE = """<ListStepsResponse xmlns="http://elasticmapreduce.amazo
{% if step.end_datetime is not none %} {% if step.end_datetime is not none %}
<EndDateTime>{{ step.end_datetime.isoformat() }}</EndDateTime> <EndDateTime>{{ step.end_datetime.isoformat() }}</EndDateTime>
{% endif %} {% endif %}
{% if step.ready_datetime is not none %} {% if step.start_datetime is not none %}
<StartDateTime>{{ step.start_datetime.isoformat() }}</StartDateTime> <StartDateTime>{{ step.start_datetime.isoformat() }}</StartDateTime>
{% endif %} {% endif %}
</Timeline> </Timeline>

View File

@ -8,7 +8,8 @@ from boto3 import Session
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.core.utils import unix_time from moto.core.utils import unix_time
from moto.utilities.tagging_service import TaggingService
from moto.core.exceptions import JsonRESTError
from moto.iam.models import ACCOUNT_ID from moto.iam.models import ACCOUNT_ID
from .utils import decrypt, encrypt, generate_key_id, generate_master_key from .utils import decrypt, encrypt, generate_key_id, generate_master_key
@ -16,7 +17,7 @@ from .utils import decrypt, encrypt, generate_key_id, generate_master_key
class Key(BaseModel): class Key(BaseModel):
def __init__( def __init__(
self, policy, key_usage, customer_master_key_spec, description, tags, region self, policy, key_usage, customer_master_key_spec, description, region
): ):
self.id = generate_key_id() self.id = generate_key_id()
self.creation_date = unix_time() self.creation_date = unix_time()
@ -29,7 +30,6 @@ class Key(BaseModel):
self.account_id = ACCOUNT_ID self.account_id = ACCOUNT_ID
self.key_rotation_status = False self.key_rotation_status = False
self.deletion_date = None self.deletion_date = None
self.tags = tags or {}
self.key_material = generate_master_key() self.key_material = generate_master_key()
self.origin = "AWS_KMS" self.origin = "AWS_KMS"
self.key_manager = "CUSTOMER" self.key_manager = "CUSTOMER"
@ -111,11 +111,12 @@ class Key(BaseModel):
key_usage="ENCRYPT_DECRYPT", key_usage="ENCRYPT_DECRYPT",
customer_master_key_spec="SYMMETRIC_DEFAULT", customer_master_key_spec="SYMMETRIC_DEFAULT",
description=properties["Description"], description=properties["Description"],
tags=properties.get("Tags"), tags=properties.get("Tags", []),
region=region_name, region=region_name,
) )
key.key_rotation_status = properties["EnableKeyRotation"] key.key_rotation_status = properties["EnableKeyRotation"]
key.enabled = properties["Enabled"] key.enabled = properties["Enabled"]
return key return key
def get_cfn_attribute(self, attribute_name): def get_cfn_attribute(self, attribute_name):
@ -130,32 +131,26 @@ class KmsBackend(BaseBackend):
def __init__(self): def __init__(self):
self.keys = {} self.keys = {}
self.key_to_aliases = defaultdict(set) self.key_to_aliases = defaultdict(set)
self.tagger = TaggingService(keyName="TagKey", valueName="TagValue")
def create_key( def create_key(
self, policy, key_usage, customer_master_key_spec, description, tags, region self, policy, key_usage, customer_master_key_spec, description, tags, region
): ):
key = Key( key = Key(policy, key_usage, customer_master_key_spec, description, region)
policy, key_usage, customer_master_key_spec, description, tags, region
)
self.keys[key.id] = key self.keys[key.id] = key
if tags is not None and len(tags) > 0:
self.tag_resource(key.id, tags)
return key return key
def update_key_description(self, key_id, description): def update_key_description(self, key_id, description):
key = self.keys[self.get_key_id(key_id)] key = self.keys[self.get_key_id(key_id)]
key.description = description key.description = description
def tag_resource(self, key_id, tags):
key = self.keys[self.get_key_id(key_id)]
key.tags = tags
def list_resource_tags(self, key_id):
key = self.keys[self.get_key_id(key_id)]
return key.tags
def delete_key(self, key_id): def delete_key(self, key_id):
if key_id in self.keys: if key_id in self.keys:
if key_id in self.key_to_aliases: if key_id in self.key_to_aliases:
self.key_to_aliases.pop(key_id) self.key_to_aliases.pop(key_id)
self.tagger.delete_all_tags_for_resource(key_id)
return self.keys.pop(key_id) return self.keys.pop(key_id)
@ -325,6 +320,32 @@ class KmsBackend(BaseBackend):
return plaintext, ciphertext_blob, arn return plaintext, ciphertext_blob, arn
def list_resource_tags(self, key_id):
if key_id in self.keys:
return self.tagger.list_tags_for_resource(key_id)
raise JsonRESTError(
"NotFoundException",
"The request was rejected because the specified entity or resource could not be found.",
)
def tag_resource(self, key_id, tags):
if key_id in self.keys:
self.tagger.tag_resource(key_id, tags)
return {}
raise JsonRESTError(
"NotFoundException",
"The request was rejected because the specified entity or resource could not be found.",
)
def untag_resource(self, key_id, tag_names):
if key_id in self.keys:
self.tagger.untag_resource_using_names(key_id, tag_names)
return {}
raise JsonRESTError(
"NotFoundException",
"The request was rejected because the specified entity or resource could not be found.",
)
kms_backends = {} kms_backends = {}
for region in Session().get_available_regions("kms"): for region in Session().get_available_regions("kms"):

View File

@ -144,17 +144,27 @@ class KmsResponse(BaseResponse):
self._validate_cmk_id(key_id) self._validate_cmk_id(key_id)
self.kms_backend.tag_resource(key_id, tags) result = self.kms_backend.tag_resource(key_id, tags)
return json.dumps({}) return json.dumps(result)
def untag_resource(self):
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_UntagResource.html"""
key_id = self.parameters.get("KeyId")
tag_names = self.parameters.get("TagKeys")
self._validate_cmk_id(key_id)
result = self.kms_backend.untag_resource(key_id, tag_names)
return json.dumps(result)
def list_resource_tags(self): def list_resource_tags(self):
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_ListResourceTags.html""" """https://docs.aws.amazon.com/kms/latest/APIReference/API_ListResourceTags.html"""
key_id = self.parameters.get("KeyId") key_id = self.parameters.get("KeyId")
self._validate_cmk_id(key_id) self._validate_cmk_id(key_id)
tags = self.kms_backend.list_resource_tags(key_id) tags = self.kms_backend.list_resource_tags(key_id)
return json.dumps({"Tags": tags, "NextMarker": None, "Truncated": False}) tags.update({"NextMarker": None, "Truncated": False})
return json.dumps(tags)
def describe_key(self): def describe_key(self):
"""https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html""" """https://docs.aws.amazon.com/kms/latest/APIReference/API_DescribeKey.html"""

View File

@ -318,7 +318,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
# KMS # KMS
def get_kms_tags(kms_key_id): def get_kms_tags(kms_key_id):
result = [] result = []
for tag in self.kms_backend.list_resource_tags(kms_key_id): for tag in self.kms_backend.list_resource_tags(kms_key_id).get("Tags", []):
result.append({"Key": tag["TagKey"], "Value": tag["TagValue"]}) result.append({"Key": tag["TagKey"], "Value": tag["TagValue"]})
return result return result

View File

@ -8,6 +8,8 @@ class WorkflowType(GenericType):
"defaultChildPolicy", "defaultChildPolicy",
"defaultExecutionStartToCloseTimeout", "defaultExecutionStartToCloseTimeout",
"defaultTaskStartToCloseTimeout", "defaultTaskStartToCloseTimeout",
"defaultTaskPriority",
"defaultLambdaRole",
] ]
@property @property

View File

@ -300,6 +300,8 @@ class SWFResponse(BaseResponse):
default_execution_start_to_close_timeout = self._params.get( default_execution_start_to_close_timeout = self._params.get(
"defaultExecutionStartToCloseTimeout" "defaultExecutionStartToCloseTimeout"
) )
default_task_priority = self._params.get("defaultTaskPriority")
default_lambda_role = self._params.get("defaultLambdaRole")
description = self._params.get("description") description = self._params.get("description")
self._check_string(domain) self._check_string(domain)
@ -309,10 +311,10 @@ class SWFResponse(BaseResponse):
self._check_none_or_string(default_child_policy) self._check_none_or_string(default_child_policy)
self._check_none_or_string(default_task_start_to_close_timeout) self._check_none_or_string(default_task_start_to_close_timeout)
self._check_none_or_string(default_execution_start_to_close_timeout) self._check_none_or_string(default_execution_start_to_close_timeout)
self._check_none_or_string(default_task_priority)
self._check_none_or_string(default_lambda_role)
self._check_none_or_string(description) self._check_none_or_string(description)
# TODO: add defaultTaskPriority when boto gets to support it
# TODO: add defaultLambdaRole when boto gets to support it
self.swf_backend.register_type( self.swf_backend.register_type(
"workflow", "workflow",
domain, domain,
@ -322,6 +324,8 @@ class SWFResponse(BaseResponse):
default_child_policy=default_child_policy, default_child_policy=default_child_policy,
default_task_start_to_close_timeout=default_task_start_to_close_timeout, default_task_start_to_close_timeout=default_task_start_to_close_timeout,
default_execution_start_to_close_timeout=default_execution_start_to_close_timeout, default_execution_start_to_close_timeout=default_execution_start_to_close_timeout,
default_task_priority=default_task_priority,
default_lambda_role=default_lambda_role,
description=description, description=description,
) )
return "" return ""

View File

@ -8,7 +8,7 @@ import sure # noqa
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
import responses import responses
from moto import mock_apigateway, settings from moto import mock_apigateway, mock_cognitoidp, settings
from moto.core import ACCOUNT_ID from moto.core import ACCOUNT_ID
from nose.tools import assert_raises from nose.tools import assert_raises
@ -576,6 +576,254 @@ def test_integration_response():
response["methodIntegration"]["integrationResponses"].should.equal({}) response["methodIntegration"]["integrationResponses"].should.equal({})
@mock_apigateway
@mock_cognitoidp
def test_update_authorizer_configuration():
client = boto3.client("apigateway", region_name="us-west-2")
authorizer_name = "my_authorizer"
response = client.create_rest_api(name="my_api", description="this is my api")
api_id = response["id"]
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
"UserPool"
]["Arn"]
response = client.create_authorizer(
restApiId=api_id,
name=authorizer_name,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id = response["id"]
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
# createdDate is hard to match against, remove it
response.pop("createdDate", None)
# this is hard to match against, so remove it
response["ResponseMetadata"].pop("HTTPHeaders", None)
response["ResponseMetadata"].pop("RetryAttempts", None)
response.should.equal(
{
"id": authorizer_id,
"name": authorizer_name,
"type": "COGNITO_USER_POOLS",
"providerARNs": [user_pool_arn],
"identitySource": "method.request.header.Authorization",
"authorizerResultTtlInSeconds": 300,
"ResponseMetadata": {"HTTPStatusCode": 200},
}
)
client.update_authorizer(
restApiId=api_id,
authorizerId=authorizer_id,
patchOperations=[{"op": "replace", "path": "/type", "value": "TOKEN"}],
)
authorizer = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
authorizer.should.have.key("type").which.should.equal("TOKEN")
client.update_authorizer(
restApiId=api_id,
authorizerId=authorizer_id,
patchOperations=[{"op": "replace", "path": "/type", "value": "REQUEST"}],
)
authorizer = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
authorizer.should.have.key("type").which.should.equal("REQUEST")
# TODO: implement mult-update tests
try:
client.update_authorizer(
restApiId=api_id,
authorizerId=authorizer_id,
patchOperations=[
{"op": "add", "path": "/notasetting", "value": "eu-west-1"}
],
)
assert False.should.be.ok # Fail, should not be here
except Exception:
assert True.should.be.ok
@mock_apigateway
def test_non_existent_authorizer():
client = boto3.client("apigateway", region_name="us-west-2")
response = client.create_rest_api(name="my_api", description="this is my api")
api_id = response["id"]
client.get_authorizer.when.called_with(
restApiId=api_id, authorizerId="xxx"
).should.throw(ClientError)
@mock_apigateway
@mock_cognitoidp
def test_create_authorizer():
client = boto3.client("apigateway", region_name="us-west-2")
authorizer_name = "my_authorizer"
response = client.create_rest_api(name="my_api", description="this is my api")
api_id = response["id"]
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
"UserPool"
]["Arn"]
response = client.create_authorizer(
restApiId=api_id,
name=authorizer_name,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id = response["id"]
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
# createdDate is hard to match against, remove it
response.pop("createdDate", None)
# this is hard to match against, so remove it
response["ResponseMetadata"].pop("HTTPHeaders", None)
response["ResponseMetadata"].pop("RetryAttempts", None)
response.should.equal(
{
"id": authorizer_id,
"name": authorizer_name,
"type": "COGNITO_USER_POOLS",
"providerARNs": [user_pool_arn],
"identitySource": "method.request.header.Authorization",
"authorizerResultTtlInSeconds": 300,
"ResponseMetadata": {"HTTPStatusCode": 200},
}
)
authorizer_name2 = "my_authorizer2"
response = client.create_authorizer(
restApiId=api_id,
name=authorizer_name2,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id2 = response["id"]
response = client.get_authorizers(restApiId=api_id)
# this is hard to match against, so remove it
response["ResponseMetadata"].pop("HTTPHeaders", None)
response["ResponseMetadata"].pop("RetryAttempts", None)
response["items"][0]["id"].should.match(
r"{0}|{1}".format(authorizer_id2, authorizer_id)
)
response["items"][1]["id"].should.match(
r"{0}|{1}".format(authorizer_id2, authorizer_id)
)
new_authorizer_name_with_vars = "authorizer_with_vars"
response = client.create_authorizer(
restApiId=api_id,
name=new_authorizer_name_with_vars,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id3 = response["id"]
# this is hard to match against, so remove it
response["ResponseMetadata"].pop("HTTPHeaders", None)
response["ResponseMetadata"].pop("RetryAttempts", None)
response.should.equal(
{
"name": new_authorizer_name_with_vars,
"id": authorizer_id3,
"type": "COGNITO_USER_POOLS",
"providerARNs": [user_pool_arn],
"identitySource": "method.request.header.Authorization",
"authorizerResultTtlInSeconds": 300,
"ResponseMetadata": {"HTTPStatusCode": 200},
}
)
stage = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id3)
stage["name"].should.equal(new_authorizer_name_with_vars)
stage["id"].should.equal(authorizer_id3)
stage["type"].should.equal("COGNITO_USER_POOLS")
stage["providerARNs"].should.equal([user_pool_arn])
stage["identitySource"].should.equal("method.request.header.Authorization")
stage["authorizerResultTtlInSeconds"].should.equal(300)
@mock_apigateway
@mock_cognitoidp
def test_delete_authorizer():
client = boto3.client("apigateway", region_name="us-west-2")
authorizer_name = "my_authorizer"
response = client.create_rest_api(name="my_api", description="this is my api")
api_id = response["id"]
cognito_client = boto3.client("cognito-idp", region_name="us-west-2")
user_pool_arn = cognito_client.create_user_pool(PoolName="my_cognito_pool")[
"UserPool"
]["Arn"]
response = client.create_authorizer(
restApiId=api_id,
name=authorizer_name,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id = response["id"]
response = client.get_authorizer(restApiId=api_id, authorizerId=authorizer_id)
# createdDate is hard to match against, remove it
response.pop("createdDate", None)
# this is hard to match against, so remove it
response["ResponseMetadata"].pop("HTTPHeaders", None)
response["ResponseMetadata"].pop("RetryAttempts", None)
response.should.equal(
{
"id": authorizer_id,
"name": authorizer_name,
"type": "COGNITO_USER_POOLS",
"providerARNs": [user_pool_arn],
"identitySource": "method.request.header.Authorization",
"authorizerResultTtlInSeconds": 300,
"ResponseMetadata": {"HTTPStatusCode": 200},
}
)
authorizer_name2 = "my_authorizer2"
response = client.create_authorizer(
restApiId=api_id,
name=authorizer_name2,
type="COGNITO_USER_POOLS",
providerARNs=[user_pool_arn],
identitySource="method.request.header.Authorization",
)
authorizer_id2 = response["id"]
authorizers = client.get_authorizers(restApiId=api_id)["items"]
sorted([authorizer["name"] for authorizer in authorizers]).should.equal(
sorted([authorizer_name2, authorizer_name])
)
# delete stage
response = client.delete_authorizer(restApiId=api_id, authorizerId=authorizer_id2)
response["ResponseMetadata"]["HTTPStatusCode"].should.equal(202)
# verify other stage still exists
authorizers = client.get_authorizers(restApiId=api_id)["items"]
sorted([authorizer["name"] for authorizer in authorizers]).should.equal(
sorted([authorizer_name])
)
@mock_apigateway @mock_apigateway
def test_update_stage_configuration(): def test_update_stage_configuration():
client = boto3.client("apigateway", region_name="us-west-2") client = boto3.client("apigateway", region_name="us-west-2")

View File

@ -1161,7 +1161,7 @@ def test_invoke_function_from_sqs():
@mock_logs @mock_logs
@mock_lambda @mock_lambda
@mock_dynamodb2 @mock_dynamodb2
def test_invoke_function_from_dynamodb(): def test_invoke_function_from_dynamodb_put():
logs_conn = boto3.client("logs", region_name="us-east-1") logs_conn = boto3.client("logs", region_name="us-east-1")
dynamodb = boto3.client("dynamodb", region_name="us-east-1") dynamodb = boto3.client("dynamodb", region_name="us-east-1")
table_name = "table_with_stream" table_name = "table_with_stream"
@ -1218,6 +1218,72 @@ def test_invoke_function_from_dynamodb():
assert False, "Test Failed" assert False, "Test Failed"
@mock_logs
@mock_lambda
@mock_dynamodb2
def test_invoke_function_from_dynamodb_update():
logs_conn = boto3.client("logs", region_name="us-east-1")
dynamodb = boto3.client("dynamodb", region_name="us-east-1")
table_name = "table_with_stream"
table = dynamodb.create_table(
TableName=table_name,
KeySchema=[{"AttributeName": "id", "KeyType": "HASH"}],
AttributeDefinitions=[{"AttributeName": "id", "AttributeType": "S"}],
StreamSpecification={
"StreamEnabled": True,
"StreamViewType": "NEW_AND_OLD_IMAGES",
},
)
dynamodb.put_item(TableName=table_name, Item={"id": {"S": "item 1"}})
conn = boto3.client("lambda", region_name="us-east-1")
func = conn.create_function(
FunctionName="testFunction",
Runtime="python2.7",
Role=get_role_name(),
Handler="lambda_function.lambda_handler",
Code={"ZipFile": get_test_zip_file3()},
Description="test lambda function executed after a DynamoDB table is updated",
Timeout=3,
MemorySize=128,
Publish=True,
)
response = conn.create_event_source_mapping(
EventSourceArn=table["TableDescription"]["LatestStreamArn"],
FunctionName=func["FunctionArn"],
)
assert response["EventSourceArn"] == table["TableDescription"]["LatestStreamArn"]
assert response["State"] == "Enabled"
dynamodb.update_item(
TableName=table_name,
Key={"id": {"S": "item 1"}},
UpdateExpression="set #attr = :val",
ExpressionAttributeNames={"#attr": "new_attr"},
ExpressionAttributeValues={":val": {"S": "new_val"}},
)
start = time.time()
while (time.time() - start) < 30:
result = logs_conn.describe_log_streams(logGroupName="/aws/lambda/testFunction")
log_streams = result.get("logStreams")
if not log_streams:
time.sleep(1)
continue
assert len(log_streams) == 1
result = logs_conn.get_log_events(
logGroupName="/aws/lambda/testFunction",
logStreamName=log_streams[0]["logStreamName"],
)
for event in result.get("events"):
if event["message"] == "get_test_zip_file3 success":
return
time.sleep(1)
assert False, "Test Failed"
@mock_logs @mock_logs
@mock_lambda @mock_lambda
@mock_sqs @mock_sqs

View File

@ -752,7 +752,9 @@ def test_steps():
# StateChangeReason # StateChangeReason
x["Status"]["Timeline"]["CreationDateTime"].should.be.a("datetime.datetime") x["Status"]["Timeline"]["CreationDateTime"].should.be.a("datetime.datetime")
# x['Status']['Timeline']['EndDateTime'].should.be.a('datetime.datetime') # x['Status']['Timeline']['EndDateTime'].should.be.a('datetime.datetime')
# x['Status']['Timeline']['StartDateTime'].should.be.a('datetime.datetime') # Only the first step will have started - we don't know anything about when it finishes, so the second step never starts
if x["Name"] == "My wordcount example":
x["Status"]["Timeline"]["StartDateTime"].should.be.a("datetime.datetime")
x = client.describe_step(ClusterId=cluster_id, StepId=x["Id"])["Step"] x = client.describe_step(ClusterId=cluster_id, StepId=x["Id"])["Step"]
x["ActionOnFailure"].should.equal("TERMINATE_CLUSTER") x["ActionOnFailure"].should.equal("TERMINATE_CLUSTER")

View File

@ -1,15 +1,15 @@
from moto.events.models import EventsBackend
from moto.events import mock_events
import json import json
import random import random
import unittest import unittest
import boto3 import boto3
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
from moto.core.exceptions import JsonRESTError
from nose.tools import assert_raises from nose.tools import assert_raises
from moto.core import ACCOUNT_ID from moto.core import ACCOUNT_ID
from moto.core.exceptions import JsonRESTError
from moto.events import mock_events
from moto.events.models import EventsBackend
RULES = [ RULES = [
{"Name": "test1", "ScheduleExpression": "rate(5 minutes)"}, {"Name": "test1", "ScheduleExpression": "rate(5 minutes)"},

View File

@ -4,15 +4,17 @@ import base64
import re import re
import boto.kms import boto.kms
import boto3
import six import six
import sure # noqa import sure # noqa
from boto.exception import JSONResponseError from boto.exception import JSONResponseError
from boto.kms.exceptions import AlreadyExistsException, NotFoundException from boto.kms.exceptions import AlreadyExistsException, NotFoundException
from nose.tools import assert_raises from nose.tools import assert_raises
from parameterized import parameterized from parameterized import parameterized
from moto.core.exceptions import JsonRESTError
from moto.kms.models import KmsBackend
from moto.kms.exceptions import NotFoundException as MotoNotFoundException from moto.kms.exceptions import NotFoundException as MotoNotFoundException
from moto import mock_kms_deprecated from moto import mock_kms_deprecated, mock_kms
PLAINTEXT_VECTORS = ( PLAINTEXT_VECTORS = (
(b"some encodeable plaintext",), (b"some encodeable plaintext",),
@ -679,3 +681,77 @@ def test__assert_default_policy():
_assert_default_policy.when.called_with("default").should_not.throw( _assert_default_policy.when.called_with("default").should_not.throw(
MotoNotFoundException MotoNotFoundException
) )
if six.PY2:
sort = sorted
else:
sort = lambda l: sorted(l, key=lambda d: d.keys())
@mock_kms
def test_key_tag_on_create_key_happy():
client = boto3.client("kms", region_name="us-east-1")
tags = [
{"TagKey": "key1", "TagValue": "value1"},
{"TagKey": "key2", "TagValue": "value2"},
]
key = client.create_key(Description="test-key-tagging", Tags=tags)
key_id = key["KeyMetadata"]["KeyId"]
result = client.list_resource_tags(KeyId=key_id)
actual = result.get("Tags", [])
assert sort(tags) == sort(actual)
client.untag_resource(KeyId=key_id, TagKeys=["key1"])
actual = client.list_resource_tags(KeyId=key_id).get("Tags", [])
expected = [{"TagKey": "key2", "TagValue": "value2"}]
assert sort(expected) == sort(actual)
@mock_kms
def test_key_tag_added_happy():
client = boto3.client("kms", region_name="us-east-1")
key = client.create_key(Description="test-key-tagging")
key_id = key["KeyMetadata"]["KeyId"]
tags = [
{"TagKey": "key1", "TagValue": "value1"},
{"TagKey": "key2", "TagValue": "value2"},
]
client.tag_resource(KeyId=key_id, Tags=tags)
result = client.list_resource_tags(KeyId=key_id)
actual = result.get("Tags", [])
assert sort(tags) == sort(actual)
client.untag_resource(KeyId=key_id, TagKeys=["key1"])
actual = client.list_resource_tags(KeyId=key_id).get("Tags", [])
expected = [{"TagKey": "key2", "TagValue": "value2"}]
assert sort(expected) == sort(actual)
@mock_kms_deprecated
def test_key_tagging_sad():
b = KmsBackend()
try:
b.tag_resource("unknown", [])
raise "tag_resource should fail if KeyId is not known"
except JsonRESTError:
pass
try:
b.untag_resource("unknown", [])
raise "untag_resource should fail if KeyId is not known"
except JsonRESTError:
pass
try:
b.list_resource_tags("unknown")
raise "list_resource_tags should fail if KeyId is not known"
except JsonRESTError:
pass

View File

@ -102,7 +102,7 @@ def test_deserialize_ciphertext_blob(raw, serialized):
@parameterized(((ec[0],) for ec in ENCRYPTION_CONTEXT_VECTORS)) @parameterized(((ec[0],) for ec in ENCRYPTION_CONTEXT_VECTORS))
def test_encrypt_decrypt_cycle(encryption_context): def test_encrypt_decrypt_cycle(encryption_context):
plaintext = b"some secret plaintext" plaintext = b"some secret plaintext"
master_key = Key("nop", "nop", "nop", "nop", [], "nop") master_key = Key("nop", "nop", "nop", "nop", "nop")
master_key_map = {master_key.id: master_key} master_key_map = {master_key.id: master_key}
ciphertext_blob = encrypt( ciphertext_blob = encrypt(
@ -133,7 +133,7 @@ def test_encrypt_unknown_key_id():
def test_decrypt_invalid_ciphertext_format(): def test_decrypt_invalid_ciphertext_format():
master_key = Key("nop", "nop", "nop", "nop", [], "nop") master_key = Key("nop", "nop", "nop", "nop", "nop")
master_key_map = {master_key.id: master_key} master_key_map = {master_key.id: master_key}
with assert_raises(InvalidCiphertextException): with assert_raises(InvalidCiphertextException):
@ -153,7 +153,7 @@ def test_decrypt_unknwown_key_id():
def test_decrypt_invalid_ciphertext(): def test_decrypt_invalid_ciphertext():
master_key = Key("nop", "nop", "nop", "nop", [], "nop") master_key = Key("nop", "nop", "nop", "nop", "nop")
master_key_map = {master_key.id: master_key} master_key_map = {master_key.id: master_key}
ciphertext_blob = ( ciphertext_blob = (
master_key.id.encode("utf-8") + b"123456789012" master_key.id.encode("utf-8") + b"123456789012"
@ -171,7 +171,7 @@ def test_decrypt_invalid_ciphertext():
def test_decrypt_invalid_encryption_context(): def test_decrypt_invalid_encryption_context():
plaintext = b"some secret plaintext" plaintext = b"some secret plaintext"
master_key = Key("nop", "nop", "nop", "nop", [], "nop") master_key = Key("nop", "nop", "nop", "nop", "nop")
master_key_map = {master_key.id: master_key} master_key_map = {master_key.id: master_key}
ciphertext_blob = encrypt( ciphertext_blob = encrypt(

View File

@ -1,7 +1,9 @@
import sure import sure
import boto import boto
import boto3
from moto import mock_swf_deprecated from moto import mock_swf_deprecated
from moto import mock_swf
from boto.swf.exceptions import SWFResponseError from boto.swf.exceptions import SWFResponseError
@ -133,6 +135,41 @@ def test_describe_workflow_type():
infos["status"].should.equal("REGISTERED") infos["status"].should.equal("REGISTERED")
@mock_swf
def test_describe_workflow_type_full_boto3():
# boto3 required as boto doesn't support all of the arguments
client = boto3.client("swf", region_name="us-east-1")
client.register_domain(
name="test-domain", workflowExecutionRetentionPeriodInDays="2"
)
client.register_workflow_type(
domain="test-domain",
name="test-workflow",
version="v1.0",
description="Test workflow.",
defaultTaskStartToCloseTimeout="20",
defaultExecutionStartToCloseTimeout="60",
defaultTaskList={"name": "foo"},
defaultTaskPriority="-2",
defaultChildPolicy="ABANDON",
defaultLambdaRole="arn:bar",
)
resp = client.describe_workflow_type(
domain="test-domain", workflowType={"name": "test-workflow", "version": "v1.0"}
)
resp["typeInfo"]["workflowType"]["name"].should.equal("test-workflow")
resp["typeInfo"]["workflowType"]["version"].should.equal("v1.0")
resp["typeInfo"]["status"].should.equal("REGISTERED")
resp["typeInfo"]["description"].should.equal("Test workflow.")
resp["configuration"]["defaultTaskStartToCloseTimeout"].should.equal("20")
resp["configuration"]["defaultExecutionStartToCloseTimeout"].should.equal("60")
resp["configuration"]["defaultTaskList"]["name"].should.equal("foo")
resp["configuration"]["defaultTaskPriority"].should.equal("-2")
resp["configuration"]["defaultChildPolicy"].should.equal("ABANDON")
resp["configuration"]["defaultLambdaRole"].should.equal("arn:bar")
@mock_swf_deprecated @mock_swf_deprecated
def test_describe_non_existent_workflow_type(): def test_describe_non_existent_workflow_type():
conn = boto.connect_swf("the_key", "the_secret") conn = boto.connect_swf("the_key", "the_secret")