localstack-core 4.7.1.dev49__py3-none-any.whl → 4.10.1.dev12__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.
- localstack/aws/api/cloudformation/__init__.py +18 -4
- localstack/aws/api/cloudwatch/__init__.py +41 -1
- localstack/aws/api/config/__init__.py +4 -0
- localstack/aws/api/core.py +6 -2
- localstack/aws/api/dynamodb/__init__.py +30 -0
- localstack/aws/api/ec2/__init__.py +1522 -65
- localstack/aws/api/iam/__init__.py +7 -0
- localstack/aws/api/kinesis/__init__.py +19 -0
- localstack/aws/api/kms/__init__.py +6 -0
- localstack/aws/api/lambda_/__init__.py +13 -0
- localstack/aws/api/logs/__init__.py +15 -0
- localstack/aws/api/redshift/__init__.py +9 -3
- localstack/aws/api/route53/__init__.py +5 -0
- localstack/aws/api/s3/__init__.py +12 -0
- localstack/aws/api/s3control/__init__.py +54 -0
- localstack/aws/api/ssm/__init__.py +2 -0
- localstack/aws/api/transcribe/__init__.py +17 -0
- localstack/aws/client.py +7 -2
- localstack/aws/forwarder.py +52 -5
- localstack/aws/handlers/analytics.py +1 -1
- localstack/aws/handlers/internal_requests.py +6 -1
- localstack/aws/handlers/logging.py +12 -2
- localstack/aws/handlers/metric_handler.py +41 -1
- localstack/aws/handlers/service.py +40 -20
- localstack/aws/mocking.py +2 -2
- localstack/aws/patches.py +2 -2
- localstack/aws/protocol/parser.py +459 -32
- localstack/aws/protocol/serializer.py +689 -69
- localstack/aws/protocol/service_router.py +120 -20
- localstack/aws/protocol/validate.py +1 -1
- localstack/aws/scaffold.py +1 -1
- localstack/aws/skeleton.py +4 -2
- localstack/aws/spec-patches.json +58 -0
- localstack/aws/spec.py +37 -16
- localstack/cli/exceptions.py +1 -1
- localstack/cli/localstack.py +6 -6
- localstack/cli/lpm.py +3 -4
- localstack/cli/plugins.py +1 -1
- localstack/cli/profiles.py +1 -2
- localstack/config.py +25 -18
- localstack/constants.py +4 -29
- localstack/dev/kubernetes/__main__.py +130 -7
- localstack/dev/run/configurators.py +1 -4
- localstack/dev/run/paths.py +1 -1
- localstack/dns/plugins.py +5 -1
- localstack/dns/server.py +13 -4
- localstack/logging/format.py +3 -3
- localstack/packages/api.py +9 -8
- localstack/packages/core.py +2 -2
- localstack/packages/plugins.py +0 -8
- localstack/runtime/analytics.py +3 -0
- localstack/runtime/hooks.py +1 -1
- localstack/runtime/init.py +2 -2
- localstack/runtime/main.py +5 -5
- localstack/runtime/patches.py +2 -2
- localstack/services/apigateway/helpers.py +1 -4
- localstack/services/apigateway/legacy/helpers.py +7 -8
- localstack/services/apigateway/legacy/integration.py +4 -3
- localstack/services/apigateway/legacy/invocations.py +6 -5
- localstack/services/apigateway/legacy/provider.py +148 -68
- localstack/services/apigateway/legacy/templates.py +1 -1
- localstack/services/apigateway/next_gen/execute_api/handlers/method_request.py +7 -2
- localstack/services/apigateway/next_gen/execute_api/handlers/resource_router.py +1 -2
- localstack/services/apigateway/next_gen/execute_api/integrations/aws.py +3 -0
- localstack/services/apigateway/next_gen/execute_api/integrations/http.py +3 -3
- localstack/services/apigateway/next_gen/execute_api/template_mapping.py +2 -2
- localstack/services/apigateway/next_gen/execute_api/test_invoke.py +114 -9
- localstack/services/apigateway/next_gen/provider.py +5 -0
- localstack/services/apigateway/resource_providers/aws_apigateway_resource.py +1 -1
- localstack/services/cloudformation/api_utils.py +4 -8
- localstack/services/cloudformation/cfn_utils.py +1 -1
- localstack/services/cloudformation/engine/entities.py +14 -4
- localstack/services/cloudformation/engine/template_deployer.py +6 -4
- localstack/services/cloudformation/engine/transformers.py +6 -4
- localstack/services/cloudformation/engine/v2/change_set_model.py +201 -13
- localstack/services/cloudformation/engine/v2/change_set_model_describer.py +52 -3
- localstack/services/cloudformation/engine/v2/change_set_model_executor.py +117 -76
- localstack/services/cloudformation/engine/v2/change_set_model_preproc.py +205 -52
- localstack/services/cloudformation/engine/v2/change_set_model_transform.py +350 -116
- localstack/services/cloudformation/engine/v2/change_set_model_validator.py +56 -14
- localstack/services/cloudformation/engine/v2/change_set_model_visitor.py +1 -0
- localstack/services/cloudformation/engine/v2/resolving.py +7 -5
- localstack/services/cloudformation/engine/yaml_parser.py +9 -2
- localstack/services/cloudformation/provider.py +7 -5
- localstack/services/cloudformation/resource_provider.py +7 -1
- localstack/services/cloudformation/resources.py +24149 -0
- localstack/services/cloudformation/service_models.py +2 -2
- localstack/services/cloudformation/v2/entities.py +19 -9
- localstack/services/cloudformation/v2/provider.py +336 -106
- localstack/services/cloudformation/v2/types.py +13 -7
- localstack/services/cloudformation/v2/utils.py +4 -1
- localstack/services/cloudwatch/alarm_scheduler.py +4 -1
- localstack/services/cloudwatch/provider.py +18 -13
- localstack/services/cloudwatch/provider_v2.py +25 -28
- localstack/services/dynamodb/packages.py +2 -1
- localstack/services/dynamodb/provider.py +42 -0
- localstack/services/dynamodb/server.py +2 -2
- localstack/services/dynamodb/v2/provider.py +42 -0
- localstack/services/ecr/resource_providers/aws_ecr_repository.py +5 -2
- localstack/services/edge.py +1 -1
- localstack/services/es/provider.py +2 -2
- localstack/services/events/event_rule_engine.py +31 -13
- localstack/services/events/models.py +4 -5
- localstack/services/events/provider.py +17 -14
- localstack/services/events/target.py +17 -9
- localstack/services/events/v1/provider.py +5 -5
- localstack/services/firehose/provider.py +14 -4
- localstack/services/iam/provider.py +11 -116
- localstack/services/iam/resources/policy_simulator.py +133 -0
- localstack/services/kinesis/models.py +15 -2
- localstack/services/kinesis/provider.py +86 -3
- localstack/services/kms/provider.py +14 -5
- localstack/services/lambda_/api_utils.py +6 -3
- localstack/services/lambda_/invocation/docker_runtime_executor.py +1 -1
- localstack/services/lambda_/invocation/event_manager.py +1 -1
- localstack/services/lambda_/invocation/internal_sqs_queue.py +5 -9
- localstack/services/lambda_/invocation/lambda_models.py +10 -7
- localstack/services/lambda_/invocation/lambda_service.py +5 -1
- localstack/services/lambda_/packages.py +1 -1
- localstack/services/lambda_/provider.py +4 -3
- localstack/services/lambda_/provider_utils.py +1 -1
- localstack/services/logs/provider.py +36 -19
- localstack/services/moto.py +2 -1
- localstack/services/opensearch/cluster.py +15 -7
- localstack/services/opensearch/packages.py +26 -7
- localstack/services/opensearch/provider.py +8 -2
- localstack/services/opensearch/versions.py +56 -7
- localstack/services/plugins.py +11 -7
- localstack/services/providers.py +10 -2
- localstack/services/redshift/provider.py +0 -21
- localstack/services/s3/constants.py +5 -2
- localstack/services/s3/cors.py +4 -4
- localstack/services/s3/models.py +1 -1
- localstack/services/s3/notifications.py +55 -39
- localstack/services/s3/presigned_url.py +35 -54
- localstack/services/s3/provider.py +73 -15
- localstack/services/s3/utils.py +42 -22
- localstack/services/s3/validation.py +46 -32
- localstack/services/s3/website_hosting.py +4 -2
- localstack/services/ses/provider.py +18 -8
- localstack/services/sns/constants.py +7 -1
- localstack/services/sns/executor.py +9 -2
- localstack/services/sns/provider.py +8 -5
- localstack/services/sns/publisher.py +31 -16
- localstack/services/sns/v2/models.py +167 -0
- localstack/services/sns/v2/provider.py +867 -0
- localstack/services/sns/v2/utils.py +130 -0
- localstack/services/sqs/constants.py +1 -1
- localstack/services/sqs/developer_api.py +205 -0
- localstack/services/sqs/models.py +48 -5
- localstack/services/sqs/provider.py +38 -311
- localstack/services/sqs/query_api.py +6 -2
- localstack/services/sqs/utils.py +121 -2
- localstack/services/ssm/provider.py +1 -1
- localstack/services/stepfunctions/asl/component/intrinsic/member.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_choice/comparison/comparison.py +5 -11
- localstack/services/stepfunctions/asl/component/state/state_choice/state_choice.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_map/state_map.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_execution/state_parallel/state_parallel.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_execution/state_task/state_task.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_fail/state_fail.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_pass/state_pass.py +2 -2
- localstack/services/stepfunctions/asl/component/state/state_succeed/state_succeed.py +1 -1
- localstack/services/stepfunctions/asl/component/state/state_wait/state_wait.py +1 -1
- localstack/services/stepfunctions/asl/eval/environment.py +1 -1
- localstack/services/stepfunctions/asl/jsonata/jsonata.py +1 -1
- localstack/services/stepfunctions/backend/execution.py +2 -1
- localstack/services/stores.py +1 -1
- localstack/services/transcribe/provider.py +6 -1
- localstack/state/codecs.py +61 -0
- localstack/state/core.py +11 -5
- localstack/state/pickle.py +10 -49
- localstack/testing/aws/cloudformation_utils.py +1 -1
- localstack/testing/pytest/cloudformation/fixtures.py +3 -3
- localstack/testing/pytest/cloudformation/transformers.py +0 -0
- localstack/testing/pytest/container.py +4 -5
- localstack/testing/pytest/fixtures.py +33 -31
- localstack/testing/pytest/in_memory_localstack.py +0 -4
- localstack/testing/pytest/marking.py +38 -11
- localstack/testing/pytest/stepfunctions/utils.py +4 -3
- localstack/testing/pytest/util.py +1 -1
- localstack/testing/pytest/validation_tracking.py +1 -2
- localstack/testing/snapshots/transformer_utility.py +6 -1
- localstack/utils/analytics/events.py +2 -2
- localstack/utils/analytics/metadata.py +6 -4
- localstack/utils/analytics/metrics/counter.py +8 -15
- localstack/utils/analytics/publisher.py +1 -2
- localstack/utils/analytics/service_providers.py +19 -0
- localstack/utils/analytics/service_request_aggregator.py +2 -2
- localstack/utils/archives.py +11 -11
- localstack/utils/asyncio.py +2 -2
- localstack/utils/aws/arns.py +24 -29
- localstack/utils/aws/aws_responses.py +8 -8
- localstack/utils/aws/aws_stack.py +2 -3
- localstack/utils/aws/dead_letter_queue.py +1 -5
- localstack/utils/aws/message_forwarding.py +1 -2
- localstack/utils/aws/request_context.py +4 -5
- localstack/utils/aws/resources.py +1 -1
- localstack/utils/aws/templating.py +1 -1
- localstack/utils/batch_policy.py +3 -3
- localstack/utils/bootstrap.py +21 -13
- localstack/utils/catalog/catalog.py +139 -0
- localstack/utils/catalog/catalog_loader.py +119 -0
- localstack/utils/catalog/common.py +58 -0
- localstack/utils/catalog/plugins.py +28 -0
- localstack/utils/cloudwatch/cloudwatch_util.py +5 -5
- localstack/utils/collections.py +7 -8
- localstack/utils/config_listener.py +1 -1
- localstack/utils/container_networking.py +2 -3
- localstack/utils/container_utils/container_client.py +135 -136
- localstack/utils/container_utils/docker_cmd_client.py +85 -69
- localstack/utils/container_utils/docker_sdk_client.py +69 -66
- localstack/utils/crypto.py +10 -10
- localstack/utils/diagnose.py +3 -4
- localstack/utils/docker_utils.py +9 -5
- localstack/utils/files.py +33 -13
- localstack/utils/functions.py +4 -3
- localstack/utils/http.py +11 -11
- localstack/utils/json.py +20 -6
- localstack/utils/kinesis/kinesis_connector.py +2 -1
- localstack/utils/net.py +15 -9
- localstack/utils/no_exit_argument_parser.py +2 -2
- localstack/utils/numbers.py +9 -2
- localstack/utils/objects.py +7 -6
- localstack/utils/patch.py +10 -3
- localstack/utils/run.py +12 -11
- localstack/utils/scheduler.py +11 -11
- localstack/utils/server/tcp_proxy.py +2 -2
- localstack/utils/serving.py +3 -4
- localstack/utils/strings.py +15 -16
- localstack/utils/sync.py +126 -1
- localstack/utils/tagging.py +8 -6
- localstack/utils/testutil.py +8 -8
- localstack/utils/threads.py +2 -2
- localstack/utils/time.py +12 -4
- localstack/utils/urls.py +1 -3
- localstack/utils/xray/traceid.py +1 -1
- localstack/version.py +16 -3
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/METADATA +18 -14
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/RECORD +248 -239
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/entry_points.txt +8 -4
- localstack_core-4.10.1.dev12.dist-info/plux.json +1 -0
- localstack/packages/terraform.py +0 -46
- localstack/services/cloudformation/deploy.html +0 -144
- localstack/services/cloudformation/deploy_ui.py +0 -47
- localstack/services/cloudformation/plugins.py +0 -12
- localstack_core-4.7.1.dev49.dist-info/plux.json +0 -1
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack-supervisor +0 -0
- {localstack_core-4.7.1.dev49.data → localstack_core-4.10.1.dev12.data}/scripts/localstack.bat +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/WHEEL +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/licenses/LICENSE.txt +0 -0
- {localstack_core-4.7.1.dev49.dist-info → localstack_core-4.10.1.dev12.dist-info}/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ from datetime import UTC, datetime
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from typing import Literal, TypeAlias, TypedDict
|
|
6
6
|
|
|
7
|
-
from localstack.aws.api
|
|
7
|
+
from localstack.aws.api import CommonServiceException
|
|
8
8
|
from localstack.aws.api.events import (
|
|
9
9
|
ApiDestinationDescription,
|
|
10
10
|
ApiDestinationHttpMethod,
|
|
@@ -66,10 +66,9 @@ from localstack.utils.tagging import TaggingService
|
|
|
66
66
|
TargetDict = dict[TargetId, Target]
|
|
67
67
|
|
|
68
68
|
|
|
69
|
-
class ValidationException(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
status_code: int = 400
|
|
69
|
+
class ValidationException(CommonServiceException):
|
|
70
|
+
def __init__(self, message: str):
|
|
71
|
+
super().__init__("ValidationException", message, 400, True)
|
|
73
72
|
|
|
74
73
|
|
|
75
74
|
class InvalidEventPatternException(Exception):
|
|
@@ -193,7 +193,7 @@ def encode_next_token(token: int) -> NextToken:
|
|
|
193
193
|
|
|
194
194
|
def get_filtered_dict(name_prefix: str, input_dict: dict) -> dict:
|
|
195
195
|
"""Filter dictionary by prefix."""
|
|
196
|
-
return {name: value for name, value in input_dict.items() if name.startswith(name_prefix)}
|
|
196
|
+
return {name: value for name, value in dict(input_dict).items() if name.startswith(name_prefix)}
|
|
197
197
|
|
|
198
198
|
|
|
199
199
|
def validate_event(event: PutEventsRequestEntry) -> None | PutEventsResultEntry:
|
|
@@ -768,8 +768,8 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
768
768
|
|
|
769
769
|
# Find all rules that have a target with the specified ARN
|
|
770
770
|
matching_rule_names = []
|
|
771
|
-
for rule_name, rule in event_bus.rules.items():
|
|
772
|
-
for target_id, target in rule.targets.items():
|
|
771
|
+
for rule_name, rule in dict(event_bus.rules).items():
|
|
772
|
+
for target_id, target in dict(rule.targets).items():
|
|
773
773
|
if target["Arn"] == target_arn:
|
|
774
774
|
matching_rule_names.append(rule_name)
|
|
775
775
|
break # Found a match in this rule, no need to check other targets
|
|
@@ -1028,7 +1028,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1028
1028
|
self._check_event_bus_exists(event_source_arn, store)
|
|
1029
1029
|
archives = {
|
|
1030
1030
|
key: archive
|
|
1031
|
-
for key, archive in store.archives.items()
|
|
1031
|
+
for key, archive in dict(store.archives).items()
|
|
1032
1032
|
if archive.event_source_arn == event_source_arn
|
|
1033
1033
|
}
|
|
1034
1034
|
elif name_prefix:
|
|
@@ -1161,7 +1161,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1161
1161
|
if event_source_arn:
|
|
1162
1162
|
replays = {
|
|
1163
1163
|
key: replay
|
|
1164
|
-
for key, replay in store.replays.items()
|
|
1164
|
+
for key, replay in dict(store.replays).items()
|
|
1165
1165
|
if replay.event_source_arn == event_source_arn
|
|
1166
1166
|
}
|
|
1167
1167
|
elif name_prefix:
|
|
@@ -1508,7 +1508,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1508
1508
|
"""
|
|
1509
1509
|
if isinstance(rules, Rule):
|
|
1510
1510
|
rules = {rules.name: rules}
|
|
1511
|
-
for rule in rules.values():
|
|
1511
|
+
for rule in list(rules.values()):
|
|
1512
1512
|
del self._rule_services_store[rule.arn]
|
|
1513
1513
|
|
|
1514
1514
|
def _delete_target_sender(self, ids: TargetIdList, rule) -> None:
|
|
@@ -1571,7 +1571,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1571
1571
|
def _get_scheduled_rule_job_function(self, account_id, region, rule: Rule) -> Callable:
|
|
1572
1572
|
def func(*args, **kwargs):
|
|
1573
1573
|
"""Create custom scheduled event and send it to all targets specified by associated rule using respective TargetSender"""
|
|
1574
|
-
for target in rule.targets.values():
|
|
1574
|
+
for target in list(rule.targets.values()):
|
|
1575
1575
|
if custom_input := target.get("Input"):
|
|
1576
1576
|
event = json.loads(custom_input)
|
|
1577
1577
|
else:
|
|
@@ -1636,7 +1636,8 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1636
1636
|
) -> EventBusList:
|
|
1637
1637
|
"""Return a converted dict of EventBus model objects as a list of event buses in API type EventBus format."""
|
|
1638
1638
|
event_bus_list = [
|
|
1639
|
-
self._event_bus_to_api_type_event_bus(event_bus)
|
|
1639
|
+
self._event_bus_to_api_type_event_bus(event_bus)
|
|
1640
|
+
for event_bus in list(event_buses.values())
|
|
1640
1641
|
]
|
|
1641
1642
|
return event_bus_list
|
|
1642
1643
|
|
|
@@ -1680,7 +1681,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1680
1681
|
|
|
1681
1682
|
def _rule_dict_to_rule_response_list(self, rules: RuleDict) -> RuleResponseList:
|
|
1682
1683
|
"""Return a converted dict of Rule model objects as a list of rules in API type Rule format."""
|
|
1683
|
-
rule_list = [self._rule_to_api_type_rule(rule) for rule in rules.values()]
|
|
1684
|
+
rule_list = [self._rule_to_api_type_rule(rule) for rule in list(rules.values())]
|
|
1684
1685
|
return rule_list
|
|
1685
1686
|
|
|
1686
1687
|
def _rule_to_api_type_rule(self, rule: Rule) -> ApiTypeRule:
|
|
@@ -1700,7 +1701,9 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1700
1701
|
|
|
1701
1702
|
def _archive_dict_to_archive_response_list(self, archives: ArchiveDict) -> ArchiveResponseList:
|
|
1702
1703
|
"""Return a converted dict of Archive model objects as a list of archives in API type Archive format."""
|
|
1703
|
-
archive_list = [
|
|
1704
|
+
archive_list = [
|
|
1705
|
+
self._archive_to_api_type_archive(archive) for archive in list(archives.values())
|
|
1706
|
+
]
|
|
1704
1707
|
return archive_list
|
|
1705
1708
|
|
|
1706
1709
|
def _archive_to_api_type_archive(self, archive: Archive) -> ApiTypeArchive:
|
|
@@ -1734,7 +1737,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1734
1737
|
|
|
1735
1738
|
def _replay_dict_to_replay_response_list(self, replays: ReplayDict) -> ReplayList:
|
|
1736
1739
|
"""Return a converted dict of Replay model objects as a list of replays in API type Replay format."""
|
|
1737
|
-
replay_list = [self._replay_to_api_type_replay(replay) for replay in replays.values()]
|
|
1740
|
+
replay_list = [self._replay_to_api_type_replay(replay) for replay in list(replays.values())]
|
|
1738
1741
|
return replay_list
|
|
1739
1742
|
|
|
1740
1743
|
def _replay_to_api_type_replay(self, replay: Replay) -> ApiTypeReplay:
|
|
@@ -1789,7 +1792,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1789
1792
|
"""Return a converted dict of Connection model objects as a list of connections in API type Connection format."""
|
|
1790
1793
|
connection_list = [
|
|
1791
1794
|
self._connection_to_api_type_connection(connection)
|
|
1792
|
-
for connection in connections.values()
|
|
1795
|
+
for connection in list(connections.values())
|
|
1793
1796
|
]
|
|
1794
1797
|
return connection_list
|
|
1795
1798
|
|
|
@@ -1816,7 +1819,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1816
1819
|
"""Return a converted dict of ApiDestination model objects as a list of connections in API type ApiDestination format."""
|
|
1817
1820
|
api_destination_list = [
|
|
1818
1821
|
self._api_destination_to_api_type_api_destination(api_destination)
|
|
1819
|
-
for api_destination in api_destinations.values()
|
|
1822
|
+
for api_destination in list(api_destinations.values())
|
|
1820
1823
|
]
|
|
1821
1824
|
return api_destination_list
|
|
1822
1825
|
|
|
@@ -1942,7 +1945,7 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
1942
1945
|
)
|
|
1943
1946
|
return
|
|
1944
1947
|
|
|
1945
|
-
for target in rule.targets.values():
|
|
1948
|
+
for target in list(rule.targets.values()):
|
|
1946
1949
|
target_id = target["Id"]
|
|
1947
1950
|
if is_archive_arn(target["Arn"]):
|
|
1948
1951
|
self._put_to_archive(
|
|
@@ -90,11 +90,20 @@ def get_template_replacements(
|
|
|
90
90
|
return template_replacements
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
def replace_template_placeholders(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
93
|
+
def replace_template_placeholders(template: str, replacements: dict[str, Any]) -> TransformedEvent:
|
|
94
|
+
"""
|
|
95
|
+
Replaces placeholders in an EventBridge-style InputTemplate string.
|
|
96
|
+
|
|
97
|
+
:param template: The template string containing placeholders like ``<$.foo.bar>``.
|
|
98
|
+
:type template: str
|
|
99
|
+
:param replacements: A dictionary providing values to fill in.
|
|
100
|
+
:type replacements: dict
|
|
101
|
+
:returns: The transformed string with placeholders replaced by values from ``replacements``.
|
|
102
|
+
:rtype: str
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
...
|
|
106
|
+
is_json_template = template.strip().startswith("{")
|
|
98
107
|
|
|
99
108
|
def replace_placeholder(match):
|
|
100
109
|
key = match.group(1)
|
|
@@ -110,6 +119,8 @@ def replace_template_placeholders(
|
|
|
110
119
|
if is_json_template:
|
|
111
120
|
return json.dumps(value)
|
|
112
121
|
return f"[{','.join(value)}]"
|
|
122
|
+
if isinstance(value, bool):
|
|
123
|
+
return json.dumps(value)
|
|
113
124
|
if is_nested_in_string(template, match):
|
|
114
125
|
return value
|
|
115
126
|
if is_json_template:
|
|
@@ -222,10 +233,7 @@ class TargetSender(ABC):
|
|
|
222
233
|
predefined_template_replacements = self._get_predefined_template_replacements(event)
|
|
223
234
|
template_replacements.update(predefined_template_replacements)
|
|
224
235
|
|
|
225
|
-
|
|
226
|
-
populated_template = replace_template_placeholders(
|
|
227
|
-
input_template, template_replacements, is_json_template
|
|
228
|
-
)
|
|
236
|
+
populated_template = replace_template_placeholders(input_template, template_replacements)
|
|
229
237
|
|
|
230
238
|
return populated_template
|
|
231
239
|
|
|
@@ -206,12 +206,12 @@ class EventsProvider(EventsApi, ServiceLifecycleHook):
|
|
|
206
206
|
raise ValueError("If the value is greater than 1, the unit must be plural")
|
|
207
207
|
|
|
208
208
|
if "minute" in unit:
|
|
209
|
-
return "
|
|
209
|
+
return f"*/{value} * * * *"
|
|
210
210
|
if "hour" in unit:
|
|
211
|
-
return "0
|
|
211
|
+
return f"0 */{value} * * *"
|
|
212
212
|
if "day" in unit:
|
|
213
|
-
return "0 0
|
|
214
|
-
raise ValueError("Unable to parse events schedule expression:
|
|
213
|
+
return f"0 0 */{value} * *"
|
|
214
|
+
raise ValueError(f"Unable to parse events schedule expression: {schedule}")
|
|
215
215
|
return schedule
|
|
216
216
|
|
|
217
217
|
@staticmethod
|
|
@@ -374,7 +374,7 @@ def _dump_events_to_files(events_with_added_uuid):
|
|
|
374
374
|
for event in events_with_added_uuid:
|
|
375
375
|
target = os.path.join(
|
|
376
376
|
_get_events_tmp_dir(),
|
|
377
|
-
"
|
|
377
|
+
"{}_{}".format(current_time_millis, event["uuid"]),
|
|
378
378
|
)
|
|
379
379
|
save_file(target, json.dumps(event["event"]))
|
|
380
380
|
except Exception as e:
|
|
@@ -706,7 +706,11 @@ class FirehoseProvider(FirehoseApi):
|
|
|
706
706
|
try:
|
|
707
707
|
requests.post(url, json=record_to_send, headers=headers)
|
|
708
708
|
except Exception as e:
|
|
709
|
-
LOG.
|
|
709
|
+
LOG.error(
|
|
710
|
+
"Unable to put Firehose records to HTTP endpoint %s.",
|
|
711
|
+
url,
|
|
712
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
713
|
+
)
|
|
710
714
|
raise e
|
|
711
715
|
if "RedshiftDestinationDescription" in destination:
|
|
712
716
|
s3_dest_desc = destination["RedshiftDestinationDescription"][
|
|
@@ -782,7 +786,11 @@ class FirehoseProvider(FirehoseApi):
|
|
|
782
786
|
try:
|
|
783
787
|
db_connection.create(index=search_db_index, id=obj_id, body=body)
|
|
784
788
|
except Exception as e:
|
|
785
|
-
LOG.
|
|
789
|
+
LOG.error(
|
|
790
|
+
"Unable to put record to stream %s.",
|
|
791
|
+
delivery_stream_name,
|
|
792
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
793
|
+
)
|
|
786
794
|
raise e
|
|
787
795
|
|
|
788
796
|
def _add_missing_record_attributes(self, records: list[dict]) -> None:
|
|
@@ -860,9 +868,10 @@ class FirehoseProvider(FirehoseApi):
|
|
|
860
868
|
LOG.debug("Publishing to S3 destination: %s. Data: %s", bucket, batched_data)
|
|
861
869
|
s3.put_object(Bucket=bucket, Key=obj_path, Body=batched_data)
|
|
862
870
|
except Exception as e:
|
|
863
|
-
LOG.
|
|
871
|
+
LOG.error(
|
|
864
872
|
"Unable to put records %s to s3 bucket.",
|
|
865
873
|
records,
|
|
874
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
866
875
|
)
|
|
867
876
|
raise e
|
|
868
877
|
|
|
@@ -917,9 +926,10 @@ class FirehoseProvider(FirehoseApi):
|
|
|
917
926
|
)
|
|
918
927
|
redshift_data.execute_statement(Parameters=row_to_insert, **execute_statement)
|
|
919
928
|
except Exception as e:
|
|
920
|
-
LOG.
|
|
929
|
+
LOG.error(
|
|
921
930
|
"Unable to put records %s to redshift cluster.",
|
|
922
931
|
row_to_insert,
|
|
932
|
+
exc_info=LOG.isEnabledFor(logging.DEBUG),
|
|
923
933
|
)
|
|
924
934
|
raise e
|
|
925
935
|
|
|
@@ -20,10 +20,7 @@ from moto.iam.utils import generate_access_key_id_from_account_id
|
|
|
20
20
|
|
|
21
21
|
from localstack.aws.api import CommonServiceException, RequestContext, handler
|
|
22
22
|
from localstack.aws.api.iam import (
|
|
23
|
-
ActionNameListType,
|
|
24
|
-
ActionNameType,
|
|
25
23
|
AttachedPermissionsBoundary,
|
|
26
|
-
ContextEntryListType,
|
|
27
24
|
CreateRoleRequest,
|
|
28
25
|
CreateRoleResponse,
|
|
29
26
|
CreateServiceLinkedRoleResponse,
|
|
@@ -33,7 +30,6 @@ from localstack.aws.api.iam import (
|
|
|
33
30
|
DeleteServiceLinkedRoleResponse,
|
|
34
31
|
DeletionTaskIdType,
|
|
35
32
|
DeletionTaskStatusType,
|
|
36
|
-
EvaluationResult,
|
|
37
33
|
GetServiceLinkedRoleDeletionStatusResponse,
|
|
38
34
|
GetUserResponse,
|
|
39
35
|
IamApi,
|
|
@@ -43,16 +39,12 @@ from localstack.aws.api.iam import (
|
|
|
43
39
|
ListServiceSpecificCredentialsResponse,
|
|
44
40
|
MalformedPolicyDocumentException,
|
|
45
41
|
NoSuchEntityException,
|
|
46
|
-
PolicyEvaluationDecisionType,
|
|
47
42
|
ResetServiceSpecificCredentialResponse,
|
|
48
|
-
ResourceHandlingOptionType,
|
|
49
|
-
ResourceNameListType,
|
|
50
|
-
ResourceNameType,
|
|
51
43
|
Role,
|
|
52
44
|
ServiceSpecificCredential,
|
|
53
45
|
ServiceSpecificCredentialMetadata,
|
|
54
46
|
SimulatePolicyResponse,
|
|
55
|
-
|
|
47
|
+
SimulatePrincipalPolicyRequest,
|
|
56
48
|
User,
|
|
57
49
|
allUsers,
|
|
58
50
|
arnType,
|
|
@@ -65,7 +57,6 @@ from localstack.aws.api.iam import (
|
|
|
65
57
|
maxItemsType,
|
|
66
58
|
pathPrefixType,
|
|
67
59
|
pathType,
|
|
68
|
-
policyDocumentType,
|
|
69
60
|
roleDescriptionType,
|
|
70
61
|
roleNameType,
|
|
71
62
|
serviceName,
|
|
@@ -78,6 +69,10 @@ from localstack.aws.api.iam import (
|
|
|
78
69
|
from localstack.aws.connect import connect_to
|
|
79
70
|
from localstack.constants import INTERNAL_AWS_SECRET_ACCESS_KEY
|
|
80
71
|
from localstack.services.iam.iam_patches import apply_iam_patches
|
|
72
|
+
from localstack.services.iam.resources.policy_simulator import (
|
|
73
|
+
BasicIAMPolicySimulator,
|
|
74
|
+
IAMPolicySimulator,
|
|
75
|
+
)
|
|
81
76
|
from localstack.services.iam.resources.service_linked_roles import SERVICE_LINKED_ROLES
|
|
82
77
|
from localstack.services.moto import call_moto
|
|
83
78
|
from localstack.utils.aws.request_context import extract_access_key_id_from_auth_header
|
|
@@ -108,58 +103,12 @@ def get_iam_backend(context: RequestContext) -> IAMBackend:
|
|
|
108
103
|
return iam_backends[context.account_id][context.partition]
|
|
109
104
|
|
|
110
105
|
|
|
111
|
-
def get_policies_from_principal(backend: IAMBackend, principal_arn: str) -> list[dict]:
|
|
112
|
-
policies = []
|
|
113
|
-
if ":role" in principal_arn:
|
|
114
|
-
role_name = principal_arn.split("/")[-1]
|
|
115
|
-
|
|
116
|
-
policies.append(backend.get_role(role_name=role_name).assume_role_policy_document)
|
|
117
|
-
|
|
118
|
-
policy_names = backend.list_role_policies(role_name=role_name)
|
|
119
|
-
policies.extend(
|
|
120
|
-
[
|
|
121
|
-
backend.get_role_policy(role_name=role_name, policy_name=policy_name)[1]
|
|
122
|
-
for policy_name in policy_names
|
|
123
|
-
]
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
attached_policies, _ = backend.list_attached_role_policies(role_name=role_name)
|
|
127
|
-
policies.extend([policy.document for policy in attached_policies])
|
|
128
|
-
|
|
129
|
-
if ":group" in principal_arn:
|
|
130
|
-
print(principal_arn)
|
|
131
|
-
group_name = principal_arn.split("/")[-1]
|
|
132
|
-
policy_names = backend.list_group_policies(group_name=group_name)
|
|
133
|
-
policies.extend(
|
|
134
|
-
[
|
|
135
|
-
backend.get_group_policy(group_name=group_name, policy_name=policy_name)[1]
|
|
136
|
-
for policy_name in policy_names
|
|
137
|
-
]
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
attached_policies, _ = backend.list_attached_group_policies(group_name=group_name)
|
|
141
|
-
policies.extend([policy.document for policy in attached_policies])
|
|
142
|
-
|
|
143
|
-
if ":user" in principal_arn:
|
|
144
|
-
print(principal_arn)
|
|
145
|
-
user_name = principal_arn.split("/")[-1]
|
|
146
|
-
policy_names = backend.list_user_policies(user_name=user_name)
|
|
147
|
-
policies.extend(
|
|
148
|
-
[
|
|
149
|
-
backend.get_user_policy(user_name=user_name, policy_name=policy_name)[1]
|
|
150
|
-
for policy_name in policy_names
|
|
151
|
-
]
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
attached_policies, _ = backend.list_attached_user_policies(user_name=user_name)
|
|
155
|
-
policies.extend([policy.document for policy in attached_policies])
|
|
156
|
-
|
|
157
|
-
return policies
|
|
158
|
-
|
|
159
|
-
|
|
160
106
|
class IamProvider(IamApi):
|
|
107
|
+
policy_simulator: IAMPolicySimulator
|
|
108
|
+
|
|
161
109
|
def __init__(self):
|
|
162
110
|
apply_iam_patches()
|
|
111
|
+
self.policy_simulator = BasicIAMPolicySimulator()
|
|
163
112
|
|
|
164
113
|
@handler("CreateRole", expand=False)
|
|
165
114
|
def create_role(
|
|
@@ -181,68 +130,14 @@ class IamProvider(IamApi):
|
|
|
181
130
|
|
|
182
131
|
return result
|
|
183
132
|
|
|
184
|
-
@
|
|
185
|
-
def build_evaluation_result(
|
|
186
|
-
action_name: ActionNameType, resource_name: ResourceNameType, policy_statements: list[dict]
|
|
187
|
-
) -> EvaluationResult:
|
|
188
|
-
eval_res = EvaluationResult()
|
|
189
|
-
eval_res["EvalActionName"] = action_name
|
|
190
|
-
eval_res["EvalResourceName"] = resource_name
|
|
191
|
-
eval_res["EvalDecision"] = PolicyEvaluationDecisionType.explicitDeny
|
|
192
|
-
for statement in policy_statements:
|
|
193
|
-
# TODO Implement evaluation logic here
|
|
194
|
-
if (
|
|
195
|
-
action_name in statement["Action"]
|
|
196
|
-
and resource_name in statement["Resource"]
|
|
197
|
-
and statement["Effect"] == "Allow"
|
|
198
|
-
):
|
|
199
|
-
eval_res["EvalDecision"] = PolicyEvaluationDecisionType.allowed
|
|
200
|
-
eval_res["MatchedStatements"] = [] # TODO: add support for statement compilation.
|
|
201
|
-
return eval_res
|
|
202
|
-
|
|
133
|
+
@handler("SimulatePrincipalPolicy", expand=False)
|
|
203
134
|
def simulate_principal_policy(
|
|
204
135
|
self,
|
|
205
136
|
context: RequestContext,
|
|
206
|
-
|
|
207
|
-
action_names: ActionNameListType,
|
|
208
|
-
policy_input_list: SimulationPolicyListType = None,
|
|
209
|
-
permissions_boundary_policy_input_list: SimulationPolicyListType = None,
|
|
210
|
-
resource_arns: ResourceNameListType = None,
|
|
211
|
-
resource_policy: policyDocumentType = None,
|
|
212
|
-
resource_owner: ResourceNameType = None,
|
|
213
|
-
caller_arn: ResourceNameType = None,
|
|
214
|
-
context_entries: ContextEntryListType = None,
|
|
215
|
-
resource_handling_option: ResourceHandlingOptionType = None,
|
|
216
|
-
max_items: maxItemsType = None,
|
|
217
|
-
marker: markerType = None,
|
|
137
|
+
request: SimulatePrincipalPolicyRequest,
|
|
218
138
|
**kwargs,
|
|
219
139
|
) -> SimulatePolicyResponse:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
policies = get_policies_from_principal(backend, policy_source_arn)
|
|
223
|
-
|
|
224
|
-
def _get_statements_from_policy_list(policies: list[str]):
|
|
225
|
-
statements = []
|
|
226
|
-
for policy_str in policies:
|
|
227
|
-
policy_dict = json.loads(policy_str)
|
|
228
|
-
if isinstance(policy_dict["Statement"], list):
|
|
229
|
-
statements.extend(policy_dict["Statement"])
|
|
230
|
-
else:
|
|
231
|
-
statements.append(policy_dict["Statement"])
|
|
232
|
-
return statements
|
|
233
|
-
|
|
234
|
-
policy_statements = _get_statements_from_policy_list(policies)
|
|
235
|
-
|
|
236
|
-
evaluations = [
|
|
237
|
-
self.build_evaluation_result(action_name, resource_arn, policy_statements)
|
|
238
|
-
for action_name in action_names
|
|
239
|
-
for resource_arn in resource_arns
|
|
240
|
-
]
|
|
241
|
-
|
|
242
|
-
response = SimulatePolicyResponse()
|
|
243
|
-
response["IsTruncated"] = False
|
|
244
|
-
response["EvaluationResults"] = evaluations
|
|
245
|
-
return response
|
|
140
|
+
return self.policy_simulator.simulate_principal_policy(context, request)
|
|
246
141
|
|
|
247
142
|
def delete_policy(self, context: RequestContext, policy_arn: arnType, **kwargs) -> None:
|
|
248
143
|
backend = get_iam_backend(context)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from moto.iam import iam_backends
|
|
5
|
+
from moto.iam.models import IAMBackend
|
|
6
|
+
|
|
7
|
+
from localstack.aws.api import RequestContext
|
|
8
|
+
from localstack.aws.api.iam import (
|
|
9
|
+
ActionNameType,
|
|
10
|
+
EvaluationResult,
|
|
11
|
+
PolicyEvaluationDecisionType,
|
|
12
|
+
ResourceNameType,
|
|
13
|
+
SimulatePolicyResponse,
|
|
14
|
+
SimulatePrincipalPolicyRequest,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class IAMPolicySimulator(abc.ABC):
|
|
19
|
+
@abc.abstractmethod
|
|
20
|
+
def simulate_principal_policy(
|
|
21
|
+
self, context: RequestContext, request: SimulatePrincipalPolicyRequest
|
|
22
|
+
) -> SimulatePolicyResponse:
|
|
23
|
+
"""
|
|
24
|
+
Simulate principal policy
|
|
25
|
+
:param request: SimulatePrincipalPolicyRequest
|
|
26
|
+
:param context: RequestContext
|
|
27
|
+
:return: SimulatePrincipalResponse
|
|
28
|
+
"""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BasicIAMPolicySimulator(IAMPolicySimulator):
|
|
33
|
+
def simulate_principal_policy(
|
|
34
|
+
self,
|
|
35
|
+
context: RequestContext,
|
|
36
|
+
request: SimulatePrincipalPolicyRequest,
|
|
37
|
+
) -> SimulatePolicyResponse:
|
|
38
|
+
backend = self.get_iam_backend(context)
|
|
39
|
+
policies = self.get_policies_from_principal(backend, request.get("PolicySourceArn"))
|
|
40
|
+
|
|
41
|
+
def _get_statements_from_policy_list(_policies: list[str]):
|
|
42
|
+
statements = []
|
|
43
|
+
for policy_str in _policies:
|
|
44
|
+
policy_dict = json.loads(policy_str)
|
|
45
|
+
if isinstance(policy_dict["Statement"], list):
|
|
46
|
+
statements.extend(policy_dict["Statement"])
|
|
47
|
+
else:
|
|
48
|
+
statements.append(policy_dict["Statement"])
|
|
49
|
+
return statements
|
|
50
|
+
|
|
51
|
+
policy_statements = _get_statements_from_policy_list(policies)
|
|
52
|
+
|
|
53
|
+
evaluations = [
|
|
54
|
+
self.build_evaluation_result(action_name, resource_arn, policy_statements)
|
|
55
|
+
for action_name in request.get("ActionNames")
|
|
56
|
+
for resource_arn in request.get("ResourceArns")
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
response = SimulatePolicyResponse()
|
|
60
|
+
response["IsTruncated"] = False
|
|
61
|
+
response["EvaluationResults"] = evaluations
|
|
62
|
+
|
|
63
|
+
return response
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def build_evaluation_result(
|
|
67
|
+
action_name: ActionNameType, resource_name: ResourceNameType, policy_statements: list[dict]
|
|
68
|
+
) -> EvaluationResult:
|
|
69
|
+
eval_res = EvaluationResult()
|
|
70
|
+
eval_res["EvalActionName"] = action_name
|
|
71
|
+
eval_res["EvalResourceName"] = resource_name
|
|
72
|
+
eval_res["EvalDecision"] = PolicyEvaluationDecisionType.explicitDeny
|
|
73
|
+
for statement in policy_statements:
|
|
74
|
+
# TODO Implement evaluation logic here
|
|
75
|
+
if (
|
|
76
|
+
action_name in statement["Action"]
|
|
77
|
+
and resource_name in statement["Resource"]
|
|
78
|
+
and statement["Effect"] == "Allow"
|
|
79
|
+
):
|
|
80
|
+
eval_res["EvalDecision"] = PolicyEvaluationDecisionType.allowed
|
|
81
|
+
eval_res["MatchedStatements"] = [] # TODO: add support for statement compilation.
|
|
82
|
+
return eval_res
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def get_iam_backend(context: RequestContext) -> IAMBackend:
|
|
86
|
+
return iam_backends[context.account_id][context.partition]
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def get_policies_from_principal(backend: IAMBackend, principal_arn: str) -> list[dict]:
|
|
90
|
+
policies = []
|
|
91
|
+
if ":role" in principal_arn:
|
|
92
|
+
role_name = principal_arn.split("/")[-1]
|
|
93
|
+
|
|
94
|
+
policies.append(backend.get_role(role_name=role_name).assume_role_policy_document)
|
|
95
|
+
|
|
96
|
+
policy_names = backend.list_role_policies(role_name=role_name)
|
|
97
|
+
policies.extend(
|
|
98
|
+
[
|
|
99
|
+
backend.get_role_policy(role_name=role_name, policy_name=policy_name)[1]
|
|
100
|
+
for policy_name in policy_names
|
|
101
|
+
]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
attached_policies, _ = backend.list_attached_role_policies(role_name=role_name)
|
|
105
|
+
policies.extend([policy.document for policy in attached_policies])
|
|
106
|
+
|
|
107
|
+
if ":group" in principal_arn:
|
|
108
|
+
group_name = principal_arn.split("/")[-1]
|
|
109
|
+
policy_names = backend.list_group_policies(group_name=group_name)
|
|
110
|
+
policies.extend(
|
|
111
|
+
[
|
|
112
|
+
backend.get_group_policy(group_name=group_name, policy_name=policy_name)[1]
|
|
113
|
+
for policy_name in policy_names
|
|
114
|
+
]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
attached_policies, _ = backend.list_attached_group_policies(group_name=group_name)
|
|
118
|
+
policies.extend([policy.document for policy in attached_policies])
|
|
119
|
+
|
|
120
|
+
if ":user" in principal_arn:
|
|
121
|
+
user_name = principal_arn.split("/")[-1]
|
|
122
|
+
policy_names = backend.list_user_policies(user_name=user_name)
|
|
123
|
+
policies.extend(
|
|
124
|
+
[
|
|
125
|
+
backend.get_user_policy(user_name=user_name, policy_name=policy_name)[1]
|
|
126
|
+
for policy_name in policy_names
|
|
127
|
+
]
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
attached_policies, _ = backend.list_attached_user_policies(user_name=user_name)
|
|
131
|
+
policies.extend([policy.document for policy in attached_policies])
|
|
132
|
+
|
|
133
|
+
return policies
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
|
|
3
|
-
from localstack.aws.api.kinesis import
|
|
4
|
-
|
|
3
|
+
from localstack.aws.api.kinesis import (
|
|
4
|
+
ConsumerDescription,
|
|
5
|
+
MetricsName,
|
|
6
|
+
Policy,
|
|
7
|
+
ResourceARN,
|
|
8
|
+
StreamName,
|
|
9
|
+
)
|
|
10
|
+
from localstack.services.stores import (
|
|
11
|
+
AccountRegionBundle,
|
|
12
|
+
BaseStore,
|
|
13
|
+
CrossAccountAttribute,
|
|
14
|
+
LocalAttribute,
|
|
15
|
+
)
|
|
5
16
|
|
|
6
17
|
|
|
7
18
|
class KinesisStore(BaseStore):
|
|
@@ -13,5 +24,7 @@ class KinesisStore(BaseStore):
|
|
|
13
24
|
default=lambda: defaultdict(set)
|
|
14
25
|
)
|
|
15
26
|
|
|
27
|
+
resource_policies: dict[ResourceARN, Policy] = CrossAccountAttribute(default=dict)
|
|
28
|
+
|
|
16
29
|
|
|
17
30
|
kinesis_stores = AccountRegionBundle("kinesis", KinesisStore)
|