From cce3a678aa382ef29d06eefc10d4ddd01ec25f18 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 6 Aug 2018 14:40:33 -0700 Subject: [PATCH 1/7] Implement secretsmanager.DescribeSecret and tests. --- moto/secretsmanager/models.py | 31 +++++++++++++ moto/secretsmanager/responses.py | 6 +++ .../test_secretsmanager.py | 18 ++++++++ tests/test_secretsmanager/test_server.py | 43 +++++++++++++++++++ 4 files changed, 98 insertions(+) diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index 3923f90b0..da3f3e6fe 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -33,6 +33,9 @@ class SecretsManagerBackend(BaseBackend): self.name = kwargs.get('name', '') self.createdate = int(time.time()) self.secret_string = '' + self.rotation_enabled = False + self.rotation_lambda_arn = '' + self.auto_rotate_after_days = 1 def reset(self): region_name = self.region @@ -70,6 +73,34 @@ class SecretsManagerBackend(BaseBackend): return response + def describe_secret(self, secret_id): + if self.secret_id == '': + raise ResourceNotFoundException + + response = json.dumps({ + "ARN": secret_arn(self.region, self.secret_id), + "Name": self.secret_id, + "Description": "", + "KmsKeyId": "", + "RotationEnabled": self.rotation_enabled, + "RotationLambdaARN": self.rotation_lambda_arn, + "RotationRules": { + "AutomaticallyAfterDays": self.auto_rotate_after_days + }, + "LastRotatedDate": None, + "LastChangedDate": None, + "LastAccessedDate": None, + "DeletedDate": None, + "Tags": [ + { + "Key": "", + "Value": "" + }, + ] + }) + + return response + def get_random_password(self, password_length, exclude_characters, exclude_numbers, exclude_punctuation, exclude_uppercase, diff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py index 06387560a..c50c6a6e1 100644 --- a/moto/secretsmanager/responses.py +++ b/moto/secretsmanager/responses.py @@ -44,3 +44,9 @@ class SecretsManagerResponse(BaseResponse): include_space=include_space, require_each_included_type=require_each_included_type ) + + def describe_secret(self): + secret_id = self._get_param('SecretId') + return secretsmanager_backends[self.region].describe_secret( + secret_id=secret_id + ) diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py index 6fefeb56f..0ef54b45b 100644 --- a/tests/test_secretsmanager/test_secretsmanager.py +++ b/tests/test_secretsmanager/test_secretsmanager.py @@ -143,3 +143,21 @@ def test_get_random_too_long_password(): with assert_raises(Exception): random_password = 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') + + secret_description = conn.describe_secret(SecretId='test-secret') + assert secret_description # Returned dict is not empty + assert secret_description['ARN'] == ( + 'arn:aws:secretsmanager:us-west-2:1234567890:secret:test-secret-rIjad') + +@mock_secretsmanager +def test_describe_secret_that_does_not_exist(): + conn = boto3.client('secretsmanager', region_name='us-west-2') + + with assert_raises(ClientError): + result = conn.get_secret_value(SecretId='i-dont-exist') diff --git a/tests/test_secretsmanager/test_server.py b/tests/test_secretsmanager/test_server.py index 2f73ece07..370a483a8 100644 --- a/tests/test_secretsmanager/test_server.py +++ b/tests/test_secretsmanager/test_server.py @@ -66,3 +66,46 @@ def test_create_secret(): assert json_data['ARN'] == ( 'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad') assert json_data['Name'] == 'test-secret' + +@mock_secretsmanager +def test_describe_secret(): + + backend = server.create_backend_app('secretsmanager') + test_client = backend.test_client() + + create_secret = test_client.post('/', + data={"Name": "test-secret", + "SecretString": "foosecret"}, + headers={ + "X-Amz-Target": "secretsmanager.CreateSecret" + }, + ) + describe_secret = test_client.post('/', + data={"SecretId": "test-secret"}, + headers={ + "X-Amz-Target": "secretsmanager.DescribeSecret" + }, + ) + + json_data = json.loads(describe_secret.data.decode("utf-8")) + assert json_data # Returned dict is not empty + assert json_data['ARN'] == ( + 'arn:aws:secretsmanager:us-east-1:1234567890:secret:test-secret-rIjad' + ) + +@mock_secretsmanager +def test_describe_secret_that_does_not_exist(): + + backend = server.create_backend_app('secretsmanager') + test_client = backend.test_client() + + describe_secret = test_client.post('/', + data={"SecretId": "i-dont-exist"}, + headers={ + "X-Amz-Target": "secretsmanager.DescribeSecret" + }, + ) + + json_data = json.loads(describe_secret.data.decode("utf-8")) + assert json_data['message'] == "Secrets Manager can't find the specified secret" + assert json_data['__type'] == 'ResourceNotFoundException' From 65ef61ca1d61294acb5f41173bc3914467d8ce98 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 6 Aug 2018 15:54:37 -0700 Subject: [PATCH 2/7] Fix linter warning. --- moto/secretsmanager/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index da3f3e6fe..26eb64c57 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -76,7 +76,7 @@ class SecretsManagerBackend(BaseBackend): def describe_secret(self, secret_id): if self.secret_id == '': raise ResourceNotFoundException - + response = json.dumps({ "ARN": secret_arn(self.region, self.secret_id), "Name": self.secret_id, From b47fc7465067f8cdf429c61b9e217d7bcf68e9ee Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Thu, 9 Aug 2018 18:19:33 -0700 Subject: [PATCH 3/7] Set correct default auto rotation period. --- moto/secretsmanager/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index 26eb64c57..b6f3b3d86 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -35,7 +35,7 @@ class SecretsManagerBackend(BaseBackend): self.secret_string = '' self.rotation_enabled = False self.rotation_lambda_arn = '' - self.auto_rotate_after_days = 1 + self.auto_rotate_after_days = 0 def reset(self): region_name = self.region From 92bc3ff910cc4323825c80f5687856131e569804 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 13 Aug 2018 12:41:43 -0700 Subject: [PATCH 4/7] Issue 1753: Add support for DescribeSecret - Add helper method to validate the secret identifier from the client. - Update describe_secret to use new helper method. - Insert friendly name into "Name" field of returned description (was SecretId). ***Assumes acceptance of PR 1772. --- moto/secretsmanager/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index b6f3b3d86..bba16ac53 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -42,6 +42,9 @@ class SecretsManagerBackend(BaseBackend): self.__dict__ = {} self.__init__(region_name) + def _is_valid_identifier(self, identifier): + return identifier in (self.name, self.secret_id) + def get_secret_value(self, secret_id, version_id, version_stage): if self.secret_id == '': @@ -74,12 +77,12 @@ class SecretsManagerBackend(BaseBackend): return response def describe_secret(self, secret_id): - if self.secret_id == '': + if not self._is_valid_identifier(secret_id): raise ResourceNotFoundException response = json.dumps({ "ARN": secret_arn(self.region, self.secret_id), - "Name": self.secret_id, + "Name": self.name, "Description": "", "KmsKeyId": "", "RotationEnabled": self.rotation_enabled, From 85d3250b893b4d4505486013168fcf69da79da78 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 13 Aug 2018 12:49:35 -0700 Subject: [PATCH 5/7] Issue 1753: add test for mismatched secret --- tests/test_secretsmanager/test_secretsmanager.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_secretsmanager/test_secretsmanager.py b/tests/test_secretsmanager/test_secretsmanager.py index 0ef54b45b..2691e2783 100644 --- a/tests/test_secretsmanager/test_secretsmanager.py +++ b/tests/test_secretsmanager/test_secretsmanager.py @@ -161,3 +161,12 @@ def test_describe_secret_that_does_not_exist(): with assert_raises(ClientError): result = 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 assert_raises(ClientError): + result = conn.get_secret_value(SecretId='i-dont-match') From b2c672a074c1346e2ac05b5fe7353c987a83f7a8 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Mon, 13 Aug 2018 12:53:22 -0700 Subject: [PATCH 6/7] Issue 1753: add server test for mismatched secret --- tests/test_secretsmanager/test_server.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/test_secretsmanager/test_server.py b/tests/test_secretsmanager/test_server.py index 370a483a8..8c6f7b970 100644 --- a/tests/test_secretsmanager/test_server.py +++ b/tests/test_secretsmanager/test_server.py @@ -109,3 +109,27 @@ def test_describe_secret_that_does_not_exist(): json_data = json.loads(describe_secret.data.decode("utf-8")) assert json_data['message'] == "Secrets Manager can't find the specified secret" assert json_data['__type'] == 'ResourceNotFoundException' + +@mock_secretsmanager +def test_describe_secret_that_does_not_match(): + + backend = server.create_backend_app('secretsmanager') + test_client = backend.test_client() + + create_secret = test_client.post('/', + data={"Name": "test-secret", + "SecretString": "foosecret"}, + headers={ + "X-Amz-Target": "secretsmanager.CreateSecret" + }, + ) + describe_secret = test_client.post('/', + data={"SecretId": "i-dont-match"}, + headers={ + "X-Amz-Target": "secretsmanager.DescribeSecret" + }, + ) + + json_data = json.loads(describe_secret.data.decode("utf-8")) + assert json_data['message'] == "Secrets Manager can't find the specified secret" + assert json_data['__type'] == 'ResourceNotFoundException' From 48a71ae329765188cf241dc3662d028e78cc345d Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Tue, 14 Aug 2018 12:04:39 -0700 Subject: [PATCH 7/7] Issue 1753: Add support for DescribeSecret - Merge changes from upstream master. - Update get_secret_value to use helper method for validating secret identifier. - Update implementation coverage checklist. --- IMPLEMENTATION_COVERAGE.md | 4 ++-- moto/secretsmanager/models.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md index 98a216c79..938cc3549 100644 --- a/IMPLEMENTATION_COVERAGE.md +++ b/IMPLEMENTATION_COVERAGE.md @@ -3645,11 +3645,11 @@ - [ ] put_attributes - [ ] select -## secretsmanager - 20% implemented +## secretsmanager - 27% implemented - [ ] cancel_rotate_secret - [X] create_secret - [ ] delete_secret -- [ ] describe_secret +- [X] describe_secret - [X] get_random_password - [X] get_secret_value - [ ] list_secret_version_ids diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py index 07b563633..c60feb530 100644 --- a/moto/secretsmanager/models.py +++ b/moto/secretsmanager/models.py @@ -47,7 +47,7 @@ class SecretsManagerBackend(BaseBackend): def get_secret_value(self, secret_id, version_id, version_stage): - if secret_id not in (self.secret_id, self.name): + if not self._is_valid_identifier(secret_id): raise ResourceNotFoundException() response = json.dumps({