From 7d43a1d23de705e346322e858032af643d018425 Mon Sep 17 00:00:00 2001 From: Bert Blommers Date: Sat, 27 Jun 2020 15:11:41 +0100 Subject: [PATCH 1/5] 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 ba4564e4aad3..c8ee1709bf76 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -10,6 +10,7 @@ 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 @@ def enable_patching(self): 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 c52e898982ca..690964df0b3d 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 2/5] 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 c8ee1709bf76..8a8bd5110ef4 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -418,7 +418,7 @@ def enable_patching(self): 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 690964df0b3d..676d7549d1de 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 3/5] 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 8a8bd5110ef4..235ee8599c78 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -417,8 +417,9 @@ def enable_patching(self): 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 @@ def _convert_to_bytes(mixed_buffer): 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 4/5] 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 235ee8599c78..d7f96fe37942 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -470,7 +470,7 @@ def _convert_to_bytes(mixed_buffer): 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 5/5] 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 d7f96fe37942..26ee1a1f5a29 100644 --- a/moto/core/models.py +++ b/moto/core/models.py @@ -419,7 +419,11 @@ def enable_patching(self): 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 37e1a56a3540..9c4b8de495d4 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")