Skip to content

Commit 145c11f

Browse files
authored
aws - ses - identity has-statement filter (cloud-custodian#8640)
1 parent 32d8cc6 commit 145c11f

4 files changed

Lines changed: 168 additions & 1 deletion

File tree

c7n/resources/ses.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# Copyright The Cloud Custodian Authors.
22
# SPDX-License-Identifier: Apache-2.0
3+
import json
4+
35
from c7n.actions import BaseAction
6+
import c7n.filters.policystatement as polstmt_filter
47
from c7n.manager import resources
58
from c7n.query import DescribeSource, QueryResourceManager, TypeInfo
6-
from c7n.utils import local_session, type_schema
9+
from c7n.utils import local_session, type_schema, format_string_values
710
from c7n.tags import universal_augment
811

912

@@ -88,3 +91,54 @@ class resource_type(TypeInfo):
8891
permission_prefix = 'ses'
8992
arn_service = 'ses'
9093
cfn_type = 'AWS::SES::EmailIdentity'
94+
95+
96+
@SESEmailIdentity.filter_registry.register('has-statement')
97+
class HasStatementFilter(polstmt_filter.HasStatementFilter):
98+
99+
def __init__(self, data, manager=None):
100+
super().__init__(data, manager)
101+
self.policy_attribute = 'Policies'
102+
103+
def get_std_format_args(self, email_identity):
104+
return {
105+
'account_id': self.manager.config.account_id,
106+
'region': self.manager.config.region,
107+
'email_identity_name': email_identity['IdentityName'],
108+
}
109+
110+
def process_resource(self, email_identity):
111+
policies = email_identity.get(self.policy_attribute)
112+
if not policies:
113+
return None
114+
115+
for policy in policies.values():
116+
p = json.loads(policy)
117+
118+
required = list(self.data.get('statement_ids', []))
119+
statements = p.get('Statement', [])
120+
for s in list(statements):
121+
if s.get('Sid') in required:
122+
required.remove(s['Sid'])
123+
124+
required_statements = format_string_values(list(self.data.get('statements', [])),
125+
**self.get_std_format_args(email_identity))
126+
127+
for required_statement in required_statements:
128+
for statement in statements:
129+
found = 0
130+
for key, value in required_statement.items():
131+
if key in ['Action', 'NotAction']:
132+
if key in statement and self.action_resource_case_insensitive(value) \
133+
== self.action_resource_case_insensitive(statement[key]):
134+
found += 1
135+
else:
136+
if key in statement and value == statement[key]:
137+
found += 1
138+
if found and found == len(required_statement):
139+
required_statements.remove(required_statement)
140+
break
141+
142+
if (self.data.get('statement_ids', []) and not required) or \
143+
(self.data.get('statements', []) and not required_statements):
144+
return email_identity
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"ResponseMetadata": {},
5+
"IdentityType": "EMAIL_ADDRESS",
6+
"FeedbackForwardingStatus": true,
7+
"VerifiedForSendingStatus": false,
8+
"DkimAttributes": {
9+
"SigningEnabled": false,
10+
"Status": "NOT_STARTED",
11+
"SigningAttributesOrigin": "AWS_SES",
12+
"NextSigningKeyLength": "RSA_1024_BIT"
13+
},
14+
"MailFromAttributes": {
15+
"BehaviorOnMxFailure": "USE_DEFAULT_VALUE"
16+
},
17+
"Policies": {
18+
"Policy2": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"AllowStatement\",\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"ses:GetEmailIdentity\",\"Resource\":\"arn:aws:ses:us-west-2:644160558196:identity/c7n@t.com\",\"Condition\":{}}]}",
19+
"PolicyTestHasStatement": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"stmt1686693628910\",\"Effect\":\"Deny\",\"Principal\":{\"AWS\":\"*\"},\"Action\":\"ses:SendEmail\",\"Resource\":\"arn:aws:ses:us-west-2:644160558196:identity/c7n@t.com\",\"Condition\":{\"StringNotEquals\":{\"ses:FromAddress\":\"*test*\"}}},{\"Sid\":\"DenyStatement\",\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"ses:SendEmail\",\"Resource\":\"arn:aws:ses:us-west-2:644160558196:identity/c7n@t.com\",\"Condition\":{\"StringNotLike\":{\"ses:FromAddress\":\"*test*\"}}}]}"
20+
},
21+
"Tags": [],
22+
"VerificationStatus": "PENDING"
23+
}
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"status_code": 200,
3+
"data": {
4+
"ResponseMetadata": {},
5+
"EmailIdentities": [
6+
{
7+
"IdentityType": "EMAIL_ADDRESS",
8+
"IdentityName": "c7n@t.com",
9+
"SendingEnabled": false,
10+
"VerificationStatus": "PENDING"
11+
}
12+
]
13+
}
14+
}

