CloudTrail improvements (#4875)
This commit is contained in:
parent
876c783a24
commit
efb19b92f0
@ -632,9 +632,9 @@
|
||||
|
||||
## cloudtrail
|
||||
<details>
|
||||
<summary>27% implemented</summary>
|
||||
<summary>55% implemented</summary>
|
||||
|
||||
- [ ] add_tags
|
||||
- [X] add_tags
|
||||
- [ ] cancel_query
|
||||
- [ ] create_event_data_store
|
||||
- [X] create_trail
|
||||
@ -643,26 +643,26 @@
|
||||
- [ ] describe_query
|
||||
- [X] describe_trails
|
||||
- [ ] get_event_data_store
|
||||
- [ ] get_event_selectors
|
||||
- [ ] get_insight_selectors
|
||||
- [X] get_event_selectors
|
||||
- [X] get_insight_selectors
|
||||
- [ ] get_query_results
|
||||
- [X] get_trail
|
||||
- [X] get_trail_status
|
||||
- [ ] list_event_data_stores
|
||||
- [ ] list_public_keys
|
||||
- [ ] list_queries
|
||||
- [ ] list_tags
|
||||
- [X] list_tags
|
||||
- [X] list_trails
|
||||
- [ ] lookup_events
|
||||
- [ ] put_event_selectors
|
||||
- [ ] put_insight_selectors
|
||||
- [ ] remove_tags
|
||||
- [X] put_event_selectors
|
||||
- [X] put_insight_selectors
|
||||
- [X] remove_tags
|
||||
- [ ] restore_event_data_store
|
||||
- [X] start_logging
|
||||
- [ ] start_query
|
||||
- [X] stop_logging
|
||||
- [ ] update_event_data_store
|
||||
- [ ] update_trail
|
||||
- [X] update_trail
|
||||
</details>
|
||||
|
||||
## cloudwatch
|
||||
|
@ -27,7 +27,7 @@ cloudtrail
|
||||
|
||||
|start-h3| Implemented features for this service |end-h3|
|
||||
|
||||
- [ ] add_tags
|
||||
- [X] add_tags
|
||||
- [ ] cancel_query
|
||||
- [ ] create_event_data_store
|
||||
- [X] create_trail
|
||||
@ -36,24 +36,28 @@ cloudtrail
|
||||
- [ ] describe_query
|
||||
- [X] describe_trails
|
||||
- [ ] get_event_data_store
|
||||
- [ ] get_event_selectors
|
||||
- [ ] get_insight_selectors
|
||||
- [X] get_event_selectors
|
||||
- [X] get_insight_selectors
|
||||
- [ ] get_query_results
|
||||
- [X] get_trail
|
||||
- [X] get_trail_status
|
||||
- [ ] list_event_data_stores
|
||||
- [ ] list_public_keys
|
||||
- [ ] list_queries
|
||||
- [ ] list_tags
|
||||
- [X] list_tags
|
||||
|
||||
Pagination is not yet implemented
|
||||
|
||||
|
||||
- [X] list_trails
|
||||
- [ ] lookup_events
|
||||
- [ ] put_event_selectors
|
||||
- [ ] put_insight_selectors
|
||||
- [ ] remove_tags
|
||||
- [X] put_event_selectors
|
||||
- [X] put_insight_selectors
|
||||
- [X] remove_tags
|
||||
- [ ] restore_event_data_store
|
||||
- [X] start_logging
|
||||
- [ ] start_query
|
||||
- [X] stop_logging
|
||||
- [ ] update_event_data_store
|
||||
- [ ] update_trail
|
||||
- [X] update_trail
|
||||
|
||||
|
@ -4,6 +4,7 @@ import time
|
||||
from datetime import datetime
|
||||
from moto.core import ACCOUNT_ID, BaseBackend, BaseModel
|
||||
from moto.core.utils import iso_8601_datetime_without_milliseconds, BackendDict
|
||||
from moto.utilities.tagging_service import TaggingService
|
||||
from .exceptions import (
|
||||
S3BucketDoesNotExistException,
|
||||
InsufficientSnsTopicPolicyException,
|
||||
@ -78,9 +79,13 @@ class Trail(BaseModel):
|
||||
bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
is_global,
|
||||
is_multi_region,
|
||||
log_validation,
|
||||
is_org_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
):
|
||||
self.region_name = region_name
|
||||
self.trail_name = trail_name
|
||||
@ -90,10 +95,17 @@ class Trail(BaseModel):
|
||||
self.is_multi_region = is_multi_region
|
||||
self.log_validation = log_validation
|
||||
self.is_org_trail = is_org_trail
|
||||
self.include_global_service_events = is_global
|
||||
self.cw_log_group_arn = cw_log_group_arn
|
||||
self.cw_role_arn = cw_role_arn
|
||||
self.kms_key_id = kms_key_id
|
||||
self.check_name()
|
||||
self.check_bucket_exists()
|
||||
self.check_topic_exists()
|
||||
self.status = TrailStatus()
|
||||
self.event_selectors = list()
|
||||
self.advanced_event_selectors = list()
|
||||
self.insight_selectors = list()
|
||||
|
||||
@property
|
||||
def arn(self):
|
||||
@ -145,6 +157,56 @@ class Trail(BaseModel):
|
||||
def stop_logging(self):
|
||||
self.status.stop_logging()
|
||||
|
||||
def put_event_selectors(self, event_selectors, advanced_event_selectors):
|
||||
if event_selectors:
|
||||
self.event_selectors = event_selectors
|
||||
elif advanced_event_selectors:
|
||||
self.event_selectors = []
|
||||
self.advanced_event_selectors = advanced_event_selectors
|
||||
|
||||
def get_event_selectors(self):
|
||||
return self.event_selectors, self.advanced_event_selectors
|
||||
|
||||
def put_insight_selectors(self, insight_selectors):
|
||||
self.insight_selectors.extend(insight_selectors)
|
||||
|
||||
def get_insight_selectors(self):
|
||||
return self.insight_selectors
|
||||
|
||||
def update(
|
||||
self,
|
||||
s3_bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
include_global_service_events,
|
||||
is_multi_region_trail,
|
||||
enable_log_file_validation,
|
||||
is_organization_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
):
|
||||
if s3_bucket_name is not None:
|
||||
self.bucket_name = s3_bucket_name
|
||||
if s3_key_prefix is not None:
|
||||
self.s3_key_prefix = s3_key_prefix
|
||||
if sns_topic_name is not None:
|
||||
self.sns_topic_name = sns_topic_name
|
||||
if include_global_service_events is not None:
|
||||
self.include_global_service_events = include_global_service_events
|
||||
if is_multi_region_trail is not None:
|
||||
self.is_multi_region = is_multi_region_trail
|
||||
if enable_log_file_validation is not None:
|
||||
self.log_validation = enable_log_file_validation
|
||||
if is_organization_trail is not None:
|
||||
self.is_org_trail = is_organization_trail
|
||||
if cw_log_group_arn is not None:
|
||||
self.cw_log_group_arn = cw_log_group_arn
|
||||
if cw_role_arn is not None:
|
||||
self.cw_role_arn = cw_role_arn
|
||||
if kms_key_id is not None:
|
||||
self.kms_key_id = kms_key_id
|
||||
|
||||
def short(self):
|
||||
return {
|
||||
"Name": self.trail_name,
|
||||
@ -156,13 +218,16 @@ class Trail(BaseModel):
|
||||
desc = {
|
||||
"Name": self.trail_name,
|
||||
"S3BucketName": self.bucket_name,
|
||||
"IncludeGlobalServiceEvents": True,
|
||||
"IncludeGlobalServiceEvents": self.include_global_service_events,
|
||||
"IsMultiRegionTrail": self.is_multi_region,
|
||||
"TrailARN": self.arn,
|
||||
"LogFileValidationEnabled": self.log_validation,
|
||||
"IsOrganizationTrail": self.is_org_trail,
|
||||
"HasCustomEventSelectors": False,
|
||||
"HasInsightSelectors": False,
|
||||
"CloudWatchLogsLogGroupArn": self.cw_log_group_arn,
|
||||
"CloudWatchLogsRoleArn": self.cw_role_arn,
|
||||
"KmsKeyId": self.kms_key_id,
|
||||
}
|
||||
if self.s3_key_prefix is not None:
|
||||
desc["S3KeyPrefix"] = self.s3_key_prefix
|
||||
@ -180,6 +245,7 @@ class CloudTrailBackend(BaseBackend):
|
||||
def __init__(self, region_name):
|
||||
self.region_name = region_name
|
||||
self.trails = dict()
|
||||
self.tagging_service = TaggingService(tag_name="TagsList")
|
||||
|
||||
def create_trail(
|
||||
self,
|
||||
@ -187,9 +253,14 @@ class CloudTrailBackend(BaseBackend):
|
||||
bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
is_global,
|
||||
is_multi_region,
|
||||
log_validation,
|
||||
is_org_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
tags_list,
|
||||
):
|
||||
trail = Trail(
|
||||
self.region_name,
|
||||
@ -197,19 +268,27 @@ class CloudTrailBackend(BaseBackend):
|
||||
bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
is_global,
|
||||
is_multi_region,
|
||||
log_validation,
|
||||
is_org_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
)
|
||||
self.trails[name] = trail
|
||||
self.tagging_service.tag_resource(trail.arn, tags_list)
|
||||
return trail
|
||||
|
||||
def get_trail(self, name):
|
||||
if len(name) < 3:
|
||||
raise TrailNameTooShort(actual_length=len(name))
|
||||
if name not in self.trails:
|
||||
raise TrailNotFoundException(name)
|
||||
return self.trails[name]
|
||||
def get_trail(self, name_or_arn):
|
||||
if len(name_or_arn) < 3:
|
||||
raise TrailNameTooShort(actual_length=len(name_or_arn))
|
||||
if name_or_arn in self.trails:
|
||||
return self.trails[name_or_arn]
|
||||
for trail in self.trails.values():
|
||||
if trail.arn == name_or_arn:
|
||||
return trail
|
||||
raise TrailNotFoundException(name_or_arn)
|
||||
|
||||
def get_trail_status(self, name):
|
||||
if len(name) < 3:
|
||||
@ -253,11 +332,79 @@ class CloudTrailBackend(BaseBackend):
|
||||
if name in self.trails:
|
||||
del self.trails[name]
|
||||
|
||||
def update_trail(
|
||||
self,
|
||||
name,
|
||||
s3_bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
include_global_service_events,
|
||||
is_multi_region_trail,
|
||||
enable_log_file_validation,
|
||||
is_organization_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
):
|
||||
trail = self.get_trail(name_or_arn=name)
|
||||
trail.update(
|
||||
s3_bucket_name=s3_bucket_name,
|
||||
s3_key_prefix=s3_key_prefix,
|
||||
sns_topic_name=sns_topic_name,
|
||||
include_global_service_events=include_global_service_events,
|
||||
is_multi_region_trail=is_multi_region_trail,
|
||||
enable_log_file_validation=enable_log_file_validation,
|
||||
is_organization_trail=is_organization_trail,
|
||||
cw_log_group_arn=cw_log_group_arn,
|
||||
cw_role_arn=cw_role_arn,
|
||||
kms_key_id=kms_key_id,
|
||||
)
|
||||
return trail
|
||||
|
||||
def reset(self):
|
||||
"""Re-initialize all attributes for this instance."""
|
||||
region_name = self.region_name
|
||||
self.__dict__ = {}
|
||||
self.__init__(region_name)
|
||||
|
||||
def put_event_selectors(
|
||||
self, trail_name, event_selectors, advanced_event_selectors
|
||||
):
|
||||
trail = self.get_trail(trail_name)
|
||||
trail.put_event_selectors(event_selectors, advanced_event_selectors)
|
||||
trail_arn = trail.arn
|
||||
return trail_arn, event_selectors, advanced_event_selectors
|
||||
|
||||
def get_event_selectors(self, trail_name):
|
||||
trail = self.get_trail(trail_name)
|
||||
event_selectors, advanced_event_selectors = trail.get_event_selectors()
|
||||
return trail.arn, event_selectors, advanced_event_selectors
|
||||
|
||||
def add_tags(self, resource_id, tags_list):
|
||||
self.tagging_service.tag_resource(resource_id, tags_list)
|
||||
|
||||
def remove_tags(self, resource_id, tags_list):
|
||||
self.tagging_service.untag_resource_using_tags(resource_id, tags_list)
|
||||
|
||||
def list_tags(self, resource_id_list):
|
||||
"""
|
||||
Pagination is not yet implemented
|
||||
"""
|
||||
resp = [{"ResourceId": r_id} for r_id in resource_id_list]
|
||||
for item in resp:
|
||||
item["TagsList"] = self.tagging_service.list_tags_for_resource(
|
||||
item["ResourceId"]
|
||||
)["TagsList"]
|
||||
return resp
|
||||
|
||||
def put_insight_selectors(self, trail_name, insight_selectors):
|
||||
trail = self.get_trail(trail_name)
|
||||
trail.put_insight_selectors(insight_selectors)
|
||||
return trail.arn, insight_selectors
|
||||
|
||||
def get_insight_selectors(self, trail_name):
|
||||
trail = self.get_trail(trail_name)
|
||||
return trail.arn, trail.get_insight_selectors()
|
||||
|
||||
|
||||
cloudtrail_backends = BackendDict(CloudTrailBackend, "cloudtrail")
|
||||
|
@ -17,7 +17,7 @@ class CloudTrailResponse(BaseResponse):
|
||||
def create_trail(self):
|
||||
name = self._get_param("Name")
|
||||
bucket_name = self._get_param("S3BucketName")
|
||||
is_global = self._get_bool_param("IncludeGlobalServiceEvents")
|
||||
is_global = self._get_bool_param("IncludeGlobalServiceEvents", True)
|
||||
is_multi_region = self._get_bool_param("IsMultiRegionTrail", False)
|
||||
if not is_global and is_multi_region:
|
||||
raise InvalidParameterCombinationException(
|
||||
@ -27,14 +27,23 @@ class CloudTrailResponse(BaseResponse):
|
||||
sns_topic_name = self._get_param("SnsTopicName")
|
||||
log_validation = self._get_bool_param("EnableLogFileValidation", False)
|
||||
is_org_trail = self._get_bool_param("IsOrganizationTrail", False)
|
||||
cw_log_group_arn = self._get_param("CloudWatchLogsLogGroupArn")
|
||||
cw_role_arn = self._get_param("CloudWatchLogsRoleArn")
|
||||
kms_key_id = self._get_param("KmsKeyId")
|
||||
tags_list = self._get_param("TagsList", [])
|
||||
trail = self.cloudtrail_backend.create_trail(
|
||||
name,
|
||||
bucket_name,
|
||||
s3_key_prefix,
|
||||
sns_topic_name,
|
||||
is_global,
|
||||
is_multi_region,
|
||||
log_validation,
|
||||
is_org_trail,
|
||||
cw_log_group_arn,
|
||||
cw_role_arn,
|
||||
kms_key_id,
|
||||
tags_list,
|
||||
)
|
||||
return json.dumps(trail.description())
|
||||
|
||||
@ -73,3 +82,111 @@ class CloudTrailResponse(BaseResponse):
|
||||
name = self._get_param("Name")
|
||||
self.cloudtrail_backend.delete_trail(name)
|
||||
return json.dumps({})
|
||||
|
||||
def update_trail(self):
|
||||
name = self._get_param("Name")
|
||||
s3_bucket_name = self._get_param("S3BucketName")
|
||||
s3_key_prefix = self._get_param("S3KeyPrefix")
|
||||
sns_topic_name = self._get_param("SnsTopicName")
|
||||
include_global_service_events = self._get_param("IncludeGlobalServiceEvents")
|
||||
is_multi_region_trail = self._get_param("IsMultiRegionTrail")
|
||||
enable_log_file_validation = self._get_param("EnableLogFileValidation")
|
||||
is_organization_trail = self._get_param("IsOrganizationTrail")
|
||||
cw_log_group_arn = self._get_param("CloudWatchLogsLogGroupArn")
|
||||
cw_role_arn = self._get_param("CloudWatchLogsRoleArn")
|
||||
kms_key_id = self._get_param("KmsKeyId")
|
||||
trail = self.cloudtrail_backend.update_trail(
|
||||
name=name,
|
||||
s3_bucket_name=s3_bucket_name,
|
||||
s3_key_prefix=s3_key_prefix,
|
||||
sns_topic_name=sns_topic_name,
|
||||
include_global_service_events=include_global_service_events,
|
||||
is_multi_region_trail=is_multi_region_trail,
|
||||
enable_log_file_validation=enable_log_file_validation,
|
||||
is_organization_trail=is_organization_trail,
|
||||
cw_log_group_arn=cw_log_group_arn,
|
||||
cw_role_arn=cw_role_arn,
|
||||
kms_key_id=kms_key_id,
|
||||
)
|
||||
return json.dumps(trail.description())
|
||||
|
||||
def put_event_selectors(self):
|
||||
params = json.loads(self.body)
|
||||
trail_name = params.get("TrailName")
|
||||
event_selectors = params.get("EventSelectors")
|
||||
advanced_event_selectors = params.get("AdvancedEventSelectors")
|
||||
(
|
||||
trail_arn,
|
||||
event_selectors,
|
||||
advanced_event_selectors,
|
||||
) = self.cloudtrail_backend.put_event_selectors(
|
||||
trail_name=trail_name,
|
||||
event_selectors=event_selectors,
|
||||
advanced_event_selectors=advanced_event_selectors,
|
||||
)
|
||||
return json.dumps(
|
||||
dict(
|
||||
TrailARN=trail_arn,
|
||||
EventSelectors=event_selectors,
|
||||
AdvancedEventSelectors=advanced_event_selectors,
|
||||
)
|
||||
)
|
||||
|
||||
def get_event_selectors(self):
|
||||
params = json.loads(self.body)
|
||||
trail_name = params.get("TrailName")
|
||||
(
|
||||
trail_arn,
|
||||
event_selectors,
|
||||
advanced_event_selectors,
|
||||
) = self.cloudtrail_backend.get_event_selectors(trail_name=trail_name,)
|
||||
return json.dumps(
|
||||
dict(
|
||||
TrailARN=trail_arn,
|
||||
EventSelectors=event_selectors,
|
||||
AdvancedEventSelectors=advanced_event_selectors,
|
||||
)
|
||||
)
|
||||
|
||||
def add_tags(self):
|
||||
params = json.loads(self.body)
|
||||
resource_id = params.get("ResourceId")
|
||||
tags_list = params.get("TagsList")
|
||||
self.cloudtrail_backend.add_tags(
|
||||
resource_id=resource_id, tags_list=tags_list,
|
||||
)
|
||||
return json.dumps(dict())
|
||||
|
||||
def remove_tags(self):
|
||||
resource_id = self._get_param("ResourceId")
|
||||
tags_list = self._get_param("TagsList")
|
||||
self.cloudtrail_backend.remove_tags(
|
||||
resource_id=resource_id, tags_list=tags_list,
|
||||
)
|
||||
return json.dumps(dict())
|
||||
|
||||
def list_tags(self):
|
||||
params = json.loads(self.body)
|
||||
resource_id_list = params.get("ResourceIdList")
|
||||
resource_tag_list = self.cloudtrail_backend.list_tags(
|
||||
resource_id_list=resource_id_list,
|
||||
)
|
||||
return json.dumps(dict(ResourceTagList=resource_tag_list))
|
||||
|
||||
def put_insight_selectors(self):
|
||||
trail_name = self._get_param("TrailName")
|
||||
insight_selectors = self._get_param("InsightSelectors")
|
||||
trail_arn, insight_selectors = self.cloudtrail_backend.put_insight_selectors(
|
||||
trail_name=trail_name, insight_selectors=insight_selectors,
|
||||
)
|
||||
return json.dumps(dict(TrailARN=trail_arn, InsightSelectors=insight_selectors))
|
||||
|
||||
def get_insight_selectors(self):
|
||||
trail_name = self._get_param("TrailName")
|
||||
trail_arn, insight_selectors = self.cloudtrail_backend.get_insight_selectors(
|
||||
trail_name=trail_name,
|
||||
)
|
||||
resp = {"TrailARN": trail_arn}
|
||||
if insight_selectors:
|
||||
resp["InsightSelectors"] = insight_selectors
|
||||
return json.dumps(resp)
|
||||
|
@ -30,7 +30,9 @@ class FakeOrganization(BaseModel):
|
||||
self.master_account_id = utils.MASTER_ACCOUNT_ID
|
||||
self.master_account_email = utils.MASTER_ACCOUNT_EMAIL
|
||||
self.available_policy_types = [
|
||||
# TODO: verify if this should be enabled by default (breaks TF tests for CloudTrail)
|
||||
# This policy is available, but not applied
|
||||
# User should use enable_policy_type/disable_policy_type to do anything else
|
||||
# This field is deprecated in AWS, but we'll return it for old time's sake
|
||||
{"Type": "SERVICE_CONTROL_POLICY", "Status": "ENABLED"}
|
||||
]
|
||||
|
||||
@ -140,10 +142,7 @@ class FakeRoot(FakeOrganizationalUnit):
|
||||
self.type = "ROOT"
|
||||
self.id = organization.root_id
|
||||
self.name = "Root"
|
||||
self.policy_types = [
|
||||
# TODO: verify if this should be enabled by default (breaks TF tests for CloudTrail)
|
||||
{"Type": "SERVICE_CONTROL_POLICY", "Status": "ENABLED"}
|
||||
]
|
||||
self.policy_types = []
|
||||
self._arn_format = utils.ROOT_ARN_FORMAT
|
||||
self.attached_policies = []
|
||||
self.tags = {tag["Key"]: tag["Value"] for tag in kwargs.get("Tags", [])}
|
||||
|
@ -11,6 +11,7 @@ TestAccAWSAppsyncGraphqlApi
|
||||
TestAccAWSAvailabilityZones
|
||||
TestAccAWSBillingServiceAccount
|
||||
TestAccAWSCallerIdentity
|
||||
TestAccAWSCloudTrail
|
||||
TestAccAWSCloudTrailServiceAccount
|
||||
TestAccAWSCloudWatchDashboard
|
||||
TestAccAWSCloudWatchEventApiDestination
|
||||
|
@ -147,7 +147,9 @@ def test_create_trail_advanced():
|
||||
)
|
||||
resp.should.have.key("LogFileValidationEnabled").equal(True)
|
||||
resp.should.have.key("IsOrganizationTrail").equal(True)
|
||||
return resp
|
||||
resp.should.have.key("CloudWatchLogsLogGroupArn").equals("cwllga")
|
||||
resp.should.have.key("CloudWatchLogsRoleArn").equals("cwlra")
|
||||
resp.should.have.key("KmsKeyId").equals("kki")
|
||||
|
||||
|
||||
def create_trail_advanced(region_name="us-east-1"):
|
||||
@ -168,6 +170,9 @@ def create_trail_advanced(region_name="us-east-1"):
|
||||
IsMultiRegionTrail=True,
|
||||
EnableLogFileValidation=True,
|
||||
IsOrganizationTrail=True,
|
||||
CloudWatchLogsLogGroupArn="cwllga",
|
||||
CloudWatchLogsRoleArn="cwlra",
|
||||
KmsKeyId="kki",
|
||||
TagsList=[{"Key": "tk", "Value": "tv"}, {"Key": "tk2", "Value": "tv2"}],
|
||||
)
|
||||
return bucket_name, resp, sns_topic_name, trail_name
|
||||
@ -461,3 +466,72 @@ def test_delete_trail():
|
||||
|
||||
trails = client.describe_trails()["trailList"]
|
||||
trails.should.have.length_of(0)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_update_trail_simple():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-2")
|
||||
bucket_name, trail, name = create_trail_simple(region_name="ap-southeast-2")
|
||||
resp = client.update_trail(Name=name)
|
||||
resp.should.have.key("Name").equal(name)
|
||||
resp.should.have.key("S3BucketName").equal(bucket_name)
|
||||
resp.should.have.key("IncludeGlobalServiceEvents").equal(True)
|
||||
resp.should.have.key("IsMultiRegionTrail").equal(False)
|
||||
resp.should.have.key("LogFileValidationEnabled").equal(False)
|
||||
resp.should.have.key("IsOrganizationTrail").equal(False)
|
||||
resp.shouldnt.have.key("S3KeyPrefix")
|
||||
resp.shouldnt.have.key("SnsTopicName")
|
||||
resp.shouldnt.have.key("SnsTopicARN")
|
||||
|
||||
trail = client.get_trail(Name=name)["Trail"]
|
||||
trail.should.have.key("Name").equal(name)
|
||||
trail.should.have.key("S3BucketName").equal(bucket_name)
|
||||
trail.should.have.key("IncludeGlobalServiceEvents").equal(True)
|
||||
trail.should.have.key("IsMultiRegionTrail").equal(False)
|
||||
trail.should.have.key("LogFileValidationEnabled").equal(False)
|
||||
trail.should.have.key("IsOrganizationTrail").equal(False)
|
||||
trail.shouldnt.have.key("S3KeyPrefix")
|
||||
trail.shouldnt.have.key("SnsTopicName")
|
||||
trail.shouldnt.have.key("SnsTopicARN")
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_update_trail_full():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-1")
|
||||
_, trail, name = create_trail_simple(region_name="ap-southeast-1")
|
||||
resp = client.update_trail(
|
||||
Name=name,
|
||||
S3BucketName="updated_bucket",
|
||||
S3KeyPrefix="s3kp",
|
||||
SnsTopicName="stn",
|
||||
IncludeGlobalServiceEvents=False,
|
||||
IsMultiRegionTrail=True,
|
||||
EnableLogFileValidation=True,
|
||||
CloudWatchLogsLogGroupArn="cwllga",
|
||||
CloudWatchLogsRoleArn="cwlra",
|
||||
KmsKeyId="kki",
|
||||
IsOrganizationTrail=True,
|
||||
)
|
||||
resp.should.have.key("Name").equal(name)
|
||||
resp.should.have.key("S3BucketName").equal("updated_bucket")
|
||||
resp.should.have.key("S3KeyPrefix").equals("s3kp")
|
||||
resp.should.have.key("SnsTopicName").equals("stn")
|
||||
resp.should.have.key("IncludeGlobalServiceEvents").equal(False)
|
||||
resp.should.have.key("IsMultiRegionTrail").equal(True)
|
||||
resp.should.have.key("LogFileValidationEnabled").equal(True)
|
||||
resp.should.have.key("IsOrganizationTrail").equal(True)
|
||||
|
||||
trail = client.get_trail(Name=name)["Trail"]
|
||||
trail.should.have.key("Name").equal(name)
|
||||
trail.should.have.key("S3BucketName").equal("updated_bucket")
|
||||
trail.should.have.key("S3KeyPrefix").equals("s3kp")
|
||||
trail.should.have.key("SnsTopicName").equals("stn")
|
||||
trail.should.have.key("IncludeGlobalServiceEvents").equal(False)
|
||||
trail.should.have.key("IsMultiRegionTrail").equal(True)
|
||||
trail.should.have.key("LogFileValidationEnabled").equal(True)
|
||||
trail.should.have.key("IsOrganizationTrail").equal(True)
|
||||
trail.should.have.key("CloudWatchLogsLogGroupArn").equals("cwllga")
|
||||
trail.should.have.key("CloudWatchLogsRoleArn").equals("cwlra")
|
||||
trail.should.have.key("KmsKeyId").equals("kki")
|
||||
|
208
tests/test_cloudtrail/test_cloudtrail_eventselectors.py
Normal file
208
tests/test_cloudtrail/test_cloudtrail_eventselectors.py
Normal file
@ -0,0 +1,208 @@
|
||||
import boto3
|
||||
import pytest
|
||||
|
||||
from moto import mock_cloudtrail, mock_s3
|
||||
from moto.core import ACCOUNT_ID
|
||||
|
||||
from .test_cloudtrail import create_trail_simple
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_put_event_selectors():
|
||||
client = boto3.client("cloudtrail", region_name="eu-west-1")
|
||||
_, _, trail_name = create_trail_simple(region_name="eu-west-1")
|
||||
|
||||
resp = client.put_event_selectors(
|
||||
TrailName=trail_name,
|
||||
EventSelectors=[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": True,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
resp.should.have.key("EventSelectors").equals(
|
||||
[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": True,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
resp.shouldnt.have.key("AdvancedEventSelectors")
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_put_event_selectors_advanced():
|
||||
client = boto3.client("cloudtrail", region_name="eu-west-1")
|
||||
_, _, trail_name = create_trail_simple(region_name="eu-west-1")
|
||||
|
||||
resp = client.put_event_selectors(
|
||||
TrailName=trail_name,
|
||||
EventSelectors=[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": True,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
],
|
||||
AdvancedEventSelectors=[
|
||||
{"Name": "aes1", "FieldSelectors": [{"Field": "f", "Equals": ["fs1"]}]}
|
||||
],
|
||||
)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
resp.should.have.key("EventSelectors").equals(
|
||||
[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": True,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
resp.should.have.key("AdvancedEventSelectors").equals(
|
||||
[{"Name": "aes1", "FieldSelectors": [{"Field": "f", "Equals": ["fs1"]}]}]
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_get_event_selectors_empty():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-1")
|
||||
_, _, trail_name = create_trail_simple(region_name="ap-southeast-1")
|
||||
|
||||
resp = client.get_event_selectors(TrailName=trail_name)
|
||||
|
||||
resp.should.have.key("TrailARN").equals(
|
||||
f"arn:aws:cloudtrail:ap-southeast-1:{ACCOUNT_ID}:trail/{trail_name}"
|
||||
)
|
||||
resp.should.have.key("EventSelectors").equals([])
|
||||
resp.should.have.key("AdvancedEventSelectors").equals([])
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_get_event_selectors():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-2")
|
||||
_, _, trail_name = create_trail_simple(region_name="ap-southeast-2")
|
||||
|
||||
client.put_event_selectors(
|
||||
TrailName=trail_name,
|
||||
EventSelectors=[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": False,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
resp = client.get_event_selectors(TrailName=trail_name)
|
||||
|
||||
resp.should.have.key("TrailARN").equals(
|
||||
f"arn:aws:cloudtrail:ap-southeast-2:{ACCOUNT_ID}:trail/{trail_name}"
|
||||
)
|
||||
resp.should.have.key("EventSelectors").equals(
|
||||
[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": False,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_get_event_selectors_multiple():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-1")
|
||||
_, _, trail_name = create_trail_simple(region_name="ap-southeast-1")
|
||||
|
||||
client.put_event_selectors(
|
||||
TrailName=trail_name,
|
||||
EventSelectors=[
|
||||
{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": False,
|
||||
"DataResources": [
|
||||
{"Type": "AWS::S3::Object", "Values": ["arn:aws:s3:::*/*"]}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
client.put_event_selectors(
|
||||
TrailName=trail_name,
|
||||
AdvancedEventSelectors=[
|
||||
{"Name": "aes1", "FieldSelectors": [{"Field": "f", "Equals": ["fs1"]}]}
|
||||
],
|
||||
)
|
||||
|
||||
resp = client.get_event_selectors(TrailName=trail_name)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
# Setting advanced selectors cancels any existing event selectors
|
||||
resp.should.have.key("EventSelectors").equals([])
|
||||
resp.should.have.key("AdvancedEventSelectors").length_of(1)
|
||||
resp.should.have.key("AdvancedEventSelectors").equals(
|
||||
[{"Name": "aes1", "FieldSelectors": [{"Field": "f", "Equals": ["fs1"]}]}]
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
@pytest.mark.parametrize("using_arn", [True, False])
|
||||
def test_put_insight_selectors(using_arn):
|
||||
client = boto3.client("cloudtrail", region_name="us-east-2")
|
||||
_, resp, trail_name = create_trail_simple(region_name="us-east-2")
|
||||
|
||||
resp = client.put_insight_selectors(
|
||||
TrailName=trail_name, InsightSelectors=[{"InsightType": "ApiCallRateInsight"}]
|
||||
)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
resp.should.have.key("InsightSelectors").equals(
|
||||
[{"InsightType": "ApiCallRateInsight"}]
|
||||
)
|
||||
|
||||
if using_arn:
|
||||
trail_arn = resp["TrailARN"]
|
||||
resp = client.get_insight_selectors(TrailName=trail_arn)
|
||||
else:
|
||||
resp = client.get_insight_selectors(TrailName=trail_name)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
resp.should.have.key("InsightSelectors").equals(
|
||||
[{"InsightType": "ApiCallRateInsight"}]
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_get_insight_selectors():
|
||||
client = boto3.client("cloudtrail", region_name="eu-west-1")
|
||||
_, resp, trail_name = create_trail_simple(region_name="eu-west-1")
|
||||
resp = client.get_insight_selectors(TrailName=trail_name)
|
||||
|
||||
resp.should.have.key("TrailARN")
|
||||
resp.shouldnt.have.key("InsightSelectors")
|
77
tests/test_cloudtrail/test_cloudtrail_tags.py
Normal file
77
tests/test_cloudtrail/test_cloudtrail_tags.py
Normal file
@ -0,0 +1,77 @@
|
||||
import boto3
|
||||
|
||||
from moto import mock_cloudtrail, mock_s3, mock_sns
|
||||
|
||||
from .test_cloudtrail import create_trail_simple, create_trail_advanced
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_add_tags():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-1")
|
||||
_, resp, _ = create_trail_simple(region_name="ap-southeast-1")
|
||||
trail_arn = resp["TrailARN"]
|
||||
|
||||
client.add_tags(ResourceId=trail_arn, TagsList=[{"Key": "k1", "Value": "v1"},])
|
||||
|
||||
resp = client.list_tags(ResourceIdList=[trail_arn])
|
||||
resp.should.have.key("ResourceTagList").length_of(1)
|
||||
resp["ResourceTagList"][0].should.equal(
|
||||
{"ResourceId": trail_arn, "TagsList": [{"Key": "k1", "Value": "v1"},]}
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
@mock_sns
|
||||
def test_remove_tags():
|
||||
client = boto3.client("cloudtrail", region_name="ap-southeast-1")
|
||||
# Start with two tags
|
||||
_, resp, _, _ = create_trail_advanced(region_name="ap-southeast-1")
|
||||
trail_arn = resp["TrailARN"]
|
||||
|
||||
# Add a third tag
|
||||
client.add_tags(ResourceId=trail_arn, TagsList=[{"Key": "tk3", "Value": "tv3"},])
|
||||
|
||||
# Remove the second tag
|
||||
client.remove_tags(ResourceId=trail_arn, TagsList=[{"Key": "tk2", "Value": "tv2"}])
|
||||
|
||||
# Verify the first and third tag are still there
|
||||
resp = client.list_tags(ResourceIdList=[trail_arn])
|
||||
resp.should.have.key("ResourceTagList").length_of(1)
|
||||
resp["ResourceTagList"][0].should.equal(
|
||||
{
|
||||
"ResourceId": trail_arn,
|
||||
"TagsList": [{"Key": "tk", "Value": "tv"}, {"Key": "tk3", "Value": "tv3"}],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
def test_create_trail_without_tags_and_list_tags():
|
||||
client = boto3.client("cloudtrail", region_name="us-east-2")
|
||||
_, resp, _ = create_trail_simple(region_name="us-east-2")
|
||||
trail_arn = resp["TrailARN"]
|
||||
|
||||
resp = client.list_tags(ResourceIdList=[trail_arn])
|
||||
resp.should.have.key("ResourceTagList").length_of(1)
|
||||
resp["ResourceTagList"][0].should.equal({"ResourceId": trail_arn, "TagsList": []})
|
||||
|
||||
|
||||
@mock_cloudtrail
|
||||
@mock_s3
|
||||
@mock_sns
|
||||
def test_create_trail_with_tags_and_list_tags():
|
||||
client = boto3.client("cloudtrail", region_name="us-east-2")
|
||||
_, resp, _, _ = create_trail_advanced(region_name="us-east-2")
|
||||
trail_arn = resp["TrailARN"]
|
||||
|
||||
resp = client.list_tags(ResourceIdList=[trail_arn])
|
||||
resp.should.have.key("ResourceTagList").length_of(1)
|
||||
resp["ResourceTagList"][0].should.equal(
|
||||
{
|
||||
"ResourceId": trail_arn,
|
||||
"TagsList": [{"Key": "tk", "Value": "tv"}, {"Key": "tk2", "Value": "tv2"}],
|
||||
}
|
||||
)
|
@ -70,11 +70,7 @@ def validate_roots(org, response):
|
||||
utils.ROOT_ARN_FORMAT.format(org["MasterAccountId"], org["Id"], root["Id"])
|
||||
)
|
||||
root.should.have.key("Name").should.be.a(str)
|
||||
root.should.have.key("PolicyTypes").should.be.a(list)
|
||||
root["PolicyTypes"][0].should.have.key("Type").should.equal(
|
||||
"SERVICE_CONTROL_POLICY"
|
||||
)
|
||||
root["PolicyTypes"][0].should.have.key("Status").should.equal("ENABLED")
|
||||
root.should.have.key("PolicyTypes").should.equal([])
|
||||
|
||||
|
||||
def validate_organizational_unit(org, response):
|
||||
|
@ -1812,10 +1812,7 @@ def test_enable_policy_type():
|
||||
)
|
||||
root["Name"].should.equal("Root")
|
||||
sorted(root["PolicyTypes"], key=lambda x: x["Type"]).should.equal(
|
||||
[
|
||||
{"Type": "AISERVICES_OPT_OUT_POLICY", "Status": "ENABLED"},
|
||||
{"Type": "SERVICE_CONTROL_POLICY", "Status": "ENABLED"},
|
||||
]
|
||||
[{"Type": "AISERVICES_OPT_OUT_POLICY", "Status": "ENABLED"}]
|
||||
)
|
||||
|
||||
|
||||
@ -1842,7 +1839,10 @@ def test_enable_policy_type_errors():
|
||||
"You specified a root that doesn't exist."
|
||||
)
|
||||
|
||||
# enable policy again ('SERVICE_CONTROL_POLICY' is enabled by default)
|
||||
# enable policy again
|
||||
# given
|
||||
client.enable_policy_type(RootId=root_id, PolicyType="SERVICE_CONTROL_POLICY")
|
||||
|
||||
# when
|
||||
with pytest.raises(ClientError) as e:
|
||||
client.enable_policy_type(RootId=root_id, PolicyType="SERVICE_CONTROL_POLICY")
|
||||
@ -1889,9 +1889,7 @@ def test_disable_policy_type():
|
||||
utils.ROOT_ARN_FORMAT.format(org["MasterAccountId"], org["Id"], root_id)
|
||||
)
|
||||
root["Name"].should.equal("Root")
|
||||
root["PolicyTypes"].should.equal(
|
||||
[{"Type": "SERVICE_CONTROL_POLICY", "Status": "ENABLED"}]
|
||||
)
|
||||
root["PolicyTypes"].should.equal([])
|
||||
|
||||
|
||||
@mock_organizations
|
||||
|
Loading…
Reference in New Issue
Block a user