Support optional Source, parse from header
The Email ``from`` header is either formatted as ``name <address>`` or ``address``. This commit will use `parseaddr` to extract a ``(name, address)`` tuple, which we will use the ``address`` to check if it's verified. Also support the case where ``Source`` is omitted (which AWS requires the ``from`` header to be set).
This commit is contained in:
		
							parent
							
								
									cb364eedc6
								
							
						
					
					
						commit
						d21c387eb6
					
				| @ -1,6 +1,7 @@ | ||||
| from __future__ import unicode_literals | ||||
| 
 | ||||
| import email | ||||
| from email.utils import parseaddr | ||||
| 
 | ||||
| from moto.core import BaseBackend, BaseModel | ||||
| from .exceptions import MessageRejectedError | ||||
| @ -84,13 +85,27 @@ class SESBackend(BaseBackend): | ||||
|         return message | ||||
| 
 | ||||
|     def send_raw_email(self, source, destinations, raw_data): | ||||
|         if source not in self.addresses: | ||||
|             raise MessageRejectedError( | ||||
|                 "Did not have authority to send from email %s" % source | ||||
|             ) | ||||
|         if source is not None: | ||||
|             _, source_email_address = parseaddr(source) | ||||
|             if source_email_address not in self.addresses: | ||||
|                 raise MessageRejectedError( | ||||
|                     "Did not have authority to send from email %s" % source_email_address | ||||
|                 ) | ||||
| 
 | ||||
|         recipient_count = len(destinations) | ||||
|         message = email.message_from_string(raw_data) | ||||
|         if source is None: | ||||
|             if message['from'] is None: | ||||
|                 raise MessageRejectedError( | ||||
|                     "Source not specified" | ||||
|                 ) | ||||
| 
 | ||||
|             _, source_email_address = parseaddr(message['from']) | ||||
|             if source_email_address not in self.addresses: | ||||
|                 raise MessageRejectedError( | ||||
|                     "Did not have authority to send from email %s" % source_email_address | ||||
|                 ) | ||||
| 
 | ||||
|         for header in 'TO', 'CC', 'BCC': | ||||
|             recipient_count += sum( | ||||
|                 d.strip() and 1 or 0 | ||||
|  | ||||
| @ -75,7 +75,10 @@ class EmailResponse(BaseResponse): | ||||
|         return template.render(message=message) | ||||
| 
 | ||||
|     def send_raw_email(self): | ||||
|         source = self.querystring.get('Source')[0] | ||||
|         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) | ||||
|         if six.PY3: | ||||
|  | ||||
| @ -136,3 +136,59 @@ def test_send_raw_email(): | ||||
|     send_quota = conn.get_send_quota() | ||||
|     sent_count = int(send_quota['SentLast24Hours']) | ||||
|     sent_count.should.equal(2) | ||||
| 
 | ||||
| 
 | ||||
| @mock_ses | ||||
| def test_send_raw_email_without_source(): | ||||
|     conn = boto3.client('ses', region_name='us-east-1') | ||||
| 
 | ||||
|     message = MIMEMultipart() | ||||
|     message['Subject'] = 'Test' | ||||
|     message['From'] = 'test@example.com' | ||||
|     message['To'] = 'to@example.com, foo@example.com' | ||||
| 
 | ||||
|     # Message body | ||||
|     part = MIMEText('test file attached') | ||||
|     message.attach(part) | ||||
| 
 | ||||
|     # Attachment | ||||
|     part = MIMEText('contents of test file here') | ||||
|     part.add_header('Content-Disposition', 'attachment; filename=test.txt') | ||||
|     message.attach(part) | ||||
| 
 | ||||
|     kwargs = dict( | ||||
|         RawMessage={'Data': message.as_string()}, | ||||
|     ) | ||||
| 
 | ||||
|     conn.send_raw_email.when.called_with(**kwargs).should.throw(ClientError) | ||||
| 
 | ||||
|     conn.verify_email_identity(EmailAddress="test@example.com") | ||||
|     conn.send_raw_email(**kwargs) | ||||
| 
 | ||||
|     send_quota = conn.get_send_quota() | ||||
|     sent_count = int(send_quota['SentLast24Hours']) | ||||
|     sent_count.should.equal(2) | ||||
| 
 | ||||
| 
 | ||||
| @mock_ses | ||||
| def test_send_raw_email_without_source_or_from(): | ||||
|     conn = boto3.client('ses', region_name='us-east-1') | ||||
| 
 | ||||
|     message = MIMEMultipart() | ||||
|     message['Subject'] = 'Test' | ||||
|     message['To'] = 'to@example.com, foo@example.com' | ||||
| 
 | ||||
|     # Message body | ||||
|     part = MIMEText('test file attached') | ||||
|     message.attach(part) | ||||
|     # Attachment | ||||
|     part = MIMEText('contents of test file here') | ||||
|     part.add_header('Content-Disposition', 'attachment; filename=test.txt') | ||||
|     message.attach(part) | ||||
| 
 | ||||
|     kwargs = dict( | ||||
|         RawMessage={'Data': message.as_string()}, | ||||
|     ) | ||||
| 
 | ||||
|     conn.send_raw_email.when.called_with(**kwargs).should.throw(ClientError) | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user