clear-skies-aws 1.10.1__py3-none-any.whl → 2.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {clear_skies_aws-1.10.1.dist-info → clear_skies_aws-2.0.1.dist-info}/METADATA +36 -35
- clear_skies_aws-2.0.1.dist-info/RECORD +4 -0
- {clear_skies_aws-1.10.1.dist-info → clear_skies_aws-2.0.1.dist-info}/WHEEL +1 -1
- clear_skies_aws-2.0.1.dist-info/licenses/LICENSE +21 -0
- clear_skies_aws-1.10.1.dist-info/LICENSE +0 -7
- clear_skies_aws-1.10.1.dist-info/RECORD +0 -71
- clearskies_aws/__init__.py +0 -2
- clearskies_aws/actions/__init__.py +0 -108
- clearskies_aws/actions/action_aws.py +0 -118
- clearskies_aws/actions/assume_role.py +0 -102
- clearskies_aws/actions/assume_role_test.py +0 -72
- clearskies_aws/actions/ses.py +0 -194
- clearskies_aws/actions/ses_test.py +0 -89
- clearskies_aws/actions/sns.py +0 -64
- clearskies_aws/actions/sns_test.py +0 -77
- clearskies_aws/actions/sqs.py +0 -82
- clearskies_aws/actions/sqs_test.py +0 -127
- clearskies_aws/actions/step_function.py +0 -66
- clearskies_aws/actions/step_function_test.py +0 -103
- clearskies_aws/backends/__init__.py +0 -12
- clearskies_aws/backends/dynamo_db_backend.py +0 -614
- clearskies_aws/backends/dynamo_db_backend_test.py +0 -300
- clearskies_aws/backends/dynamo_db_condition_parser.py +0 -365
- clearskies_aws/backends/dynamo_db_condition_parser_test.py +0 -266
- clearskies_aws/backends/dynamo_db_parti_ql_backend.py +0 -1123
- clearskies_aws/backends/dynamo_db_parti_ql_backend_test.py +0 -544
- clearskies_aws/backends/sqs_backend.py +0 -80
- clearskies_aws/backends/sqs_backend_test.py +0 -31
- clearskies_aws/contexts/__init__.py +0 -10
- clearskies_aws/contexts/cli.py +0 -19
- clearskies_aws/contexts/cli_websocket_mock.py +0 -33
- clearskies_aws/contexts/lambda_api_gateway.py +0 -30
- clearskies_aws/contexts/lambda_api_gateway_web_socket.py +0 -30
- clearskies_aws/contexts/lambda_elb.py +0 -30
- clearskies_aws/contexts/lambda_http_gateway.py +0 -30
- clearskies_aws/contexts/lambda_invocation.py +0 -48
- clearskies_aws/contexts/lambda_sns.py +0 -43
- clearskies_aws/contexts/lambda_sqs_standard_partial_batch.py +0 -51
- clearskies_aws/contexts/lambda_sqs_standard_partial_batch_test.py +0 -66
- clearskies_aws/contexts/wsgi.py +0 -19
- clearskies_aws/di/__init__.py +0 -1
- clearskies_aws/di/standard_dependencies.py +0 -60
- clearskies_aws/handlers/__init__.py +0 -2
- clearskies_aws/handlers/secrets_manager_rotation.py +0 -174
- clearskies_aws/handlers/simple_body_routing.py +0 -39
- clearskies_aws/input_outputs/__init__.py +0 -8
- clearskies_aws/input_outputs/cli_websocket_mock.py +0 -12
- clearskies_aws/input_outputs/lambda_api_gateway.py +0 -105
- clearskies_aws/input_outputs/lambda_api_gateway_test.py +0 -87
- clearskies_aws/input_outputs/lambda_api_gateway_web_socket.py +0 -8
- clearskies_aws/input_outputs/lambda_elb.py +0 -21
- clearskies_aws/input_outputs/lambda_http_gateway.py +0 -12
- clearskies_aws/input_outputs/lambda_invocation.py +0 -34
- clearskies_aws/input_outputs/lambda_sns.py +0 -52
- clearskies_aws/input_outputs/lambda_sqs_standard.py +0 -54
- clearskies_aws/mocks/__init__.py +0 -1
- clearskies_aws/mocks/actions/__init__.py +0 -6
- clearskies_aws/mocks/actions/ses.py +0 -28
- clearskies_aws/mocks/actions/sns.py +0 -23
- clearskies_aws/mocks/actions/sqs.py +0 -23
- clearskies_aws/mocks/actions/step_function.py +0 -26
- clearskies_aws/secrets/__init__.py +0 -7
- clearskies_aws/secrets/additional_configs/__init__.py +0 -54
- clearskies_aws/secrets/additional_configs/iam_db_auth.py +0 -29
- clearskies_aws/secrets/additional_configs/iam_db_auth_with_ssm.py +0 -92
- clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssh_cert_bastion.py +0 -81
- clearskies_aws/secrets/additional_configs/mysql_connection_dynamic_producer_via_ssm_bastion.py +0 -141
- clearskies_aws/secrets/akeyless_with_ssm_cache.py +0 -38
- clearskies_aws/secrets/parameter_store.py +0 -50
- clearskies_aws/secrets/parameter_store_test.py +0 -18
- clearskies_aws/secrets/secrets_manager.py +0 -75
- clearskies_aws/secrets/secrets_manager_test.py +0 -18
- clearskies_aws/web_socket_connection_model.py +0 -43
clearskies_aws/contexts/cli.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from ..di import StandardDependencies
|
|
2
|
-
from clearskies.contexts.cli import CLI, build_context
|
|
3
|
-
def cli(
|
|
4
|
-
application,
|
|
5
|
-
di_class=StandardDependencies,
|
|
6
|
-
bindings=None,
|
|
7
|
-
binding_classes=None,
|
|
8
|
-
binding_modules=None,
|
|
9
|
-
additional_configs=None,
|
|
10
|
-
):
|
|
11
|
-
return build_context(
|
|
12
|
-
CLI,
|
|
13
|
-
application,
|
|
14
|
-
di_class=di_class,
|
|
15
|
-
bindings=bindings,
|
|
16
|
-
binding_classes=binding_classes,
|
|
17
|
-
binding_modules=binding_modules,
|
|
18
|
-
additional_configs=additional_configs,
|
|
19
|
-
)
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import clearskies
|
|
2
|
-
import clearskies_aws
|
|
3
|
-
from ..input_outputs import CLIWebsocketMock as CLIWebsocketMockInputOutput
|
|
4
|
-
from clearskies_aws.contexts.cli import CLI
|
|
5
|
-
class CLIWebsocketMock(CLI):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def __call__(self):
|
|
10
|
-
if self.handler is None:
|
|
11
|
-
raise ValueError("Cannot execute CLIWebsocketMock context without first configuring it")
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
return self.handler(self.di.build(CLIWebsocketMockInputOutput))
|
|
15
|
-
except clearskies.input_outputs.exceptions.CLINotFound:
|
|
16
|
-
print("help (aka 404 not found)!")
|
|
17
|
-
def cli_websocket_mock(
|
|
18
|
-
application,
|
|
19
|
-
di_class=clearskies_aws.di.StandardDependencies,
|
|
20
|
-
bindings=None,
|
|
21
|
-
binding_classes=None,
|
|
22
|
-
binding_modules=None,
|
|
23
|
-
additional_configs=None,
|
|
24
|
-
):
|
|
25
|
-
return clearskies.contexts.build_context(
|
|
26
|
-
CLIWebsocketMock,
|
|
27
|
-
application,
|
|
28
|
-
di_class=di_class,
|
|
29
|
-
bindings=bindings,
|
|
30
|
-
binding_classes=binding_classes,
|
|
31
|
-
binding_modules=binding_modules,
|
|
32
|
-
additional_configs=additional_configs,
|
|
33
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from ..input_outputs import LambdaAPIGateway as LambdaAPIGatewayInputOutput
|
|
2
|
-
from ..di import StandardDependencies
|
|
3
|
-
from clearskies.contexts.build_context import build_context
|
|
4
|
-
from clearskies.contexts.context import Context
|
|
5
|
-
class LambdaAPIGateway(Context):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def __call__(self, event, context):
|
|
10
|
-
if self.handler is None:
|
|
11
|
-
raise ValueError("Cannot execute LambdaAPIGateway context without first configuring it")
|
|
12
|
-
|
|
13
|
-
return self.handler(LambdaAPIGatewayInputOutput(event, context))
|
|
14
|
-
def lambda_api_gateway(
|
|
15
|
-
application,
|
|
16
|
-
di_class=StandardDependencies,
|
|
17
|
-
bindings=None,
|
|
18
|
-
binding_classes=None,
|
|
19
|
-
binding_modules=None,
|
|
20
|
-
additional_configs=None,
|
|
21
|
-
):
|
|
22
|
-
return build_context(
|
|
23
|
-
LambdaAPIGateway,
|
|
24
|
-
application,
|
|
25
|
-
di_class=di_class,
|
|
26
|
-
bindings=bindings,
|
|
27
|
-
binding_classes=binding_classes,
|
|
28
|
-
binding_modules=binding_modules,
|
|
29
|
-
additional_configs=additional_configs,
|
|
30
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from ..input_outputs import LambdaAPIGatewayWebSocket as LambdaAPIGatewayWebSocketInputOutput
|
|
2
|
-
from ..di import StandardDependencies
|
|
3
|
-
from clearskies.contexts.build_context import build_context
|
|
4
|
-
from clearskies.contexts.context import Context
|
|
5
|
-
class LambdaAPIGatewayWebSocket(Context):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def __call__(self, event, context):
|
|
10
|
-
if self.handler is None:
|
|
11
|
-
raise ValueError("Cannot execute LambdaAPIGatewayWebSocket context without first configuring it")
|
|
12
|
-
|
|
13
|
-
return self.handler(LambdaAPIGatewayWebSocketInputOutput(event, context))
|
|
14
|
-
def lambda_api_gateway_web_socket(
|
|
15
|
-
application,
|
|
16
|
-
di_class=StandardDependencies,
|
|
17
|
-
bindings=None,
|
|
18
|
-
binding_classes=None,
|
|
19
|
-
binding_modules=None,
|
|
20
|
-
additional_configs=None,
|
|
21
|
-
):
|
|
22
|
-
return build_context(
|
|
23
|
-
LambdaAPIGatewayWebSocket,
|
|
24
|
-
application,
|
|
25
|
-
di_class=di_class,
|
|
26
|
-
bindings=bindings,
|
|
27
|
-
binding_classes=binding_classes,
|
|
28
|
-
binding_modules=binding_modules,
|
|
29
|
-
additional_configs=additional_configs,
|
|
30
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from ..input_outputs import LambdaELB as LambdaELBInputOutput
|
|
2
|
-
from ..di import StandardDependencies
|
|
3
|
-
from clearskies.contexts.build_context import build_context
|
|
4
|
-
from clearskies.contexts.context import Context
|
|
5
|
-
class LambdaELB(Context):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def __call__(self, event, context):
|
|
10
|
-
if self.handler is None:
|
|
11
|
-
raise ValueError("Cannot execute LambdaELB context without first configuring it")
|
|
12
|
-
|
|
13
|
-
return self.handler(LambdaELBInputOutput(event, context))
|
|
14
|
-
def lambda_elb(
|
|
15
|
-
application,
|
|
16
|
-
di_class=StandardDependencies,
|
|
17
|
-
bindings=None,
|
|
18
|
-
binding_classes=None,
|
|
19
|
-
binding_modules=None,
|
|
20
|
-
additional_configs=None,
|
|
21
|
-
):
|
|
22
|
-
return build_context(
|
|
23
|
-
LambdaELB,
|
|
24
|
-
application,
|
|
25
|
-
di_class=di_class,
|
|
26
|
-
bindings=bindings,
|
|
27
|
-
binding_classes=binding_classes,
|
|
28
|
-
binding_modules=binding_modules,
|
|
29
|
-
additional_configs=additional_configs,
|
|
30
|
-
)
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
from ..input_outputs import LambdaHTTPGateway as LambdaHTTPGatewayInputOutput
|
|
2
|
-
from ..di import StandardDependencies
|
|
3
|
-
from clearskies.contexts.build_context import build_context
|
|
4
|
-
from clearskies.contexts.context import Context
|
|
5
|
-
class LambdaHTTPGateway(Context):
|
|
6
|
-
def __init__(self, di):
|
|
7
|
-
super().__init__(di)
|
|
8
|
-
|
|
9
|
-
def __call__(self, event, context):
|
|
10
|
-
if self.handler is None:
|
|
11
|
-
raise ValueError("Cannot execute LambdaHTTPGateway context without first configuring it")
|
|
12
|
-
|
|
13
|
-
return self.handler(LambdaHTTPGatewayInputOutput(event, context))
|
|
14
|
-
def lambda_http_gateway(
|
|
15
|
-
application,
|
|
16
|
-
di_class=StandardDependencies,
|
|
17
|
-
bindings=None,
|
|
18
|
-
binding_classes=None,
|
|
19
|
-
binding_modules=None,
|
|
20
|
-
additional_configs=None,
|
|
21
|
-
):
|
|
22
|
-
return build_context(
|
|
23
|
-
LambdaHTTPGateway,
|
|
24
|
-
application,
|
|
25
|
-
di_class=di_class,
|
|
26
|
-
bindings=bindings,
|
|
27
|
-
binding_classes=binding_classes,
|
|
28
|
-
binding_modules=binding_modules,
|
|
29
|
-
additional_configs=additional_configs,
|
|
30
|
-
)
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
from clearskies.authentication import public
|
|
2
|
-
from ..input_outputs import LambdaInvocation as LambdaInvocationInputOutput
|
|
3
|
-
from ..di import StandardDependencies
|
|
4
|
-
from clearskies.contexts.build_context import build_context
|
|
5
|
-
from clearskies.contexts.context import Context
|
|
6
|
-
class LambdaInvocation(Context):
|
|
7
|
-
def __init__(self, di):
|
|
8
|
-
super().__init__(di)
|
|
9
|
-
|
|
10
|
-
def finalize_handler_config(self, config):
|
|
11
|
-
return {
|
|
12
|
-
'authentication': public(),
|
|
13
|
-
**config,
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
def __call__(
|
|
17
|
-
self,
|
|
18
|
-
event,
|
|
19
|
-
context,
|
|
20
|
-
method=None,
|
|
21
|
-
url=None,
|
|
22
|
-
):
|
|
23
|
-
if self.handler is None:
|
|
24
|
-
raise ValueError("Cannot execute LambdaInvocation context without first configuring it")
|
|
25
|
-
|
|
26
|
-
return self.handler(LambdaInvocationInputOutput(
|
|
27
|
-
event,
|
|
28
|
-
context,
|
|
29
|
-
method=method,
|
|
30
|
-
url=url,
|
|
31
|
-
))
|
|
32
|
-
def lambda_invocation(
|
|
33
|
-
application,
|
|
34
|
-
di_class=StandardDependencies,
|
|
35
|
-
bindings=None,
|
|
36
|
-
binding_classes=None,
|
|
37
|
-
binding_modules=None,
|
|
38
|
-
additional_configs=None,
|
|
39
|
-
):
|
|
40
|
-
return build_context(
|
|
41
|
-
LambdaInvocation,
|
|
42
|
-
application,
|
|
43
|
-
di_class=di_class,
|
|
44
|
-
bindings=bindings,
|
|
45
|
-
binding_classes=binding_classes,
|
|
46
|
-
binding_modules=binding_modules,
|
|
47
|
-
additional_configs=additional_configs,
|
|
48
|
-
)
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
from ..input_outputs import LambdaSns as LambdaSnsInputOutput
|
|
2
|
-
from ..di import StandardDependencies
|
|
3
|
-
from clearskies.contexts.build_context import build_context
|
|
4
|
-
from clearskies.contexts.context import Context
|
|
5
|
-
from clearskies.authentication import public
|
|
6
|
-
|
|
7
|
-
class LambdaSns(Context):
|
|
8
|
-
def __init__(self, di):
|
|
9
|
-
super().__init__(di)
|
|
10
|
-
|
|
11
|
-
def finalize_handler_config(self, config):
|
|
12
|
-
return {
|
|
13
|
-
'authentication': public(),
|
|
14
|
-
**config,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
def __call__(self, event, context, method=None, url=None):
|
|
18
|
-
if self.handler is None:
|
|
19
|
-
raise ValueError("Cannot execute LambdaSnsEvent context without first configuring it")
|
|
20
|
-
|
|
21
|
-
try:
|
|
22
|
-
return self.handler(LambdaSnsInputOutput(event, context, method=method, url=url))
|
|
23
|
-
except Exception as e:
|
|
24
|
-
print('Failed message ' + event['Records'][0]['Sns']['MessageId'] + '. Error error: ' + str(e))
|
|
25
|
-
raise e
|
|
26
|
-
|
|
27
|
-
def lambda_sns(
|
|
28
|
-
application,
|
|
29
|
-
di_class=StandardDependencies,
|
|
30
|
-
bindings=None,
|
|
31
|
-
binding_classes=None,
|
|
32
|
-
binding_modules=None,
|
|
33
|
-
additional_configs=None,
|
|
34
|
-
):
|
|
35
|
-
return build_context(
|
|
36
|
-
LambdaSns,
|
|
37
|
-
application,
|
|
38
|
-
di_class=di_class,
|
|
39
|
-
bindings=bindings,
|
|
40
|
-
binding_classes=binding_classes,
|
|
41
|
-
binding_modules=binding_modules,
|
|
42
|
-
additional_configs=additional_configs,
|
|
43
|
-
)
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import traceback
|
|
2
|
-
from ..input_outputs import LambdaSqsStandard as LambdaSqsStandardInputOutput
|
|
3
|
-
from ..di import StandardDependencies
|
|
4
|
-
from clearskies.contexts.build_context import build_context
|
|
5
|
-
from clearskies.contexts.context import Context
|
|
6
|
-
from clearskies.authentication import public
|
|
7
|
-
class LambdaSqsStandardPartialBatch(Context):
|
|
8
|
-
def __init__(self, di):
|
|
9
|
-
super().__init__(di)
|
|
10
|
-
|
|
11
|
-
def finalize_handler_config(self, config):
|
|
12
|
-
return {
|
|
13
|
-
'authentication': public(),
|
|
14
|
-
**config,
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
def __call__(self, event, context, url=None, method=None):
|
|
18
|
-
if self.handler is None:
|
|
19
|
-
raise ValueError("Cannot execute LambdaELB context without first configuring it")
|
|
20
|
-
|
|
21
|
-
item_failures = []
|
|
22
|
-
for record in event['Records']:
|
|
23
|
-
try:
|
|
24
|
-
self.handler(LambdaSqsStandardInputOutput(record['body'], event, context, url=url, method=method))
|
|
25
|
-
except Exception as e:
|
|
26
|
-
print('Failed message ' + record['messageId'] + ' being returned for retry. Error error: ' + str(e))
|
|
27
|
-
traceback.print_tb(e.__traceback__)
|
|
28
|
-
item_failures.append({'itemIdentifier': record['messageId']})
|
|
29
|
-
|
|
30
|
-
if item_failures:
|
|
31
|
-
return {
|
|
32
|
-
"batchItemFailures": item_failures,
|
|
33
|
-
}
|
|
34
|
-
return {}
|
|
35
|
-
def lambda_sqs_standard_partial_batch(
|
|
36
|
-
application,
|
|
37
|
-
di_class=StandardDependencies,
|
|
38
|
-
bindings=None,
|
|
39
|
-
binding_classes=None,
|
|
40
|
-
binding_modules=None,
|
|
41
|
-
additional_configs=None,
|
|
42
|
-
):
|
|
43
|
-
return build_context(
|
|
44
|
-
LambdaSqsStandardPartialBatch,
|
|
45
|
-
application,
|
|
46
|
-
di_class=di_class,
|
|
47
|
-
bindings=bindings,
|
|
48
|
-
binding_classes=binding_classes,
|
|
49
|
-
binding_modules=binding_modules,
|
|
50
|
-
additional_configs=additional_configs,
|
|
51
|
-
)
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import unittest
|
|
3
|
-
from types import SimpleNamespace
|
|
4
|
-
from unittest.mock import MagicMock, call
|
|
5
|
-
|
|
6
|
-
from .lambda_sqs_standard_partial_batch import lambda_sqs_standard_partial_batch
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class LambdaSqsStandardPartialBatchTest(unittest.TestCase):
|
|
10
|
-
def setUp(self):
|
|
11
|
-
self.calls = []
|
|
12
|
-
|
|
13
|
-
def my_callable(self, request_data):
|
|
14
|
-
if 'boom' in request_data:
|
|
15
|
-
raise ValueError('oops')
|
|
16
|
-
self.calls.append(request_data)
|
|
17
|
-
|
|
18
|
-
def test_simple_execution(self):
|
|
19
|
-
sqs_handler = lambda_sqs_standard_partial_batch(self.my_callable)
|
|
20
|
-
sqs_handler(
|
|
21
|
-
{
|
|
22
|
-
'Records': [
|
|
23
|
-
{
|
|
24
|
-
'messageId': '1-2-3-4',
|
|
25
|
-
'body': json.dumps({'hey': 'sup'}),
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
'messageId': '2-3-4-5',
|
|
29
|
-
'body': json.dumps({'cool': 'yo'}),
|
|
30
|
-
},
|
|
31
|
-
]
|
|
32
|
-
},
|
|
33
|
-
{},
|
|
34
|
-
)
|
|
35
|
-
self.assertEqual(
|
|
36
|
-
[
|
|
37
|
-
{"hey": "sup"},
|
|
38
|
-
{"cool": "yo"},
|
|
39
|
-
],
|
|
40
|
-
self.calls,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
def test_with_failure(self):
|
|
44
|
-
sqs_handler = lambda_sqs_standard_partial_batch(self.my_callable)
|
|
45
|
-
results = sqs_handler({
|
|
46
|
-
'Records': [
|
|
47
|
-
{
|
|
48
|
-
'messageId': '1-2-3-4',
|
|
49
|
-
'body': json.dumps({'hey': 'sup'}),
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
'messageId': '2-3-4-5',
|
|
53
|
-
'body': json.dumps({'boom': 'yo'}),
|
|
54
|
-
},
|
|
55
|
-
]
|
|
56
|
-
}, {})
|
|
57
|
-
self.assertEqual(
|
|
58
|
-
[
|
|
59
|
-
{"hey": "sup"},
|
|
60
|
-
],
|
|
61
|
-
self.calls,
|
|
62
|
-
)
|
|
63
|
-
self.assertEqual(
|
|
64
|
-
{"batchItemFailures": [{"itemIdentifier": "2-3-4-5"}]},
|
|
65
|
-
results,
|
|
66
|
-
)
|
clearskies_aws/contexts/wsgi.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from ..di import StandardDependencies
|
|
2
|
-
from clearskies.contexts.wsgi import WSGI, build_context
|
|
3
|
-
def wsgi(
|
|
4
|
-
application,
|
|
5
|
-
di_class=StandardDependencies,
|
|
6
|
-
bindings=None,
|
|
7
|
-
binding_classes=None,
|
|
8
|
-
binding_modules=None,
|
|
9
|
-
additional_configs=None,
|
|
10
|
-
):
|
|
11
|
-
return build_context(
|
|
12
|
-
WSGI,
|
|
13
|
-
application,
|
|
14
|
-
di_class=di_class,
|
|
15
|
-
bindings=bindings,
|
|
16
|
-
binding_classes=binding_classes,
|
|
17
|
-
binding_modules=binding_modules,
|
|
18
|
-
additional_configs=additional_configs,
|
|
19
|
-
)
|
clearskies_aws/di/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
from .standard_dependencies import StandardDependencies
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
from types import ModuleType
|
|
2
|
-
|
|
3
|
-
import boto3 as boto3_module
|
|
4
|
-
from boto3.session import Session as Boto3Session
|
|
5
|
-
from clearskies import Environment
|
|
6
|
-
from clearskies.di import StandardDependencies as DefaultStandardDependencies
|
|
7
|
-
|
|
8
|
-
from ..backends import (
|
|
9
|
-
DynamoDBBackend,
|
|
10
|
-
DynamoDBPartiQLBackend,
|
|
11
|
-
DynamoDBPartiQLCursor,
|
|
12
|
-
SqsBackend,
|
|
13
|
-
)
|
|
14
|
-
from ..secrets import ParameterStore
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class StandardDependencies(DefaultStandardDependencies):
|
|
18
|
-
|
|
19
|
-
def provide_dynamo_db_parti_ql_cursor(
|
|
20
|
-
self, boto3_session: Boto3Session
|
|
21
|
-
) -> DynamoDBPartiQLCursor:
|
|
22
|
-
return DynamoDBPartiQLCursor(boto3_session)
|
|
23
|
-
|
|
24
|
-
def provide_dynamo_db_backend(
|
|
25
|
-
self, boto3: ModuleType, environment: Environment
|
|
26
|
-
) -> DynamoDBBackend:
|
|
27
|
-
return DynamoDBBackend(boto3, environment)
|
|
28
|
-
|
|
29
|
-
def provide_dynamo_db_parti_ql_backend(
|
|
30
|
-
self, dynamo_db_parti_ql_cursor: DynamoDBPartiQLCursor, environment: Environment
|
|
31
|
-
) -> DynamoDBPartiQLBackend:
|
|
32
|
-
return DynamoDBPartiQLBackend(dynamo_db_parti_ql_cursor)
|
|
33
|
-
|
|
34
|
-
def provide_sqs_backend(
|
|
35
|
-
self, boto3: ModuleType, environment: Environment
|
|
36
|
-
) -> SqsBackend:
|
|
37
|
-
return SqsBackend(boto3, environment)
|
|
38
|
-
|
|
39
|
-
def provide_boto3(self) -> ModuleType:
|
|
40
|
-
import boto3
|
|
41
|
-
return boto3
|
|
42
|
-
|
|
43
|
-
def provide_secrets(
|
|
44
|
-
self, boto3: ModuleType, environment: Environment
|
|
45
|
-
) -> ParameterStore:
|
|
46
|
-
# This is just here so that we can auto-inject the secrets into the environment without having
|
|
47
|
-
# to force the developer to define a secrets manager
|
|
48
|
-
return ParameterStore(boto3, environment)
|
|
49
|
-
|
|
50
|
-
def provide_boto3_session(
|
|
51
|
-
self, boto3: ModuleType, environment: Environment
|
|
52
|
-
) -> boto3_module.session.Session:
|
|
53
|
-
|
|
54
|
-
if not environment.get("AWS_REGION", True):
|
|
55
|
-
raise ValueError(
|
|
56
|
-
"To use AWS Session you must use set AWS_REGION in the .env file or an environment variable"
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
session = boto3.session.Session(region_name=environment.get("AWS_REGION", True))
|
|
60
|
-
return session
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
|
|
3
|
-
import clearskies
|
|
4
|
-
from clearskies.handlers.exceptions import ClientError
|
|
5
|
-
from clearskies.handlers.base import Base
|
|
6
|
-
import botocore
|
|
7
|
-
|
|
8
|
-
class SecretsManagerRotation(Base, clearskies.handlers.SchemaHelper):
|
|
9
|
-
_steps = ["createSecret", "setSecret", "testSecret", "finishSecret"]
|
|
10
|
-
|
|
11
|
-
current = "AWSCURRENT"
|
|
12
|
-
pending = "AWSPENDING"
|
|
13
|
-
|
|
14
|
-
_configuration_defaults = {
|
|
15
|
-
"createSecret": None,
|
|
16
|
-
"setSecret": None,
|
|
17
|
-
"testSecret": None,
|
|
18
|
-
"finishSecret": None,
|
|
19
|
-
"schema": [],
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def __init__(self, boto3, di):
|
|
23
|
-
super().__init__(di)
|
|
24
|
-
self.boto3 = boto3
|
|
25
|
-
|
|
26
|
-
def _check_configuration(self, configuration):
|
|
27
|
-
super()._check_configuration(configuration)
|
|
28
|
-
class_name = self.__class__.__name__
|
|
29
|
-
if not configuration.get("createSecret"):
|
|
30
|
-
raise KeyError(f"Missing required configuration 'createSecret' for handler {class_name}")
|
|
31
|
-
|
|
32
|
-
for config_name in self._steps:
|
|
33
|
-
config = configuration.get(config_name)
|
|
34
|
-
if config is None:
|
|
35
|
-
continue
|
|
36
|
-
if not callable(config):
|
|
37
|
-
raise ValueError(f"Misconfiguration for handler {class_name}: configuration '{config_name}' is not callable")
|
|
38
|
-
|
|
39
|
-
if configuration.get("schema") is not None:
|
|
40
|
-
self._check_schema(configuration["schema"], None, f"Misconfiguration for handler {class_name}")
|
|
41
|
-
|
|
42
|
-
def _finalize_configuration(self, configuration):
|
|
43
|
-
if configuration.get('schema'):
|
|
44
|
-
configuration['schema'] = self._schema_to_columns(configuration['schema'])
|
|
45
|
-
return super()._finalize_configuration(configuration)
|
|
46
|
-
|
|
47
|
-
def handle(self, input_output):
|
|
48
|
-
request_data = input_output.json_body()
|
|
49
|
-
|
|
50
|
-
arn = request_data.get('SecretId')
|
|
51
|
-
request_token = request_data.get('ClientRequestToken')
|
|
52
|
-
step = request_data.get('Step')
|
|
53
|
-
secretsmanager = self.boto3.client('secretsmanager')
|
|
54
|
-
metadata = secretsmanager.describe_secret(SecretId=arn)
|
|
55
|
-
|
|
56
|
-
self._validate_secret_and_request(step, arn, metadata, request_token)
|
|
57
|
-
|
|
58
|
-
current_secret_data = {}
|
|
59
|
-
pending_secret_data = {}
|
|
60
|
-
|
|
61
|
-
current_secret = secretsmanager.get_secret_value(SecretId=arn, VersionStage=self.current)
|
|
62
|
-
current_secret_data = json.loads(current_secret['SecretString'])
|
|
63
|
-
|
|
64
|
-
# validate the current secret
|
|
65
|
-
secret_errors = {
|
|
66
|
-
**self._extra_column_errors(current_secret_data),
|
|
67
|
-
**self._find_input_errors(current_secret_data),
|
|
68
|
-
}
|
|
69
|
-
if secret_errors:
|
|
70
|
-
raise ValueError(f"The current secret did not match the configured schema: {secret_errors}")
|
|
71
|
-
|
|
72
|
-
# check for a pending secret. Note that this is not always available. In the event that we are retrying a failed
|
|
73
|
-
# rotation it will already be set, in which case we need to skip the createSecret step.
|
|
74
|
-
try:
|
|
75
|
-
pending_secret = secretsmanager.get_secret_value(SecretId=arn, VersionId=request_token, VersionStage=self.pending)
|
|
76
|
-
pending_secret_data = json.loads(pending_secret['SecretString'])
|
|
77
|
-
except botocore.exceptions.ClientError as error:
|
|
78
|
-
if error.response['Error']['Code'] == 'ResourceNotFoundException':
|
|
79
|
-
pending_secret_data = None
|
|
80
|
-
else:
|
|
81
|
-
raise error
|
|
82
|
-
|
|
83
|
-
# we can't call the createSecret step if we already have a pending secret or this will generate an error from AWS.
|
|
84
|
-
if step == "createSecret" and pending_secret_data is not None:
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
# call the appropriate step and pass along *everything*.
|
|
88
|
-
getattr(self, step)(
|
|
89
|
-
current_secret_data=current_secret_data,
|
|
90
|
-
pending_secret_data=pending_secret_data,
|
|
91
|
-
secretsmanager=secretsmanager,
|
|
92
|
-
metadata=metadata,
|
|
93
|
-
request_token=request_token,
|
|
94
|
-
arn=arn,
|
|
95
|
-
)
|
|
96
|
-
|
|
97
|
-
def _validate_secret_and_request(self, step, arn, metadata, request_token):
|
|
98
|
-
""" This function does some basic checks suggested by AWS of both the request and the secret to make sure everything is on the up-and-up. """
|
|
99
|
-
if step not in self._steps:
|
|
100
|
-
raise ClientError(f"Invalid step: {step}")
|
|
101
|
-
|
|
102
|
-
if not metadata.get('RotationEnabled'):
|
|
103
|
-
raise ValueError("Secret %s is not enabled for rotation" % arn)
|
|
104
|
-
|
|
105
|
-
versions = metadata["VersionIdsToStages"]
|
|
106
|
-
prefix = f"Rotation config error for version '{request_token}' of secret '{arn}': "
|
|
107
|
-
if request_token not in versions:
|
|
108
|
-
raise ValueError(f"{prefix} we don't have a stage for rotation")
|
|
109
|
-
if self.current in versions[request_token]:
|
|
110
|
-
raise ValueError(f"{prefix} it's already the current version, which shouldn't happen. I'm quitting with prejudice.")
|
|
111
|
-
elif self.pending not in versions[request_token]:
|
|
112
|
-
raise ValueError(f"{prefix} it hasn't been set to pending yet, which makes no sense!")
|
|
113
|
-
|
|
114
|
-
def createSecret(self, **kwargs):
|
|
115
|
-
new_secret_data = self._di.call_function(self._configuration["createSecret"], **kwargs)
|
|
116
|
-
if new_secret_data is None:
|
|
117
|
-
raise ValueError(f"I called the configured createSecret function but it didn't return anything. It has to return the new secret data.")
|
|
118
|
-
if not isinstance(new_secret_data, dict):
|
|
119
|
-
raise ValueError(f"I called the configured createSecret function but it didn't return a dictionary. The createSecret function must return a dictionary.")
|
|
120
|
-
|
|
121
|
-
secret_errors = {
|
|
122
|
-
**self._extra_column_errors(new_secret_data),
|
|
123
|
-
**self._find_input_errors(new_secret_data),
|
|
124
|
-
}
|
|
125
|
-
if secret_errors:
|
|
126
|
-
raise ValueError(f"The secret data returned by the call to createSecret did not match the configured schema: {secret_errors}")
|
|
127
|
-
|
|
128
|
-
# if we get this far we can store the new data
|
|
129
|
-
secretsmanager = kwargs["secretsmanager"]
|
|
130
|
-
request_token = kwargs["request_token"]
|
|
131
|
-
arn = kwargs["arn"]
|
|
132
|
-
secretsmanager.put_secret_value(
|
|
133
|
-
SecretId=arn,
|
|
134
|
-
SecretString=json.dumps(new_secret_data),
|
|
135
|
-
ClientRequestToken=request_token,
|
|
136
|
-
VersionStages=[self.pending],
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
def setSecret(self, **kwargs):
|
|
140
|
-
if not self._configuration.get("setSecret"):
|
|
141
|
-
return
|
|
142
|
-
self._di.call_function(self._configuration["setSecret"], **kwargs)
|
|
143
|
-
|
|
144
|
-
def testSecret(self, **kwargs):
|
|
145
|
-
if not self._configuration.get("testSecret"):
|
|
146
|
-
return
|
|
147
|
-
self._di.call_function(self._configuration["testSecret"], **kwargs)
|
|
148
|
-
|
|
149
|
-
def finishSecret(self, **kwargs):
|
|
150
|
-
if self._configuration.get("finishSecret"):
|
|
151
|
-
self._di.call_function(self._configuration["finishSecret"], **kwargs)
|
|
152
|
-
|
|
153
|
-
secretsmanager = kwargs["secretsmanager"]
|
|
154
|
-
request_token = kwargs["request_token"]
|
|
155
|
-
arn = kwargs["arn"]
|
|
156
|
-
metadata = kwargs["metadata"]
|
|
157
|
-
current_version = None
|
|
158
|
-
for version in metadata["VersionIdsToStages"]:
|
|
159
|
-
if self.current not in metadata["VersionIdsToStages"][version]:
|
|
160
|
-
continue
|
|
161
|
-
|
|
162
|
-
if version == request_token:
|
|
163
|
-
return
|
|
164
|
-
|
|
165
|
-
current_version = version
|
|
166
|
-
break
|
|
167
|
-
|
|
168
|
-
# finish the rotation by taking the new version and making it current.
|
|
169
|
-
secretsmanager.update_secret_version_stage(
|
|
170
|
-
SecretId=arn,
|
|
171
|
-
VersionStage=self.current,
|
|
172
|
-
MoveToVersionId=request_token,
|
|
173
|
-
RemoveFromVersionId=current_version
|
|
174
|
-
)
|