From 55fe629112dee63308b747b7ed428200ecbca211 Mon Sep 17 00:00:00 2001 From: Christopher Kilding Date: Sun, 21 Apr 2019 18:11:20 +0100 Subject: [PATCH] Feature: Secrets Manager restore-secret --- IMPLEMENTATION_COVERAGE.md | 2 +- moto/secretsmanager/models.py | 14 +++++++ moto/secretsmanager/responses.py | 7 ++++ .../test_secretsmanager.py | 40 +++++++++++++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 2afd3f19c..34d0097b4 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -3661,7 +3661,7 @@ - [ ] list_secret_version_ids - [x] list_secrets - [ ] put_secret_value -- [ ] restore_secret +- [X] restore_secret - [X] rotate_secret - [ ] tag_resource - [ ] untag_resource diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index af8846a66..44ac1ef47 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -271,6 +271,20 @@ class SecretsManagerBackend(BaseBackend): return arn, name, self._unix_time_secs(deletion_date) + def restore_secret(self, secret_id): + + if not self._is_valid_identifier(secret_id): + raise ResourceNotFoundException + + self.secrets[secret_id].pop('deleted_date', None) + + secret = self.secrets[secret_id] + + arn = secret_arn(self.region, secret['secret_id']) + name = secret['name'] + + return arn, name + available_regions = ( boto3.session.Session().get_available_regions("secretsmanager") diff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py index a33890202..0eb02e39b 100644 --- a/moto/secretsmanager/responses.py +++ b/moto/secretsmanager/responses.py @@ -86,3 +86,10 @@ class SecretsManagerResponse(BaseResponse): force_delete_without_recovery=force_delete_without_recovery, ) return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date)) + + def restore_secret(self): + secret_id = self._get_param("SecretId") + arn, name = secretsmanager_backends[self.region].restore_secret( + secret_id=secret_id, + ) + return json.dumps(dict(ARN=arn, Name=name)) diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py index 7456c7d13..81ce93cc3 100644 --- a/tests/test_secretsmanager/test_secretsmanager.py +++ b/tests/test_secretsmanager/test_secretsmanager.py @@ -347,6 +347,46 @@ def test_list_secrets(): }] +@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 assert_raises(ClientError): + result = conn.restore_secret(SecretId='i-dont-exist') + + @mock_secretsmanager def test_rotate_secret(): secret_name = 'test-secret'