tests/test_ses.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,78 @@ def test_ses_email_identity_query(self):
5656
)
5757
resources = p.run()
5858
self.assertEqual(len(resources), 1)
59+
60+
def test_ses_email_identity_has_statement_definition(self):
61+
session_factory = self.replay_flight_data("test_ses_email_identity_has_statement")
62+
p = self.load_policy(
63+
{
64+
"name": "test_ses_email_identity_has_statement_definition",
65+
"resource": "ses-email-identity",
66+
"filters": [
67+
{
68+
"type": "has-statement",
69+
"statements": [
70+
{
71+
"Effect": "Deny",
72+
"Action": "ses:SendEmail",
73+
"Principal": {"AWS": "*"},
74+
"Condition":
75+
{"StringNotEquals": {"ses:FromAddress": "*test*"}},
76+
"Resource": "arn:aws:ses:us-west-2:644160558196:identity/c7n@t.com"
77+
}
78+
]
79+
}
80+
],
81+
}, session_factory=session_factory,
82+
config={'region': 'us-west-2'},
83+
)
84+
resources = p.run()
85+
self.assertEqual(1, len(resources))
86+
self.assertEqual(resources[0]["IdentityName"], "c7n@t.com")
87+
88+
def test_ses_email_identity_has_statement_star_definition(self):
89+
session_factory = self.replay_flight_data("test_ses_email_identity_has_statement")
90+
p = self.load_policy(
91+
{
92+
"name": "test_ses_email_identity_has_statement_star_definition",
93+
"resource": "ses-email-identity",
94+
"filters": [
95+
{
96+
"type": "has-statement",
97+
"statements": [
98+
{
99+
"Effect": "Deny",
100+
"Action": "ses:SendEmail",
101+
"Principal": "*",
102+
"Condition":
103+
{"StringNotLike": {"ses:FromAddress": "*test*"}},
104+
"Resource": "arn:aws:ses:us-west-2:644160558196:identity/c7n@t.com"
105+
}
106+
]
107+
}
108+
],
109+
}, session_factory=session_factory,
110+
config={'region': 'us-west-2'},
111+
)
112+
resources = p.run()
113+
self.assertEqual(1, len(resources))
114+
self.assertEqual(resources[0]["IdentityName"], "c7n@t.com")
115+
116+
def test_ses_email_identity_has_statement_id(self):
117+
session_factory = self.replay_flight_data("test_ses_email_identity_has_statement")
118+
p = self.load_policy(
119+
{
120+
"name": "test_ses_email_identity_has_statement_id",
121+
"resource": "ses-email-identity",
122+
"filters": [
123+
{
124+
"type": "has-statement",
125+
"statement_ids": ["AllowStatement"]
126+
}
127+
],
128+
}, session_factory=session_factory,
129+
config={'region': 'us-west-2'},
130+
)
131+
resources = p.run()
132+
self.assertEqual(1, len(resources))
133+
self.assertEqual(resources[0]["IdentityName"], "c7n@t.com")

0 commit comments

Comments
 (0)