| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | """Unit tests for cloudtrail-supported APIs.""" | 
					
						
							|  |  |  | import boto3 | 
					
						
							|  |  |  | import pytest | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from botocore.exceptions import ClientError | 
					
						
							|  |  |  | from datetime import datetime | 
					
						
							|  |  |  | from moto import mock_cloudtrail, mock_s3, mock_sns | 
					
						
							| 
									
										
										
										
											2022-08-13 09:49:43 +00:00
										 |  |  | from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | from uuid import uuid4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_create_trail_without_bucket(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.create_trail( | 
					
						
							|  |  |  |             Name="mytrailname", S3BucketName="specificweirdbucketthatdoesnotexist" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "S3BucketDoesNotExistException" | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         err["Message"] | 
					
						
							|  |  |  |         == "S3 bucket specificweirdbucketthatdoesnotexist does not exist!" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @pytest.mark.parametrize( | 
					
						
							|  |  |  |     "name,message", | 
					
						
							|  |  |  |     [ | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |             "a", | 
					
						
							|  |  |  |             "Trail name too short. Minimum allowed length: 3 characters. Specified name length: 1 characters.", | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |             "aa", | 
					
						
							|  |  |  |             "Trail name too short. Minimum allowed length: 3 characters. Specified name length: 2 characters.", | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |             "a" * 129, | 
					
						
							|  |  |  |             "Trail name too long. Maximum allowed length: 128 characters. Specified name length: 129 characters.", | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         ("trail!", "Trail name must ends with a letter or number."), | 
					
						
							|  |  |  |         ( | 
					
						
							|  |  |  |             "my#trail", | 
					
						
							|  |  |  |             "Trail name or ARN can only contain uppercase letters, lowercase letters, numbers, periods (.), hyphens (-), and underscores (_).", | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         ("-trail", "Trail name must starts with a letter or number."), | 
					
						
							|  |  |  |     ], | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_create_trail_invalid_name(name, message): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.create_trail( | 
					
						
							|  |  |  |             Name=name, S3BucketName="specificweirdbucketthatdoesnotexist" | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "InvalidTrailNameException" | 
					
						
							|  |  |  |     assert err["Message"] == message | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_create_trail_simple(): | 
					
						
							|  |  |  |     bucket_name, resp, trail_name = create_trail_simple() | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["Name"] == trail_name | 
					
						
							|  |  |  |     assert resp["S3BucketName"] == bucket_name | 
					
						
							|  |  |  |     assert "S3KeyPrefix" not in resp | 
					
						
							|  |  |  |     assert "SnsTopicName" not in resp | 
					
						
							|  |  |  |     assert "SnsTopicARN" not in resp | 
					
						
							|  |  |  |     assert resp["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert resp["IsMultiRegionTrail"] is False | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         resp["TrailARN"] | 
					
						
							|  |  |  |         == f"arn:aws:cloudtrail:us-east-1:{ACCOUNT_ID}:trail/{trail_name}" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["LogFileValidationEnabled"] is False | 
					
						
							|  |  |  |     assert resp["IsOrganizationTrail"] is False | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_trail_simple(region_name="us-east-1"): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name=region_name) | 
					
						
							|  |  |  |     s3 = boto3.client("s3", region_name="us-east-1") | 
					
						
							|  |  |  |     bucket_name = str(uuid4()) | 
					
						
							|  |  |  |     s3.create_bucket(Bucket=bucket_name) | 
					
						
							|  |  |  |     trail_name = str(uuid4()) | 
					
						
							|  |  |  |     resp = client.create_trail(Name=trail_name, S3BucketName=bucket_name) | 
					
						
							|  |  |  |     return bucket_name, resp, trail_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_create_trail_multi_but_not_global(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.create_trail( | 
					
						
							|  |  |  |             Name="mytrailname", | 
					
						
							|  |  |  |             S3BucketName="non-existent", | 
					
						
							|  |  |  |             IncludeGlobalServiceEvents=False, | 
					
						
							|  |  |  |             IsMultiRegionTrail=True, | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "InvalidParameterCombinationException" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     # Note that this validation occurs before the S3 bucket is validated | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Message"] == "Multi-Region trail must include global service events." | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_create_trail_with_nonexisting_topic(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     s3 = boto3.client("s3", region_name="us-east-1") | 
					
						
							|  |  |  |     bucket_name = str(uuid4()) | 
					
						
							|  |  |  |     s3.create_bucket(Bucket=bucket_name) | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.create_trail( | 
					
						
							|  |  |  |             Name="mytrailname", | 
					
						
							|  |  |  |             S3BucketName=bucket_name, | 
					
						
							|  |  |  |             SnsTopicName="nonexistingtopic", | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "InsufficientSnsTopicPolicyException" | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         err["Message"] == "SNS Topic does not exist or the topic policy is incorrect!" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_create_trail_advanced(): | 
					
						
							|  |  |  |     bucket_name, resp, sns_topic_name, trail_name = create_trail_advanced() | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["Name"] == trail_name | 
					
						
							|  |  |  |     assert resp["S3BucketName"] == bucket_name | 
					
						
							|  |  |  |     assert resp["S3KeyPrefix"] == "s3kp" | 
					
						
							|  |  |  |     assert resp["SnsTopicName"] == sns_topic_name | 
					
						
							|  |  |  |     assert resp["SnsTopicARN"] == f"arn:aws:sns:us-east-1:{ACCOUNT_ID}:{sns_topic_name}" | 
					
						
							|  |  |  |     assert resp["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert resp["IsMultiRegionTrail"] is True | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         resp["TrailARN"] | 
					
						
							|  |  |  |         == f"arn:aws:cloudtrail:us-east-1:{ACCOUNT_ID}:trail/{trail_name}" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["LogFileValidationEnabled"] is True | 
					
						
							|  |  |  |     assert resp["IsOrganizationTrail"] is True | 
					
						
							|  |  |  |     assert resp["CloudWatchLogsLogGroupArn"] == "cwllga" | 
					
						
							|  |  |  |     assert resp["CloudWatchLogsRoleArn"] == "cwlra" | 
					
						
							|  |  |  |     assert resp["KmsKeyId"] == "kki" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def create_trail_advanced(region_name="us-east-1"): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name=region_name) | 
					
						
							|  |  |  |     s3 = boto3.client("s3", region_name="us-east-1") | 
					
						
							|  |  |  |     sns = boto3.client("sns", region_name=region_name) | 
					
						
							|  |  |  |     bucket_name = str(uuid4()) | 
					
						
							|  |  |  |     s3.create_bucket(Bucket=bucket_name) | 
					
						
							|  |  |  |     sns_topic_name = "cloudtrailtopic" | 
					
						
							|  |  |  |     sns.create_topic(Name=sns_topic_name) | 
					
						
							|  |  |  |     trail_name = str(uuid4()) | 
					
						
							|  |  |  |     resp = client.create_trail( | 
					
						
							|  |  |  |         Name=trail_name, | 
					
						
							|  |  |  |         S3BucketName=bucket_name, | 
					
						
							|  |  |  |         S3KeyPrefix="s3kp", | 
					
						
							|  |  |  |         SnsTopicName=sns_topic_name, | 
					
						
							|  |  |  |         IncludeGlobalServiceEvents=True, | 
					
						
							|  |  |  |         IsMultiRegionTrail=True, | 
					
						
							|  |  |  |         EnableLogFileValidation=True, | 
					
						
							|  |  |  |         IsOrganizationTrail=True, | 
					
						
							| 
									
										
										
										
											2022-02-19 23:45:36 -01:00
										 |  |  |         CloudWatchLogsLogGroupArn="cwllga", | 
					
						
							|  |  |  |         CloudWatchLogsRoleArn="cwlra", | 
					
						
							|  |  |  |         KmsKeyId="kki", | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |         TagsList=[{"Key": "tk", "Value": "tv"}, {"Key": "tk2", "Value": "tv2"}], | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  |     return bucket_name, resp, sns_topic_name, trail_name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_get_trail_with_one_char(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.get_trail(Name="?") | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "InvalidTrailNameException" | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         err["Message"] | 
					
						
							|  |  |  |         == "Trail name too short. Minimum allowed length: 3 characters. Specified name length: 1 characters." | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_get_trail_unknown(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.get_trail(Name="unknowntrail") | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "TrailNotFoundException" | 
					
						
							|  |  |  |     assert err["Message"] == f"Unknown trail: unknowntrail for the user: {ACCOUNT_ID}" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							| 
									
										
										
										
											2022-10-27 17:15:17 -07:00
										 |  |  | @mock_s3 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | def test_get_trail(): | 
					
						
							| 
									
										
										
										
											2022-10-27 17:15:17 -07:00
										 |  |  |     create_trail_simple() | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							| 
									
										
										
										
											2021-10-18 19:44:29 +00:00
										 |  |  |     _, _, name = create_trail_simple() | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     trail = client.get_trail(Name=name)["Trail"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert trail["Name"] == name | 
					
						
							|  |  |  |     assert trail["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert trail["IsMultiRegionTrail"] is False | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         trail["TrailARN"] == f"arn:aws:cloudtrail:us-east-1:{ACCOUNT_ID}:trail/{name}" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_get_trail_status_with_one_char(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.get_trail_status(Name="?") | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "InvalidTrailNameException" | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         err["Message"] | 
					
						
							|  |  |  |         == "Trail name too short. Minimum allowed length: 3 characters. Specified name length: 1 characters." | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | def test_get_trail_status_unknown_trail(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     with pytest.raises(ClientError) as exc: | 
					
						
							|  |  |  |         client.get_trail_status(Name="unknowntrail") | 
					
						
							|  |  |  |     err = exc.value.response["Error"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert err["Code"] == "TrailNotFoundException" | 
					
						
							|  |  |  |     assert ( | 
					
						
							|  |  |  |         err["Message"] | 
					
						
							|  |  |  |         == f"Unknown trail: arn:aws:cloudtrail:us-east-1:{ACCOUNT_ID}:trail/unknowntrail for the user: {ACCOUNT_ID}" | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_get_trail_status_inactive(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     _, _, trail_name = create_trail_simple() | 
					
						
							|  |  |  |     status = client.get_trail_status(Name=trail_name) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert status["IsLogging"] is False | 
					
						
							|  |  |  |     assert status["LatestDeliveryAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     assert status["LatestDeliveryAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     assert status["TimeLoggingStarted"] == "" | 
					
						
							|  |  |  |     assert status["TimeLoggingStopped"] == "" | 
					
						
							|  |  |  |     assert "StartLoggingTime" not in status | 
					
						
							| 
									
										
										
										
											2021-11-09 14:50:56 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_get_trail_status_arn_inactive(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     _, resp, _ = create_trail_simple() | 
					
						
							|  |  |  |     status = client.get_trail_status(Name=resp["TrailARN"]) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert status["IsLogging"] is False | 
					
						
							|  |  |  |     assert status["LatestDeliveryAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     assert status["LatestDeliveryAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     assert status["TimeLoggingStarted"] == "" | 
					
						
							|  |  |  |     assert status["TimeLoggingStopped"] == "" | 
					
						
							|  |  |  |     assert "StartLoggingTime" not in status | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_get_trail_status_after_starting(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="eu-west-3") | 
					
						
							|  |  |  |     _, _, trail_name = create_trail_simple(region_name="eu-west-3") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.start_logging(Name=trail_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     status = client.get_trail_status(Name=trail_name) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert status["IsLogging"] is True | 
					
						
							|  |  |  |     assert isinstance(status["LatestDeliveryTime"], datetime) | 
					
						
							|  |  |  |     assert isinstance(status["StartLoggingTime"], datetime) | 
					
						
							|  |  |  |     # .equal("2021-10-13T15:36:53Z") | 
					
						
							|  |  |  |     assert "LatestDeliveryAttemptTime" in status | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     # .equal("2021-10-13T15:36:53Z") | 
					
						
							|  |  |  |     assert "LatestDeliveryAttemptSucceeded" in status | 
					
						
							|  |  |  |     assert "TimeLoggingStarted" in status  # .equal("2021-10-13T15:02:21Z") | 
					
						
							|  |  |  |     assert status["TimeLoggingStopped"] == "" | 
					
						
							|  |  |  |     assert "StopLoggingTime" not in status | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_get_trail_status_after_starting_and_stopping(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="eu-west-3") | 
					
						
							|  |  |  |     _, _, trail_name = create_trail_simple(region_name="eu-west-3") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.start_logging(Name=trail_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client.stop_logging(Name=trail_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     status = client.get_trail_status(Name=trail_name) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert status["IsLogging"] is False | 
					
						
							|  |  |  |     assert isinstance(status["LatestDeliveryTime"], datetime) | 
					
						
							|  |  |  |     assert isinstance(status["StartLoggingTime"], datetime) | 
					
						
							|  |  |  |     assert isinstance(status["StopLoggingTime"], datetime) | 
					
						
							|  |  |  |     # .equal("2021-10-13T15:36:53Z") | 
					
						
							|  |  |  |     assert "LatestDeliveryAttemptTime" in status | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptTime"] == "" | 
					
						
							|  |  |  |     assert status["LatestNotificationAttemptSucceeded"] == "" | 
					
						
							|  |  |  |     # .equal("2021-10-13T15:36:53Z") | 
					
						
							|  |  |  |     assert "LatestDeliveryAttemptSucceeded" in status | 
					
						
							|  |  |  |     assert "TimeLoggingStarted" in status  # .equal("2021-10-13T15:02:21Z") | 
					
						
							|  |  |  |     assert "TimeLoggingStopped" in status  # .equal("2021-10-13T15:03:21Z") | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-18 10:14:19 +02:00
										 |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_get_trail_status_multi_region_not_from_the_home_region(): | 
					
						
							|  |  |  |     # CloudTrail client | 
					
						
							|  |  |  |     client_us_east_1 = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Create Trail | 
					
						
							|  |  |  |     _, _, _, trail_name_us_east_1 = create_trail_advanced() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Start Logging | 
					
						
							|  |  |  |     _ = client_us_east_1.start_logging(Name=trail_name_us_east_1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check Trails in the Home Region us-east-1 | 
					
						
							|  |  |  |     trails_us_east_1 = client_us_east_1.describe_trails()["trailList"] | 
					
						
							|  |  |  |     trail_arn_us_east_1 = trails_us_east_1[0]["TrailARN"] | 
					
						
							|  |  |  |     assert len(trails_us_east_1) == 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get Trail status in the Home Region us-east-1 | 
					
						
							|  |  |  |     trail_status_us_east_1 = client_us_east_1.get_trail_status(Name=trail_arn_us_east_1) | 
					
						
							|  |  |  |     assert trail_status_us_east_1["IsLogging"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Check Trails in another region eu-west-1 for a MultiRegion trail | 
					
						
							|  |  |  |     client_eu_west_1 = boto3.client("cloudtrail", region_name="eu-west-1") | 
					
						
							|  |  |  |     trails_eu_west_1 = client_eu_west_1.describe_trails()["trailList"] | 
					
						
							|  |  |  |     assert len(trails_eu_west_1) == 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get Trail status in another region eu-west-1 for a MultiRegion trail | 
					
						
							|  |  |  |     trail_status_us_east_1 = client_eu_west_1.get_trail_status(Name=trail_arn_us_east_1) | 
					
						
							|  |  |  |     assert trail_status_us_east_1["IsLogging"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  | def test_list_trails_different_home_region_one_multiregion(): | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     client = boto3.client("cloudtrail", region_name="eu-west-3") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     create_trail_simple() | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     _, trail2, _, _ = create_trail_advanced(region_name="ap-southeast-2") | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     create_trail_simple(region_name="eu-west-1") | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     all_trails = client.list_trails()["Trails"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     # Only the Trail created in the ap-southeast-2 is MultiRegion | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert all_trails == [ | 
					
						
							| 
									
										
										
										
											2021-12-24 20:02:45 -01:00
										 |  |  |         { | 
					
						
							|  |  |  |             "TrailARN": trail2["TrailARN"], | 
					
						
							|  |  |  |             "Name": trail2["Name"], | 
					
						
							|  |  |  |             "HomeRegion": "ap-southeast-2", | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     ] | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_list_trails_different_home_region_no_multiregion(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="eu-west-3") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     create_trail_simple() | 
					
						
							|  |  |  |     create_trail_simple(region_name="ap-southeast-2") | 
					
						
							|  |  |  |     create_trail_simple(region_name="eu-west-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     all_trails = client.list_trails()["Trails"] | 
					
						
							|  |  |  |     # Since there is no MultiRegion Trail created | 
					
						
							|  |  |  |     # the eu-west-3 has no Trails | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(all_trails) == 0 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_describe_trails_without_shadowtrails(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     _, trail1, _ = create_trail_simple() | 
					
						
							|  |  |  |     _, trail2, _, _ = create_trail_advanced() | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     create_trail_simple(region_name="eu-west-1") | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     # There are two Trails created in the us-east-1 | 
					
						
							|  |  |  |     # one MultiRegion and the other is not MultiRegion | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     trails = client.describe_trails()["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 2 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     first_trail = [t for t in trails if t["Name"] == trail1["Name"]][0] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert first_trail["Name"] == trail1["Name"] | 
					
						
							|  |  |  |     assert first_trail["S3BucketName"] == trail1["S3BucketName"] | 
					
						
							|  |  |  |     assert first_trail["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert first_trail["IsMultiRegionTrail"] is False | 
					
						
							|  |  |  |     assert first_trail["HomeRegion"] == "us-east-1" | 
					
						
							|  |  |  |     assert first_trail["LogFileValidationEnabled"] is False | 
					
						
							|  |  |  |     assert first_trail["HasCustomEventSelectors"] is False | 
					
						
							|  |  |  |     assert first_trail["HasInsightSelectors"] is False | 
					
						
							|  |  |  |     assert first_trail["IsOrganizationTrail"] is False | 
					
						
							|  |  |  |     assert "S3KeyPrefix" not in first_trail | 
					
						
							|  |  |  |     assert "SnsTopicName" not in first_trail | 
					
						
							|  |  |  |     assert "SnsTopicARN" not in first_trail | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     second_trail = [t for t in trails if t["Name"] == trail2["Name"]][0] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert second_trail["Name"] == trail2["Name"] | 
					
						
							|  |  |  |     assert second_trail["S3BucketName"] == trail2["S3BucketName"] | 
					
						
							|  |  |  |     assert second_trail["S3KeyPrefix"] == trail2["S3KeyPrefix"] | 
					
						
							|  |  |  |     assert second_trail["SnsTopicName"] == trail2["SnsTopicName"] | 
					
						
							|  |  |  |     assert second_trail["SnsTopicARN"] == trail2["SnsTopicARN"] | 
					
						
							|  |  |  |     assert second_trail["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert second_trail["IsMultiRegionTrail"] is True | 
					
						
							|  |  |  |     assert second_trail["HomeRegion"] == "us-east-1" | 
					
						
							|  |  |  |     assert second_trail["LogFileValidationEnabled"] is True | 
					
						
							|  |  |  |     assert second_trail["HasCustomEventSelectors"] is False | 
					
						
							|  |  |  |     assert second_trail["HasInsightSelectors"] is False | 
					
						
							|  |  |  |     assert second_trail["IsOrganizationTrail"] is True | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_describe_trails_with_shadowtrails_true(): | 
					
						
							|  |  |  |     # Same behaviour as if shadowtrails-parameter was not supplied | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							| 
									
										
										
										
											2021-10-18 19:44:29 +00:00
										 |  |  |     create_trail_simple() | 
					
						
							|  |  |  |     create_trail_advanced() | 
					
						
							|  |  |  |     create_trail_simple(region_name="eu-west-1") | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     # There are two Trails created in the us-east-1 | 
					
						
							|  |  |  |     # one MultiRegion and the other is not MultiRegion | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     trails = client.describe_trails(includeShadowTrails=True)["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 2 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-29 20:34:13 +02:00
										 |  |  |     # There are two Trails in the eu-west-1 | 
					
						
							|  |  |  |     # one MultiRegion (created in the us-east-1) | 
					
						
							|  |  |  |     # and another not MultiRegion created in the us-east-1 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  |     eu_client = boto3.client("cloudtrail", region_name="eu-west-1") | 
					
						
							|  |  |  |     trails = eu_client.describe_trails(includeShadowTrails=True)["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 2 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | @mock_sns | 
					
						
							|  |  |  | def test_describe_trails_with_shadowtrails_false(): | 
					
						
							|  |  |  |     # Only trails for the current region should now be returned | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     _, _, name1 = create_trail_simple() | 
					
						
							|  |  |  |     _, _, _, name2 = create_trail_advanced() | 
					
						
							|  |  |  |     _, _, name3 = create_trail_simple(region_name="eu-west-1") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trails = client.describe_trails(includeShadowTrails=False)["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 2 | 
					
						
							|  |  |  |     assert [t["Name"] for t in trails] == [name1, name2] | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     eu_client = boto3.client("cloudtrail", region_name="eu-west-1") | 
					
						
							|  |  |  |     trails = eu_client.describe_trails(includeShadowTrails=False)["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 1 | 
					
						
							|  |  |  |     assert [t["Name"] for t in trails] == [name3] | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @mock_cloudtrail | 
					
						
							|  |  |  | @mock_s3 | 
					
						
							|  |  |  | def test_delete_trail(): | 
					
						
							|  |  |  |     client = boto3.client("cloudtrail", region_name="us-east-1") | 
					
						
							|  |  |  |     _, _, name = create_trail_simple() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trails = client.describe_trails()["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 1 | 
					
						
							| 
									
										
										
										
											2021-10-13 22:22:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     client.delete_trail(Name=name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trails = client.describe_trails()["trailList"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert len(trails) == 0 | 
					
						
							| 
									
										
										
										
											2022-02-19 23:45:36 -01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @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) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["Name"] == name | 
					
						
							|  |  |  |     assert resp["S3BucketName"] == bucket_name | 
					
						
							|  |  |  |     assert resp["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert resp["IsMultiRegionTrail"] is False | 
					
						
							|  |  |  |     assert resp["LogFileValidationEnabled"] is False | 
					
						
							|  |  |  |     assert resp["IsOrganizationTrail"] is False | 
					
						
							|  |  |  |     assert "S3KeyPrefix" not in resp | 
					
						
							|  |  |  |     assert "SnsTopicName" not in resp | 
					
						
							|  |  |  |     assert "SnsTopicARN" not in resp | 
					
						
							| 
									
										
										
										
											2022-02-19 23:45:36 -01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     trail = client.get_trail(Name=name)["Trail"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert trail["Name"] == name | 
					
						
							|  |  |  |     assert trail["S3BucketName"] == bucket_name | 
					
						
							|  |  |  |     assert trail["IncludeGlobalServiceEvents"] is True | 
					
						
							|  |  |  |     assert trail["IsMultiRegionTrail"] is False | 
					
						
							|  |  |  |     assert trail["LogFileValidationEnabled"] is False | 
					
						
							|  |  |  |     assert trail["IsOrganizationTrail"] is False | 
					
						
							|  |  |  |     assert "S3KeyPrefix" not in trail | 
					
						
							|  |  |  |     assert "SnsTopicName" not in trail | 
					
						
							|  |  |  |     assert "SnsTopicARN" not in trail | 
					
						
							| 
									
										
										
										
											2022-02-19 23:45:36 -01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @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, | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert resp["Name"] == name | 
					
						
							|  |  |  |     assert resp["S3BucketName"] == "updated_bucket" | 
					
						
							|  |  |  |     assert resp["S3KeyPrefix"] == "s3kp" | 
					
						
							|  |  |  |     assert resp["SnsTopicName"] == "stn" | 
					
						
							|  |  |  |     assert resp["IncludeGlobalServiceEvents"] is False | 
					
						
							|  |  |  |     assert resp["IsMultiRegionTrail"] is True | 
					
						
							|  |  |  |     assert resp["LogFileValidationEnabled"] is True | 
					
						
							|  |  |  |     assert resp["IsOrganizationTrail"] is True | 
					
						
							| 
									
										
										
										
											2022-02-19 23:45:36 -01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     trail = client.get_trail(Name=name)["Trail"] | 
					
						
							| 
									
										
										
										
											2023-07-06 14:48:33 +00:00
										 |  |  |     assert trail["Name"] == name | 
					
						
							|  |  |  |     assert trail["S3BucketName"] == "updated_bucket" | 
					
						
							|  |  |  |     assert trail["S3KeyPrefix"] == "s3kp" | 
					
						
							|  |  |  |     assert trail["SnsTopicName"] == "stn" | 
					
						
							|  |  |  |     assert trail["IncludeGlobalServiceEvents"] is False | 
					
						
							|  |  |  |     assert trail["IsMultiRegionTrail"] is True | 
					
						
							|  |  |  |     assert trail["LogFileValidationEnabled"] is True | 
					
						
							|  |  |  |     assert trail["IsOrganizationTrail"] is True | 
					
						
							|  |  |  |     assert trail["CloudWatchLogsLogGroupArn"] == "cwllga" | 
					
						
							|  |  |  |     assert trail["CloudWatchLogsRoleArn"] == "cwlra" | 
					
						
							|  |  |  |     assert trail["KmsKeyId"] == "kki" |