moto/tests/test_secretsmanager/test_secretsmanager.py

1736 lines
59 KiB
Python
Raw Normal View History

import os
import re
import string
from datetime import datetime, timedelta, timezone
from unittest import SkipTest
from uuid import uuid4
2018-06-08 05:31:44 +00:00
import boto3
import pytest
from botocore.exceptions import ClientError, ParamValidationError
from dateutil.tz import tzlocal
from freezegun import freeze_time
2018-06-08 05:31:44 +00:00
2024-01-07 12:03:33 +00:00
from moto import mock_aws, settings
2022-08-13 09:49:43 +00:00
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
2018-06-08 05:31:44 +00:00
DEFAULT_SECRET_NAME = "test-secret7"
2024-01-07 12:03:33 +00:00
@mock_aws
2018-06-08 05:31:44 +00:00
def test_get_secret_value():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2018-06-08 05:31:44 +00:00
2021-10-18 19:44:29 +00:00
conn.create_secret(Name="java-util-test-password", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
result = conn.get_secret_value(SecretId="java-util-test-password")
assert result["SecretString"] == "foosecret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_secret_arn():
region = "us-west-2"
conn = boto3.client("secretsmanager", region_name=region)
create_dict = conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="secret_string",
)
assert re.match(
f"arn:aws:secretsmanager:{region}:{ACCOUNT_ID}:secret:{DEFAULT_SECRET_NAME}-"
+ r"\w{6}",
create_dict["ARN"],
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret_with_client_request_token():
conn = boto3.client("secretsmanager", region_name="us-west-2")
version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
create_dict = conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="secret_string",
ClientRequestToken=version_id,
)
assert create_dict
assert create_dict["VersionId"] == version_id
2024-01-07 12:03:33 +00:00
@mock_aws
2019-11-23 06:29:30 +00:00
def test_get_secret_value_by_arn():
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-06-23 17:03:11 +00:00
name = "java-util-test-password"
2019-11-23 06:29:30 +00:00
secret_value = "test_get_secret_value_by_arn"
2021-06-23 17:03:11 +00:00
result = conn.create_secret(Name=name, SecretString=secret_value)
arn = result["ARN"]
assert re.match(
f"^arn:aws:secretsmanager:us-west-2:{ACCOUNT_ID}:secret:{name}", arn
)
2021-06-23 17:03:11 +00:00
result = conn.get_secret_value(SecretId=arn)
2019-11-23 06:29:30 +00:00
assert result["SecretString"] == secret_value
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_value_binary():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-10-18 19:44:29 +00:00
conn.create_secret(Name="java-util-test-password", SecretBinary=b"foosecret")
2019-10-31 15:44:26 +00:00
result = conn.get_secret_value(SecretId="java-util-test-password")
2021-07-26 06:40:39 +00:00
assert result["SecretBinary"] == b"foosecret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_that_does_not_exist():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError) as cm:
2021-10-18 19:44:29 +00:00
conn.get_secret_value(SecretId="i-dont-exist")
2020-10-06 06:46:05 +00:00
assert (
"Secrets Manager can't find the specified secret."
== cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_that_does_not_match():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-10-18 19:44:29 +00:00
conn.create_secret(Name="java-util-test-password", SecretString="foosecret")
with pytest.raises(ClientError) as cm:
2021-10-18 19:44:29 +00:00
conn.get_secret_value(SecretId="i-dont-match")
2020-10-06 06:46:05 +00:00
assert (
"Secrets Manager can't find the specified secret."
== cm.value.response["Error"]["Message"]
)
2019-04-18 11:58:50 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2019-04-18 11:58:50 +00:00
def test_get_secret_value_that_is_marked_deleted():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
conn.delete_secret(SecretId="test-secret")
2019-04-18 11:58:50 +00:00
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.get_secret_value(SecretId="test-secret")
2019-04-18 11:58:50 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_that_has_no_value():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name="secret-no-value")
with pytest.raises(ClientError) as cm:
conn.get_secret_value(SecretId="secret-no-value")
2020-10-06 06:46:05 +00:00
assert (
"Secrets Manager can't find the specified secret value for staging label: AWSCURRENT"
== cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_version_that_does_not_exist():
conn = boto3.client("secretsmanager", region_name="us-west-2")
result = conn.create_secret(Name="java-util-test-password", SecretString="v")
secret_arn = result["ARN"]
missing_version_id = "00000000-0000-0000-0000-000000000000"
2020-10-06 06:04:09 +00:00
with pytest.raises(ClientError) as cm:
conn.get_secret_value(SecretId=secret_arn, VersionId=missing_version_id)
2020-10-06 06:46:05 +00:00
assert (
"Secrets Manager can't find the specified "
"secret value for VersionId: 00000000-0000-0000-0000-000000000000"
2020-10-06 06:46:05 +00:00
) == cm.value.response["Error"]["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_secret_version_stage_mismatch():
conn = boto3.client("secretsmanager", region_name="us-west-2")
result = conn.create_secret(Name="test-secret", SecretString="secret")
secret_arn = result["ARN"]
rotated_secret = conn.rotate_secret(
SecretId=secret_arn, RotationRules={"AutomaticallyAfterDays": 42}
)
desc_secret = conn.describe_secret(SecretId=secret_arn)
versions_to_stages = desc_secret["VersionIdsToStages"]
version_for_test = rotated_secret["VersionId"]
stages_for_version = versions_to_stages[version_for_test]
assert "AWSPENDING" not in stages_for_version
with pytest.raises(ClientError) as cm:
conn.get_secret_value(
SecretId=secret_arn, VersionId=version_for_test, VersionStage="AWSPENDING"
)
assert (
"You provided a VersionStage that is not associated to the provided VersionId."
) == cm.value.response["Error"]["Message"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-east-1")
2019-10-31 15:44:26 +00:00
result = conn.create_secret(Name="test-secret", SecretString="foosecret")
assert result["ARN"]
assert result["Name"] == "test-secret"
secret = conn.get_secret_value(SecretId="test-secret")
assert secret["SecretString"] == "foosecret"
2024-01-07 12:03:33 +00:00
@mock_aws
2018-12-21 22:04:52 +00:00
def test_create_secret_with_tags():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-east-1")
secret_name = "test-secret-with-tags"
2018-12-21 22:04:52 +00:00
result = conn.create_secret(
Name=secret_name,
SecretString="foosecret",
2019-10-31 15:44:26 +00:00
Tags=[{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}],
2018-12-21 22:04:52 +00:00
)
2019-10-31 15:44:26 +00:00
assert result["ARN"]
assert result["Name"] == secret_name
2018-12-21 22:04:52 +00:00
secret_value = conn.get_secret_value(SecretId=secret_name)
2019-10-31 15:44:26 +00:00
assert secret_value["SecretString"] == "foosecret"
2018-12-21 22:04:52 +00:00
secret_details = conn.describe_secret(SecretId=secret_name)
2019-10-31 15:44:26 +00:00
assert secret_details["Tags"] == [
{"Key": "Foo", "Value": "Bar"},
{"Key": "Mykey", "Value": "Myvalue"},
]
2018-12-21 22:04:52 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret_with_description():
conn = boto3.client("secretsmanager", region_name="us-east-1")
secret_name = "test-secret-with-tags"
result = conn.create_secret(
Name=secret_name, SecretString="foosecret", Description="desc"
)
assert result["ARN"]
assert result["Name"] == secret_name
secret_value = conn.get_secret_value(SecretId=secret_name)
assert secret_value["SecretString"] == "foosecret"
secret_details = conn.describe_secret(SecretId=secret_name)
assert secret_details["Description"] == "desc"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret_with_tags_and_description():
conn = boto3.client("secretsmanager", region_name="us-east-1")
secret_name = "test-secret-with-tags"
result = conn.create_secret(
Name=secret_name,
SecretString="foosecret",
Description="desc",
Tags=[{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}],
)
assert result["ARN"]
assert result["Name"] == secret_name
secret_value = conn.get_secret_value(SecretId=secret_name)
assert secret_value["SecretString"] == "foosecret"
secret_details = conn.describe_secret(SecretId=secret_name)
assert secret_details["Tags"] == [
{"Key": "Foo", "Value": "Bar"},
{"Key": "Mykey", "Value": "Myvalue"},
]
assert secret_details["Description"] == "desc"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret_without_value():
conn = boto3.client("secretsmanager", region_name="us-east-2")
secret_name = f"secret-{str(uuid4())[0:6]}"
create = conn.create_secret(Name=secret_name)
assert set(create.keys()) == {"ARN", "Name", "ResponseMetadata"}
describe = conn.describe_secret(SecretId=secret_name)
assert set(describe.keys()) == {
"ARN",
"Name",
"LastChangedDate",
"CreatedDate",
"ResponseMetadata",
}
with pytest.raises(ClientError) as exc:
conn.get_secret_value(SecretId=secret_name)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
updated = conn.update_secret(
SecretId=secret_name,
Description="new desc",
)
assert set(updated.keys()) == {"ARN", "Name", "ResponseMetadata"}
describe = conn.describe_secret(SecretId=secret_name)
assert set(describe.keys()) == {
"ARN",
"Name",
"Description",
"LastChangedDate",
"CreatedDate",
"ResponseMetadata",
}
deleted = conn.delete_secret(SecretId=secret_name)
assert set(deleted.keys()) == {"ARN", "Name", "DeletionDate", "ResponseMetadata"}
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_secret_that_has_no_value_and_then_update():
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name="secret-no-value")
conn.update_secret(
SecretId="secret-no-value",
SecretString="barsecret",
Description="desc",
)
secret = conn.get_secret_value(SecretId="secret-no-value")
assert secret["SecretString"] == "barsecret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_without_value():
conn = boto3.client("secretsmanager", region_name="us-east-2")
secret_name = f"secret-{str(uuid4())[0:6]}"
create = conn.create_secret(Name=secret_name, SecretString="foosecret")
assert set(create.keys()) == {"ARN", "Name", "VersionId", "ResponseMetadata"}
version_id = create["VersionId"]
describe1 = conn.describe_secret(SecretId=secret_name)
assert set(describe1.keys()) == {
"ARN",
"Name",
"LastChangedDate",
"VersionIdsToStages",
"CreatedDate",
"ResponseMetadata",
}
conn.get_secret_value(SecretId=secret_name)
updated = conn.update_secret(SecretId=secret_name, Description="desc")
assert set(updated.keys()) == {"ARN", "Name", "ResponseMetadata"}
describe2 = conn.describe_secret(SecretId=secret_name)
# AWS also includes 'LastAccessedDate'
assert set(describe2.keys()) == {
"ARN",
"Name",
"Description",
"LastChangedDate",
"VersionIdsToStages",
"CreatedDate",
"ResponseMetadata",
}
assert describe1["VersionIdsToStages"] == describe2["VersionIdsToStages"]
value = conn.get_secret_value(SecretId=secret_name)
assert value["SecretString"] == "foosecret"
assert value["VersionId"] == version_id
conn.delete_secret(SecretId=secret_name)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
deleted_secret = conn.delete_secret(SecretId="test-secret")
2019-10-31 15:44:26 +00:00
assert deleted_secret["ARN"]
assert deleted_secret["Name"] == "test-secret"
2022-12-09 23:56:08 +00:00
assert deleted_secret["DeletionDate"] > datetime.fromtimestamp(1, timezone.utc)
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
secret_details = conn.describe_secret(SecretId="test-secret")
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
assert secret_details["ARN"]
assert secret_details["Name"] == "test-secret"
2022-12-09 23:56:08 +00:00
assert secret_details["DeletedDate"] > datetime.fromtimestamp(1, timezone.utc)
2019-04-18 11:58:50 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2022-03-08 10:17:28 +00:00
def test_delete_secret_by_arn():
conn = boto3.client("secretsmanager", region_name="us-west-2")
secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
deleted_secret = conn.delete_secret(SecretId=secret["ARN"])
assert deleted_secret["ARN"] == secret["ARN"]
assert deleted_secret["Name"] == "test-secret"
2022-12-09 23:56:08 +00:00
assert deleted_secret["DeletionDate"] > datetime.fromtimestamp(1, timezone.utc)
2022-03-08 10:17:28 +00:00
secret_details = conn.describe_secret(SecretId="test-secret")
assert secret_details["ARN"] == secret["ARN"]
assert secret_details["Name"] == "test-secret"
2022-12-09 23:56:08 +00:00
assert secret_details["DeletedDate"] > datetime.fromtimestamp(1, timezone.utc)
2022-03-08 10:17:28 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
2019-04-18 11:58:50 +00:00
def test_delete_secret_force():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
result = conn.delete_secret(SecretId="test-secret", ForceDeleteWithoutRecovery=True)
2019-10-31 15:44:26 +00:00
assert result["ARN"]
2022-12-09 23:56:08 +00:00
assert result["DeletionDate"] > datetime.fromtimestamp(1, timezone.utc)
2019-10-31 15:44:26 +00:00
assert result["Name"] == "test-secret"
with pytest.raises(ClientError):
conn.get_secret_value(SecretId="test-secret")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret_force_no_such_secret():
conn = boto3.client("secretsmanager", region_name="us-west-2")
deleted_secret = conn.delete_secret(
SecretId=DEFAULT_SECRET_NAME, ForceDeleteWithoutRecovery=True
)
assert deleted_secret
assert deleted_secret["Name"] == DEFAULT_SECRET_NAME
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret_force_with_arn():
conn = boto3.client("secretsmanager", region_name="us-west-2")
create_secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
result = conn.delete_secret(
SecretId=create_secret["ARN"], ForceDeleteWithoutRecovery=True
)
assert result["ARN"]
2022-12-09 23:56:08 +00:00
assert result["DeletionDate"] > datetime.fromtimestamp(1, timezone.utc)
assert result["Name"] == "test-secret"
with pytest.raises(ClientError):
conn.get_secret_value(SecretId="test-secret")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret_that_does_not_exist():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError):
conn.delete_secret(SecretId="i-dont-exist")
2024-01-07 12:03:33 +00:00
@mock_aws
2019-04-18 11:58:50 +00:00
def test_delete_secret_fails_with_both_force_delete_flag_and_recovery_window_flag():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.delete_secret(
2019-10-31 15:44:26 +00:00
SecretId="test-secret",
RecoveryWindowInDays=1,
ForceDeleteWithoutRecovery=True,
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret_recovery_window_invalid_values():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
for nr in [0, 2, 6, 31, 100]:
with pytest.raises(ClientError) as exc:
conn.delete_secret(SecretId="test-secret", RecoveryWindowInDays=nr)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert (
"RecoveryWindowInDays value must be between 7 and 30 days (inclusive)"
in err["Message"]
)
2019-04-18 11:58:50 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_delete_secret_force_no_such_secret_with_invalid_recovery_window():
conn = boto3.client("secretsmanager", region_name="us-west-2")
for nr in [0, 2, 6, 31, 100]:
with pytest.raises(ClientError) as exc:
conn.delete_secret(
SecretId="test-secret",
RecoveryWindowInDays=nr,
ForceDeleteWithoutRecovery=True,
)
err = exc.value.response["Error"]
assert err["Code"] == "InvalidParameterException"
assert (
"RecoveryWindowInDays value must be between 7 and 30 days (inclusive)"
in err["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
2019-04-18 11:58:50 +00:00
def test_delete_secret_that_is_marked_deleted():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-04-18 11:58:50 +00:00
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-04-18 11:58:50 +00:00
2021-10-18 19:44:29 +00:00
conn.delete_secret(SecretId="test-secret")
2019-04-18 11:58:50 +00:00
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.delete_secret(SecretId="test-secret")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_password_default_length():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
random_password = conn.get_random_password()
2019-10-31 15:44:26 +00:00
assert len(random_password["RandomPassword"]) == 32
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_password_default_requirements():
# When require_each_included_type, default true
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
random_password = conn.get_random_password()
# Should contain lowercase, upppercase, digit, special character
2019-10-31 15:44:26 +00:00
assert any(c.islower() for c in random_password["RandomPassword"])
assert any(c.isupper() for c in random_password["RandomPassword"])
assert any(c.isdigit() for c in random_password["RandomPassword"])
assert any(c in string.punctuation for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_password_custom_length():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
random_password = conn.get_random_password(PasswordLength=50)
2019-10-31 15:44:26 +00:00
assert len(random_password["RandomPassword"]) == 50
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_exclude_lowercase():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(PasswordLength=55, ExcludeLowercase=True)
assert not any(c.islower() for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_exclude_uppercase():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(PasswordLength=55, ExcludeUppercase=True)
assert not any(c.isupper() for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_exclude_characters_and_symbols():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(
PasswordLength=20, ExcludeCharacters="xyzDje@?!."
)
assert not any(c in "xyzDje@?!." for c in random_password["RandomPassword"])
assert len(random_password["RandomPassword"]) == 20
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_exclude_numbers():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(PasswordLength=100, ExcludeNumbers=True)
assert not any(c.isdigit() for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_exclude_punctuation():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(
PasswordLength=100, ExcludePunctuation=True
)
assert not any(c in string.punctuation for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_include_space_false():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
random_password = conn.get_random_password(PasswordLength=300)
assert not any(c.isspace() for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_include_space_true():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(PasswordLength=4, IncludeSpace=True)
assert any(c.isspace() for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_require_each_included_type():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
random_password = conn.get_random_password(
PasswordLength=4, RequireEachIncludedType=True
)
assert any(c in string.punctuation for c in random_password["RandomPassword"])
assert any(c in string.ascii_lowercase for c in random_password["RandomPassword"])
assert any(c in string.ascii_uppercase for c in random_password["RandomPassword"])
assert any(c in string.digits for c in random_password["RandomPassword"])
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_too_short_password():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.get_random_password(PasswordLength=3)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_get_random_too_long_password():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(Exception):
2021-10-18 19:44:29 +00:00
conn.get_random_password(PasswordLength=5555)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_describe_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name="test-secret", SecretString="foosecret")
conn.create_secret(Name="test-secret-2", SecretString="barsecret")
secret_description = conn.describe_secret(SecretId="test-secret")
secret_description_2 = conn.describe_secret(SecretId="test-secret-2")
2019-10-31 15:44:26 +00:00
assert secret_description # Returned dict is not empty
assert secret_description["Name"] == ("test-secret")
assert secret_description["ARN"] != "" # Test arn not empty
assert secret_description_2["Name"] == ("test-secret-2")
assert secret_description_2["ARN"] != "" # Test arn not empty
assert secret_description["CreatedDate"] <= datetime.now(tz=tzlocal())
2022-12-09 23:56:08 +00:00
assert secret_description["CreatedDate"] > datetime.fromtimestamp(1, timezone.utc)
assert secret_description_2["CreatedDate"] <= datetime.now(tz=tzlocal())
2022-12-09 23:56:08 +00:00
assert secret_description_2["CreatedDate"] > datetime.fromtimestamp(1, timezone.utc)
assert secret_description["LastChangedDate"] <= datetime.now(tz=tzlocal())
2022-12-09 23:56:08 +00:00
assert secret_description["LastChangedDate"] > datetime.fromtimestamp(
1, timezone.utc
)
assert secret_description_2["LastChangedDate"] <= datetime.now(tz=tzlocal())
2022-12-09 23:56:08 +00:00
assert secret_description_2["LastChangedDate"] > datetime.fromtimestamp(
1, timezone.utc
)
2024-01-07 12:03:33 +00:00
@mock_aws
@pytest.mark.parametrize("name", ["testsecret", "test-secret"])
def test_describe_secret_with_arn(name):
conn = boto3.client("secretsmanager", region_name="us-west-2")
results = conn.create_secret(Name=name, SecretString="foosecret")
2019-11-23 10:02:00 +00:00
secret_description = conn.describe_secret(SecretId=results["ARN"])
assert secret_description # Returned dict is not empty
assert secret_description["Name"] == name
assert secret_description["ARN"] == results["ARN"]
assert conn.list_secrets()["SecretList"][0]["ARN"] == results["ARN"]
# We can also supply a partial ARN
partial_arn = f"arn:aws:secretsmanager:us-west-2:{ACCOUNT_ID}:secret:{name}"
resp = conn.get_secret_value(SecretId=partial_arn)
assert resp["Name"] == name
resp = conn.describe_secret(SecretId=partial_arn)
assert resp["Name"] == name
2024-01-07 12:03:33 +00:00
@mock_aws
def test_describe_secret_with_KmsKeyId():
conn = boto3.client("secretsmanager", region_name="us-west-2")
results = conn.create_secret(
Name="test-secret", SecretString="foosecret", KmsKeyId="dummy_arn"
)
secret_description = conn.describe_secret(SecretId=results["ARN"])
assert secret_description["KmsKeyId"] == "dummy_arn"
assert conn.list_secrets()["SecretList"][0]["KmsKeyId"] == (
secret_description["KmsKeyId"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_describe_secret_that_does_not_exist():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.get_secret_value(SecretId="i-dont-exist")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_describe_secret_that_does_not_match():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name="test-secret", SecretString="foosecret")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.get_secret_value(SecretId="i-dont-match")
2019-04-05 12:33:28 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_restore_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
conn.delete_secret(SecretId="test-secret")
2019-10-31 15:44:26 +00:00
described_secret_before = conn.describe_secret(SecretId="test-secret")
2022-12-09 23:56:08 +00:00
assert described_secret_before["DeletedDate"] > datetime.fromtimestamp(
1, timezone.utc
)
2019-10-31 15:44:26 +00:00
restored_secret = conn.restore_secret(SecretId="test-secret")
assert restored_secret["ARN"]
assert restored_secret["Name"] == "test-secret"
2019-10-31 15:44:26 +00:00
described_secret_after = conn.describe_secret(SecretId="test-secret")
assert "DeletedDate" not in described_secret_after
2024-01-07 12:03:33 +00:00
@mock_aws
def test_restore_secret_that_is_not_deleted():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
restored_secret = conn.restore_secret(SecretId="test-secret")
assert restored_secret["ARN"]
assert restored_secret["Name"] == "test-secret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_restore_secret_that_does_not_exist():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.restore_secret(SecretId="i-dont-exist")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_cancel_rotate_secret_with_invalid_secret_id():
conn = boto3.client("secretsmanager", region_name="us-east-1")
with pytest.raises(ClientError):
conn.cancel_rotate_secret(SecretId="invalid_id")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_cancel_rotate_secret_after_delete():
conn = boto3.client("secretsmanager", region_name="us-east-1")
conn.create_secret(
Name=DEFAULT_SECRET_NAME, SecretString="foosecret", Description="foodescription"
)
conn.delete_secret(
SecretId=DEFAULT_SECRET_NAME,
RecoveryWindowInDays=7,
ForceDeleteWithoutRecovery=False,
)
with pytest.raises(ClientError):
conn.cancel_rotate_secret(SecretId=DEFAULT_SECRET_NAME)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_cancel_rotate_secret_before_enable():
conn = boto3.client("secretsmanager", region_name="us-east-1")
conn.create_secret(
Name=DEFAULT_SECRET_NAME, SecretString="foosecret", Description="foodescription"
)
with pytest.raises(ClientError):
conn.cancel_rotate_secret(SecretId=DEFAULT_SECRET_NAME)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_cancel_rotate_secret():
if not settings.TEST_SERVER_MODE:
raise SkipTest("rotation requires a server to be running")
from tests.test_awslambda.utilities import get_role_name
lambda_conn = boto3.client(
"lambda", region_name="us-east-1", endpoint_url="http://localhost:5000"
)
func = lambda_conn.create_function(
FunctionName="testFunction",
Runtime="python3.11",
Role=get_role_name(),
Handler="lambda_function.lambda_handler",
Code={"ZipFile": get_rotation_zip_file()},
Description="Secret rotator",
Timeout=3,
MemorySize=128,
Publish=True,
)
secrets_conn = boto3.client("secretsmanager", region_name="us-east-1")
secrets_conn.create_secret(
Name=DEFAULT_SECRET_NAME, SecretString="foosecret", Description="foodescription"
)
secrets_conn.rotate_secret(
SecretId=DEFAULT_SECRET_NAME,
RotationLambdaARN=func["FunctionArn"],
RotationRules={"AutomaticallyAfterDays": 30},
)
secrets_conn.cancel_rotate_secret(SecretId=DEFAULT_SECRET_NAME)
cancelled_rotation = secrets_conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert not cancelled_rotation["RotationEnabled"]
# The function config should be preserved
assert cancelled_rotation["RotationLambdaARN"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret():
# Setup
frozen_time = datetime(2023, 5, 20, 10, 20, 30, tzinfo=tzlocal())
rotate_after_days = 10
with freeze_time(frozen_time):
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="foosecret",
Description="foodescription",
)
# Execute
rotated_secret = conn.rotate_secret(
SecretId=DEFAULT_SECRET_NAME,
RotationRules={"AutomaticallyAfterDays": rotate_after_days},
)
describe_secret = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
# Verify
assert rotated_secret
assert rotated_secret["ARN"] != "" # Test arn not empty
assert rotated_secret["Name"] == DEFAULT_SECRET_NAME
assert rotated_secret["VersionId"] != ""
assert describe_secret["Description"] == "foodescription"
assert "NextRotationDate" in describe_secret
assert "LastRotatedDate" in describe_secret
# can't do freeze time tests in servermode tests
if settings.TEST_SERVER_MODE:
return
assert describe_secret["LastChangedDate"] == frozen_time
assert describe_secret["NextRotationDate"] == frozen_time + timedelta(
days=rotate_after_days
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_without_secretstring():
# This test just verifies that Moto does not fail
conn = boto3.client("secretsmanager", region_name="us-east-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, Description="foodescription")
# AWS will always require a Lambda ARN to do the actual rotating
rotated_secret = conn.rotate_secret(SecretId=DEFAULT_SECRET_NAME)
assert rotated_secret["Name"] == DEFAULT_SECRET_NAME
# Without secret-value, and without actual rotating, we can't verify much
# Just that the secret exists/can be described
# We cannot verify any versions info (as that is not created without a secret-value)
describe_secret = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert describe_secret["Description"] == "foodescription"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_enable_rotation():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret")
initial_description = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert "RotationEnabled" not in initial_description
2019-10-31 15:44:26 +00:00
conn.rotate_secret(
SecretId=DEFAULT_SECRET_NAME, RotationRules={"AutomaticallyAfterDays": 42}
)
rotated_description = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert rotated_description
2019-10-31 15:44:26 +00:00
assert rotated_description["RotationEnabled"] is True
assert rotated_description["RotationRules"]["AutomaticallyAfterDays"] == 42
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_that_is_marked_deleted():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2019-10-31 15:44:26 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
2019-10-31 15:44:26 +00:00
conn.delete_secret(SecretId="test-secret")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(SecretId="test-secret")
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_that_does_not_exist():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", "us-west-2")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(SecretId="i-dont-exist")
2019-10-31 15:44:26 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_that_does_not_match():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name="test-secret", SecretString="foosecret")
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(SecretId="i-dont-match")
2019-10-31 15:44:26 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_client_request_token_too_short():
# Test is intentionally empty. Boto3 catches too short ClientRequestToken
# and raises ParamValidationError before Moto can see it.
# test_server actually handles this error.
assert True
2019-10-31 15:44:26 +00:00
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_client_request_token_too_long():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret")
client_request_token = (
"ED9F8B6C-85B7-446A-B7E4-38F2A3BEB13C-ED9F8B6C-85B7-446A-B7E4-38F2A3BEB13C"
)
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(
2019-10-31 15:44:26 +00:00
SecretId=DEFAULT_SECRET_NAME, ClientRequestToken=client_request_token
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_rotation_lambda_arn_too_long():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret")
2019-10-31 15:44:26 +00:00
rotation_lambda_arn = "85B7-446A-B7E4" * 147 # == 2058 characters
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(
2019-10-31 15:44:26 +00:00
SecretId=DEFAULT_SECRET_NAME, RotationLambdaARN=rotation_lambda_arn
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_rotation_period_zero():
# Test is intentionally empty. Boto3 catches zero day rotation period
# and raises ParamValidationError before Moto can see it.
# test_server actually handles this error.
assert True
2024-01-07 12:03:33 +00:00
@mock_aws
def test_rotate_secret_rotation_period_too_long():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret")
2019-10-31 15:44:26 +00:00
rotation_rules = {"AutomaticallyAfterDays": 1001}
with pytest.raises(ClientError):
2021-10-18 19:44:29 +00:00
conn.rotate_secret(SecretId=DEFAULT_SECRET_NAME, RotationRules=rotation_rules)
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
def get_rotation_zip_file():
2021-09-21 15:19:49 +00:00
from tests.test_awslambda.utilities import _process_lambda
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
func_str = """
import boto3
import json
def lambda_handler(event, context):
arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
client = boto3.client("secretsmanager", region_name="us-west-2", endpoint_url="http://motoserver:5000")
metadata = client.describe_secret(SecretId=arn)
value = client.get_secret_value(SecretId=arn, VersionId=token, VersionStage="AWSPENDING")
if not metadata['RotationEnabled']:
print("Secret %s is not enabled for rotation." % arn)
raise ValueError("Secret %s is not enabled for rotation." % arn)
versions = metadata['VersionIdsToStages']
if token not in versions:
print("Secret version %s has no stage for rotation of secret %s." % (token, arn))
raise ValueError("Secret version %s has no stage for rotation of secret %s." % (token, arn))
if "AWSCURRENT" in versions[token]:
print("Secret version %s already set as AWSCURRENT for secret %s." % (token, arn))
return
elif "AWSPENDING" not in versions[token]:
print("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn))
raise ValueError("Secret version %s not set as AWSPENDING for rotation of secret %s." % (token, arn))
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
if step == 'createSecret':
try:
client.get_secret_value(SecretId=arn, VersionId=token, VersionStage='AWSPENDING')
except client.exceptions.ResourceNotFoundException:
client.put_secret_value(
SecretId=arn,
ClientRequestToken=token,
SecretString=json.dumps({'create': True}),
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
VersionStages=['AWSPENDING']
)
if step == 'setSecret':
client.put_secret_value(
SecretId=arn,
ClientRequestToken=token,
SecretString='UpdatedValue',
2022-03-10 14:39:59 +00:00
VersionStages=["AWSPENDING"]
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
elif step == 'finishSecret':
current_version = next(
version
for version, stages in metadata['VersionIdsToStages'].items()
if 'AWSCURRENT' in stages
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
print("current: %s new: %s" % (current_version, token))
client.update_secret_version_stage(
SecretId=arn,
VersionStage='AWSCURRENT',
MoveToVersionId=token,
2022-03-10 14:39:59 +00:00
RemoveFromVersionId=current_version
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
client.update_secret_version_stage(
SecretId=arn,
VersionStage='AWSPENDING',
2022-03-10 14:39:59 +00:00
RemoveFromVersionId=token
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
"""
return _process_lambda(func_str)
if settings.TEST_SERVER_MODE:
2024-01-07 12:03:33 +00:00
@mock_aws
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
def test_rotate_secret_using_lambda():
2021-09-21 15:19:49 +00:00
from tests.test_awslambda.utilities import get_role_name
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
# Passing a `RotationLambdaARN` value to `rotate_secret` should invoke lambda
lambda_conn = boto3.client(
2022-03-10 14:39:59 +00:00
"lambda", region_name="us-west-2", endpoint_url="http://localhost:5000"
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
func = lambda_conn.create_function(
FunctionName="testFunction",
Runtime="python3.11",
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
Role=get_role_name(),
Handler="lambda_function.lambda_handler",
Code={"ZipFile": get_rotation_zip_file()},
Description="Secret rotator",
Timeout=3,
MemorySize=128,
Publish=True,
)
secrets_conn = boto3.client(
"secretsmanager",
region_name="us-west-2",
endpoint_url="http://localhost:5000",
)
secret = secrets_conn.create_secret(
2022-03-10 14:39:59 +00:00
Name=DEFAULT_SECRET_NAME, SecretString="InitialValue"
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
initial_version = secret["VersionId"]
rotated_secret = secrets_conn.rotate_secret(
SecretId=DEFAULT_SECRET_NAME,
RotationLambdaARN=func["FunctionArn"],
RotationRules={"AutomaticallyAfterDays": 30},
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
# Ensure we received an updated VersionId from `rotate_secret`
assert rotated_secret["VersionId"] != initial_version
updated_secret = secrets_conn.get_secret_value(
2022-03-10 14:39:59 +00:00
SecretId=DEFAULT_SECRET_NAME, VersionStage="AWSCURRENT"
Support rotating secrets using Lambda [#3905] (#3912) * Support rotating secrets using Lambda The Secrets manager rotation process uses an AWS Lambda function to perform the rotation of a secret. [1] In fact, it's not possible to trigger rotation of a Secret without specifying a Lambda function at some point in the life of the secret: ``` $ aws secretsmanager rotate-secret --secret-id /rotationTest An error occurred (InvalidRequestException) when calling the RotateSecret operation: No Lambda rotation function ARN is associated with this secret. ``` `moto` can be a little more lenient in this regard and allow `rotate_secret` to be called without a Lambda function being present, if only to allow simulation of the `AWSCURRENT` and `AWSPREVIOUS` labels moving across versions. However, if a lambda function _has_ been specified when calling `rotate_secret`, it should be invoked therefore providing the developer with the full multi-stage process [3] which can be used to test the Lambda function itself and ensuring that full end-to-end testing is performed. Without this there's no easy way to configure the Secret in the state needed to provide the Lambda function with the data in the format it needs to be in at each step of the invocation process. [1]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html [2]: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.rotate_secret [3]: https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-lambda-function-overview.html#rotation-explanation-of-steps * Run `black` over `secretsmanager/models.py` * Make `lambda_backends` import local to the condition * Implement `update_secret_version_stage` Allow a staging label to be moved across versions. https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/secretsmanager.html#SecretsManager.Client.update_secret_version_stage * Add an integration test for Secrets Manager & Lambda * Support passing `ClientRequestToken` to `put_secret_value` By passing `ClientRequestToken` to `put_secret_value` within the lambda function invoked by calling `rotate_secret`, one can update the value associated with the existing (pending) version, without causing a new secret version to be created. * Add application logic for `AWSPENDING` The rotation function must end with the versions of the secret in one of two states: - The `AWSPENDING` and `AWSCURRENT` staging labels are attached to the same version of the secret, or - The `AWSPENDING` staging label is not attached to any version of the secret. If the `AWSPENDING` staging label is present but not attached to the same version as `AWSCURRENT` then any later invocation of RotateSecret assumes that a previous rotation request is still in progress and returns an error. * Update `default_version_id` after Lambda rotation concludes Call `set_default_version_id` directly, rather than going through `reset_default_version` as the Lambda function is responsible for moving the version labels around, not `rotate_secret`. * Run `black` over changed files * Fix Python 2.7 compatibility * Add additional test coverage for Secrets Manager * Fix bug found by tests AWSPENDING + AWSCURRENT check wasn't using `version_stages`. Also tidy up the AWSCURRENT moving in `update_secret_version_stage` to remove AWSPREVIOUS it from the new stage. * Run `black` over changed files * Add additional `rotate_secret` tests * Skip `test_rotate_secret_lambda_invocations` in test server mode * Add test for invalid Lambda ARN
2021-05-11 11:08:01 +00:00
)
rotated_version = updated_secret["VersionId"]
assert initial_version != rotated_version
metadata = secrets_conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert metadata["VersionIdsToStages"][initial_version] == ["AWSPREVIOUS"]
assert metadata["VersionIdsToStages"][rotated_version] == ["AWSCURRENT"]
assert updated_secret["SecretString"] == "UpdatedValue"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_on_non_existing_secret():
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError) as cm:
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="foosecret",
VersionStages=["AWSCURRENT"],
)
assert cm.value.response["Error"]["Message"] == (
2020-11-11 15:55:37 +00:00
"Secrets Manager can't find the specified secret."
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_puts_new_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-07-26 06:40:39 +00:00
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret")
2019-10-31 15:44:26 +00:00
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="foosecret",
VersionStages=["AWSCURRENT"],
)
version_id = put_secret_value_dict["VersionId"]
2019-10-31 15:44:26 +00:00
get_secret_value_dict = conn.get_secret_value(
SecretId=DEFAULT_SECRET_NAME, VersionId=version_id, VersionStage="AWSCURRENT"
)
assert get_secret_value_dict
2019-10-31 15:44:26 +00:00
assert get_secret_value_dict["SecretString"] == "foosecret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_binary_value_puts_new_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-07-26 06:40:39 +00:00
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret")
2019-10-31 15:44:26 +00:00
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
2021-07-26 06:40:39 +00:00
SecretBinary=b"foosecret",
2019-10-31 15:44:26 +00:00
VersionStages=["AWSCURRENT"],
)
version_id = put_secret_value_dict["VersionId"]
2019-10-31 15:44:26 +00:00
get_secret_value_dict = conn.get_secret_value(
SecretId=DEFAULT_SECRET_NAME, VersionId=version_id, VersionStage="AWSCURRENT"
)
assert get_secret_value_dict
2021-07-26 06:40:39 +00:00
assert get_secret_value_dict["SecretBinary"] == b"foosecret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_create_and_put_secret_binary_value_puts_new_secret():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-07-26 06:40:39 +00:00
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret")
2019-10-31 15:44:26 +00:00
conn.put_secret_value(
2021-07-26 06:40:39 +00:00
SecretId=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret_update"
2019-10-31 15:44:26 +00:00
)
latest_secret = conn.get_secret_value(SecretId=DEFAULT_SECRET_NAME)
assert latest_secret
2021-07-26 06:40:39 +00:00
assert latest_secret["SecretBinary"] == b"foosecret_update"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_binary_requires_either_string_or_binary():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError) as ire:
conn.put_secret_value(SecretId=DEFAULT_SECRET_NAME)
assert ire.value.response["Error"]["Code"] == "InvalidRequestException"
assert ire.value.response["Error"]["Message"] == (
2019-10-31 15:44:26 +00:00
"You must provide either SecretString or SecretBinary."
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_can_get_first_version_if_put_twice():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-07-26 06:40:39 +00:00
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret")
2019-10-31 15:44:26 +00:00
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="first_secret",
VersionStages=["AWSCURRENT"],
)
first_version_id = put_secret_value_dict["VersionId"]
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret",
VersionStages=["AWSCURRENT"],
)
2019-10-31 15:44:26 +00:00
first_secret_value_dict = conn.get_secret_value(
SecretId=DEFAULT_SECRET_NAME, VersionId=first_version_id
)
first_secret_value = first_secret_value_dict["SecretString"]
2019-10-31 15:44:26 +00:00
assert first_secret_value == "first_secret"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_versions_differ_if_same_secret_put_twice():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary="foosecret")
2019-10-31 15:44:26 +00:00
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
first_version_id = put_secret_value_dict["VersionId"]
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
second_version_id = put_secret_value_dict["VersionId"]
assert first_version_id != second_version_id
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_maintains_description_and_tags():
conn = boto3.client("secretsmanager", region_name="us-west-2")
previous_response = conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="foosecret",
Description="desc",
Tags=[{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}],
)
previous_version_id = previous_response["VersionId"]
conn = boto3.client("secretsmanager", region_name="us-west-2")
current_response = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
current_version_id = current_response["VersionId"]
secret_details = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME)
assert secret_details["Tags"] == [
{"Key": "Foo", "Value": "Bar"},
{"Key": "Mykey", "Value": "Myvalue"},
]
assert secret_details["Description"] == "desc"
assert secret_details["VersionIdsToStages"] is not None
assert previous_version_id in secret_details["VersionIdsToStages"]
assert current_version_id in secret_details["VersionIdsToStages"]
assert secret_details["VersionIdsToStages"][previous_version_id] == ["AWSPREVIOUS"]
assert secret_details["VersionIdsToStages"][current_version_id] == ["AWSCURRENT"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_can_list_secret_version_ids():
2019-10-31 15:44:26 +00:00
conn = boto3.client("secretsmanager", region_name="us-west-2")
conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary="foosecret")
2019-10-31 15:44:26 +00:00
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
first_version_id = put_secret_value_dict["VersionId"]
put_secret_value_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
second_version_id = put_secret_value_dict["VersionId"]
versions_list = conn.list_secret_version_ids(SecretId=DEFAULT_SECRET_NAME)
2019-10-31 15:44:26 +00:00
returned_version_ids = [v["VersionId"] for v in versions_list["Versions"]]
assert [first_version_id, second_version_id].sort() == returned_version_ids.sort()
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_version_stages_response():
conn = boto3.client("secretsmanager", region_name="us-west-2")
# Creation.
first_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="first_secret_string",
ClientRequestToken=first_version_id,
)
# Use PutSecretValue to push a new version with new version stages.
second_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce72"
second_version_stages = ["SAMPLESTAGE1", "SAMPLESTAGE0"]
second_put_res_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret_string",
VersionStages=second_version_stages,
ClientRequestToken=second_version_id,
)
assert second_put_res_dict
assert second_put_res_dict["VersionId"] == second_version_id
assert second_put_res_dict["VersionStages"] == second_version_stages
2024-01-07 12:03:33 +00:00
@mock_aws
def test_put_secret_value_version_stages_pending_response():
conn = boto3.client("secretsmanager", region_name="us-west-2")
# Creation.
first_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString="first_secret_string",
ClientRequestToken=first_version_id,
)
# Use PutSecretValue to push a new version with new version stages.
second_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce72"
second_version_stages = ["AWSPENDING"]
second_put_res_dict = conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret_string",
VersionStages=second_version_stages,
ClientRequestToken=second_version_id,
)
assert second_put_res_dict
assert second_put_res_dict["VersionId"] == second_version_id
assert second_put_res_dict["VersionStages"] == second_version_stages
2024-01-07 12:03:33 +00:00
@mock_aws
def test_after_put_secret_value_version_stages_can_get_current():
conn = boto3.client("secretsmanager", region_name="us-west-2")
# Creation.
first_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
first_secret_string = "first_secret_string"
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString=first_secret_string,
ClientRequestToken=first_version_id,
)
# Use PutSecretValue to push a new version with new version stages.
second_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce72"
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret_string",
VersionStages=["SAMPLESTAGE1", "SAMPLESTAGE0"],
ClientRequestToken=second_version_id,
)
# Get current.
get_dict = conn.get_secret_value(SecretId=DEFAULT_SECRET_NAME)
assert get_dict
assert get_dict["VersionId"] == first_version_id
assert get_dict["SecretString"] == first_secret_string
assert get_dict["VersionStages"] == ["AWSCURRENT"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_after_put_secret_value_version_stages_can_get_current_with_custom_version_stage():
conn = boto3.client("secretsmanager", region_name="us-west-2")
# Creation.
first_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
first_secret_string = "first_secret_string"
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString=first_secret_string,
ClientRequestToken=first_version_id,
)
# Use PutSecretValue to push a new version with new version stages.
second_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce72"
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret_string",
VersionStages=["SAMPLESTAGE1", "SAMPLESTAGE0"],
ClientRequestToken=second_version_id,
)
# Create a third version with one of the old stages
third_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce73"
third_secret_string = "third_secret_string"
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString=third_secret_string,
VersionStages=["SAMPLESTAGE1"],
ClientRequestToken=third_version_id,
)
# Get current with the stage label of the third version.
get_dict = conn.get_secret_value(
SecretId=DEFAULT_SECRET_NAME, VersionStage="SAMPLESTAGE1"
)
versions = conn.list_secret_version_ids(SecretId=DEFAULT_SECRET_NAME)["Versions"]
versions_by_key = {version["VersionId"]: version for version in versions}
# Check if indeed the third version is returned
assert get_dict
assert get_dict["VersionId"] == third_version_id
assert get_dict["SecretString"] == third_secret_string
assert get_dict["VersionStages"] == ["SAMPLESTAGE1"]
# Check if all the versions have the proper labels
assert versions_by_key[first_version_id]["VersionStages"] == ["AWSCURRENT"]
assert versions_by_key[second_version_id]["VersionStages"] == ["SAMPLESTAGE0"]
assert versions_by_key[third_version_id]["VersionStages"] == ["SAMPLESTAGE1"]
2024-01-07 12:03:33 +00:00
@mock_aws
def test_after_put_secret_value_version_stages_pending_can_get_current():
conn = boto3.client("secretsmanager", region_name="us-west-2")
# Creation.
first_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce71"
first_secret_string = "first_secret_string"
conn.create_secret(
Name=DEFAULT_SECRET_NAME,
SecretString=first_secret_string,
ClientRequestToken=first_version_id,
)
# Use PutSecretValue to push a new version with new version stages.
pending_version_id = "eb41453f-25bb-4025-b7f4-850cfca0ce72"
conn.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="second_secret_string",
VersionStages=["AWSPENDING"],
ClientRequestToken=pending_version_id,
)
# Get current.
get_dict = conn.get_secret_value(SecretId=DEFAULT_SECRET_NAME)
assert get_dict
assert get_dict["VersionId"] == first_version_id
assert get_dict["SecretString"] == first_secret_string
assert get_dict["VersionStages"] == ["AWSCURRENT"]
2024-01-07 12:03:33 +00:00
@mock_aws
@pytest.mark.parametrize("pass_arn", [True, False])
def test_update_secret(pass_arn):
conn = boto3.client("secretsmanager", region_name="us-west-2")
created_secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
assert created_secret["ARN"]
assert created_secret["Name"] == "test-secret"
assert created_secret["VersionId"] != ""
secret_id = created_secret["ARN"] if pass_arn else "test-secret"
secret = conn.get_secret_value(SecretId=secret_id)
assert secret["SecretString"] == "foosecret"
updated_secret = conn.update_secret(
SecretId=secret_id,
SecretString="barsecret",
Description="new desc",
)
assert updated_secret["ARN"]
assert updated_secret["Name"] == "test-secret"
assert updated_secret["VersionId"] != ""
secret = conn.get_secret_value(SecretId=secret_id)
assert secret["SecretString"] == "barsecret"
assert created_secret["VersionId"] != updated_secret["VersionId"]
assert conn.describe_secret(SecretId=secret_id)["Description"] == "new desc"
2024-01-07 12:03:33 +00:00
@mock_aws
@pytest.mark.parametrize("pass_arn", [True, False])
def test_update_secret_updates_last_changed_dates(pass_arn):
conn = boto3.client("secretsmanager", region_name="us-west-2")
# create a secret
created_secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
secret_id = created_secret["ARN"] if pass_arn else "test-secret"
# save details for secret before modification
secret_details_1 = conn.describe_secret(SecretId=secret_id)
# check if only LastChangedDate changed, CreatedDate should stay the same
with freeze_time(timedelta(minutes=1)):
conn.update_secret(SecretId="test-secret", Description="new-desc")
secret_details_2 = conn.describe_secret(SecretId=secret_id)
assert secret_details_1["CreatedDate"] == secret_details_2["CreatedDate"]
if os.environ.get("TEST_SERVER_MODE", "false").lower() == "false":
assert (
secret_details_1["LastChangedDate"]
< secret_details_2["LastChangedDate"]
)
else:
# Can't manipulate time in server mode, so use weaker constraints here
assert (
secret_details_1["LastChangedDate"]
<= secret_details_2["LastChangedDate"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_with_tags_and_description():
conn = boto3.client("secretsmanager", region_name="us-west-2")
created_secret = conn.create_secret(
Name="test-secret",
SecretString="foosecret",
Description="desc",
Tags=[{"Key": "Foo", "Value": "Bar"}, {"Key": "Mykey", "Value": "Myvalue"}],
)
assert created_secret["ARN"]
assert created_secret["Name"] == "test-secret"
assert created_secret["VersionId"] != ""
secret = conn.get_secret_value(SecretId="test-secret")
assert secret["SecretString"] == "foosecret"
updated_secret = conn.update_secret(
SecretId="test-secret", SecretString="barsecret"
)
assert updated_secret["ARN"]
assert updated_secret["Name"] == "test-secret"
assert updated_secret["VersionId"] != ""
secret = conn.get_secret_value(SecretId="test-secret")
assert secret["SecretString"] == "barsecret"
assert created_secret["VersionId"] != updated_secret["VersionId"]
secret_details = conn.describe_secret(SecretId="test-secret")
assert secret_details["Tags"] == [
{"Key": "Foo", "Value": "Bar"},
{"Key": "Mykey", "Value": "Myvalue"},
]
assert secret_details["Description"] == "desc"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_with_KmsKeyId():
conn = boto3.client("secretsmanager", region_name="us-west-2")
created_secret = conn.create_secret(
Name="test-secret", SecretString="foosecret", KmsKeyId="foo_arn"
)
assert created_secret["ARN"]
assert created_secret["Name"] == "test-secret"
assert created_secret["VersionId"] != ""
secret = conn.get_secret_value(SecretId="test-secret")
assert secret["SecretString"] == "foosecret"
secret_details = conn.describe_secret(SecretId="test-secret")
assert secret_details["KmsKeyId"] == "foo_arn"
updated_secret = conn.update_secret(
SecretId="test-secret", SecretString="barsecret", KmsKeyId="bar_arn"
)
assert updated_secret["ARN"]
assert updated_secret["Name"] == "test-secret"
assert updated_secret["VersionId"] != ""
secret = conn.get_secret_value(SecretId="test-secret")
assert secret["SecretString"] == "barsecret"
assert created_secret["VersionId"] != updated_secret["VersionId"]
secret_details = conn.describe_secret(SecretId="test-secret")
assert secret_details["KmsKeyId"] == "bar_arn"
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_which_does_not_exit():
conn = boto3.client("secretsmanager", region_name="us-west-2")
with pytest.raises(ClientError) as cm:
2021-10-18 19:44:29 +00:00
conn.update_secret(SecretId="test-secret", SecretString="barsecret")
2020-10-06 06:46:05 +00:00
assert (
"Secrets Manager can't find the specified secret."
== cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_marked_as_deleted():
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-10-18 19:44:29 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
conn.delete_secret(SecretId="test-secret")
with pytest.raises(ClientError) as cm:
2021-10-18 19:44:29 +00:00
conn.update_secret(SecretId="test-secret", SecretString="barsecret")
assert (
2020-10-06 06:46:05 +00:00
"because it was marked for deletion." in cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_marked_as_deleted_after_restoring():
conn = boto3.client("secretsmanager", region_name="us-west-2")
2021-10-18 19:44:29 +00:00
conn.create_secret(Name="test-secret", SecretString="foosecret")
conn.delete_secret(SecretId="test-secret")
conn.restore_secret(SecretId="test-secret")
updated_secret = conn.update_secret(
SecretId="test-secret", SecretString="barsecret"
)
assert updated_secret["ARN"]
assert updated_secret["Name"] == "test-secret"
assert updated_secret["VersionId"] != ""
2024-01-07 12:03:33 +00:00
@mock_aws
@pytest.mark.parametrize("pass_arn", [True, False])
def test_tag_resource(pass_arn):
conn = boto3.client("secretsmanager", region_name="us-west-2")
created_secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
secret_id = created_secret["ARN"] if pass_arn else "test-secret"
conn.tag_resource(
2022-03-10 14:39:59 +00:00
SecretId=secret_id, Tags=[{"Key": "FirstTag", "Value": "SomeValue"}]
)
conn.tag_resource(
2022-03-10 14:39:59 +00:00
SecretId="test-secret", Tags=[{"Key": "FirstTag", "Value": "SomeOtherValue"}]
)
conn.tag_resource(
2022-03-10 14:39:59 +00:00
SecretId=secret_id, Tags=[{"Key": "SecondTag", "Value": "AnotherValue"}]
)
secrets = conn.list_secrets()
assert secrets["SecretList"][0].get("Tags") == [
{"Key": "FirstTag", "Value": "SomeOtherValue"},
{"Key": "SecondTag", "Value": "AnotherValue"},
]
with pytest.raises(ClientError) as cm:
conn.tag_resource(
SecretId="dummy-test-secret",
2022-03-10 14:39:59 +00:00
Tags=[{"Key": "FirstTag", "Value": "SomeValue"}],
)
2020-11-11 15:55:37 +00:00
assert (
"Secrets Manager can't find the specified secret."
== cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
@pytest.mark.parametrize("pass_arn", [True, False])
def test_untag_resource(pass_arn):
conn = boto3.client("secretsmanager", region_name="us-west-2")
created_secret = conn.create_secret(Name="test-secret", SecretString="foosecret")
secret_id = created_secret["ARN"] if pass_arn else "test-secret"
conn.tag_resource(
SecretId=secret_id,
Tags=[
{"Key": "FirstTag", "Value": "SomeValue"},
{"Key": "SecondTag", "Value": "SomeValue"},
],
)
conn.untag_resource(SecretId=secret_id, TagKeys=["FirstTag"])
secrets = conn.list_secrets()
assert secrets["SecretList"][0].get("Tags") == [
{"Key": "SecondTag", "Value": "SomeValue"},
]
with pytest.raises(ClientError) as cm:
2022-03-10 14:39:59 +00:00
conn.untag_resource(SecretId="dummy-test-secret", TagKeys=["FirstTag"])
assert (
"Secrets Manager can't find the specified secret."
== cm.value.response["Error"]["Message"]
)
2024-01-07 12:03:33 +00:00
@mock_aws
def test_secret_versions_to_stages_attribute_discrepancy():
client = boto3.client("secretsmanager", region_name="us-west-2")
resp = client.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret")
previous_version_id = resp["VersionId"]
resp = client.put_secret_value(
SecretId=DEFAULT_SECRET_NAME,
SecretString="dupe_secret",
VersionStages=["AWSCURRENT"],
)
current_version_id = resp["VersionId"]
secret = client.describe_secret(SecretId=DEFAULT_SECRET_NAME)
describe_vtos = secret["VersionIdsToStages"]
assert describe_vtos[current_version_id] == ["AWSCURRENT"]
assert describe_vtos[previous_version_id] == ["AWSPREVIOUS"]
secret = client.list_secrets(
Filters=[{"Key": "name", "Values": [DEFAULT_SECRET_NAME]}]
).get("SecretList")[0]
list_vtos = secret["SecretVersionsToStages"]
assert list_vtos[current_version_id] == ["AWSCURRENT"]
assert list_vtos[previous_version_id] == ["AWSPREVIOUS"]
assert describe_vtos == list_vtos
2024-01-07 12:03:33 +00:00
@mock_aws
def test_update_secret_with_client_request_token():
client = boto3.client("secretsmanager", region_name="us-west-2")
secret_name = "test-secret"
client_request_token = str(uuid4())
client.create_secret(Name=secret_name, SecretString="first-secret")
updated_secret = client.update_secret(
SecretId=secret_name,
SecretString="second-secret",
ClientRequestToken=client_request_token,
)
assert client_request_token == updated_secret["VersionId"]
updated_secret = client.update_secret(
2022-03-10 14:39:59 +00:00
SecretId=secret_name, SecretString="third-secret"
)
assert client_request_token != updated_secret["VersionId"]
invalid_request_token = "test-token"
with pytest.raises(ParamValidationError) as pve:
client.update_secret(
SecretId=secret_name,
SecretString="fourth-secret",
ClientRequestToken=invalid_request_token,
)
assert pve.value.response["Error"]["Code"] == "InvalidParameterException"
assert pve.value.response["Error"]["Message"] == (
"ClientRequestToken must be 32-64 characters long."
)