Skip to content

Commit 45fee2b

Browse files
committed
Raise error when trying to tag key with duplicated tags
1 parent 4832016 commit 45fee2b

File tree

5 files changed

+46
-42
lines changed

5 files changed

+46
-42
lines changed

localstack-core/localstack/services/kms/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ def __init__(self, message: str):
99
class AccessDeniedException(CommonServiceException):
1010
def __init__(self, message: str):
1111
super().__init__("AccessDeniedException", message, 400, True)
12+
13+
14+
class TagException(CommonServiceException):
15+
def __init__(self, message=None):
16+
super().__init__("TagException", status_code=400, message=message)

localstack-core/localstack/services/kms/models.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
UnsupportedOperationException,
4848
)
4949
from localstack.constants import TAG_KEY_CUSTOM_ID
50-
from localstack.services.kms.exceptions import ValidationException
50+
from localstack.services.kms.exceptions import TagException, ValidationException
5151
from localstack.services.kms.utils import is_valid_key_arn
5252
from localstack.services.stores import AccountRegionBundle, BaseStore, LocalAttribute
5353
from localstack.utils.aws.arns import get_partition, kms_alias_arn, kms_key_arn
@@ -561,6 +561,10 @@ def add_tags(self, tags: List) -> None:
561561
if not tags:
562562
return
563563

564+
tag_keys = [tag["TagKey"] for tag in tags]
565+
if len(tag_keys) != len(set(tag_keys)):
566+
raise TagException("Duplicate tag keys")
567+
564568
# Do not care if we overwrite an existing tag:
565569
# https://docs.aws.amazon.com/kms/latest/APIReference/API_TagResource.html
566570
# "To edit a tag, specify an existing tag key and a new tag value."

tests/aws/services/kms/test_kms.py

Lines changed: 17 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,23 @@ def test_create_key(
103103
assert f":{region_name}:" in response["Arn"]
104104
assert f":{account_id}:" in response["Arn"]
105105

106+
@markers.aws.validated
107+
def test_tag_key_with_duplicate_tag_keys_raises_error(
108+
self, kms_client_for_region, kms_create_key, snapshot, region_name
109+
):
110+
kms_client = kms_client_for_region(region_name)
111+
key_id = kms_create_key(
112+
region_name=region_name, Description="test key 123", KeyUsage="ENCRYPT_DECRYPT"
113+
)["KeyId"]
114+
115+
tags = [
116+
{"TagKey": "tag1", "TagValue": "value1"},
117+
{"TagKey": "tag1", "TagValue": "another-value1"},
118+
]
119+
with pytest.raises(ClientError) as e:
120+
kms_client.tag_resource(KeyId=key_id, Tags=tags)
121+
snapshot.match("duplicate-tag-keys", e.value.response)
122+
106123
@markers.aws.only_localstack
107124
def test_create_key_custom_id(self, kms_create_key, aws_client):
108125
custom_id = str(uuid.uuid4())
@@ -987,47 +1004,6 @@ def test_get_put_list_key_policies(self, kms_create_key, aws_client, account_id)
9871004
key_policy = aws_client.kms.get_key_policy(KeyId=key_id, PolicyName="default")["Policy"]
9881005
assert json.dumps(json.loads(key_policy)) == policy_two
9891006

990-
@markers.aws.validated
991-
def test_tag_untag_list_tags(self, kms_create_key, aws_client):
992-
def _create_tag(key):
993-
return {"TagKey": key, "TagValue": short_uid()}
994-
995-
def _are_tags_there(tags, key_id):
996-
if not tags:
997-
return True
998-
next_token = None
999-
while True:
1000-
kwargs = {"nextToken": next_token} if next_token else {}
1001-
response = aws_client.kms.list_resource_tags(KeyId=key_id, **kwargs)
1002-
for response_tag in response["Tags"]:
1003-
for i in range(len(tags)):
1004-
if response_tag.get("TagKey") == tags[i].get("TagKey") and response_tag.get(
1005-
"TagValue"
1006-
) == tags[i].get("TagValue"):
1007-
del tags[i]
1008-
if not tags:
1009-
return True
1010-
break
1011-
if "nextToken" not in response:
1012-
break
1013-
next_token = response["nextToken"]
1014-
return False
1015-
1016-
old_tag_one = _create_tag("one")
1017-
new_tag_one = _create_tag("one")
1018-
tag_two = _create_tag("two")
1019-
tag_three = _create_tag("three")
1020-
1021-
key_id = kms_create_key(Tags=[old_tag_one, tag_two])["KeyId"]
1022-
assert _are_tags_there([old_tag_one, tag_two], key_id) is True
1023-
# Going to rewrite one of the tags and then add a new one.
1024-
aws_client.kms.tag_resource(KeyId=key_id, Tags=[new_tag_one, tag_three])
1025-
assert _are_tags_there([new_tag_one, tag_two, tag_three], key_id) is True
1026-
assert _are_tags_there([old_tag_one], key_id) is False
1027-
aws_client.kms.untag_resource(KeyId=key_id, TagKeys=[new_tag_one.get("TagKey")])
1028-
assert _are_tags_there([tag_two, tag_three], key_id) is True
1029-
assert _are_tags_there([new_tag_one], key_id) is False
1030-
10311007
@markers.aws.validated
10321008
def test_cant_use_disabled_or_deleted_keys(self, kms_create_key, aws_client):
10331009
key_id = kms_create_key(KeySpec="SYMMETRIC_DEFAULT", KeyUsage="ENCRYPT_DECRYPT")["KeyId"]

tests/aws/services/kms/test_kms.snapshot.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,5 +1793,21 @@
17931793
}
17941794
}
17951795
}
1796+
},
1797+
"tests/aws/services/kms/test_kms.py::TestKMS::test_tag_key_with_duplicate_tag_keys_raises_error": {
1798+
"recorded-date": "17-01-2025, 13:35:08",
1799+
"recorded-content": {
1800+
"duplicate-tag-keys": {
1801+
"Error": {
1802+
"Code": "TagException",
1803+
"Message": "Duplicate tag keys"
1804+
},
1805+
"message": "Duplicate tag keys",
1806+
"ResponseMetadata": {
1807+
"HTTPHeaders": {},
1808+
"HTTPStatusCode": 400
1809+
}
1810+
}
1811+
}
17961812
}
17971813
}

tests/aws/services/kms/test_kms.validation.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
"tests/aws/services/kms/test_kms.py::TestKMS::test_disable_and_enable_key": {
3939
"last_validated_date": "2024-04-11T15:52:38+00:00"
4040
},
41+
"tests/aws/services/kms/test_kms.py::TestKMS::test_tag_key_with_duplicate_tag_keys_raises_error": {
42+
"last_validated_date": "2025-01-17T13:35:08+00:00"
43+
},
4144
"tests/aws/services/kms/test_kms.py::TestKMS::test_encrypt_decrypt[RSA_2048-RSAES_OAEP_SHA_256]": {
4245
"last_validated_date": "2024-04-11T15:53:19+00:00"
4346
},

0 commit comments

Comments
 (0)