CloudTrail improvements (#4875)

This commit is contained in:
Bert Blommers 2022-02-19 23:45:36 -01:00 committed by GitHub
parent 876c783a24
commit efb19b92f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 665 additions and 44 deletions

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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)

View File

@ -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", [])}

View File

@ -11,6 +11,7 @@ TestAccAWSAppsyncGraphqlApi
TestAccAWSAvailabilityZones
TestAccAWSBillingServiceAccount
TestAccAWSCallerIdentity
TestAccAWSCloudTrail
TestAccAWSCloudTrailServiceAccount
TestAccAWSCloudWatchDashboard
TestAccAWSCloudWatchEventApiDestination

View File

@ -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")

View 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")

View 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"}],
}
)

View File

@ -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):

View File

@ -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