Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions localstack-core/localstack/services/kms/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ class KmsKey:
tags: Dict[str, str]
policy: str
is_key_rotation_enabled: bool
rotation_period_in_days: int
next_rotation_date: datetime.datetime

def __init__(
self,
Expand Down Expand Up @@ -280,6 +282,8 @@ def __init__(
# remove the _custom_key_material_ tag from the tags to not readily expose the custom key material
del self.tags[TAG_KEY_CUSTOM_KEY_MATERIAL]
self.crypto_key = KmsCryptoKey(self.metadata.get("KeySpec"), custom_key_material)
self.rotation_period_in_days = 365
self.next_rotation_date = None

def calculate_and_set_arn(self, account_id, region):
self.metadata["Arn"] = kms_key_arn(self.metadata.get("KeyId"), account_id, region)
Expand Down Expand Up @@ -587,6 +591,12 @@ def schedule_key_deletion(self, pending_window_in_days: int) -> None:
days=pending_window_in_days
)

def _update_key_rotation_date(self) -> None:
if not self.next_rotation_date or self.next_rotation_date < datetime.datetime.now():
self.next_rotation_date = datetime.datetime.now() + datetime.timedelta(
days=self.rotation_period_in_days
)

# An example of how the whole policy should look like:
# https://docs.aws.amazon.com/kms/latest/developerguide/key-policy-overview.html
# The default statement is here:
Expand Down
13 changes: 11 additions & 2 deletions localstack-core/localstack/services/kms/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
DisableKeyRequest,
DisableKeyRotationRequest,
EnableKeyRequest,
EnableKeyRotationRequest,
EncryptionAlgorithmSpec,
EncryptionContextType,
EncryptResponse,
Expand Down Expand Up @@ -1253,7 +1254,12 @@ def get_key_rotation_status(
# We do not model that here, though.
account_id, region_name, key_id = self._parse_key_id(request["KeyId"], context)
key = self._get_kms_key(account_id, region_name, key_id, any_key_state_allowed=True)
return GetKeyRotationStatusResponse(KeyRotationEnabled=key.is_key_rotation_enabled)
return GetKeyRotationStatusResponse(
KeyId=key_id,
KeyRotationEnabled=key.is_key_rotation_enabled,
NextRotationDate=key.next_rotation_date,
RotationPeriodInDays=key.rotation_period_in_days,
)

@handler("DisableKeyRotation", expand=False)
def disable_key_rotation(
Expand All @@ -1267,13 +1273,16 @@ def disable_key_rotation(

@handler("EnableKeyRotation", expand=False)
def enable_key_rotation(
self, context: RequestContext, request: DisableKeyRotationRequest
self, context: RequestContext, request: EnableKeyRotationRequest
) -> None:
# https://docs.aws.amazon.com/kms/latest/developerguide/key-state.html
# "If the KMS key has imported key material or is in a custom key store: UnsupportedOperationException."
# We do not model that here, though.
key = self._get_kms_key(context.account_id, context.region, request.get("KeyId"))
key.is_key_rotation_enabled = True
if request.get("RotationPeriodInDays"):
key.rotation_period_in_days = request.get("RotationPeriodInDays")
key._update_key_rotation_date()

@handler("ListKeyPolicies", expand=False)
def list_key_policies(
Expand Down
17 changes: 17 additions & 0 deletions tests/aws/services/kms/test_kms.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def _get_all_key_ids(kms_client):

def _get_alias(kms_client, alias_name, key_id=None):
next_token = None
# TODO potential bug on pagination on "nextToken" attribute key
while True:
kwargs = {"nextToken": next_token} if next_token else {}
if key_id:
Expand Down Expand Up @@ -1097,6 +1098,22 @@ def test_key_rotation_status(self, kms_key, aws_client):
aws_client.kms.disable_key_rotation(KeyId=key_id)
assert aws_client.kms.get_key_rotation_status(KeyId=key_id)["KeyRotationEnabled"] is False

@markers.aws.validated
@pytest.mark.parametrize("rotation_period_in_days", [90, 180])
def test_key_enable_rotation_status(
self,
kms_key,
aws_client,
rotation_period_in_days,
snapshot,
):
key_id = kms_key["KeyId"]
aws_client.kms.enable_key_rotation(
KeyId=key_id, RotationPeriodInDays=rotation_period_in_days
)
result = aws_client.kms.get_key_rotation_status(KeyId=key_id)
snapshot.match("match_response", result)

@markers.aws.validated
def test_create_list_delete_alias(self, kms_create_alias, aws_client):
alias_name = f"alias/{short_uid()}"
Expand Down
30 changes: 30 additions & 0 deletions tests/aws/services/kms/test_kms.snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -2005,5 +2005,35 @@
}
}
}
},
"tests/aws/services/kms/test_kms.py::TestKMS::test_key_enable_rotation_status[90]": {
"recorded-date": "02-03-2025, 13:34:02",
"recorded-content": {
"match_response": {
"KeyId": "<key-id:1>",
"KeyRotationEnabled": true,
"NextRotationDate": "datetime",
"RotationPeriodInDays": 90,
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
}
}
},
"tests/aws/services/kms/test_kms.py::TestKMS::test_key_enable_rotation_status[180]": {
"recorded-date": "02-03-2025, 13:34:03",
"recorded-content": {
"match_response": {
"KeyId": "<key-id:1>",
"KeyRotationEnabled": true,
"NextRotationDate": "datetime",
"RotationPeriodInDays": 180,
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 200
}
}
}
}
}
6 changes: 6 additions & 0 deletions tests/aws/services/kms/test_kms.validation.json
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,12 @@
"tests/aws/services/kms/test_kms.py::TestKMS::test_invalid_verify_mac[HMAC_256-INVALID-some important message]": {
"last_validated_date": "2024-04-11T15:54:16+00:00"
},
"tests/aws/services/kms/test_kms.py::TestKMS::test_key_enable_rotation_status[180]": {
"last_validated_date": "2025-03-02T13:34:03+00:00"
},
"tests/aws/services/kms/test_kms.py::TestKMS::test_key_enable_rotation_status[90]": {
"last_validated_date": "2025-03-02T13:34:02+00:00"
},
"tests/aws/services/kms/test_kms.py::TestKMS::test_key_rotation_status": {
"last_validated_date": "2024-04-11T15:53:48+00:00"
},
Expand Down