def log_email_event(request, secret): # From Amazon SNS: # https://docs.aws.amazon.com/ses/latest/DeveloperGuide/event-publishing-retrieving-sns-examples.html if secret != settings.SNS_EMAIL_EVENT_SECRET: return HttpResponse("Incorrect secret", status=403, content_type='text/plain') request_json = json.loads(request.body) if request_json['Type'] == "SubscriptionConfirmation": # When creating an SNS topic, the first message is a subscription # confirmation, where we need to access the subscribe URL to confirm we # are able to receive messages at this endpoint subscribe_url = request_json['SubscribeURL'] requests.get(subscribe_url) return HttpResponse() message = json.loads(request_json['Message']) headers = message.get('mail', {}).get('headers', []) for header in headers: if header["name"] == COMMCARE_MESSAGE_ID_HEADER: if Invitation.EMAIL_ID_PREFIX in header["value"]: handle_email_invite_message(message, header["value"].split(Invitation.EMAIL_ID_PREFIX)[1]) else: subevent_id = header["value"] handle_email_messaging_subevent(message, subevent_id) break handle_email_sns_event(message) return HttpResponse()
def test_scheduled_transient_bounce(self): transient_message = self._get_message('scheduled_transient_bounce') transient_email = '*****@*****.**' self.assertEqual( get_relevant_aws_meta(transient_message), [ AwsMeta( notification_type='Bounce', main_type='Transient', sub_type='General', email='*****@*****.**', reason='smtp; 554 4.4.7 Message expired: unable to deliver in 840 minutes.<421 4.4.0 Unable to lookup DNS for company.org>', headers={ 'returnPath': '*****@*****.**', 'from': ['*****@*****.**'], 'date': 'Fri, 31 Jul 2020 06:01:50 -0000', 'to': ['*****@*****.**'], 'messageId': '<redacted>', 'subject': 'Scheduled report from CommCare HQ' }, timestamp=datetime.datetime(2020, 7, 31, 21, 8, 57, 723000, tzinfo=datetime.timezone.utc), destination=['*****@*****.**'] ) ] ) handle_email_sns_event(transient_message) bounced_email = BouncedEmail.objects.filter(email=transient_email) self.assertFalse(bounced_email.exists()) transient_info = TransientBounceEmail.objects.filter(email=transient_email) self.assertTrue(transient_info.exists())
def test_send_event(self): send_event = self._get_message('send_event') sender_email = "*****@*****.**" self.assertEqual(get_relevant_aws_meta(send_event), []) handle_email_sns_event(send_event) bounced_email = BouncedEmail.objects.filter(email=sender_email) self.assertFalse(bounced_email.exists()) transient_info = TransientBounceEmail.objects.filter(email=sender_email) self.assertFalse(transient_info.exists())
def test_scheduled_suppressed_bounce(self): bounce_message = self._get_message('scheduled_suppressed_bounce') self.assertEqual(get_relevant_aws_meta(bounce_message), [ AwsMeta( notification_type='Bounce', main_type='Permanent', sub_type='Suppressed', email='*****@*****.**', reason= 'Amazon SES has suppressed sending to this address because ' 'it has a recent history of bouncing as an invalid address. ' 'For more information about how to remove an address from the ' 'suppression list, see the Amazon SES Developer Guide: ' 'http://docs.aws.amazon.com/ses/latest/DeveloperGuide/remove-from-suppressionlist.html ', headers={ 'returnPath': '*****@*****.**', 'from': ['*****@*****.**'], 'date': 'Tue, 28 Jul 2020 00:28:28 -0000', 'to': ['*****@*****.**'], 'cc': ['*****@*****.**'], 'messageId': '<redacted@server-name>', 'subject': 'Invitation from John Doe to join CommCareHQ' }, timestamp=datetime.datetime(2020, 7, 28, 0, 28, 28, 622000, tzinfo=datetime.timezone.utc), destination=[ '*****@*****.**', '*****@*****.**' ]) ]) handle_email_sns_event(bounce_message) bounced_email = BouncedEmail.objects.filter( email='*****@*****.**') self.assertTrue(bounced_email.exists()) permanent_meta = PermanentBounceMeta.objects.filter( bounced_email=bounced_email.first()) self.assertTrue(permanent_meta.exists()) self.assertEqual(permanent_meta.first().sub_type, BounceSubType.SUPPRESSED)
def test_scheduled_general_bounce(self): bounce_message = self._get_message('scheduled_general_bounce') self.assertEqual(get_relevant_aws_meta(bounce_message), [ AwsMeta( notification_type='Bounce', main_type='Permanent', sub_type='General', email='*****@*****.**', reason= 'smtp; 550 5.4.1 Recipient address rejected: Access denied. AS(redacted) [redacted.outlook.com]', headers={ 'returnPath': '*****@*****.**', 'from': ['*****@*****.**'], 'date': 'Fri, 31 Jul 2020 20:09:55 -0000', 'to': ['*****@*****.**'], 'cc': ['*****@*****.**'], 'messageId': '<redacted@server-name>', 'subject': 'Invitation from John Doe to join CommCareHQ' }, timestamp=datetime.datetime(2020, 7, 31, 20, 9, 56, 637000, tzinfo=datetime.timezone.utc), destination=[ '*****@*****.**', '*****@*****.**' ]) ]) handle_email_sns_event(bounce_message) bounced_email = BouncedEmail.objects.filter( email='*****@*****.**') self.assertTrue(bounced_email.exists()) permanent_meta = PermanentBounceMeta.objects.filter( bounced_email=bounced_email.first()) self.assertTrue(permanent_meta.exists()) self.assertEqual(permanent_meta.first().sub_type, BounceSubType.GENERAL)
def test_scheduled_complaint(self): complaint_message = self._get_message('scheduled_complaint') self.assertEqual( get_relevant_aws_meta(complaint_message), [ AwsMeta( notification_type='Complaint', main_type=None, sub_type=None, email='*****@*****.**', reason=None, headers={ 'returnPath': '*****@*****.**', 'from': ['*****@*****.**'], 'date': 'Thu, 16 Jul 2020 03:02:22 -0000', 'to': ['*****@*****.**'], 'cc': ['*****@*****.**'], 'messageId': '<redacted@server-name>', 'subject': 'Invitation from Alice Doe to join CommCareHQ' }, timestamp=datetime.datetime(2020, 7, 16, 3, 16, 55, 129000, tzinfo=datetime.timezone.utc), destination=[ '*****@*****.**', '*****@*****.**' ] ), AwsMeta( notification_type='Complaint', main_type=None, sub_type=None, email='*****@*****.**', reason=None, headers={ 'returnPath': '*****@*****.**', 'from': ['*****@*****.**'], 'date': 'Thu, 16 Jul 2020 03:02:22 -0000', 'to': ['*****@*****.**'], 'cc': ['*****@*****.**'], 'messageId': '<redacted@server-name>', 'subject': 'Invitation from Alice Doe to join CommCareHQ' }, timestamp=datetime.datetime(2020, 7, 16, 3, 16, 55, 129000, tzinfo=datetime.timezone.utc), destination=[ '*****@*****.**', '*****@*****.**' ] ), ] ) handle_email_sns_event(complaint_message) first_bounced_email = BouncedEmail.objects.filter(email='*****@*****.**') second_bounced_email = BouncedEmail.objects.filter(email='*****@*****.**') self.assertTrue(first_bounced_email.exists()) self.assertTrue(second_bounced_email.exists()) self.assertTrue( ComplaintBounceMeta.objects.filter( bounced_email=first_bounced_email.first() ).exists() ) self.assertTrue( ComplaintBounceMeta.objects.filter( bounced_email=second_bounced_email.first() ).exists() )