import base64
from moto.core.responses import BaseResponse
from .models import ses_backends
from datetime import datetime
class EmailResponse(BaseResponse):
def __init__(self):
super().__init__(service_name="ses")
@property
def backend(self):
return ses_backends[self.current_account]["global"]
def verify_email_identity(self):
address = self.querystring.get("EmailAddress")[0]
self.backend.verify_email_identity(address)
template = self.response_template(VERIFY_EMAIL_IDENTITY)
return template.render()
def verify_email_address(self):
address = self.querystring.get("EmailAddress")[0]
self.backend.verify_email_address(address)
template = self.response_template(VERIFY_EMAIL_ADDRESS)
return template.render()
def list_identities(self):
identities = self.backend.list_identities()
template = self.response_template(LIST_IDENTITIES_RESPONSE)
return template.render(identities=identities)
def list_verified_email_addresses(self):
email_addresses = self.backend.list_verified_email_addresses()
template = self.response_template(LIST_VERIFIED_EMAIL_RESPONSE)
return template.render(email_addresses=email_addresses)
def verify_domain_dkim(self):
domain = self.querystring.get("Domain")[0]
self.backend.verify_domain(domain)
template = self.response_template(VERIFY_DOMAIN_DKIM_RESPONSE)
return template.render()
def verify_domain_identity(self):
domain = self.querystring.get("Domain")[0]
self.backend.verify_domain(domain)
template = self.response_template(VERIFY_DOMAIN_IDENTITY_RESPONSE)
return template.render()
def delete_identity(self):
domain = self.querystring.get("Identity")[0]
self.backend.delete_identity(domain)
template = self.response_template(DELETE_IDENTITY_RESPONSE)
return template.render()
def send_email(self):
bodydatakey = "Message.Body.Text.Data"
if "Message.Body.Html.Data" in self.querystring:
bodydatakey = "Message.Body.Html.Data"
body = self.querystring.get(bodydatakey)[0]
source = self.querystring.get("Source")[0]
subject = self.querystring.get("Message.Subject.Data")[0]
destinations = {"ToAddresses": [], "CcAddresses": [], "BccAddresses": []}
for dest_type in destinations:
# consume up to 51 to allow exception
for i in range(1, 52):
field = f"Destination.{dest_type}.member.{i}"
address = self.querystring.get(field)
if address is None:
break
destinations[dest_type].append(address[0])
message = self.backend.send_email(
source, subject, body, destinations, self.region
)
template = self.response_template(SEND_EMAIL_RESPONSE)
return template.render(message=message)
def send_templated_email(self):
source = self.querystring.get("Source")[0]
template = self.querystring.get("Template")
template_data = self.querystring.get("TemplateData")
destinations = {"ToAddresses": [], "CcAddresses": [], "BccAddresses": []}
for dest_type in destinations:
# consume up to 51 to allow exception
for i in range(1, 52):
field = f"Destination.{dest_type}.member.{i}"
address = self.querystring.get(field)
if address is None:
break
destinations[dest_type].append(address[0])
message = self.backend.send_templated_email(
source, template, template_data, destinations, self.region
)
template = self.response_template(SEND_TEMPLATED_EMAIL_RESPONSE)
return template.render(message=message)
def send_bulk_templated_email(self):
source = self.querystring.get("Source")[0]
template = self.querystring.get("Template")
template_data = self.querystring.get("DefaultTemplateData")
destinations = []
for i in range(1, 52):
destination_field = (
f"Destinations.member.{i}.Destination.ToAddresses.member.1"
)
if self.querystring.get(destination_field) is None:
break
destination = {"ToAddresses": [], "CcAddresses": [], "BccAddresses": []}
for dest_type in destination:
# consume up to 51 to allow exception
for j in range(1, 52):
field = (
f"Destinations.member.{i}.Destination.{dest_type}.member.{j}"
)
address = self.querystring.get(field)
if address is None:
break
destination[dest_type].append(address[0])
destinations.append({"Destination": destination})
message = self.backend.send_bulk_templated_email(
source, template, template_data, destinations, self.region
)
template = self.response_template(SEND_BULK_TEMPLATED_EMAIL_RESPONSE)
result = template.render(message=message)
return result
def send_raw_email(self):
source = self.querystring.get("Source")
if source is not None:
(source,) = source
raw_data = self.querystring.get("RawMessage.Data")[0]
raw_data = base64.b64decode(raw_data)
raw_data = raw_data.decode("utf-8")
destinations = []
# consume up to 51 to allow exception
for i in range(1, 52):
field = f"Destinations.member.{i}"
address = self.querystring.get(field)
if address is None:
break
destinations.append(address[0])
message = self.backend.send_raw_email(
source, destinations, raw_data, self.region
)
template = self.response_template(SEND_RAW_EMAIL_RESPONSE)
return template.render(message=message)
def get_send_quota(self):
quota = self.backend.get_send_quota()
template = self.response_template(GET_SEND_QUOTA_RESPONSE)
return template.render(quota=quota)
def get_identity_notification_attributes(self):
identities = self._get_params()["Identities"]
identities = self.backend.get_identity_notification_attributes(identities)
template = self.response_template(GET_IDENTITY_NOTIFICATION_ATTRIBUTES)
return template.render(identities=identities)
def set_identity_feedback_forwarding_enabled(self):
identity = self._get_param("Identity")
enabled = self._get_bool_param("ForwardingEnabled")
self.backend.set_identity_feedback_forwarding_enabled(identity, enabled)
template = self.response_template(SET_IDENTITY_FORWARDING_ENABLED_RESPONSE)
return template.render()
def set_identity_notification_topic(self):
identity = self.querystring.get("Identity")[0]
not_type = self.querystring.get("NotificationType")[0]
sns_topic = self.querystring.get("SnsTopic")
if sns_topic:
sns_topic = sns_topic[0]
self.backend.set_identity_notification_topic(identity, not_type, sns_topic)
template = self.response_template(SET_IDENTITY_NOTIFICATION_TOPIC_RESPONSE)
return template.render()
def get_send_statistics(self):
statistics = self.backend.get_send_statistics()
template = self.response_template(GET_SEND_STATISTICS)
return template.render(all_statistics=[statistics])
def create_configuration_set(self):
configuration_set_name = self.querystring.get("ConfigurationSet.Name")[0]
self.backend.create_configuration_set(
configuration_set_name=configuration_set_name
)
template = self.response_template(CREATE_CONFIGURATION_SET)
return template.render()
def describe_configuration_set(self):
configuration_set_name = self.querystring.get("ConfigurationSetName")[0]
self.backend.describe_configuration_set(configuration_set_name)
template = self.response_template(DESCRIBE_CONFIGURATION_SET)
return template.render(name=configuration_set_name)
def create_configuration_set_event_destination(self):
configuration_set_name = self._get_param("ConfigurationSetName")
is_configuration_event_enabled = self.querystring.get(
"EventDestination.Enabled"
)[0]
configuration_event_name = self.querystring.get("EventDestination.Name")[0]
event_topic_arn = self.querystring.get(
"EventDestination.SNSDestination.TopicARN"
)[0]
event_matching_types = self._get_multi_param(
"EventDestination.MatchingEventTypes.member"
)
event_destination = {
"Name": configuration_event_name,
"Enabled": is_configuration_event_enabled,
"EventMatchingTypes": event_matching_types,
"SNSDestination": event_topic_arn,
}
self.backend.create_configuration_set_event_destination(
configuration_set_name=configuration_set_name,
event_destination=event_destination,
)
template = self.response_template(CREATE_CONFIGURATION_SET_EVENT_DESTINATION)
return template.render()
def create_template(self):
template_data = self._get_dict_param("Template")
template_info = {}
template_info["text_part"] = template_data.get("._text_part", "")
template_info["html_part"] = template_data.get("._html_part", "")
template_info["template_name"] = template_data.get("._name", "")
template_info["subject_part"] = template_data.get("._subject_part", "")
template_info["Timestamp"] = datetime.utcnow()
self.backend.add_template(template_info=template_info)
template = self.response_template(CREATE_TEMPLATE)
return template.render()
def update_template(self):
template_data = self._get_dict_param("Template")
template_info = {}
template_info["text_part"] = template_data.get("._text_part", "")
template_info["html_part"] = template_data.get("._html_part", "")
template_info["template_name"] = template_data.get("._name", "")
template_info["subject_part"] = template_data.get("._subject_part", "")
template_info["Timestamp"] = datetime.utcnow()
self.backend.update_template(template_info=template_info)
template = self.response_template(UPDATE_TEMPLATE)
return template.render()
def get_template(self):
template_name = self._get_param("TemplateName")
template_data = self.backend.get_template(template_name)
template = self.response_template(GET_TEMPLATE)
return template.render(template_data=template_data)
def list_templates(self):
email_templates = self.backend.list_templates()
template = self.response_template(LIST_TEMPLATES)
return template.render(templates=email_templates)
def test_render_template(self):
render_info = self._get_dict_param("Template")
rendered_template = self.backend.render_template(render_info)
template = self.response_template(RENDER_TEMPLATE)
return template.render(template=rendered_template)
def create_receipt_rule_set(self):
rule_set_name = self._get_param("RuleSetName")
self.backend.create_receipt_rule_set(rule_set_name)
template = self.response_template(CREATE_RECEIPT_RULE_SET)
return template.render()
def create_receipt_rule(self):
rule_set_name = self._get_param("RuleSetName")
rule = self._get_dict_param("Rule.")
self.backend.create_receipt_rule(rule_set_name, rule)
template = self.response_template(CREATE_RECEIPT_RULE)
return template.render()
def describe_receipt_rule_set(self):
rule_set_name = self._get_param("RuleSetName")
rule_set = self.backend.describe_receipt_rule_set(rule_set_name)
for i, rule in enumerate(rule_set):
formatted_rule = {}
for k, v in rule.items():
self._parse_param(k, v, formatted_rule)
rule_set[i] = formatted_rule
template = self.response_template(DESCRIBE_RECEIPT_RULE_SET)
return template.render(rule_set=rule_set, rule_set_name=rule_set_name)
def describe_receipt_rule(self):
rule_set_name = self._get_param("RuleSetName")
rule_name = self._get_param("RuleName")
receipt_rule = self.backend.describe_receipt_rule(rule_set_name, rule_name)
rule = {}
for k, v in receipt_rule.items():
self._parse_param(k, v, rule)
template = self.response_template(DESCRIBE_RECEIPT_RULE)
return template.render(rule=rule)
def update_receipt_rule(self):
rule_set_name = self._get_param("RuleSetName")
rule = self._get_dict_param("Rule.")
self.backend.update_receipt_rule(rule_set_name, rule)
template = self.response_template(UPDATE_RECEIPT_RULE)
return template.render()
def set_identity_mail_from_domain(self):
identity = self._get_param("Identity")
mail_from_domain = self._get_param("MailFromDomain")
behavior_on_mx_failure = self._get_param("BehaviorOnMXFailure")
self.backend.set_identity_mail_from_domain(
identity, mail_from_domain, behavior_on_mx_failure
)
template = self.response_template(SET_IDENTITY_MAIL_FROM_DOMAIN)
return template.render()
def get_identity_mail_from_domain_attributes(self):
identities = self._get_multi_param("Identities.member.")
identities = self.backend.get_identity_mail_from_domain_attributes(identities)
template = self.response_template(GET_IDENTITY_MAIL_FROM_DOMAIN_ATTRIBUTES)
return template.render(identities=identities)
def get_identity_verification_attributes(self):
params = self._get_params()
identities = params.get("Identities")
verification_attributes = self.backend.get_identity_verification_attributes(
identities=identities,
)
template = self.response_template(GET_IDENTITY_VERIFICATION_ATTRIBUTES_TEMPLATE)
return template.render(verification_attributes=verification_attributes)
VERIFY_EMAIL_IDENTITY = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
VERIFY_EMAIL_ADDRESS = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
LIST_IDENTITIES_RESPONSE = """
{% for identity in identities %}
{{ identity }}
{% endfor %}
cacecf23-9bf1-11e1-9279-0100e8cf109a
"""
LIST_VERIFIED_EMAIL_RESPONSE = """
{% for email in email_addresses %}
{{ email }}
{% endfor %}
cacecf23-9bf1-11e1-9279-0100e8cf109a
"""
VERIFY_DOMAIN_DKIM_RESPONSE = """
vvjuipp74whm76gqoni7qmwwn4w4qusjiainivf6sf
3frqe7jn4obpuxjpwpolz6ipb3k5nvt2nhjpik2oy
wrqplteh7oodxnad7hsl4mixg2uavzneazxv5sxi2
9662c15b-c469-11e1-99d1-797d6ecd6414
"""
VERIFY_DOMAIN_IDENTITY_RESPONSE = """\
QTKknzFg2J4ygwa+XvHAxUl1hyHoY0gVfZdfjIedHZ0=
94f6368e-9bf2-11e1-8ee7-c98a0037a2b6
"""
DELETE_IDENTITY_RESPONSE = """
d96bd874-9bf2-11e1-8ee7-c98a0037a2b6
"""
SEND_EMAIL_RESPONSE = """
{{ message.id }}
d5964849-c866-11e0-9beb-01a62d68c57f
"""
SEND_TEMPLATED_EMAIL_RESPONSE = """
{{ message.id }}
d5964849-c866-11e0-9beb-01a62d68c57f
"""
SEND_BULK_TEMPLATED_EMAIL_RESPONSE = """
{% for id in message.ids %}
{{ id }}
{% endfor %}
d5964849-c866-11e0-9beb-01a62d68c57f
"""
SEND_RAW_EMAIL_RESPONSE = """
{{ message.id }}
e0abcdfa-c866-11e0-b6d0-273d09173b49
"""
GET_SEND_QUOTA_RESPONSE = """
{{ quota.sent_past_24 }}
200.0
1.0
273021c6-c866-11e0-b926-699e21c3af9e
"""
GET_IDENTITY_NOTIFICATION_ATTRIBUTES = """
{% for identity, config in identities.items() %}
{{ identity }}
false
false
false
{% if config.get("feedback_forwarding_enabled", True) == False %}
false
{% else %}
true
{% endif %}
{% endfor %}
46c90cfc-9055-4b84-96e3-4d6a309a8b9b
"""
SET_IDENTITY_FORWARDING_ENABLED_RESPONSE = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
SET_IDENTITY_NOTIFICATION_TOPIC_RESPONSE = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
GET_SEND_STATISTICS = """
{% for statistics in all_statistics %}
{{ statistics["DeliveryAttempts"] }}
{{ statistics["Rejects"] }}
{{ statistics["Bounces"] }}
{{ statistics["Complaints"] }}
{{ statistics["Timestamp"].isoformat() }}
{% endfor %}
e0abcdfa-c866-11e0-b6d0-273d09173z49
"""
CREATE_CONFIGURATION_SET = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
DESCRIBE_CONFIGURATION_SET = """
{{ name }}
8e410745-c1bd-4450-82e0-f968cf2105f2
"""
CREATE_CONFIGURATION_SET_EVENT_DESTINATION = """
67e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
CREATE_TEMPLATE = """
47e0ef1a-9bf2-11e1-9279-0100e8cf12ba
"""
UPDATE_TEMPLATE = """
47e0ef1a-9bf2-11e1-9279-0100e8cf12ba
"""
GET_TEMPLATE = """
{{ template_data["template_name"] }}
{{ template_data["subject_part"] }}
{{ template_data["text_part"] }}
47e0ef1a-9bf2-11e1-9279-0100e8cf12ba
"""
LIST_TEMPLATES = """
{% for template in templates %}
-
{{ template["template_name"] }}
{{ template["Timestamp"] }}
{% endfor %}
47e0ef1a-9bf2-11e1-9279-0100e8cf12ba
"""
RENDER_TEMPLATE = """
{{template | e}}
47e0ef1a-9bf2-11e1-9279-0100e8cf12ba
"""
CREATE_RECEIPT_RULE_SET = """
47e0ef1a-9bf2-11e1-9279-01ab88cf109a
"""
CREATE_RECEIPT_RULE = """
15e0ef1a-9bf2-11e1-9279-01ab88cf109a
"""
DESCRIBE_RECEIPT_RULE_SET = """
{% for rule in rule_set %}
{% for recipient in rule["recipients"] %}
{{recipient}}
{% endfor %}
{{rule["name"]}}
{% for action in rule["actions"] %}
{% if action["_s3_action"] %}
{{action["_s3_action"]["_bucket_name"]}}
{{action["_s3_action"]["_kms_key_arn"]}}
{{action["_s3_action"]["_object_key_prefix"]}}
{{action["_s3_action"]["_topic_arn"]}}
{% endif %}
{% if action["_bounce_action"] %}
{{action["_bounce_action"]["_topic_arn"]}}
{{action["_bounce_action"]["_smtp_reply_code"]}}
{{action["_bounce_action"]["_status_code"]}}
{{action["_bounce_action"]["_message"]}}
{{action["_bounce_action"]["_sender"]}}
{% endif %}
{% endfor %}
{{rule["tls_policy"]}}
{{rule["scan_enabled"]}}
{{rule["enabled"]}}
{% endfor %}
{{rule_set_name}}
2021-10-31
15e0ef1a-9bf2-11e1-9279-01ab88cf109a
"""
DESCRIBE_RECEIPT_RULE = """
{% for recipient in rule["recipients"] %}
{{recipient}}
{% endfor %}
{{rule["name"]}}
{% for action in rule["actions"] %}
{% if action["_s3_action"] %}
{{action["_s3_action"]["_bucket_name"]}}
{{action["_s3_action"]["_kms_key_arn"]}}
{{action["_s3_action"]["_object_key_prefix"]}}
{{action["_s3_action"]["_topic_arn"]}}
{% endif %}
{% if action["_bounce_action"] %}
{{action["_bounce_action"]["_topic_arn"]}}
{{action["_bounce_action"]["_smtp_reply_code"]}}
{{action["_bounce_action"]["_status_code"]}}
{{action["_bounce_action"]["_message"]}}
{{action["_bounce_action"]["_sender"]}}
{% endif %}
{% endfor %}
{{rule["tls_policy"]}}
{{rule["scan_enabled"]}}
{{rule["enabled"]}}
15e0ef1a-9bf2-11e1-9279-01ab88cf109a
"""
UPDATE_RECEIPT_RULE = """
15e0ef1a-9bf2-11e1-9279-01ab88cf109a
"""
SET_IDENTITY_MAIL_FROM_DOMAIN = """
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
GET_IDENTITY_MAIL_FROM_DOMAIN_ATTRIBUTES = """
{% if identities.items()|length > 0 %}
{% for name, value in identities.items() %}
{{ name }}
{% if 'mail_from_domain' in value %}
{{ value.get("mail_from_domain") }}
Success
{% endif %}
{{ value.get("behavior_on_mx_failure") }}
{% endfor %}
{% else %}
{% endif %}
47e0ef1a-9bf2-11e1-9279-0100e8cf109a
"""
GET_IDENTITY_VERIFICATION_ATTRIBUTES_TEMPLATE = """
{% for name, value in verification_attributes.items() %}
{{ name }}
{{ value }}
ILQMESfEW0p6i6gIJcEWvO65TP5hg6B99hGFZ2lxrIs=
{% endfor %}
d435c1b8-a225-4b89-acff-81fcf7ef9236
"""