From e033d6cd68bdabba7710ebea62822ca2f1b4dd5d Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Wed, 24 Jun 2020 09:50:55 +0100 Subject: [PATCH 1/8] Update README -'mock_dynamodb' is deprecated --- README.md | 178 +++++++++++++++++++++++++++--------------------------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 6fb942aef..7a2862744 100644 --- a/README.md +++ b/README.md @@ -58,95 +58,95 @@ With the decorator wrapping the test, all the calls to s3 are automatically mock It gets even better! Moto isn't just for Python code and it isn't just for S3. Look at the [standalone server mode](https://github.com/spulec/moto#stand-alone-server-mode) for more information about running Moto with other languages. Here's the status of the other AWS services implemented: ```gherkin -|-------------------------------------------------------------------------------------| -| Service Name | Decorator | Development Status | -|-------------------------------------------------------------------------------------| -| ACM | @mock_acm | all endpoints done | -|-------------------------------------------------------------------------------------| -| API Gateway | @mock_apigateway | core endpoints done | -|-------------------------------------------------------------------------------------| -| Autoscaling | @mock_autoscaling | core endpoints done | -|-------------------------------------------------------------------------------------| -| Cloudformation | @mock_cloudformation | core endpoints done | -|-------------------------------------------------------------------------------------| -| Cloudwatch | @mock_cloudwatch | basic endpoints done | -|-------------------------------------------------------------------------------------| -| CloudwatchEvents | @mock_events | all endpoints done | -|-------------------------------------------------------------------------------------| -| Cognito Identity | @mock_cognitoidentity | basic endpoints done | -|-------------------------------------------------------------------------------------| -| Cognito Identity Provider | @mock_cognitoidp | basic endpoints done | -|-------------------------------------------------------------------------------------| -| Config | @mock_config | basic endpoints done | -| | | core endpoints done | -|-------------------------------------------------------------------------------------| -| Data Pipeline | @mock_datapipeline | basic endpoints done | -|-------------------------------------------------------------------------------------| -| DynamoDB | @mock_dynamodb | core endpoints done | -| DynamoDB2 | @mock_dynamodb2 | all endpoints + partial indexes | -|-------------------------------------------------------------------------------------| -| EC2 | @mock_ec2 | core endpoints done | -| - AMI | | core endpoints done | -| - EBS | | core endpoints done | -| - Instances | | all endpoints done | -| - Security Groups | | core endpoints done | -| - Tags | | all endpoints done | -|-------------------------------------------------------------------------------------| -| ECR | @mock_ecr | basic endpoints done | -|-------------------------------------------------------------------------------------| -| ECS | @mock_ecs | basic endpoints done | -|-------------------------------------------------------------------------------------| -| ELB | @mock_elb | core endpoints done | -|-------------------------------------------------------------------------------------| -| ELBv2 | @mock_elbv2 | all endpoints done | -|-------------------------------------------------------------------------------------| -| EMR | @mock_emr | core endpoints done | -|-------------------------------------------------------------------------------------| -| Glacier | @mock_glacier | core endpoints done | -|-------------------------------------------------------------------------------------| -| IAM | @mock_iam | core endpoints done | -|-------------------------------------------------------------------------------------| -| IoT | @mock_iot | core endpoints done | -| | @mock_iotdata | core endpoints done | -|-------------------------------------------------------------------------------------| -| Kinesis | @mock_kinesis | core endpoints done | -|-------------------------------------------------------------------------------------| -| KMS | @mock_kms | basic endpoints done | -|-------------------------------------------------------------------------------------| -| Lambda | @mock_lambda | basic endpoints done, requires | -| | | docker | -|-------------------------------------------------------------------------------------| -| Logs | @mock_logs | basic endpoints done | -|-------------------------------------------------------------------------------------| -| Organizations | @mock_organizations | some core endpoints done | -|-------------------------------------------------------------------------------------| -| Polly | @mock_polly | all endpoints done | -|-------------------------------------------------------------------------------------| -| RDS | @mock_rds | core endpoints done | -|-------------------------------------------------------------------------------------| -| RDS2 | @mock_rds2 | core endpoints done | -|-------------------------------------------------------------------------------------| -| Redshift | @mock_redshift | core endpoints done | -|-------------------------------------------------------------------------------------| -| Route53 | @mock_route53 | core endpoints done | -|-------------------------------------------------------------------------------------| -| S3 | @mock_s3 | core endpoints done | -|-------------------------------------------------------------------------------------| -| SecretsManager | @mock_secretsmanager | basic endpoints done | -|-------------------------------------------------------------------------------------| -| SES | @mock_ses | all endpoints done | -|-------------------------------------------------------------------------------------| -| SNS | @mock_sns | all endpoints done | -|-------------------------------------------------------------------------------------| -| SQS | @mock_sqs | core endpoints done | -|-------------------------------------------------------------------------------------| -| SSM | @mock_ssm | core endpoints done | -|-------------------------------------------------------------------------------------| -| STS | @mock_sts | core endpoints done | -|-------------------------------------------------------------------------------------| -| SWF | @mock_swf | basic endpoints done | -|-------------------------------------------------------------------------------------| -| X-Ray | @mock_xray | all endpoints done | +|-------------------------------------------------------------------------------------|-----------------------------| +| Service Name | Decorator | Development Status | Comment | +|-------------------------------------------------------------------------------------| | +| ACM | @mock_acm | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| API Gateway | @mock_apigateway | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Autoscaling | @mock_autoscaling | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Cloudformation | @mock_cloudformation | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Cloudwatch | @mock_cloudwatch | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| CloudwatchEvents | @mock_events | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| Cognito Identity | @mock_cognitoidentity | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| Cognito Identity Provider | @mock_cognitoidp | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| Config | @mock_config | basic endpoints done | | +| | | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Data Pipeline | @mock_datapipeline | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| DynamoDB | @mock_dynamodb | core endpoints done | API 20111205. Deprecated. | +| DynamoDB2 | @mock_dynamodb2 | all endpoints + partial indexes | API 20120810 (Latest) | +|-------------------------------------------------------------------------------------| | +| EC2 | @mock_ec2 | core endpoints done | | +| - AMI | | core endpoints done | | +| - EBS | | core endpoints done | | +| - Instances | | all endpoints done | | +| - Security Groups | | core endpoints done | | +| - Tags | | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| ECR | @mock_ecr | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| ECS | @mock_ecs | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| ELB | @mock_elb | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| ELBv2 | @mock_elbv2 | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| EMR | @mock_emr | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Glacier | @mock_glacier | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| IAM | @mock_iam | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| IoT | @mock_iot | core endpoints done | | +| | @mock_iotdata | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Kinesis | @mock_kinesis | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| KMS | @mock_kms | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| Lambda | @mock_lambda | basic endpoints done, requires | | +| | | docker | | +|-------------------------------------------------------------------------------------| | +| Logs | @mock_logs | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| Organizations | @mock_organizations | some core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Polly | @mock_polly | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| RDS | @mock_rds | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| RDS2 | @mock_rds2 | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Redshift | @mock_redshift | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| Route53 | @mock_route53 | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| S3 | @mock_s3 | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| SecretsManager | @mock_secretsmanager | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| SES | @mock_ses | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| SNS | @mock_sns | all endpoints done | | +|-------------------------------------------------------------------------------------| | +| SQS | @mock_sqs | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| SSM | @mock_ssm | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| STS | @mock_sts | core endpoints done | | +|-------------------------------------------------------------------------------------| | +| SWF | @mock_swf | basic endpoints done | | +|-------------------------------------------------------------------------------------| | +| X-Ray | @mock_xray | all endpoints done | | |-------------------------------------------------------------------------------------| ``` From 80c53d8b5a10f5e01d3fd16d9f31218bb69c05bb Mon Sep 17 00:00:00 2001 From: Shane Dowling Date: Fri, 26 Jun 2020 14:01:57 +0100 Subject: [PATCH 2/8] Add support for template urls in cfn validation (#3089) Added as boto supports both TemplateBody and TemplateUrl * Adds TemplateURL as a validate_template option * Adds a test to validate this --- moto/cloudformation/responses.py | 13 ++++++++----- tests/test_cloudformation/test_validate.py | 19 +++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/moto/cloudformation/responses.py b/moto/cloudformation/responses.py index cceedc86e..c4a085705 100644 --- a/moto/cloudformation/responses.py +++ b/moto/cloudformation/responses.py @@ -351,18 +351,21 @@ class CloudFormationResponse(BaseResponse): return template.render(exports=exports, next_token=next_token) def validate_template(self): - cfn_lint = self.cloudformation_backend.validate_template( - self._get_param("TemplateBody") - ) + template_body = self._get_param("TemplateBody") + template_url = self._get_param("TemplateURL") + if template_url: + template_body = self._get_stack_from_s3_url(template_url) + + cfn_lint = self.cloudformation_backend.validate_template(template_body) if cfn_lint: raise ValidationError(cfn_lint[0].message) description = "" try: - description = json.loads(self._get_param("TemplateBody"))["Description"] + description = json.loads(template_body)["Description"] except (ValueError, KeyError): pass try: - description = yaml.load(self._get_param("TemplateBody"))["Description"] + description = yaml.load(template_body)["Description"] except (yaml.ParserError, KeyError): pass template = self.response_template(VALIDATE_STACK_RESPONSE_TEMPLATE) diff --git a/tests/test_cloudformation/test_validate.py b/tests/test_cloudformation/test_validate.py index 5ffaeafb9..19dec46ef 100644 --- a/tests/test_cloudformation/test_validate.py +++ b/tests/test_cloudformation/test_validate.py @@ -96,6 +96,25 @@ def test_boto3_yaml_validate_successful(): assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 +@mock_cloudformation +@mock_s3 +def test_boto3_yaml_validate_template_url_successful(): + s3 = boto3.client("s3") + s3_conn = boto3.resource("s3", region_name="us-east-1") + s3_conn.create_bucket(Bucket="foobar") + + s3_conn.Object("foobar", "template-key").put(Body=yaml_template) + key_url = s3.generate_presigned_url( + ClientMethod="get_object", Params={"Bucket": "foobar", "Key": "template-key"} + ) + + cf_conn = boto3.client("cloudformation", region_name="us-east-1") + response = cf_conn.validate_template(TemplateURL=key_url) + assert response["Description"] == "Simple CloudFormation Test Template" + assert response["Parameters"] == [] + assert response["ResponseMetadata"]["HTTPStatusCode"] == 200 + + @mock_cloudformation def test_boto3_yaml_invalid_missing_resource(): cf_conn = boto3.client("cloudformation", region_name="us-east-1") From 73813460b66119dd2491f286a01bf07e0b18d193 Mon Sep 17 00:00:00 2001 From: Adrian <40185566+adriank-convoy@users.noreply.github.com> Date: Sat, 27 Jun 2020 01:42:32 -0700 Subject: [PATCH 3/8] Fix condition filtering bug in elbv2.create_rule() (#3092) * Fix condition filtering bug * Update test_handle_listener_rules unit test * Run black --- moto/elbv2/responses.py | 4 ++-- tests/test_elbv2/test_elbv2.py | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/moto/elbv2/responses.py b/moto/elbv2/responses.py index 922de96d4..68fbc8816 100644 --- a/moto/elbv2/responses.py +++ b/moto/elbv2/responses.py @@ -158,7 +158,7 @@ class ELBV2Response(BaseResponse): condition = {} condition["field"] = _condition["field"] values = sorted( - [e for e in _condition.items() if e[0].startswith("values.member")], + [e for e in _condition.items() if "values.member" in e[0]], key=lambda x: x[0], ) condition["values"] = [e[1] for e in values] @@ -356,7 +356,7 @@ class ELBV2Response(BaseResponse): condition = {} condition["field"] = _condition["field"] values = sorted( - [e for e in _condition.items() if e[0].startswith("values.member")], + [e for e in _condition.items() if "values.member" in e[0]], key=lambda x: x[0], ) condition["values"] = [e[1] for e in values] diff --git a/tests/test_elbv2/test_elbv2.py b/tests/test_elbv2/test_elbv2.py index af1b19f09..c155cba20 100644 --- a/tests/test_elbv2/test_elbv2.py +++ b/tests/test_elbv2/test_elbv2.py @@ -994,12 +994,17 @@ def test_handle_listener_rules(): priority = 100 host = "xxx.example.com" path_pattern = "foobar" + pathpatternconfig_pattern = "foobar2" created_rule = conn.create_rule( ListenerArn=http_listener_arn, Priority=priority, Conditions=[ {"Field": "host-header", "Values": [host]}, {"Field": "path-pattern", "Values": [path_pattern]}, + { + "Field": "path-pattern", + "PathPatternConfig": {"Values": [pathpatternconfig_pattern]}, + }, ], Actions=[ {"TargetGroupArn": target_group.get("TargetGroupArn"), "Type": "forward"} @@ -1017,6 +1022,10 @@ def test_handle_listener_rules(): Conditions=[ {"Field": "host-header", "Values": [host]}, {"Field": "path-pattern", "Values": [path_pattern]}, + { + "Field": "path-pattern", + "PathPatternConfig": {"Values": [pathpatternconfig_pattern]}, + }, ], Actions=[ {"TargetGroupArn": target_group.get("TargetGroupArn"), "Type": "forward"} @@ -1031,6 +1040,10 @@ def test_handle_listener_rules(): Conditions=[ {"Field": "host-header", "Values": [host]}, {"Field": "path-pattern", "Values": [path_pattern]}, + { + "Field": "path-pattern", + "PathPatternConfig": {"Values": [pathpatternconfig_pattern]}, + }, ], Actions=[ { @@ -1079,11 +1092,16 @@ def test_handle_listener_rules(): # modify rule partially new_host = "new.example.com" new_path_pattern = "new_path" + new_pathpatternconfig_pattern = "new_path2" modified_rule = conn.modify_rule( RuleArn=first_rule["RuleArn"], Conditions=[ {"Field": "host-header", "Values": [new_host]}, {"Field": "path-pattern", "Values": [new_path_pattern]}, + { + "Field": "path-pattern", + "PathPatternConfig": {"Values": [new_pathpatternconfig_pattern]}, + }, ], )["Rules"][0] @@ -1092,6 +1110,9 @@ def test_handle_listener_rules(): modified_rule.should.equal(obtained_rule) obtained_rule["Conditions"][0]["Values"][0].should.equal(new_host) obtained_rule["Conditions"][1]["Values"][0].should.equal(new_path_pattern) + obtained_rule["Conditions"][2]["Values"][0].should.equal( + new_pathpatternconfig_pattern + ) obtained_rule["Actions"][0]["TargetGroupArn"].should.equal( target_group.get("TargetGroupArn") ) From 7d43a1d23de705e346322e858032af643d018425 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sat, 27 Jun 2020 15:11:41 +0100 Subject: [PATCH 4/8] Store Region-info in UserAgent-header --- moto/core/models.py | 3 +++ moto/core/responses.py | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/moto/core/models.py b/moto/core/models.py index ba4564e4a..c8ee1709b 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -10,6 +10,7 @@ import six import types from io import BytesIO from collections import defaultdict +from botocore.config import Config from botocore.handlers import BUILTIN_HANDLERS from botocore.awsrequest import AWSResponse from six.moves.urllib.parse import urlparse @@ -416,6 +417,8 @@ class ServerModeMockAWS(BaseMockAWS): import mock def fake_boto3_client(*args, **kwargs): + service, region = args + kwargs["config"] = Config(user_agent_extra="region/"+region) if "endpoint_url" not in kwargs: kwargs["endpoint_url"] = "http://localhost:5000" return real_boto3_client(*args, **kwargs) diff --git a/moto/core/responses.py b/moto/core/responses.py index c52e89898..690964df0 100644 --- a/moto/core/responses.py +++ b/moto/core/responses.py @@ -188,6 +188,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): default_region = "us-east-1" # to extract region, use [^.] region_regex = re.compile(r"\.(?P[a-z]{2}-[a-z]+-\d{1})\.amazonaws\.com") + region_from_useragent_regex = re.compile(r"region/(?P[a-z]{2}-[a-z]+-\d{1})") param_list_regex = re.compile(r"(.*)\.(\d+)\.") access_key_regex = re.compile( r"AWS.*(?P(? Date: Sat, 27 Jun 2020 19:05:34 +0100 Subject: [PATCH 5/8] Linting --- moto/core/models.py | 2 +- moto/core/responses.py | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/moto/core/models.py b/moto/core/models.py index c8ee1709b..8a8bd5110 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -418,7 +418,7 @@ class ServerModeMockAWS(BaseMockAWS): def fake_boto3_client(*args, **kwargs): service, region = args - kwargs["config"] = Config(user_agent_extra="region/"+region) + kwargs["config"] = Config(user_agent_extra="region/" + region) if "endpoint_url" not in kwargs: kwargs["endpoint_url"] = "http://localhost:5000" return real_boto3_client(*args, **kwargs) diff --git a/moto/core/responses.py b/moto/core/responses.py index 690964df0..676d7549d 100644 --- a/moto/core/responses.py +++ b/moto/core/responses.py @@ -188,7 +188,9 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin): default_region = "us-east-1" # to extract region, use [^.] region_regex = re.compile(r"\.(?P[a-z]{2}-[a-z]+-\d{1})\.amazonaws\.com") - region_from_useragent_regex = re.compile(r"region/(?P[a-z]{2}-[a-z]+-\d{1})") + region_from_useragent_regex = re.compile( + r"region/(?P[a-z]{2}-[a-z]+-\d{1})" + ) param_list_regex = re.compile(r"(.*)\.(\d+)\.") access_key_regex = re.compile( r"AWS.*(?P(? Date: Sat, 27 Jun 2020 19:46:26 +0100 Subject: [PATCH 6/8] Get region from args or kwargs --- moto/core/models.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/moto/core/models.py b/moto/core/models.py index 8a8bd5110..235ee8599 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -417,8 +417,9 @@ class ServerModeMockAWS(BaseMockAWS): import mock def fake_boto3_client(*args, **kwargs): - service, region = args - kwargs["config"] = Config(user_agent_extra="region/" + region) + region = self._get_region(*args, **kwargs) + if region: + kwargs["config"] = Config(user_agent_extra="region/" + region) if "endpoint_url" not in kwargs: kwargs["endpoint_url"] = "http://localhost:5000" return real_boto3_client(*args, **kwargs) @@ -466,6 +467,14 @@ class ServerModeMockAWS(BaseMockAWS): if six.PY2: self._httplib_patcher.start() + def _get_region(self, *args, **kwargs): + if "region_name" in kwargs: + return kwargs["region_name"] + if type(args) == tuple: + service, region = args + return region + return None + def disable_patching(self): if self._client_patcher: self._client_patcher.stop() From f963d2ebaab54c223ac78d6a56d60379f576d46b Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sat, 27 Jun 2020 20:13:42 +0100 Subject: [PATCH 7/8] Allow service-invocations without region (S3, e.g.) --- moto/core/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/core/models.py b/moto/core/models.py index 235ee8599..d7f96fe37 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -470,7 +470,7 @@ class ServerModeMockAWS(BaseMockAWS): def _get_region(self, *args, **kwargs): if "region_name" in kwargs: return kwargs["region_name"] - if type(args) == tuple: + if type(args) == tuple and len(args) == 2: service, region = args return region return None From 8ff32bf4fab28b02877bce5e2b414805c3969ef4 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Tue, 30 Jun 2020 15:00:08 +0100 Subject: [PATCH 8/8] Append region-info to UserAgent-header, if it already exists --- moto/core/models.py | 6 ++++- tests/test_cognitoidp/test_cognitoidp.py | 32 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/moto/core/models.py b/moto/core/models.py index d7f96fe37..26ee1a1f5 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -419,7 +419,11 @@ class ServerModeMockAWS(BaseMockAWS): def fake_boto3_client(*args, **kwargs): region = self._get_region(*args, **kwargs) if region: - kwargs["config"] = Config(user_agent_extra="region/" + region) + if "config" in kwargs: + kwargs["config"].__dict__["user_agent_extra"] += " region/" + region + else: + config = Config(user_agent_extra="region/" + region) + kwargs["config"] = config if "endpoint_url" not in kwargs: kwargs["endpoint_url"] = "http://localhost:5000" return real_boto3_client(*args, **kwargs) diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py index 37e1a56a3..9c4b8de49 100644 --- a/tests/test_cognitoidp/test_cognitoidp.py +++ b/tests/test_cognitoidp/test_cognitoidp.py @@ -1243,6 +1243,38 @@ def test_change_password(): result["AuthenticationResult"].should_not.be.none +@mock_cognitoidp +def test_change_password__using_custom_user_agent_header(): + # https://github.com/spulec/moto/issues/3098 + # As the admin_initiate_auth-method is unauthenticated, we use the user-agent header to pass in the region + # This test verifies this works, even if we pass in our own user-agent header + from botocore.config import Config + + my_config = Config(user_agent_extra="more/info", signature_version="v4") + conn = boto3.client("cognito-idp", "us-west-2", config=my_config) + + outputs = authentication_flow(conn) + + # Take this opportunity to test change_password, which requires an access token. + newer_password = str(uuid.uuid4()) + conn.change_password( + AccessToken=outputs["access_token"], + PreviousPassword=outputs["password"], + ProposedPassword=newer_password, + ) + + # Log in again, which should succeed without a challenge because the user is no + # longer in the force-new-password state. + result = conn.admin_initiate_auth( + UserPoolId=outputs["user_pool_id"], + ClientId=outputs["client_id"], + AuthFlow="ADMIN_NO_SRP_AUTH", + AuthParameters={"USERNAME": outputs["username"], "PASSWORD": newer_password}, + ) + + result["AuthenticationResult"].should_not.be.none + + @mock_cognitoidp def test_forgot_password(): conn = boto3.client("cognito-idp", "us-west-2")