def testValidateReferencesHeader(self): project = project_pb2.Project() project.project_name = 'open-open' subject = 'slipped disk' expected = emailfmt.MakeMessageID( '*****@*****.**', subject, '%s@%s' % (project.project_name, emailfmt.MailDomain())) self.assertTrue( emailfmt.ValidateReferencesHeader(expected, project, '*****@*****.**', subject)) self.assertFalse( emailfmt.ValidateReferencesHeader(expected, project, '*****@*****.**', 'something else')) self.assertFalse( emailfmt.ValidateReferencesHeader(expected, project, '*****@*****.**', subject)) project.project_name = 'other-project' self.assertFalse( emailfmt.ValidateReferencesHeader(expected, project, '*****@*****.**', subject))
def testProcessMail_BannedAccount(self): self.services.user.TestAddUser('*****@*****.**', 111L) class MockAuthData: def __init__(self): self.user_pb = user_pb2.MakeUser(111L) self.effective_ids = set([1, 2, 3]) self.user_id = 111L mock_auth_data = MockAuthData() mock_auth_data.user_pb.banned = 'banned' self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(True) self.mox.StubOutWithMock(monorailrequest.AuthData, 'FromEmail') monorailrequest.AuthData.FromEmail( mox.IgnoreArg(), '*****@*****.**', self.services, autocreate=False).AndReturn(mock_auth_data) self.mox.ReplayAll() email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertEquals(1, len(email_tasks)) email_task = email_tasks[0] self.assertEquals('*****@*****.**', email_task['to']) self.assertEquals('You are banned from using this issue tracker', email_task['subject'])
def testProcessMail_NoRefHeader(self): self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(False) emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(False) self.mox.ReplayAll() email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertEquals(1, len(email_tasks)) email_task = email_tasks[0] self.assertEquals('*****@*****.**', email_task['to']) self.assertEquals( 'Your message is not a reply to a notification email', email_task['subject'])
def testProcessMail_NoAccount(self): self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(True) self.mox.ReplayAll() email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertEquals(1, len(email_tasks)) email_task = email_tasks[0] self.assertEquals('*****@*****.**', email_task['to']) self.assertEquals('Could not determine account of sender', email_task['subject'])
def testProcessMail_Success(self): self.services.user.TestAddUser('*****@*****.**', 111) self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(True) self.mox.StubOutWithMock(self.inbound, 'ProcessIssueReply') self.inbound.ProcessIssueReply(mox.IgnoreArg(), self.project, 123, self.project_addr, 'awesome!') self.mox.ReplayAll() ret = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertIsNone(ret)
def testProcessMail_BannedAccount(self): user_pb = self.services.user.TestAddUser('*****@*****.**', 111) user_pb.banned = 'banned' self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(True) self.mox.ReplayAll() email_tasks = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertEquals(1, len(email_tasks)) email_task = email_tasks[0] self.assertEquals('*****@*****.**', email_task['to']) self.assertEquals('You are banned from using this issue tracker', email_task['subject'])
def testProcessMail_Success(self): self.services.user.TestAddUser('*****@*****.**', 111L) class MockAuthData: def __init__(self): self.user_pb = user_pb2.MakeUser(111L) self.effective_ids = set([1, 2, 3]) self.user_id = 111L mock_auth_data = MockAuthData() self.mox.StubOutWithMock(emailfmt, 'ValidateReferencesHeader') emailfmt.ValidateReferencesHeader(mox.IgnoreArg(), self.project, mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(True) self.mox.StubOutWithMock(monorailrequest.AuthData, 'FromEmail') monorailrequest.AuthData.FromEmail( mox.IgnoreArg(), '*****@*****.**', self.services, autocreate=False).AndReturn(mock_auth_data) self.mox.StubOutWithMock(permissions, 'GetPermissions') permissions.GetPermissions(mock_auth_data.user_pb, mock_auth_data.effective_ids, self.project).AndReturn('test permissions') self.mox.StubOutWithMock(self.inbound, 'ProcessIssueReply') self.inbound.ProcessIssueReply(mox.IgnoreArg(), self.project, 123, self.project_addr, '*****@*****.**', 111L, mock_auth_data.effective_ids, 'test permissions', 'awesome!') self.mox.ReplayAll() ret = self.inbound.ProcessMail(self.msg, self.project_addr) self.mox.VerifyAll() self.assertIsNone(ret)
def ProcessMail(self, msg, project_addr): """Process an inbound email message.""" # TODO(jrobbins): If the message is HUGE, don't even try to parse # it. Silently give up. (from_addr, to_addrs, cc_addrs, references, incident_id, subject, body) = emailfmt.ParseEmailMessage(msg) logging.info('Proj addr: %r', project_addr) logging.info('From addr: %r', from_addr) logging.info('Subject: %r', subject) logging.info('To: %r', to_addrs) logging.info('Cc: %r', cc_addrs) logging.info('References: %r', references) logging.info('Incident Id: %r', incident_id) logging.info('Body: %r', body) # If message body is very large, reject it and send an error email. if emailfmt.IsBodyTooBigToParse(body): return _MakeErrorMessageReplyTask( project_addr, from_addr, self._templates['body_too_long']) # Make sure that the project reply-to address is in the To: line. if not emailfmt.IsProjectAddressOnToLine(project_addr, to_addrs): return None project_name, verb, trooper_queue = emailfmt.IdentifyProjectVerbAndLabel( project_addr) is_alert = bool(verb and verb.lower() == 'alert') error_addr = from_addr local_id = None author_addr = from_addr if is_alert: error_addr = settings.alert_escalation_email author_addr = settings.alert_service_account else: local_id = emailfmt.IdentifyIssue(project_name, subject) if not local_id: logging.info('Could not identify issue: %s %s', project_addr, subject) # No error message, because message was probably not intended for us. return None cnxn = sql.MonorailConnection() if self.services.cache_manager: self.services.cache_manager.DoDistributedInvalidation(cnxn) project = self.services.project.GetProjectByName(cnxn, project_name) # Authenticate the author_addr and perm check. try: mc = monorailcontext.MonorailContext( self.services, cnxn=cnxn, requester=author_addr, autocreate=is_alert) mc.LookupLoggedInUserPerms(project) except exceptions.NoSuchUserException: return _MakeErrorMessageReplyTask( project_addr, error_addr, self._templates['no_account']) # TODO(zhangtiff): Add separate email templates for alert error cases. if not project or project.state != project_pb2.ProjectState.LIVE: return _MakeErrorMessageReplyTask( project_addr, error_addr, self._templates['project_not_found']) if not project.process_inbound_email: return _MakeErrorMessageReplyTask( project_addr, error_addr, self._templates['replies_disabled'], project_name=project_name) # Verify that this is a reply to a notification that we could have sent. is_development = os.environ['SERVER_SOFTWARE'].startswith('Development') if not (is_alert or is_development): for ref in references: if emailfmt.ValidateReferencesHeader(ref, project, from_addr, subject): break # Found a message ID that we could have sent. if emailfmt.ValidateReferencesHeader( ref, project, from_addr.lower(), subject): break # Also match all-lowercase from-address. else: # for-else: if loop completes with no valid reference found. return _MakeErrorMessageReplyTask( project_addr, from_addr, self._templates['not_a_reply']) # Note: If the issue summary line is changed, a new thread is created, # and replies to the old thread will no longer work because the subject # line hash will not match, which seems reasonable. if mc.auth.user_pb.banned: logging.info('Banned user %s tried to post to %s', from_addr, project_addr) return _MakeErrorMessageReplyTask( project_addr, error_addr, self._templates['banned']) # If the email is an alert, switch to the alert handling path. if is_alert: alert2issue.ProcessEmailNotification( self.services, cnxn, project, project_addr, from_addr, mc.auth, subject, body, incident_id, msg, trooper_queue) return None # This email is a response to an email about a comment. self.ProcessIssueReply( mc, project, local_id, project_addr, body) return None