def test_should_map_event_names_with_timestamps_to_iam_actions(): assert Record('lambda', "ListVersionsByFunction20150331")._event_name_to_iam_action() == \ 'ListVersionsByFunction' assert Record('lambda', "GetFunctionConfiguration20150331v2")._event_name_to_iam_action() == \ 'GetFunctionConfiguration' assert Record('cloudfront', "UpdateDistribution2016_11_25")._event_name_to_iam_action() == \ 'UpdateDistribution'
def test_should_group_by_action_and_resource_independent_of_order(): records = [ Record("rds.amazonaws.com", "ListTagsForResource", ["arn:aws:rds:eu-central-1:111111111111:db:some-db"]), Record("rds.amazonaws.com", "SomethingDifferent", ["arn:aws:rds:eu-central-1:111111111111:db:a-third-db"]), Record("rds.amazonaws.com", "ListTagsForResource", ["arn:aws:rds:eu-central-1:111111111111:db:some-other-db"]), ] expected = PolicyDocument( Version="2012-10-17", Statement=[ Statement( Effect="Allow", Action=[ Action("rds", "ListTagsForResource"), ], Resource=[ "arn:aws:rds:eu-central-1:111111111111:db:some-db", "arn:aws:rds:eu-central-1:111111111111:db:some-other-db", ]), Statement( Effect="Allow", Action=[ Action("rds", "SomethingDifferent"), ], Resource=[ "arn:aws:rds:eu-central-1:111111111111:db:a-third-db", ]), ]) actual = generate_policy(records) assert actual == expected
def test_load_gzipped_files_in_timeframe_from_dir(): records = LocalDirectoryRecordSource(cloudtrail_data_dir()).load_from_dir( datetime.datetime(2017, 12, 1, tzinfo=pytz.utc), datetime.datetime(2017, 12, 12, tzinfo=pytz.utc)) assert records == [ Record( "autoscaling.amazonaws.com", "DescribeLaunchConfigurations", assumed_role_arn="arn:aws:iam::111111111111:role/someRole", # "2017-12-11T15:01:51Z" event_time=datetime.datetime(2017, 12, 11, 15, 1, 51, tzinfo=pytz.utc)), Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"], event_time=datetime.datetime(2017, 12, 11, 15, 4, 51, tzinfo=pytz.utc)) ]
def test_load_gzipped_files_including_those_that_were_delivered_only_an_hour_after_the_event_time_we_are_looking_for( ): records = LocalDirectoryRecordSource(cloudtrail_data_dir()).load_from_dir( datetime.datetime(2017, 12, 11, 0, 0, tzinfo=pytz.utc), datetime.datetime(2017, 12, 11, 14, 5, tzinfo=pytz.utc)) assert records == [ Record( "autoscaling.amazonaws.com", "DescribeLaunchConfigurations", assumed_role_arn="arn:aws:iam::111111111111:role/someRole", # "2017-12-11T15:01:51Z" event_time=datetime.datetime(2017, 12, 11, 15, 1, 51, tzinfo=pytz.utc)), Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"], event_time=datetime.datetime(2017, 12, 11, 15, 4, 51, tzinfo=pytz.utc)) ]
def test_parse_records_from_gzipped_file(): logfile = LogFile( cloudtrail_data( "111111111111_CloudTrail_eu-central-1_20171211T1505Z_A6kvhMoVeCsc7v8U.json.gz" )) assert logfile.records() == [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations", assumed_role_arn="arn:aws:iam::111111111111:role/someRole", event_time=datetime.datetime(2017, 12, 11, 15, 1, 51, tzinfo=pytz.utc)), Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"], event_time=datetime.datetime(2017, 12, 11, 15, 4, 51, tzinfo=pytz.utc)) ]
def unknown_actions(): iam_actions_from_api_calls = set() for api_call in all_aws_api_methods(): x = api_call.split(":") r = Record(x[0] + ".amazonaws.com", x[1]) statement = r.to_statement() if statement is not None: iam_actions_from_api_calls.add(statement.Action[0].json_repr()) known_actions = all_known_iam_permissions() return iam_actions_from_api_calls.difference(known_actions)
def test_should_convert_api_gateway_events_with_parameters_properly(): record = Record("apigateway.amazonaws.com", "UpdateMethod") expected_statment = Statement( Effect="Allow", Action=[ Action("apigateway", "PATCH"), ], Resource=["arn:aws:apigateway:*::/restapis/*/resources/*/methods/*"] ) assert record.to_statement() == expected_statment
def test_should_convert_into_iam_statement(): record = Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations") expected_statment = Statement( Effect="Allow", Action=[ Action('autoscaling', 'DescribeLaunchConfigurations'), ], Resource=["*"] ) assert record.to_statement() == expected_statment
def test_should_convert_special_event_sources_properly(): record = Record("monitoring.amazonaws.com", "DescribeLogStreams") expected_statment = Statement( Effect="Allow", Action=[ Action("cloudwatch", "DescribeLogStreams"), ], Resource=["*"] ) assert record.to_statement() == expected_statment
def test_should_convert_special_actions_properly(): record = Record("lambda", "ListVersionsByFunction20150331") expected_statment = Statement( Effect="Allow", Action=[ Action("lambda", "ListVersionsByFunction"), ], Resource=["*"] ) assert record.to_statement() == expected_statment
def test_should_convert_api_gateway_events_properly(): record = Record("apigateway.amazonaws.com", "CreateRestApi") expected_statment = Statement( Effect="Allow", Action=[ Action("apigateway", "POST"), ], Resource=["arn:aws:apigateway:*::/restapis"] ) assert record.to_statement() == expected_statment
def test_should_allow_events_that_dont_map_to_statement(): records = [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations"), Record("sts.amazonaws.com", "GetCallerIdentity") ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement(Effect="Allow", Action=[ Action('autoscaling', 'DescribeLaunchConfigurations'), ], Resource=["*"]) ])
def test_should_remove_duplicate_actions(): records = [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations"), Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations"), ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement(Effect="Allow", Action=[ Action('autoscaling', 'DescribeLaunchConfigurations'), ], Resource=["*"]) ])
def test_parse_record_should_be_able_to_cope_with_missing_type(): assert _parse_record({'userIdentity': {'accountId': '111111111111'}, 'eventSource': 'kms.amazonaws.com', 'eventName': 'DeleteKey', 'eventTime': '2017-11-19T00:21:51Z'}) == \ Record('kms.amazonaws.com', 'DeleteKey', event_time=datetime.datetime(2017, 11, 19, 0, 21, 51, tzinfo=pytz.utc))
def test_should_warn_if_records_passed_but_filtered_away(caplog): records = [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations", event_time=datetime.datetime(2017, 1, 1)), Record("sts.amazonaws.com", "AssumeRole", event_time=datetime.datetime(2017, 6, 6)) ] assert filter_records(records, from_date=datetime.datetime(2010, 1, 1), to_date=datetime.datetime(2010, 1, 2)) == [] assert caplog.record_tuples == [ ('root', logging.WARNING, cloudtrail.ALL_RECORDS_FILTERED), ]
def test_should_generate_simple_policy(): records = [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations"), Record("sts.amazonaws.com", "AssumeRole") ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement(Effect="Allow", Action=[ Action('autoscaling', 'DescribeLaunchConfigurations'), Action('sts', 'AssumeRole'), ], Resource=["*"]) ])
def test_should_filter_for_event_time(): records = [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations", event_time=datetime.datetime(2017, 1, 1)), Record("sts.amazonaws.com", "AssumeRole", event_time=datetime.datetime(2017, 6, 6)) ] assert filter_records(records, from_date=datetime.datetime(2017, 1, 1), to_date=datetime.datetime(2017, 3, 1)) == \ [ Record("autoscaling.amazonaws.com", "DescribeLaunchConfigurations", event_time=datetime.datetime(2017, 1, 1)), ]
def test_should_sort_actions_alphabetically(): records = [ Record("ec2.amazonaws.com", "DescribeSecurityGroups"), Record("rds.amazonaws.com", "ListTagsForResource"), Record("ec2.amazonaws.com", "DescribeInstances"), ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement(Effect="Allow", Action=[ Action("ec2", "DescribeInstances"), Action("ec2", "DescribeSecurityGroups"), Action("rds", "ListTagsForResource"), ], Resource=["*"]) ])
def test_parse_records_should_ignore_records_that_cant_be_parsed(): assert parse_records([{}, {'eventVersion': '1.05', 'userIdentity': {'type': 'SomeType'}, 'eventSource': 'someSource', 'eventName': 'SomeEvent', 'eventTime': '2017-11-19T00:21:51Z'}]) == \ [Record('someSource', 'SomeEvent', event_time=datetime.datetime(2017, 11, 19, 0, 21, 51, tzinfo=pytz.utc))]
def test_parse_record_should_be_able_to_cope_with_missing_session_context_in_assumed_role(): assert _parse_record({'eventVersion': '1.05', 'userIdentity': {'type': 'AssumedRole', 'principalId': 'some-key:some-user', 'arn': 'arn:aws:sts::111111111111:assumed-role/some-role/some-user', 'accountId': '111111111111'}, 'eventSource': 'signin.amazonaws.com', 'eventTime': '2017-11-19T00:21:51Z', 'eventName': 'RenewRole'}) == \ Record('signin.amazonaws.com', 'RenewRole', event_time=datetime.datetime(2017, 11, 19, 0, 21, 51, tzinfo=pytz.utc))
def test_should_group_by_resources_and_combine_statements_with_same_actions_but_different_resources( ): records = [ Record("rds.amazonaws.com", "ListTagsForResource", ["arn:aws:rds:eu-central-1:111111111111:db:some-db"]), Record("rds.amazonaws.com", "ListTagsForResource", ["arn:aws:rds:eu-central-1:111111111111:db:some-other-db"]), ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement( Effect="Allow", Action=[ Action("rds", "ListTagsForResource"), ], Resource=[ "arn:aws:rds:eu-central-1:111111111111:db:some-db", "arn:aws:rds:eu-central-1:111111111111:db:some-other-db", ]) ])
def test_should_group_by_resources(): records = [ Record("ec2.amazonaws.com", "DescribeSecurityGroups"), Record("rds.amazonaws.com", "ListTagsForResource", ["arn:aws:rds:eu-central-1:111111111111:db:some-db"]), Record("ec2.amazonaws.com", "DescribeInstances"), ] assert generate_policy(records) == PolicyDocument( Version="2012-10-17", Statement=[ Statement(Effect="Allow", Action=[ Action("ec2", "DescribeInstances"), Action("ec2", "DescribeSecurityGroups"), ], Resource=["*"]), Statement( Effect="Allow", Action=[ Action("rds", "ListTagsForResource"), ], Resource=["arn:aws:rds:eu-central-1:111111111111:db:some-db"]) ])
def test_parse_record_should_be_able_to_cope_with_missing_arn_in_resource(): assert _parse_record({'eventVersion': '1.05', 'eventTime': '2018-05-15T02:18:43Z', 'eventName': 'ListObjects', 'eventSource': 's3.amazonaws.com', 'userIdentity': {'type': 'AssumedRole', 'principalId': 'some-key:some-user', 'arn': 'arn:aws:sts::111111111111:assumed-role/some-role/some-user', 'accountId': '111111111111'}, 'resources': [ {'ARNPrefix': 'arn:aws:s3:::some-bucket/env:/', 'type': 'AWS::S3::Object'}, {'type': 'AWS::S3::Bucket', 'ARN': 'arn:aws:s3:::some-bucket', 'accountId': '201571571865'}], }) == \ Record('s3.amazonaws.com', 'ListObjects', event_time=datetime.datetime(2018, 5, 15, 2, 18, 43, tzinfo=pytz.utc), resource_arns=["arn:aws:s3:::some-bucket"])
def test_should_have_a_string_representation(): assert str(Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"], event_time=datetime.datetime(2017, 11, 19, 0, 21, 51))) == \ "Record(event_source=sts.amazonaws.com event_name=AssumeRole event_time=2017-11-19 00:21:51 " \ "resource_arns=['arn:aws:iam::111111111111:role/someRole'])"
def test_should_be_hashable(): assert hash(Record("sts.amazonaws.com", "AssumeRole")) == hash(Record("sts.amazonaws.com", "AssumeRole")) assert hash(Record("sts.amazonaws.com", "AssumeRole")) != hash(Record("sts.amazonaws.com", "AssumeRoles")) assert hash(Record("sts.amazonaws.com", "AssumeRole")) != hash(Record("ec2.amazonaws.com", "AssumeRole")) assert hash(Record("sts.amazonaws.com", "AssumeRole")) != hash(Record("ec2.amazonaws.com", "DescribeInstances"))
def test_should_fix_cors(): assert Record('s3', "PutBucketCors")._event_name_to_iam_action() == \ 'PutBucketCORS' assert Record('s3', "DeleteBucketCors")._event_name_to_iam_action() == \ 'PutBucketCORS'
def test_should_know_about_equality(): assert Record("sts.amazonaws.com", "AssumeRole") == Record("sts.amazonaws.com", "AssumeRole") assert Record("sts.amazonaws.com", "AssumeRole", []) == Record("sts.amazonaws.com", "AssumeRole") assert Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"]) == \ Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"]) assert Record("sts.amazonaws.com", "AssumeRole", assumed_role_arn="arn:aws:iam::111111111111:role/someRole") == \ Record("sts.amazonaws.com", "AssumeRole", assumed_role_arn="arn:aws:iam::111111111111:role/someRole") assert Record("sts.amazonaws.com", "AssumeRole") != Record("sts.amazonaws.com", "AssumeRoles") assert Record("sts.amazonaws.com", "AssumeRole") != Record("ec2.amazonaws.com", "AssumeRole") assert Record("sts.amazonaws.com", "AssumeRole") != Record("ec2.amazonaws.com", "DescribeInstances") assert Record("sts.amazonaws.com", "AssumeRole", resource_arns=["arn:aws:iam::111111111111:role/someRole"]) != \ Record("sts.amazonaws.com", "AssumeRole", ["arn:aws:iam::222222222222:role/someRole"]) assert Record("sts.amazonaws.com", "AssumeRole", assumed_role_arn="arn:aws:iam::111111111111:role/someRole") != \ Record("sts.amazonaws.com", "AssumeRole", assumed_role_arn="arn:aws:iam::111111111111:role/someOtherRole")
def test_should_map_normal_event_names_to_iam_actions(): assert Record('autoscaling.amazonaws.com', "DescribeAutoScalingGroups")._event_name_to_iam_action() == \ 'DescribeAutoScalingGroups'
def test_should_map_unknown_sources(): assert Record('unknown.amazonaws.com', "something")._source_to_iam_prefix() == 'unknown'
def test_should_map_special_cases_of_event_sources(): assert Record('monitoring.amazonaws.com', "something")._source_to_iam_prefix() == 'cloudwatch'