Example #1
0
    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))
Example #2
0
    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'])
Example #3
0
    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'])
Example #4
0
    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'])
Example #5
0
    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)
Example #6
0
    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'])
Example #7
0
    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)
Example #8
0
  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