import boto3 from moto import mock_secretsmanager, mock_lambda, settings from moto.core import ACCOUNT_ID from botocore.exceptions import ClientError, ParamValidationError import string import pytz from datetime import datetime import sure # noqa # pylint: disable=unused-import from uuid import uuid4 import pytest DEFAULT_SECRET_NAME = "test-secret" @mock_secretsmanager def test_get_secret_value(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="java-util-test-password", SecretString="foosecret") result = conn.get_secret_value(SecretId="java-util-test-password") assert result["SecretString"] == "foosecret" @mock_secretsmanager def test_get_secret_value_by_arn(): conn = boto3.client("secretsmanager", region_name="us-west-2") name = "java-util-test-password" secret_value = "test_get_secret_value_by_arn" result = conn.create_secret(Name=name, SecretString=secret_value) arn = result["ARN"] arn.should.match( "^arn:aws:secretsmanager:us-west-2:{}:secret:{}".format(ACCOUNT_ID, name) ) result = conn.get_secret_value(SecretId=arn) assert result["SecretString"] == secret_value @mock_secretsmanager def test_get_secret_value_binary(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="java-util-test-password", SecretBinary=b"foosecret") result = conn.get_secret_value(SecretId="java-util-test-password") assert result["SecretBinary"] == b"foosecret" @mock_secretsmanager def test_get_secret_that_does_not_exist(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError) as cm: conn.get_secret_value(SecretId="i-dont-exist") assert ( "Secrets Manager can't find the specified secret." == cm.value.response["Error"]["Message"] ) @mock_secretsmanager def test_get_secret_that_does_not_match(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="java-util-test-password", SecretString="foosecret") with pytest.raises(ClientError) as cm: conn.get_secret_value(SecretId="i-dont-match") assert ( "Secrets Manager can't find the specified secret." == cm.value.response["Error"]["Message"] ) @mock_secretsmanager def test_get_secret_value_that_is_marked_deleted(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") conn.delete_secret(SecretId="test-secret") with pytest.raises(ClientError): conn.get_secret_value(SecretId="test-secret") @mock_secretsmanager def test_get_secret_that_has_no_value(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="java-util-test-password") with pytest.raises(ClientError) as cm: conn.get_secret_value(SecretId="java-util-test-password") assert ( "Secrets Manager can't find the specified secret value for staging label: AWSCURRENT" == cm.value.response["Error"]["Message"] ) @mock_secretsmanager 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") secret_arn = result["ARN"] missing_version_id = "00000000-0000-0000-0000-000000000000" with pytest.raises(ClientError) as cm: conn.get_secret_value(SecretId=secret_arn, VersionId=missing_version_id) assert ( "An error occurred (ResourceNotFoundException) when calling the GetSecretValue operation: Secrets " "Manager can't find the specified secret value for VersionId: 00000000-0000-0000-0000-000000000000" ) == cm.value.response["Error"]["Message"] @mock_secretsmanager def test_create_secret(): conn = boto3.client("secretsmanager", region_name="us-east-1") 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" @mock_secretsmanager def test_create_secret_with_tags(): conn = boto3.client("secretsmanager", region_name="us-east-1") secret_name = "test-secret-with-tags" result = conn.create_secret( Name=secret_name, SecretString="foosecret", 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"}, ] @mock_secretsmanager 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" @mock_secretsmanager 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" @mock_secretsmanager def test_delete_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") deleted_secret = conn.delete_secret(SecretId="test-secret") assert deleted_secret["ARN"] assert deleted_secret["Name"] == "test-secret" assert deleted_secret["DeletionDate"] > datetime.fromtimestamp(1, pytz.utc) secret_details = conn.describe_secret(SecretId="test-secret") assert secret_details["ARN"] assert secret_details["Name"] == "test-secret" assert secret_details["DeletedDate"] > datetime.fromtimestamp(1, pytz.utc) @mock_secretsmanager def test_delete_secret_force(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") result = conn.delete_secret(SecretId="test-secret", ForceDeleteWithoutRecovery=True) assert result["ARN"] assert result["DeletionDate"] > datetime.fromtimestamp(1, pytz.utc) assert result["Name"] == "test-secret" with pytest.raises(ClientError): result = conn.get_secret_value(SecretId="test-secret") @mock_secretsmanager 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"] assert result["DeletionDate"] > datetime.fromtimestamp(1, pytz.utc) assert result["Name"] == "test-secret" with pytest.raises(ClientError): result = conn.get_secret_value(SecretId="test-secret") @mock_secretsmanager def test_delete_secret_that_does_not_exist(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError): conn.delete_secret(SecretId="i-dont-exist", ForceDeleteWithoutRecovery=True) @mock_secretsmanager def test_delete_secret_fails_with_both_force_delete_flag_and_recovery_window_flag(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") with pytest.raises(ClientError): conn.delete_secret( SecretId="test-secret", RecoveryWindowInDays=1, ForceDeleteWithoutRecovery=True, ) @mock_secretsmanager def test_delete_secret_recovery_window_too_short(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") with pytest.raises(ClientError): conn.delete_secret(SecretId="test-secret", RecoveryWindowInDays=6) @mock_secretsmanager def test_delete_secret_recovery_window_too_long(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") with pytest.raises(ClientError): conn.delete_secret(SecretId="test-secret", RecoveryWindowInDays=31) @mock_secretsmanager def test_delete_secret_that_is_marked_deleted(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") conn.delete_secret(SecretId="test-secret") with pytest.raises(ClientError): conn.delete_secret(SecretId="test-secret") @mock_secretsmanager def test_get_random_password_default_length(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password() assert len(random_password["RandomPassword"]) == 32 @mock_secretsmanager def test_get_random_password_default_requirements(): # When require_each_included_type, default true conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password() # Should contain lowercase, upppercase, digit, special character 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"]) @mock_secretsmanager def test_get_random_password_custom_length(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password(PasswordLength=50) assert len(random_password["RandomPassword"]) == 50 @mock_secretsmanager def test_get_random_exclude_lowercase(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password(PasswordLength=55, ExcludeLowercase=True) assert not any(c.islower() for c in random_password["RandomPassword"]) @mock_secretsmanager def test_get_random_exclude_uppercase(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password(PasswordLength=55, ExcludeUppercase=True) assert not any(c.isupper() for c in random_password["RandomPassword"]) @mock_secretsmanager def test_get_random_exclude_characters_and_symbols(): conn = boto3.client("secretsmanager", region_name="us-west-2") 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 @mock_secretsmanager def test_get_random_exclude_numbers(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password(PasswordLength=100, ExcludeNumbers=True) assert not any(c.isdigit() for c in random_password["RandomPassword"]) @mock_secretsmanager def test_get_random_exclude_punctuation(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password( PasswordLength=100, ExcludePunctuation=True ) assert not any(c in string.punctuation for c in random_password["RandomPassword"]) @mock_secretsmanager def test_get_random_include_space_false(): 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"]) @mock_secretsmanager def test_get_random_include_space_true(): conn = boto3.client("secretsmanager", region_name="us-west-2") random_password = conn.get_random_password(PasswordLength=4, IncludeSpace=True) assert any(c.isspace() for c in random_password["RandomPassword"]) @mock_secretsmanager def test_get_random_require_each_included_type(): conn = boto3.client("secretsmanager", region_name="us-west-2") 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"]) @mock_secretsmanager def test_get_random_too_short_password(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError): conn.get_random_password(PasswordLength=3) @mock_secretsmanager def test_get_random_too_long_password(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(Exception): conn.get_random_password(PasswordLength=5555) @mock_secretsmanager def test_describe_secret(): 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") 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 @mock_secretsmanager def test_describe_secret_with_arn(): conn = boto3.client("secretsmanager", region_name="us-west-2") results = conn.create_secret(Name="test-secret", SecretString="foosecret") secret_description = conn.describe_secret(SecretId=results["ARN"]) assert secret_description # Returned dict is not empty secret_description["Name"].should.equal("test-secret") secret_description["ARN"].should.equal(results["ARN"]) conn.list_secrets()["SecretList"][0]["ARN"].should.equal(results["ARN"]) @mock_secretsmanager 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"]) secret_description["KmsKeyId"].should.equal("dummy_arn") conn.list_secrets()["SecretList"][0]["KmsKeyId"].should.equal( secret_description["KmsKeyId"] ) @mock_secretsmanager def test_describe_secret_that_does_not_exist(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError): conn.get_secret_value(SecretId="i-dont-exist") @mock_secretsmanager def test_describe_secret_that_does_not_match(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") with pytest.raises(ClientError): conn.get_secret_value(SecretId="i-dont-match") @mock_secretsmanager def test_restore_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") conn.delete_secret(SecretId="test-secret") described_secret_before = conn.describe_secret(SecretId="test-secret") assert described_secret_before["DeletedDate"] > datetime.fromtimestamp(1, pytz.utc) restored_secret = conn.restore_secret(SecretId="test-secret") assert restored_secret["ARN"] assert restored_secret["Name"] == "test-secret" described_secret_after = conn.describe_secret(SecretId="test-secret") assert "DeletedDate" not in described_secret_after @mock_secretsmanager def test_restore_secret_that_is_not_deleted(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") restored_secret = conn.restore_secret(SecretId="test-secret") assert restored_secret["ARN"] assert restored_secret["Name"] == "test-secret" @mock_secretsmanager def test_restore_secret_that_does_not_exist(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError): conn.restore_secret(SecretId="i-dont-exist") @mock_secretsmanager def test_rotate_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret( Name=DEFAULT_SECRET_NAME, SecretString="foosecret", Description="foodescription" ) rotated_secret = conn.rotate_secret(SecretId=DEFAULT_SECRET_NAME) assert rotated_secret assert rotated_secret["ARN"] != "" # Test arn not empty assert rotated_secret["Name"] == DEFAULT_SECRET_NAME assert rotated_secret["VersionId"] != "" describe_secret = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME) assert describe_secret["Description"] == "foodescription" @mock_secretsmanager def test_rotate_secret_enable_rotation(): 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 initial_description assert initial_description["RotationEnabled"] is False assert initial_description["RotationRules"]["AutomaticallyAfterDays"] == 0 conn.rotate_secret( SecretId=DEFAULT_SECRET_NAME, RotationRules={"AutomaticallyAfterDays": 42} ) rotated_description = conn.describe_secret(SecretId=DEFAULT_SECRET_NAME) assert rotated_description assert rotated_description["RotationEnabled"] is True assert rotated_description["RotationRules"]["AutomaticallyAfterDays"] == 42 @mock_secretsmanager def test_rotate_secret_that_is_marked_deleted(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") conn.delete_secret(SecretId="test-secret") with pytest.raises(ClientError): conn.rotate_secret(SecretId="test-secret") @mock_secretsmanager def test_rotate_secret_that_does_not_exist(): conn = boto3.client("secretsmanager", "us-west-2") with pytest.raises(ClientError): conn.rotate_secret(SecretId="i-dont-exist") @mock_secretsmanager def test_rotate_secret_that_does_not_match(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") with pytest.raises(ClientError): conn.rotate_secret(SecretId="i-dont-match") @mock_secretsmanager 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 @mock_secretsmanager def test_rotate_secret_client_request_token_too_long(): 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): conn.rotate_secret( SecretId=DEFAULT_SECRET_NAME, ClientRequestToken=client_request_token ) @mock_secretsmanager def test_rotate_secret_rotation_lambda_arn_too_long(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret") rotation_lambda_arn = "85B7-446A-B7E4" * 147 # == 2058 characters with pytest.raises(ClientError): conn.rotate_secret( SecretId=DEFAULT_SECRET_NAME, RotationLambdaARN=rotation_lambda_arn ) @mock_secretsmanager 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 @mock_secretsmanager def test_rotate_secret_rotation_period_too_long(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretString="foosecret") rotation_rules = {"AutomaticallyAfterDays": 1001} with pytest.raises(ClientError): conn.rotate_secret(SecretId=DEFAULT_SECRET_NAME, RotationRules=rotation_rules) def get_rotation_zip_file(): from tests.test_awslambda.utilities import _process_lambda func_str = """ import boto3 import json def lambda_handler(event, context): arn = event['SecretId'] token = event['ClientRequestToken'] step = event['Step'] 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)) 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}), VersionStages=['AWSPENDING'] ) if step == 'setSecret': client.put_secret_value( SecretId=arn, ClientRequestToken=token, SecretString='UpdatedValue', VersionStages=["AWSPENDING"], ) elif step == 'finishSecret': current_version = next( version for version, stages in metadata['VersionIdsToStages'].items() if 'AWSCURRENT' in stages ) print("current: %s new: %s" % (current_version, token)) client.update_secret_version_stage( SecretId=arn, VersionStage='AWSCURRENT', MoveToVersionId=token, RemoveFromVersionId=current_version, ) client.update_secret_version_stage( SecretId=arn, VersionStage='AWSPENDING', RemoveFromVersionId=token, ) """ return _process_lambda(func_str) if settings.TEST_SERVER_MODE: @mock_lambda @mock_secretsmanager def test_rotate_secret_using_lambda(): from tests.test_awslambda.utilities import get_role_name # Passing a `RotationLambdaARN` value to `rotate_secret` should invoke lambda lambda_conn = boto3.client( "lambda", region_name="us-west-2", endpoint_url="http://localhost:5000", ) func = lambda_conn.create_function( FunctionName="testFunction", Runtime="python3.8", 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( Name=DEFAULT_SECRET_NAME, SecretString="InitialValue", ) initial_version = secret["VersionId"] rotated_secret = secrets_conn.rotate_secret( SecretId=DEFAULT_SECRET_NAME, RotationLambdaARN=func["FunctionArn"], RotationRules=dict(AutomaticallyAfterDays=30,), ) # Ensure we received an updated VersionId from `rotate_secret` assert rotated_secret["VersionId"] != initial_version updated_secret = secrets_conn.get_secret_value( SecretId=DEFAULT_SECRET_NAME, VersionStage="AWSCURRENT", ) 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" @mock_secretsmanager 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"], ) cm.value.response["Error"]["Message"].should.equal( "Secrets Manager can't find the specified secret." ) @mock_secretsmanager def test_put_secret_value_puts_new_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret") put_secret_value_dict = conn.put_secret_value( SecretId=DEFAULT_SECRET_NAME, SecretString="foosecret", VersionStages=["AWSCURRENT"], ) version_id = put_secret_value_dict["VersionId"] get_secret_value_dict = conn.get_secret_value( SecretId=DEFAULT_SECRET_NAME, VersionId=version_id, VersionStage="AWSCURRENT" ) assert get_secret_value_dict assert get_secret_value_dict["SecretString"] == "foosecret" @mock_secretsmanager def test_put_secret_binary_value_puts_new_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret") put_secret_value_dict = conn.put_secret_value( SecretId=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret", VersionStages=["AWSCURRENT"], ) version_id = put_secret_value_dict["VersionId"] get_secret_value_dict = conn.get_secret_value( SecretId=DEFAULT_SECRET_NAME, VersionId=version_id, VersionStage="AWSCURRENT" ) assert get_secret_value_dict assert get_secret_value_dict["SecretBinary"] == b"foosecret" @mock_secretsmanager def test_create_and_put_secret_binary_value_puts_new_secret(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret") conn.put_secret_value( SecretId=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret_update" ) latest_secret = conn.get_secret_value(SecretId=DEFAULT_SECRET_NAME) assert latest_secret assert latest_secret["SecretBinary"] == b"foosecret_update" @mock_secretsmanager def test_put_secret_binary_requires_either_string_or_binary(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError) as ire: conn.put_secret_value(SecretId=DEFAULT_SECRET_NAME) ire.value.response["Error"]["Code"].should.equal("InvalidRequestException") ire.value.response["Error"]["Message"].should.equal( "You must provide either SecretString or SecretBinary." ) @mock_secretsmanager def test_put_secret_value_can_get_first_version_if_put_twice(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary=b"foosecret") 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"], ) first_secret_value_dict = conn.get_secret_value( SecretId=DEFAULT_SECRET_NAME, VersionId=first_version_id ) first_secret_value = first_secret_value_dict["SecretString"] assert first_secret_value == "first_secret" @mock_secretsmanager def test_put_secret_value_versions_differ_if_same_secret_put_twice(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary="foosecret") 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 @mock_secretsmanager 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"] @mock_secretsmanager def test_can_list_secret_version_ids(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name=DEFAULT_SECRET_NAME, SecretBinary="foosecret") 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) returned_version_ids = [v["VersionId"] for v in versions_list["Versions"]] assert [first_version_id, second_version_id].sort() == returned_version_ids.sort() @mock_secretsmanager @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") 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"] @mock_secretsmanager 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" @mock_secretsmanager 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") secret_details["KmsKeyId"].should.equal("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") secret_details["KmsKeyId"].should.equal("bar_arn") @mock_secretsmanager def test_update_secret_which_does_not_exit(): conn = boto3.client("secretsmanager", region_name="us-west-2") with pytest.raises(ClientError) as cm: conn.update_secret(SecretId="test-secret", SecretString="barsecret") assert ( "Secrets Manager can't find the specified secret." == cm.value.response["Error"]["Message"] ) @mock_secretsmanager def test_update_secret_marked_as_deleted(): conn = boto3.client("secretsmanager", region_name="us-west-2") conn.create_secret(Name="test-secret", SecretString="foosecret") conn.delete_secret(SecretId="test-secret") with pytest.raises(ClientError) as cm: conn.update_secret(SecretId="test-secret", SecretString="barsecret") assert ( "because it was marked for deletion." in cm.value.response["Error"]["Message"] ) @mock_secretsmanager def test_update_secret_marked_as_deleted_after_restoring(): conn = boto3.client("secretsmanager", region_name="us-west-2") 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"] != "" @mock_secretsmanager @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( SecretId=secret_id, Tags=[{"Key": "FirstTag", "Value": "SomeValue"},], ) conn.tag_resource( SecretId=secret_id, Tags=[{"Key": "SecondTag", "Value": "AnotherValue"},], ) secrets = conn.list_secrets() assert secrets["SecretList"][0].get("Tags") == [ {"Key": "FirstTag", "Value": "SomeValue"}, {"Key": "SecondTag", "Value": "AnotherValue"}, ] with pytest.raises(ClientError) as cm: conn.tag_resource( SecretId="dummy-test-secret", Tags=[{"Key": "FirstTag", "Value": "SomeValue"},], ) assert ( "Secrets Manager can't find the specified secret." == cm.value.response["Error"]["Message"] ) @mock_secretsmanager @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: conn.untag_resource( SecretId="dummy-test-secret", TagKeys=["FirstTag"], ) assert ( "Secrets Manager can't find the specified secret." == cm.value.response["Error"]["Message"] ) @mock_secretsmanager 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 @mock_secretsmanager 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( 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, ) pve.value.response["Error"]["Code"].should.equal("InvalidParameterException") pve.value.response["Error"]["Message"].should.equal( "ClientRequestToken must be 32-64 characters long." )