We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support Secrets Manager restore-secret
Implement support for `aws secretsmanager restore-secret`.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `moto/secretsmanager/responses.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 from moto.core.responses import BaseResponse
4 
5 from .models import secretsmanager_backends
6 
7 import json
8 
9 
10 class SecretsManagerResponse(BaseResponse):
11 
12     def get_secret_value(self):
13         secret_id = self._get_param('SecretId')
14         version_id = self._get_param('VersionId')
15         version_stage = self._get_param('VersionStage')
16         return secretsmanager_backends[self.region].get_secret_value(
17             secret_id=secret_id,
18             version_id=version_id,
19             version_stage=version_stage)
20 
21     def create_secret(self):
22         name = self._get_param('Name')
23         secret_string = self._get_param('SecretString')
24         tags = self._get_param('Tags', if_none=[])
25         return secretsmanager_backends[self.region].create_secret(
26             name=name,
27             secret_string=secret_string,
28             tags=tags
29         )
30 
31     def get_random_password(self):
32         password_length = self._get_param('PasswordLength', if_none=32)
33         exclude_characters = self._get_param('ExcludeCharacters', if_none='')
34         exclude_numbers = self._get_param('ExcludeNumbers', if_none=False)
35         exclude_punctuation = self._get_param('ExcludePunctuation', if_none=False)
36         exclude_uppercase = self._get_param('ExcludeUppercase', if_none=False)
37         exclude_lowercase = self._get_param('ExcludeLowercase', if_none=False)
38         include_space = self._get_param('IncludeSpace', if_none=False)
39         require_each_included_type = self._get_param(
40             'RequireEachIncludedType', if_none=True)
41         return secretsmanager_backends[self.region].get_random_password(
42             password_length=password_length,
43             exclude_characters=exclude_characters,
44             exclude_numbers=exclude_numbers,
45             exclude_punctuation=exclude_punctuation,
46             exclude_uppercase=exclude_uppercase,
47             exclude_lowercase=exclude_lowercase,
48             include_space=include_space,
49             require_each_included_type=require_each_included_type
50         )
51 
52     def describe_secret(self):
53         secret_id = self._get_param('SecretId')
54         return secretsmanager_backends[self.region].describe_secret(
55             secret_id=secret_id
56         )
57 
58     def rotate_secret(self):
59         client_request_token = self._get_param('ClientRequestToken')
60         rotation_lambda_arn = self._get_param('RotationLambdaARN')
61         rotation_rules = self._get_param('RotationRules')
62         secret_id = self._get_param('SecretId')
63         return secretsmanager_backends[self.region].rotate_secret(
64             secret_id=secret_id,
65             client_request_token=client_request_token,
66             rotation_lambda_arn=rotation_lambda_arn,
67             rotation_rules=rotation_rules
68         )
69 
70     def list_secrets(self):
71         max_results = self._get_int_param("MaxResults")
72         next_token = self._get_param("NextToken")
73         secret_list, next_token = secretsmanager_backends[self.region].list_secrets(
74             max_results=max_results,
75             next_token=next_token,
76         )
77         return json.dumps(dict(SecretList=secret_list, NextToken=next_token))
78 
79     def delete_secret(self):
80         secret_id = self._get_param("SecretId")
81         recovery_window_in_days = self._get_param("RecoveryWindowInDays")
82         force_delete_without_recovery = self._get_param("ForceDeleteWithoutRecovery")
83         arn, name, deletion_date = secretsmanager_backends[self.region].delete_secret(
84             secret_id=secret_id,
85             recovery_window_in_days=recovery_window_in_days,
86             force_delete_without_recovery=force_delete_without_recovery,
87         )
88         return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date))
89
```

