Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Elasticache] CreateUser:Engine parameter is case-insensitive #8539

Merged
merged 11 commits into from
Feb 2, 2025
Merged
13 changes: 9 additions & 4 deletions moto/elasticache/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
PasswordTooShort,
)
from .models import ElastiCacheBackend, elasticache_backends
from .utils import AuthenticationTypes
from .utils import VALID_AUTH_MODE_KEYS, VALID_ENGINE_TYPES, AuthenticationTypes


class ElastiCacheResponse(BaseResponse):
Expand All @@ -24,7 +24,7 @@ def create_user(self) -> str:
params = self._get_params()
user_id = params.get("UserId")
user_name = params.get("UserName")
engine = params.get("Engine")
engine = params.get("Engine", "").lower()
passwords = params.get("Passwords", [])
no_password_required = self._get_bool_param("NoPasswordRequired")
authentication_mode = params.get("AuthenticationMode")
Expand All @@ -40,11 +40,16 @@ def create_user(self) -> str:
if passwords:
authentication_type = AuthenticationTypes.PASSWORD.value

if engine not in VALID_ENGINE_TYPES:
raise InvalidParameterValueException(
f'Unknown parameter for Engine: "{engine}", must be one of: {", ".join(VALID_ENGINE_TYPES)}'
)

if authentication_mode:
for key in authentication_mode.keys():
if key not in ["Type", "Passwords"]:
if key not in VALID_AUTH_MODE_KEYS:
raise InvalidParameterValueException(
f'Unknown parameter in AuthenticationMode: "{key}", must be one of: Type, Passwords'
f'Unknown parameter in AuthenticationMode: "{key}", must be one of: {", ".join(VALID_AUTH_MODE_KEYS)}'
)

authentication_type = authentication_mode.get("Type")
Expand Down
2 changes: 2 additions & 0 deletions moto/elasticache/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"unique_attribute": "cache_cluster_id",
},
}
VALID_AUTH_MODE_KEYS = ["Type", "Passwords"]
VALID_ENGINE_TYPES = ["redis", "valkey"]


class AuthenticationTypes(str, Enum):
Expand Down
47 changes: 39 additions & 8 deletions tests/test_elasticache/test_elasticache.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_create_user_no_password_required():
assert resp["UserId"] == user_id
assert resp["UserName"] == "User1"
assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["MinimumEngineVersion"] == "6.0"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
Expand Down Expand Up @@ -67,7 +67,7 @@ def test_create_user_with_password():
assert resp["UserId"] == user_id
assert resp["UserName"] == "User1"
assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["MinimumEngineVersion"] == "6.0"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
Expand Down Expand Up @@ -106,7 +106,7 @@ def test_create_user_with_iam():
)

assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
assert resp["Authentication"]["Type"] == "iam"
Expand Down Expand Up @@ -205,7 +205,7 @@ def test_create_user_with_authmode_no_password():
)

assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
assert resp["Authentication"]["Type"] == "no-password-required"
Expand All @@ -228,7 +228,7 @@ def test_create_user_with_no_password_required_and_authmode_nopassword():
)

assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
assert resp["Authentication"]["Type"] == "no-password"
Expand Down Expand Up @@ -275,7 +275,7 @@ def test_create_user_with_authmode_password():
)

assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
assert resp["Authentication"]["Type"] == "password"
Expand All @@ -299,7 +299,7 @@ def test_create_user_with_authmode_password_multiple():
)

assert resp["Status"] == "active"
assert resp["Engine"] == "Redis"
assert resp["Engine"] == "redis"
assert resp["AccessString"] == "on ~* +@all"
assert resp["UserGroupIds"] == []
assert resp["Authentication"]["Type"] == "password"
Expand Down Expand Up @@ -331,6 +331,37 @@ def test_create_user_twice():
assert err["Message"] == "User user1 already exists."


@mock_aws
@pytest.mark.parametrize("engine", ["redis", "Redis", "reDis"])
def test_create_user_engine_parameter_is_case_insensitive(engine):
client = boto3.client("elasticache", region_name="us-east-1")
user_id = "user1"
resp = client.create_user(
UserId=user_id,
UserName="User1",
Engine=engine,
AccessString="on ~* +@all",
AuthenticationMode={"Type": "iam"},
)
assert resp["Engine"] == engine.lower()


@mock_aws
def test_create_user_with_invalid_engine_type():
client = boto3.client("elasticache", region_name="ap-southeast-1")
user_id = "user1"
with pytest.raises(ClientError) as exc:
client.create_user(
UserId=user_id,
UserName="User1",
Engine="invalidengine",
AccessString="on ~* +@all",
Passwords=["mysecretpassthatsverylong"],
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterValue"


@mock_aws
def test_delete_user_unknown():
client = boto3.client("elasticache", region_name="ap-southeast-1")
Expand Down Expand Up @@ -403,7 +434,7 @@ def test_describe_users():
"UserId": "user1",
"UserName": "User1",
"Status": "active",
"Engine": "Redis",
"Engine": "redis",
"MinimumEngineVersion": "6.0",
"AccessString": "on ~* +@all",
"UserGroupIds": [],
Expand Down
Loading