diff --git a/moto/elbv2/exceptions.py b/moto/elbv2/exceptions.py index f67db5143..ccbfd06dd 100644 --- a/moto/elbv2/exceptions.py +++ b/moto/elbv2/exceptions.py @@ -190,3 +190,18 @@ class InvalidModifyRuleArgumentsError(ELBClientError): "ValidationError", "Either conditions or actions must be specified" ) + + +class InvalidStatusCodeActionTypeError(ELBClientError): + def __init__(self, msg): + super(InvalidStatusCodeActionTypeError, self).__init__( + "ValidationError", msg + ) + + +class InvalidLoadBalancerActionException(ELBClientError): + + def __init__(self, msg): + super(InvalidLoadBalancerActionException, self).__init__( + "InvalidLoadBalancerAction", msg + ) diff --git a/moto/elbv2/models.py b/moto/elbv2/models.py index 28c91c3e5..636cc56a1 100644 --- a/moto/elbv2/models.py +++ b/moto/elbv2/models.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import datetime import re from jinja2 import Template +from botocore.exceptions import ParamValidationError from moto.compat import OrderedDict from moto.core.exceptions import RESTError from moto.core import BaseBackend, BaseModel @@ -31,8 +32,8 @@ from .exceptions import ( RuleNotFoundError, DuplicatePriorityError, InvalidTargetGroupNameError, - InvalidModifyRuleArgumentsError -) + InvalidModifyRuleArgumentsError, + InvalidStatusCodeActionTypeError, InvalidLoadBalancerActionException) class FakeHealthStatus(BaseModel): @@ -488,11 +489,30 @@ class ELBv2Backend(BaseBackend): action_target_group_arn = action.data['target_group_arn'] if action_target_group_arn not in target_group_arns: raise ActionTargetGroupNotFoundError(action_target_group_arn) - elif action_type in ['redirect', 'authenticate-cognito', 'fixed-response']: + elif action_type == 'fixed-response': + self._validate_fixed_response_action(action, i, index) + elif action_type in ['redirect', 'authenticate-cognito']: pass else: raise InvalidActionTypeError(action_type, index) + def _validate_fixed_response_action(self, action, i, index): + status_code = action.data.get('fixed_response_config._status_code') + if status_code is None: + raise ParamValidationError( + report='Missing required parameter in Actions[%s].FixedResponseConfig: "StatusCode"' % i) + if not re.match(r'^(2|4|5)\d\d$', status_code): + raise InvalidStatusCodeActionTypeError( + "1 validation error detected: Value '%s' at 'actions.%s.member.fixedResponseConfig.statusCode' failed to satisfy constraint: \ +Member must satisfy regular expression pattern: ^(2|4|5)\d\d$" % (status_code, index) + ) + content_type = action.data['fixed_response_config._content_type'] + if content_type and content_type not in ['text/plain', 'text/css', 'text/html', 'application/javascript', + 'application/json']: + raise InvalidLoadBalancerActionException( + "The ContentType must be one of:'text/html', 'application/json', 'application/javascript', 'text/css', 'text/plain'" + ) + def create_target_group(self, name, **kwargs): if len(name) > 32: raise InvalidTargetGroupNameError( diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py index 538a2d911..97b876fec 100644 --- a/tests/test_elbv2/test_elbv2.py +++ b/tests/test_elbv2/test_elbv2.py @@ -4,7 +4,7 @@ import json import os import boto3 import botocore -from botocore.exceptions import ClientError +from botocore.exceptions import ClientError, ParamValidationError from nose.tools import assert_raises import sure # noqa @@ -2146,3 +2146,150 @@ def test_fixed_response_action_listener_rule_cloudformation(): 'StatusCode': '404', } },]) + + +@mock_elbv2 +@mock_ec2 +def test_fixed_response_action_listener_rule_validates_status_code(): + conn = boto3.client('elbv2', region_name='us-east-1') + ec2 = boto3.resource('ec2', region_name='us-east-1') + + security_group = ec2.create_security_group( + GroupName='a-security-group', Description='First One') + vpc = ec2.create_vpc(CidrBlock='172.28.7.0/24', InstanceTenancy='default') + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.192/26', + AvailabilityZone='us-east-1a') + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.128/26', + AvailabilityZone='us-east-1b') + + response = conn.create_load_balancer( + Name='my-lb', + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme='internal', + Tags=[{'Key': 'key_name', 'Value': 'a_value'}]) + load_balancer_arn = response.get('LoadBalancers')[0].get('LoadBalancerArn') + + missing_status_code_action = { + 'Type': 'fixed-response', + 'FixedResponseConfig': { + 'ContentType': 'text/plain', + 'MessageBody': 'This page does not exist', + } + } + with assert_raises(ParamValidationError): + conn.create_listener(LoadBalancerArn=load_balancer_arn, + Protocol='HTTP', + Port=80, + DefaultActions=[missing_status_code_action]) + + invalid_status_code_action = { + 'Type': 'fixed-response', + 'FixedResponseConfig': { + 'ContentType': 'text/plain', + 'MessageBody': 'This page does not exist', + 'StatusCode': '100' + } + } + + @mock_elbv2 + @mock_ec2 + def test_fixed_response_action_listener_rule_validates_status_code(): + conn = boto3.client('elbv2', region_name='us-east-1') + ec2 = boto3.resource('ec2', region_name='us-east-1') + + security_group = ec2.create_security_group( + GroupName='a-security-group', Description='First One') + vpc = ec2.create_vpc(CidrBlock='172.28.7.0/24', InstanceTenancy='default') + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.192/26', + AvailabilityZone='us-east-1a') + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.128/26', + AvailabilityZone='us-east-1b') + + response = conn.create_load_balancer( + Name='my-lb', + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme='internal', + Tags=[{'Key': 'key_name', 'Value': 'a_value'}]) + load_balancer_arn = response.get('LoadBalancers')[0].get('LoadBalancerArn') + + missing_status_code_action = { + 'Type': 'fixed-response', + 'FixedResponseConfig': { + 'ContentType': 'text/plain', + 'MessageBody': 'This page does not exist', + } + } + with assert_raises(ParamValidationError): + conn.create_listener(LoadBalancerArn=load_balancer_arn, + Protocol='HTTP', + Port=80, + DefaultActions=[missing_status_code_action]) + + invalid_status_code_action = { + 'Type': 'fixed-response', + 'FixedResponseConfig': { + 'ContentType': 'text/plain', + 'MessageBody': 'This page does not exist', + 'StatusCode': '100' + } + } + + with assert_raises(ClientError) as invalid_status_code_exception: + conn.create_listener(LoadBalancerArn=load_balancer_arn, + Protocol='HTTP', + Port=80, + DefaultActions=[invalid_status_code_action]) + + invalid_status_code_exception.exception.response['Error']['Code'].should.equal('ValidationError') + + +@mock_elbv2 +@mock_ec2 +def test_fixed_response_action_listener_rule_validates_content_type(): + conn = boto3.client('elbv2', region_name='us-east-1') + ec2 = boto3.resource('ec2', region_name='us-east-1') + + security_group = ec2.create_security_group( + GroupName='a-security-group', Description='First One') + vpc = ec2.create_vpc(CidrBlock='172.28.7.0/24', InstanceTenancy='default') + subnet1 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.192/26', + AvailabilityZone='us-east-1a') + subnet2 = ec2.create_subnet( + VpcId=vpc.id, + CidrBlock='172.28.7.128/26', + AvailabilityZone='us-east-1b') + + response = conn.create_load_balancer( + Name='my-lb', + Subnets=[subnet1.id, subnet2.id], + SecurityGroups=[security_group.id], + Scheme='internal', + Tags=[{'Key': 'key_name', 'Value': 'a_value'}]) + load_balancer_arn = response.get('LoadBalancers')[0].get('LoadBalancerArn') + + invalid_content_type_action = { + 'Type': 'fixed-response', + 'FixedResponseConfig': { + 'ContentType': 'Fake content type', + 'MessageBody': 'This page does not exist', + 'StatusCode': '200' + } + } + with assert_raises(ClientError) as invalid_content_type_exception: + conn.create_listener(LoadBalancerArn=load_balancer_arn, + Protocol='HTTP', + Port=80, + DefaultActions=[invalid_content_type_action]) + invalid_content_type_exception.exception.response['Error']['Code'].should.equal('InvalidLoadBalancerAction')