Path: `moto/secretsmanager/models.py`
Content:
```
1 from __future__ import unicode_literals
2 
3 import time
4 import json
5 import uuid
6 import datetime
7 
8 import boto3
9 
10 from moto.core import BaseBackend, BaseModel
11 from .exceptions import (
12     ResourceNotFoundException,
13     InvalidParameterException,
14     InvalidRequestException,
15     ClientError
16 )
17 from .utils import random_password, secret_arn
18 
19 
20 class SecretsManager(BaseModel):
21 
22     def __init__(self, region_name, **kwargs):
23         self.region = region_name
24 
25 
26 class SecretsManagerBackend(BaseBackend):
27 
28     def __init__(self, region_name=None, **kwargs):
29         super(SecretsManagerBackend, self).__init__()
30         self.region = region_name
31         self.secrets = {}
32 
33     def reset(self):
34         region_name = self.region
35         self.__dict__ = {}
36         self.__init__(region_name)
37 
38     def _is_valid_identifier(self, identifier):
39         return identifier in self.secrets
40 
41     def _unix_time_secs(self, dt):
42         epoch = datetime.datetime.utcfromtimestamp(0)
43         return (dt - epoch).total_seconds()
44 
45     def get_secret_value(self, secret_id, version_id, version_stage):
46 
47         if not self._is_valid_identifier(secret_id):
48             raise ResourceNotFoundException()
49 
50         if 'deleted_date' in self.secrets[secret_id]:
51             raise InvalidRequestException(
52                 "An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You tried to \
53                 perform the operation on a secret that's currently marked deleted."
54             )
55 
56         secret = self.secrets[secret_id]
57 
58         response = json.dumps({
59             "ARN": secret_arn(self.region, secret['secret_id']),
60             "Name": secret['name'],
61             "VersionId": secret['version_id'],
62             "SecretString": secret['secret_string'],
63             "VersionStages": [
64                 "AWSCURRENT",
65             ],
66             "CreatedDate": secret['createdate']
67         })
68 
69         return response
70 
71     def create_secret(self, name, secret_string, tags, **kwargs):
72 
73         generated_version_id = str(uuid.uuid4())
74 
75         secret = {
76             'secret_string': secret_string,
77             'secret_id': name,
78             'name': name,
79             'createdate': int(time.time()),
80             'rotation_enabled': False,
81             'rotation_lambda_arn': '',
82             'auto_rotate_after_days': 0,
83             'version_id': generated_version_id,
84             'tags': tags
85         }
86 
87         self.secrets[name] = secret
88 
89         response = json.dumps({
90             "ARN": secret_arn(self.region, name),
91             "Name": name,
92             "VersionId": generated_version_id,
93         })
94 
95         return response
96 
97     def describe_secret(self, secret_id):
98         if not self._is_valid_identifier(secret_id):
99             raise ResourceNotFoundException
100 
101         secret = self.secrets[secret_id]
102 
103         response = json.dumps({
104             "ARN": secret_arn(self.region, secret['secret_id']),
105             "Name": secret['name'],
106             "Description": "",
107             "KmsKeyId": "",
108             "RotationEnabled": secret['rotation_enabled'],
109             "RotationLambdaARN": secret['rotation_lambda_arn'],
110             "RotationRules": {
111                 "AutomaticallyAfterDays": secret['auto_rotate_after_days']
112             },
113             "LastRotatedDate": None,
114             "LastChangedDate": None,
115             "LastAccessedDate": None,
116             "DeletedDate": secret.get('deleted_date', None),
117             "Tags": secret['tags']
118         })
119 
120         return response
121 
122     def rotate_secret(self, secret_id, client_request_token=None,
123                       rotation_lambda_arn=None, rotation_rules=None):
124 
125         rotation_days = 'AutomaticallyAfterDays'
126 
127         if not self._is_valid_identifier(secret_id):
128             raise ResourceNotFoundException
129 
130         if 'deleted_date' in self.secrets[secret_id]:
131             raise InvalidRequestException(
132                 "An error occurred (InvalidRequestException) when calling the RotateSecret operation: You tried to \
133                 perform the operation on a secret that's currently marked deleted."
134             )
135 
136         if client_request_token:
137             token_length = len(client_request_token)
138             if token_length < 32 or token_length > 64:
139                 msg = (
140                     'ClientRequestToken '
141                     'must be 32-64 characters long.'
142                 )
143                 raise InvalidParameterException(msg)
144 
145         if rotation_lambda_arn:
146             if len(rotation_lambda_arn) > 2048:
147                 msg = (
148                     'RotationLambdaARN '
149                     'must <= 2048 characters long.'
150                 )
151                 raise InvalidParameterException(msg)
152 
153         if rotation_rules:
154             if rotation_days in rotation_rules:
155                 rotation_period = rotation_rules[rotation_days]
156                 if rotation_period < 1 or rotation_period > 1000:
157                     msg = (
158                         'RotationRules.AutomaticallyAfterDays '
159                         'must be within 1-1000.'
160                     )
161                     raise InvalidParameterException(msg)
162 
163         secret = self.secrets[secret_id]
164 
165         secret['version_id'] = client_request_token or ''
166         secret['rotation_lambda_arn'] = rotation_lambda_arn or ''
167         if rotation_rules:
168             secret['auto_rotate_after_days'] = rotation_rules.get(rotation_days, 0)
169         if secret['auto_rotate_after_days'] > 0:
170             secret['rotation_enabled'] = True
171 
172         response = json.dumps({
173             "ARN": secret_arn(self.region, secret['secret_id']),
174             "Name": secret['name'],
175             "VersionId": secret['version_id']
176         })
177 
178         return response
179 
180     def get_random_password(self, password_length,
181                             exclude_characters, exclude_numbers,
182                             exclude_punctuation, exclude_uppercase,
183                             exclude_lowercase, include_space,
184                             require_each_included_type):
185         # password size must have value less than or equal to 4096
186         if password_length > 4096:
187             raise ClientError(
188                 "ClientError: An error occurred (ValidationException) \
189                 when calling the GetRandomPassword operation: 1 validation error detected: Value '{}' at 'passwordLength' \
190                 failed to satisfy constraint: Member must have value less than or equal to 4096".format(password_length))
191         if password_length < 4:
192             raise InvalidParameterException(
193                 "InvalidParameterException: An error occurred (InvalidParameterException) \
194                 when calling the GetRandomPassword operation: Password length is too short based on the required types.")
195 
196         response = json.dumps({
197             "RandomPassword": random_password(password_length,
198                                               exclude_characters,
199                                               exclude_numbers,
200                                               exclude_punctuation,
201                                               exclude_uppercase,
202                                               exclude_lowercase,
203                                               include_space,
204                                               require_each_included_type)
205         })
206 
207         return response
208 
209     def list_secrets(self, max_results, next_token):
210         # TODO implement pagination and limits
211 
212         secret_list = [{
213             "ARN": secret_arn(self.region, secret['secret_id']),
214             "DeletedDate": secret.get('deleted_date', None),
215             "Description": "",
216             "KmsKeyId": "",
217             "LastAccessedDate": None,
218             "LastChangedDate": None,
219             "LastRotatedDate": None,
220             "Name": secret['name'],
221             "RotationEnabled": secret['rotation_enabled'],
222             "RotationLambdaARN": secret['rotation_lambda_arn'],
223             "RotationRules": {
224                 "AutomaticallyAfterDays": secret['auto_rotate_after_days']
225             },
226             "SecretVersionsToStages": {
227                 secret['version_id']: ["AWSCURRENT"]
228             },
229             "Tags": secret['tags']
230         } for secret in self.secrets.values()]
231 
232         return secret_list, None
233 
234     def delete_secret(self, secret_id, recovery_window_in_days, force_delete_without_recovery):
235 
236         if not self._is_valid_identifier(secret_id):
237             raise ResourceNotFoundException
238 
239         if 'deleted_date' in self.secrets[secret_id]:
240             raise InvalidRequestException(
241                 "An error occurred (InvalidRequestException) when calling the DeleteSecret operation: You tried to \
242                 perform the operation on a secret that's currently marked deleted."
243             )
244 
245         if recovery_window_in_days and force_delete_without_recovery:
246             raise InvalidParameterException(
247                 "An error occurred (InvalidParameterException) when calling the DeleteSecret operation: You can't \
248                 use ForceDeleteWithoutRecovery in conjunction with RecoveryWindowInDays."
249             )
250 
251         if recovery_window_in_days and (recovery_window_in_days < 7 or recovery_window_in_days > 30):
252             raise InvalidParameterException(
253                 "An error occurred (InvalidParameterException) when calling the DeleteSecret operation: The \
254                 RecoveryWindowInDays value must be between 7 and 30 days (inclusive)."
255             )
256 
257         deletion_date = datetime.datetime.utcnow()
258 
259         if force_delete_without_recovery:
260             secret = self.secrets.pop(secret_id, None)
261         else:
262             deletion_date += datetime.timedelta(days=recovery_window_in_days or 30)
263             self.secrets[secret_id]['deleted_date'] = self._unix_time_secs(deletion_date)
264             secret = self.secrets.get(secret_id, None)
265 
266         if not secret:
267             raise ResourceNotFoundException
268 
269         arn = secret_arn(self.region, secret['secret_id'])
270         name = secret['name']
271 
272         return arn, name, self._unix_time_secs(deletion_date)
273 
274 
275 available_regions = (
276     boto3.session.Session().get_available_regions("secretsmanager")
277 )
278 secretsmanager_backends = {region: SecretsManagerBackend(region_name=region)
279                            for region in available_regions}
280
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -271,6 +271,20 @@
 
         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
--- a/moto/secretsmanager/responses.py
+++ b/moto/secretsmanager/responses.py
@@ -86,3 +86,10 @@
             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))

{"golden_diff": "diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py\n--- a/moto/secretsmanager/models.py\n+++ b/moto/secretsmanager/models.py\n@@ -271,6 +271,20 @@\n \n         return arn, name, self._unix_time_secs(deletion_date)\n \n+    def restore_secret(self, secret_id):\n+\n+        if not self._is_valid_identifier(secret_id):\n+            raise ResourceNotFoundException\n+\n+        self.secrets[secret_id].pop('deleted_date', None)\n+\n+        secret = self.secrets[secret_id]\n+\n+        arn = secret_arn(self.region, secret['secret_id'])\n+        name = secret['name']\n+\n+        return arn, name\n+\n \n available_regions = (\n     boto3.session.Session().get_available_regions(\"secretsmanager\")\ndiff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py\n--- a/moto/secretsmanager/responses.py\n+++ b/moto/secretsmanager/responses.py\n@@ -86,3 +86,10 @@\n             force_delete_without_recovery=force_delete_without_recovery,\n         )\n         return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date))\n+\n+    def restore_secret(self):\n+        secret_id = self._get_param(\"SecretId\")\n+        arn, name = secretsmanager_backends[self.region].restore_secret(\n+            secret_id=secret_id,\n+        )\n+        return json.dumps(dict(ARN=arn, Name=name))\n", "issue": "Support Secrets Manager restore-secret\nImplement support for `aws secretsmanager restore-secret`.\n", "before_files": [{"content": "from __future__ import unicode_literals\n\nfrom moto.core.responses import BaseResponse\n\nfrom .models import secretsmanager_backends\n\nimport json\n\n\nclass SecretsManagerResponse(BaseResponse):\n\n    def get_secret_value(self):\n        secret_id = self._get_param('SecretId')\n        version_id = self._get_param('VersionId')\n        version_stage = self._get_param('VersionStage')\n        return secretsmanager_backends[self.region].get_secret_value(\n            secret_id=secret_id,\n            version_id=version_id,\n            version_stage=version_stage)\n\n    def create_secret(self):\n        name = self._get_param('Name')\n        secret_string = self._get_param('SecretString')\n        tags = self._get_param('Tags', if_none=[])\n        return secretsmanager_backends[self.region].create_secret(\n            name=name,\n            secret_string=secret_string,\n            tags=tags\n        )\n\n    def get_random_password(self):\n        password_length = self._get_param('PasswordLength', if_none=32)\n        exclude_characters = self._get_param('ExcludeCharacters', if_none='')\n        exclude_numbers = self._get_param('ExcludeNumbers', if_none=False)\n        exclude_punctuation = self._get_param('ExcludePunctuation', if_none=False)\n        exclude_uppercase = self._get_param('ExcludeUppercase', if_none=False)\n        exclude_lowercase = self._get_param('ExcludeLowercase', if_none=False)\n        include_space = self._get_param('IncludeSpace', if_none=False)\n        require_each_included_type = self._get_param(\n            'RequireEachIncludedType', if_none=True)\n        return secretsmanager_backends[self.region].get_random_password(\n            password_length=password_length,\n            exclude_characters=exclude_characters,\n            exclude_numbers=exclude_numbers,\n            exclude_punctuation=exclude_punctuation,\n            exclude_uppercase=exclude_uppercase,\n            exclude_lowercase=exclude_lowercase,\n            include_space=include_space,\n            require_each_included_type=require_each_included_type\n        )\n\n    def describe_secret(self):\n        secret_id = self._get_param('SecretId')\n        return secretsmanager_backends[self.region].describe_secret(\n            secret_id=secret_id\n        )\n\n    def rotate_secret(self):\n        client_request_token = self._get_param('ClientRequestToken')\n        rotation_lambda_arn = self._get_param('RotationLambdaARN')\n        rotation_rules = self._get_param('RotationRules')\n        secret_id = self._get_param('SecretId')\n        return secretsmanager_backends[self.region].rotate_secret(\n            secret_id=secret_id,\n            client_request_token=client_request_token,\n            rotation_lambda_arn=rotation_lambda_arn,\n            rotation_rules=rotation_rules\n        )\n\n    def list_secrets(self):\n        max_results = self._get_int_param(\"MaxResults\")\n        next_token = self._get_param(\"NextToken\")\n        secret_list, next_token = secretsmanager_backends[self.region].list_secrets(\n            max_results=max_results,\n            next_token=next_token,\n        )\n        return json.dumps(dict(SecretList=secret_list, NextToken=next_token))\n\n    def delete_secret(self):\n        secret_id = self._get_param(\"SecretId\")\n        recovery_window_in_days = self._get_param(\"RecoveryWindowInDays\")\n        force_delete_without_recovery = self._get_param(\"ForceDeleteWithoutRecovery\")\n        arn, name, deletion_date = secretsmanager_backends[self.region].delete_secret(\n            secret_id=secret_id,\n            recovery_window_in_days=recovery_window_in_days,\n            force_delete_without_recovery=force_delete_without_recovery,\n        )\n        return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date))\n", "path": "moto/secretsmanager/responses.py"}, {"content": "from __future__ import unicode_literals\n\nimport time\nimport json\nimport uuid\nimport datetime\n\nimport boto3\n\nfrom moto.core import BaseBackend, BaseModel\nfrom .exceptions import (\n    ResourceNotFoundException,\n    InvalidParameterException,\n    InvalidRequestException,\n    ClientError\n)\nfrom .utils import random_password, secret_arn\n\n\nclass SecretsManager(BaseModel):\n\n    def __init__(self, region_name, **kwargs):\n        self.region = region_name\n\n\nclass SecretsManagerBackend(BaseBackend):\n\n    def __init__(self, region_name=None, **kwargs):\n        super(SecretsManagerBackend, self).__init__()\n        self.region = region_name\n        self.secrets = {}\n\n    def reset(self):\n        region_name = self.region\n        self.__dict__ = {}\n        self.__init__(region_name)\n\n    def _is_valid_identifier(self, identifier):\n        return identifier in self.secrets\n\n    def _unix_time_secs(self, dt):\n        epoch = datetime.datetime.utcfromtimestamp(0)\n        return (dt - epoch).total_seconds()\n\n    def get_secret_value(self, secret_id, version_id, version_stage):\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException()\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        secret = self.secrets[secret_id]\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"VersionId\": secret['version_id'],\n            \"SecretString\": secret['secret_string'],\n            \"VersionStages\": [\n                \"AWSCURRENT\",\n            ],\n            \"CreatedDate\": secret['createdate']\n        })\n\n        return response\n\n    def create_secret(self, name, secret_string, tags, **kwargs):\n\n        generated_version_id = str(uuid.uuid4())\n\n        secret = {\n            'secret_string': secret_string,\n            'secret_id': name,\n            'name': name,\n            'createdate': int(time.time()),\n            'rotation_enabled': False,\n            'rotation_lambda_arn': '',\n            'auto_rotate_after_days': 0,\n            'version_id': generated_version_id,\n            'tags': tags\n        }\n\n        self.secrets[name] = secret\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, name),\n            \"Name\": name,\n            \"VersionId\": generated_version_id,\n        })\n\n        return response\n\n    def describe_secret(self, secret_id):\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        secret = self.secrets[secret_id]\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"Description\": \"\",\n            \"KmsKeyId\": \"\",\n            \"RotationEnabled\": secret['rotation_enabled'],\n            \"RotationLambdaARN\": secret['rotation_lambda_arn'],\n            \"RotationRules\": {\n                \"AutomaticallyAfterDays\": secret['auto_rotate_after_days']\n            },\n            \"LastRotatedDate\": None,\n            \"LastChangedDate\": None,\n            \"LastAccessedDate\": None,\n            \"DeletedDate\": secret.get('deleted_date', None),\n            \"Tags\": secret['tags']\n        })\n\n        return response\n\n    def rotate_secret(self, secret_id, client_request_token=None,\n                      rotation_lambda_arn=None, rotation_rules=None):\n\n        rotation_days = 'AutomaticallyAfterDays'\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the RotateSecret operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        if client_request_token:\n            token_length = len(client_request_token)\n            if token_length < 32 or token_length > 64:\n                msg = (\n                    'ClientRequestToken '\n                    'must be 32-64 characters long.'\n                )\n                raise InvalidParameterException(msg)\n\n        if rotation_lambda_arn:\n            if len(rotation_lambda_arn) > 2048:\n                msg = (\n                    'RotationLambdaARN '\n                    'must <= 2048 characters long.'\n                )\n                raise InvalidParameterException(msg)\n\n        if rotation_rules:\n            if rotation_days in rotation_rules:\n                rotation_period = rotation_rules[rotation_days]\n                if rotation_period < 1 or rotation_period > 1000:\n                    msg = (\n                        'RotationRules.AutomaticallyAfterDays '\n                        'must be within 1-1000.'\n                    )\n                    raise InvalidParameterException(msg)\n\n        secret = self.secrets[secret_id]\n\n        secret['version_id'] = client_request_token or ''\n        secret['rotation_lambda_arn'] = rotation_lambda_arn or ''\n        if rotation_rules:\n            secret['auto_rotate_after_days'] = rotation_rules.get(rotation_days, 0)\n        if secret['auto_rotate_after_days'] > 0:\n            secret['rotation_enabled'] = True\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"VersionId\": secret['version_id']\n        })\n\n        return response\n\n    def get_random_password(self, password_length,\n                            exclude_characters, exclude_numbers,\n                            exclude_punctuation, exclude_uppercase,\n                            exclude_lowercase, include_space,\n                            require_each_included_type):\n        # password size must have value less than or equal to 4096\n        if password_length > 4096:\n            raise ClientError(\n                \"ClientError: An error occurred (ValidationException) \\\n                when calling the GetRandomPassword operation: 1 validation error detected: Value '{}' at 'passwordLength' \\\n                failed to satisfy constraint: Member must have value less than or equal to 4096\".format(password_length))\n        if password_length < 4:\n            raise InvalidParameterException(\n                \"InvalidParameterException: An error occurred (InvalidParameterException) \\\n                when calling the GetRandomPassword operation: Password length is too short based on the required types.\")\n\n        response = json.dumps({\n            \"RandomPassword\": random_password(password_length,\n                                              exclude_characters,\n                                              exclude_numbers,\n                                              exclude_punctuation,\n                                              exclude_uppercase,\n                                              exclude_lowercase,\n                                              include_space,\n                                              require_each_included_type)\n        })\n\n        return response\n\n    def list_secrets(self, max_results, next_token):\n        # TODO implement pagination and limits\n\n        secret_list = [{\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"DeletedDate\": secret.get('deleted_date', None),\n            \"Description\": \"\",\n            \"KmsKeyId\": \"\",\n            \"LastAccessedDate\": None,\n            \"LastChangedDate\": None,\n            \"LastRotatedDate\": None,\n            \"Name\": secret['name'],\n            \"RotationEnabled\": secret['rotation_enabled'],\n            \"RotationLambdaARN\": secret['rotation_lambda_arn'],\n            \"RotationRules\": {\n                \"AutomaticallyAfterDays\": secret['auto_rotate_after_days']\n            },\n            \"SecretVersionsToStages\": {\n                secret['version_id']: [\"AWSCURRENT\"]\n            },\n            \"Tags\": secret['tags']\n        } for secret in self.secrets.values()]\n\n        return secret_list, None\n\n    def delete_secret(self, secret_id, recovery_window_in_days, force_delete_without_recovery):\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the DeleteSecret operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        if recovery_window_in_days and force_delete_without_recovery:\n            raise InvalidParameterException(\n                \"An error occurred (InvalidParameterException) when calling the DeleteSecret operation: You can't \\\n                use ForceDeleteWithoutRecovery in conjunction with RecoveryWindowInDays.\"\n            )\n\n        if recovery_window_in_days and (recovery_window_in_days < 7 or recovery_window_in_days > 30):\n            raise InvalidParameterException(\n                \"An error occurred (InvalidParameterException) when calling the DeleteSecret operation: The \\\n                RecoveryWindowInDays value must be between 7 and 30 days (inclusive).\"\n            )\n\n        deletion_date = datetime.datetime.utcnow()\n\n        if force_delete_without_recovery:\n            secret = self.secrets.pop(secret_id, None)\n        else:\n            deletion_date += datetime.timedelta(days=recovery_window_in_days or 30)\n            self.secrets[secret_id]['deleted_date'] = self._unix_time_secs(deletion_date)\n            secret = self.secrets.get(secret_id, None)\n\n        if not secret:\n            raise ResourceNotFoundException\n\n        arn = secret_arn(self.region, secret['secret_id'])\n        name = secret['name']\n\n        return arn, name, self._unix_time_secs(deletion_date)\n\n\navailable_regions = (\n    boto3.session.Session().get_available_regions(\"secretsmanager\")\n)\nsecretsmanager_backends = {region: SecretsManagerBackend(region_name=region)\n                           for region in available_regions}\n", "path": "moto/secretsmanager/models.py"}], "after_files": [{"content": "from __future__ import unicode_literals\n\nfrom moto.core.responses import BaseResponse\n\nfrom .models import secretsmanager_backends\n\nimport json\n\n\nclass SecretsManagerResponse(BaseResponse):\n\n    def get_secret_value(self):\n        secret_id = self._get_param('SecretId')\n        version_id = self._get_param('VersionId')\n        version_stage = self._get_param('VersionStage')\n        return secretsmanager_backends[self.region].get_secret_value(\n            secret_id=secret_id,\n            version_id=version_id,\n            version_stage=version_stage)\n\n    def create_secret(self):\n        name = self._get_param('Name')\n        secret_string = self._get_param('SecretString')\n        tags = self._get_param('Tags', if_none=[])\n        return secretsmanager_backends[self.region].create_secret(\n            name=name,\n            secret_string=secret_string,\n            tags=tags\n        )\n\n    def get_random_password(self):\n        password_length = self._get_param('PasswordLength', if_none=32)\n        exclude_characters = self._get_param('ExcludeCharacters', if_none='')\n        exclude_numbers = self._get_param('ExcludeNumbers', if_none=False)\n        exclude_punctuation = self._get_param('ExcludePunctuation', if_none=False)\n        exclude_uppercase = self._get_param('ExcludeUppercase', if_none=False)\n        exclude_lowercase = self._get_param('ExcludeLowercase', if_none=False)\n        include_space = self._get_param('IncludeSpace', if_none=False)\n        require_each_included_type = self._get_param(\n            'RequireEachIncludedType', if_none=True)\n        return secretsmanager_backends[self.region].get_random_password(\n            password_length=password_length,\n            exclude_characters=exclude_characters,\n            exclude_numbers=exclude_numbers,\n            exclude_punctuation=exclude_punctuation,\n            exclude_uppercase=exclude_uppercase,\n            exclude_lowercase=exclude_lowercase,\n            include_space=include_space,\n            require_each_included_type=require_each_included_type\n        )\n\n    def describe_secret(self):\n        secret_id = self._get_param('SecretId')\n        return secretsmanager_backends[self.region].describe_secret(\n            secret_id=secret_id\n        )\n\n    def rotate_secret(self):\n        client_request_token = self._get_param('ClientRequestToken')\n        rotation_lambda_arn = self._get_param('RotationLambdaARN')\n        rotation_rules = self._get_param('RotationRules')\n        secret_id = self._get_param('SecretId')\n        return secretsmanager_backends[self.region].rotate_secret(\n            secret_id=secret_id,\n            client_request_token=client_request_token,\n            rotation_lambda_arn=rotation_lambda_arn,\n            rotation_rules=rotation_rules\n        )\n\n    def list_secrets(self):\n        max_results = self._get_int_param(\"MaxResults\")\n        next_token = self._get_param(\"NextToken\")\n        secret_list, next_token = secretsmanager_backends[self.region].list_secrets(\n            max_results=max_results,\n            next_token=next_token,\n        )\n        return json.dumps(dict(SecretList=secret_list, NextToken=next_token))\n\n    def delete_secret(self):\n        secret_id = self._get_param(\"SecretId\")\n        recovery_window_in_days = self._get_param(\"RecoveryWindowInDays\")\n        force_delete_without_recovery = self._get_param(\"ForceDeleteWithoutRecovery\")\n        arn, name, deletion_date = secretsmanager_backends[self.region].delete_secret(\n            secret_id=secret_id,\n            recovery_window_in_days=recovery_window_in_days,\n            force_delete_without_recovery=force_delete_without_recovery,\n        )\n        return json.dumps(dict(ARN=arn, Name=name, DeletionDate=deletion_date))\n\n    def restore_secret(self):\n        secret_id = self._get_param(\"SecretId\")\n        arn, name = secretsmanager_backends[self.region].restore_secret(\n            secret_id=secret_id,\n        )\n        return json.dumps(dict(ARN=arn, Name=name))\n", "path": "moto/secretsmanager/responses.py"}, {"content": "from __future__ import unicode_literals\n\nimport time\nimport json\nimport uuid\nimport datetime\n\nimport boto3\n\nfrom moto.core import BaseBackend, BaseModel\nfrom .exceptions import (\n    ResourceNotFoundException,\n    InvalidParameterException,\n    InvalidRequestException,\n    ClientError\n)\nfrom .utils import random_password, secret_arn\n\n\nclass SecretsManager(BaseModel):\n\n    def __init__(self, region_name, **kwargs):\n        self.region = region_name\n\n\nclass SecretsManagerBackend(BaseBackend):\n\n    def __init__(self, region_name=None, **kwargs):\n        super(SecretsManagerBackend, self).__init__()\n        self.region = region_name\n        self.secrets = {}\n\n    def reset(self):\n        region_name = self.region\n        self.__dict__ = {}\n        self.__init__(region_name)\n\n    def _is_valid_identifier(self, identifier):\n        return identifier in self.secrets\n\n    def _unix_time_secs(self, dt):\n        epoch = datetime.datetime.utcfromtimestamp(0)\n        return (dt - epoch).total_seconds()\n\n    def get_secret_value(self, secret_id, version_id, version_stage):\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException()\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the GetSecretValue operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        secret = self.secrets[secret_id]\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"VersionId\": secret['version_id'],\n            \"SecretString\": secret['secret_string'],\n            \"VersionStages\": [\n                \"AWSCURRENT\",\n            ],\n            \"CreatedDate\": secret['createdate']\n        })\n\n        return response\n\n    def create_secret(self, name, secret_string, tags, **kwargs):\n\n        generated_version_id = str(uuid.uuid4())\n\n        secret = {\n            'secret_string': secret_string,\n            'secret_id': name,\n            'name': name,\n            'createdate': int(time.time()),\n            'rotation_enabled': False,\n            'rotation_lambda_arn': '',\n            'auto_rotate_after_days': 0,\n            'version_id': generated_version_id,\n            'tags': tags\n        }\n\n        self.secrets[name] = secret\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, name),\n            \"Name\": name,\n            \"VersionId\": generated_version_id,\n        })\n\n        return response\n\n    def describe_secret(self, secret_id):\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        secret = self.secrets[secret_id]\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"Description\": \"\",\n            \"KmsKeyId\": \"\",\n            \"RotationEnabled\": secret['rotation_enabled'],\n            \"RotationLambdaARN\": secret['rotation_lambda_arn'],\n            \"RotationRules\": {\n                \"AutomaticallyAfterDays\": secret['auto_rotate_after_days']\n            },\n            \"LastRotatedDate\": None,\n            \"LastChangedDate\": None,\n            \"LastAccessedDate\": None,\n            \"DeletedDate\": secret.get('deleted_date', None),\n            \"Tags\": secret['tags']\n        })\n\n        return response\n\n    def rotate_secret(self, secret_id, client_request_token=None,\n                      rotation_lambda_arn=None, rotation_rules=None):\n\n        rotation_days = 'AutomaticallyAfterDays'\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the RotateSecret operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        if client_request_token:\n            token_length = len(client_request_token)\n            if token_length < 32 or token_length > 64:\n                msg = (\n                    'ClientRequestToken '\n                    'must be 32-64 characters long.'\n                )\n                raise InvalidParameterException(msg)\n\n        if rotation_lambda_arn:\n            if len(rotation_lambda_arn) > 2048:\n                msg = (\n                    'RotationLambdaARN '\n                    'must <= 2048 characters long.'\n                )\n                raise InvalidParameterException(msg)\n\n        if rotation_rules:\n            if rotation_days in rotation_rules:\n                rotation_period = rotation_rules[rotation_days]\n                if rotation_period < 1 or rotation_period > 1000:\n                    msg = (\n                        'RotationRules.AutomaticallyAfterDays '\n                        'must be within 1-1000.'\n                    )\n                    raise InvalidParameterException(msg)\n\n        secret = self.secrets[secret_id]\n\n        secret['version_id'] = client_request_token or ''\n        secret['rotation_lambda_arn'] = rotation_lambda_arn or ''\n        if rotation_rules:\n            secret['auto_rotate_after_days'] = rotation_rules.get(rotation_days, 0)\n        if secret['auto_rotate_after_days'] > 0:\n            secret['rotation_enabled'] = True\n\n        response = json.dumps({\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"Name\": secret['name'],\n            \"VersionId\": secret['version_id']\n        })\n\n        return response\n\n    def get_random_password(self, password_length,\n                            exclude_characters, exclude_numbers,\n                            exclude_punctuation, exclude_uppercase,\n                            exclude_lowercase, include_space,\n                            require_each_included_type):\n        # password size must have value less than or equal to 4096\n        if password_length > 4096:\n            raise ClientError(\n                \"ClientError: An error occurred (ValidationException) \\\n                when calling the GetRandomPassword operation: 1 validation error detected: Value '{}' at 'passwordLength' \\\n                failed to satisfy constraint: Member must have value less than or equal to 4096\".format(password_length))\n        if password_length < 4:\n            raise InvalidParameterException(\n                \"InvalidParameterException: An error occurred (InvalidParameterException) \\\n                when calling the GetRandomPassword operation: Password length is too short based on the required types.\")\n\n        response = json.dumps({\n            \"RandomPassword\": random_password(password_length,\n                                              exclude_characters,\n                                              exclude_numbers,\n                                              exclude_punctuation,\n                                              exclude_uppercase,\n                                              exclude_lowercase,\n                                              include_space,\n                                              require_each_included_type)\n        })\n\n        return response\n\n    def list_secrets(self, max_results, next_token):\n        # TODO implement pagination and limits\n\n        secret_list = [{\n            \"ARN\": secret_arn(self.region, secret['secret_id']),\n            \"DeletedDate\": secret.get('deleted_date', None),\n            \"Description\": \"\",\n            \"KmsKeyId\": \"\",\n            \"LastAccessedDate\": None,\n            \"LastChangedDate\": None,\n            \"LastRotatedDate\": None,\n            \"Name\": secret['name'],\n            \"RotationEnabled\": secret['rotation_enabled'],\n            \"RotationLambdaARN\": secret['rotation_lambda_arn'],\n            \"RotationRules\": {\n                \"AutomaticallyAfterDays\": secret['auto_rotate_after_days']\n            },\n            \"SecretVersionsToStages\": {\n                secret['version_id']: [\"AWSCURRENT\"]\n            },\n            \"Tags\": secret['tags']\n        } for secret in self.secrets.values()]\n\n        return secret_list, None\n\n    def delete_secret(self, secret_id, recovery_window_in_days, force_delete_without_recovery):\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        if 'deleted_date' in self.secrets[secret_id]:\n            raise InvalidRequestException(\n                \"An error occurred (InvalidRequestException) when calling the DeleteSecret operation: You tried to \\\n                perform the operation on a secret that's currently marked deleted.\"\n            )\n\n        if recovery_window_in_days and force_delete_without_recovery:\n            raise InvalidParameterException(\n                \"An error occurred (InvalidParameterException) when calling the DeleteSecret operation: You can't \\\n                use ForceDeleteWithoutRecovery in conjunction with RecoveryWindowInDays.\"\n            )\n\n        if recovery_window_in_days and (recovery_window_in_days < 7 or recovery_window_in_days > 30):\n            raise InvalidParameterException(\n                \"An error occurred (InvalidParameterException) when calling the DeleteSecret operation: The \\\n                RecoveryWindowInDays value must be between 7 and 30 days (inclusive).\"\n            )\n\n        deletion_date = datetime.datetime.utcnow()\n\n        if force_delete_without_recovery:\n            secret = self.secrets.pop(secret_id, None)\n        else:\n            deletion_date += datetime.timedelta(days=recovery_window_in_days or 30)\n            self.secrets[secret_id]['deleted_date'] = self._unix_time_secs(deletion_date)\n            secret = self.secrets.get(secret_id, None)\n\n        if not secret:\n            raise ResourceNotFoundException\n\n        arn = secret_arn(self.region, secret['secret_id'])\n        name = secret['name']\n\n        return arn, name, self._unix_time_secs(deletion_date)\n\n    def restore_secret(self, secret_id):\n\n        if not self._is_valid_identifier(secret_id):\n            raise ResourceNotFoundException\n\n        self.secrets[secret_id].pop('deleted_date', None)\n\n        secret = self.secrets[secret_id]\n\n        arn = secret_arn(self.region, secret['secret_id'])\n        name = secret['name']\n\n        return arn, name\n\n\navailable_regions = (\n    boto3.session.Session().get_available_regions(\"secretsmanager\")\n)\nsecretsmanager_backends = {region: SecretsManagerBackend(region_name=region)\n                           for region in available_regions}\n", "path": "moto/secretsmanager/models.py"}]}