Repository: getmoto/moto
Pull request #6553 (2023-07-22 07:23:40)

Deny login if user account is disabled
When a user is disabled, it should raise a `NotAuthorizedException` exception when attempting to login


Patch:
diff --git a/moto/backend_index.py b/moto/backend_index.py
index 9f99ed313..477175fb2 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -110,9 +110,12 @@ backend_url_patterns = [
     ),
     (
         "meteringmarketplace",
-        re.compile("https?://metering.marketplace.(.+).amazonaws.com"),
+        re.compile("https?://metering.marketplace\\.(.+)\\.amazonaws.com"),
+    ),
+    (
+        "meteringmarketplace",
+        re.compile("https?://aws-marketplace\\.(.+)\\.amazonaws.com"),
     ),
-    ("meteringmarketplace", re.compile("https?://aws-marketplace.(.+).amazonaws.com")),
     ("mq", re.compile("https?://mq\\.(.+)\\.amazonaws\\.com")),
     ("opsworks", re.compile("https?://opsworks\\.us-east-1\\.amazonaws.com")),
     ("organizations", re.compile("https?://organizations\\.(.+)\\.amazonaws\\.com")),
diff --git a/moto/cognitoidp/models.py b/moto/cognitoidp/models.py
index 624b27d7c..4772fd3eb 100644
--- a/moto/cognitoidp/models.py
+++ b/moto/cognitoidp/models.py
@@ -1422,6 +1422,9 @@ class CognitoIdpBackend(BaseBackend):
             password: str = auth_parameters.get("PASSWORD")  # type: ignore[assignment]
             user = self.admin_get_user(user_pool_id, username)
 
+            if not user.enabled:
+                raise NotAuthorizedError("User is disabled.")
+
             if user.password != password:
                 raise NotAuthorizedError(username)
 
@@ -1813,6 +1816,9 @@ class CognitoIdpBackend(BaseBackend):
 
             user = self.admin_get_user(user_pool.id, username)
 
+            if not user.enabled:
+                raise NotAuthorizedError("User is disabled.")
+
             if user.status is UserStatus.UNCONFIRMED:
                 raise UserNotConfirmedException("User is not confirmed.")
 
@@ -1839,6 +1845,9 @@ class CognitoIdpBackend(BaseBackend):
             if not user:
                 raise UserNotFoundError(username)
 
+            if not user.enabled:
+                raise NotAuthorizedError("User is disabled.")
+
             if user.password != password:
                 raise NotAuthorizedError("Incorrect username or password.")
 
diff --git a/moto/glue/glue_schema_registry_constants.py b/moto/glue/glue_schema_registry_constants.py
index 2dc709a97..7aef1186f 100644
--- a/moto/glue/glue_schema_registry_constants.py
+++ b/moto/glue/glue_schema_registry_constants.py
@@ -39,7 +39,7 @@ SCHEMA_VERSION_ID_PATTERN = re.compile(
     r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$"
 )
 MIN_SCHEMA_VERSION_ID_LENGTH = 36
-SCHEMA_VERSION_METADATA_PATTERN = re.compile(r"^[a-zA-Z0-9+-=._./@]+$")
+SCHEMA_VERSION_METADATA_PATTERN = re.compile(r"^[a-zA-Z0-9+=._/@-]+$")
 MAX_SCHEMA_VERSION_METADATA_ALLOWED = 10
 MAX_SCHEMA_VERSION_METADATA_LENGTH = 128
 METADATA_KEY = "MetadataKey"
diff --git a/moto/sqs/models.py b/moto/sqs/models.py
index 58d3bf52b..f9abd81b2 100644
--- a/moto/sqs/models.py
+++ b/moto/sqs/models.py
@@ -884,15 +884,8 @@ class SQSBackend(BaseBackend):
         ):
             raise InvalidBatchEntryId()
 
-        body_length = next(
-            (
-                len(entry["MessageBody"])
-                for entry in entries.values()
-                if len(entry["MessageBody"]) > MAXIMUM_MESSAGE_LENGTH
-            ),
-            False,
-        )
-        if body_length:
+        body_length = sum(len(entry["MessageBody"]) for entry in entries.values())
+        if body_length > MAXIMUM_MESSAGE_LENGTH:
             raise BatchRequestTooLong(body_length)
 
         duplicate_id = self._get_first_duplicate_id(
diff --git a/moto/ssm/models.py b/moto/ssm/models.py
index 1ffc1a9d5..f570edf32 100644
--- a/moto/ssm/models.py
+++ b/moto/ssm/models.py
@@ -1981,7 +1981,7 @@ class SimpleSystemManagerBackend(BaseBackend):
                 label.startswith("aws")
                 or label.startswith("ssm")
                 or label[:1].isdigit()
-                or not re.match(r"^[a-zA-z0-9_\.\-]*$", label)
+                or not re.match(r"^[a-zA-Z0-9_\.\-]*$", label)
             ):
                 invalid_labels.append(label)
                 continue


Test patch:
diff --git a/tests/test_cognitoidp/test_cognitoidp.py b/tests/test_cognitoidp/test_cognitoidp.py
index 462471f2e..0d8d44951 100644
--- a/tests/test_cognitoidp/test_cognitoidp.py
+++ b/tests/test_cognitoidp/test_cognitoidp.py
@@ -4375,6 +4375,60 @@ def test_admin_reset_password_multiple_invocations():
         assert user["UserStatus"] == "RESET_REQUIRED"
 
 
+@mock_cognitoidp
+def test_login_denied_if_account_disabled():
+    """Make sure a disabled account is denied login"""
+    conn = boto3.client("cognito-idp", "us-west-2")
+
+    username = str(uuid.uuid4())
+    temporary_password = "P2$Sword"
+    user_pool_id = conn.create_user_pool(PoolName=str(uuid.uuid4()))["UserPool"]["Id"]
+    user_attribute_name = str(uuid.uuid4())
+    user_attribute_value = str(uuid.uuid4())
+    client_id = conn.create_user_pool_client(
+        UserPoolId=user_pool_id,
+        ClientName=str(uuid.uuid4()),
+        ReadAttributes=[user_attribute_name],
+    )["UserPoolClient"]["ClientId"]
+
+    # Create a user and disable the account
+    conn.admin_create_user(
+        UserPoolId=user_pool_id,
+        Username=username,
+        TemporaryPassword=temporary_password,
+        UserAttributes=[{"Name": user_attribute_name, "Value": user_attribute_value}],
+    )
+    conn.admin_disable_user(
+        UserPoolId=user_pool_id,
+        Username=username,
+    )
+
+    # User should not be able to login into a disabled account
+    with pytest.raises(conn.exceptions.NotAuthorizedException) as ex:
+        conn.initiate_auth(
+            ClientId=client_id,
+            AuthFlow="USER_PASSWORD_AUTH",
+            AuthParameters={"USERNAME": username, "PASSWORD": temporary_password},
+        )
+
+    assert ex.value.response["Error"]["Code"] == "NotAuthorizedException"
+    assert ex.value.response["Error"]["Message"] == "User is disabled."
+    assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+
+    # Using admin login should yield the same result
+    with pytest.raises(conn.exceptions.NotAuthorizedException) as ex:
+        conn.admin_initiate_auth(
+            UserPoolId=user_pool_id,
+            ClientId=client_id,
+            AuthFlow="ADMIN_NO_SRP_AUTH",
+            AuthParameters={"USERNAME": username, "PASSWORD": temporary_password},
+        )
+
+    assert ex.value.response["Error"]["Code"] == "NotAuthorizedException"
+    assert ex.value.response["Error"]["Message"] == "User is disabled."
+    assert ex.value.response["ResponseMetadata"]["HTTPStatusCode"] == 400
+
+
 # Test will retrieve public key from cognito.amazonaws.com/.well-known/jwks.json,
 # which isnt mocked in ServerMode
 if not settings.TEST_SERVER_MODE: