def test_new_security_bug(self):
     # Structural subscribers are not notified of security bugs.
     maintainer = self.factory.makePerson(name='maintainer')
     project = self.factory.makeProduct(name='fnord', owner=maintainer)
     subscriber = self.factory.makePerson(name='subscriber')
     with person_logged_in(subscriber):
         project.addBugSubscription(subscriber, subscriber)
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='bad thing\n security yes\n affects fnord',
             subject='security issue',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('security issue', bug.title)
     self.assertTrue(bug.security_related)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
     recipients = set()
     for notification in BugNotification.select():
         for recipient in notification.recipients:
             recipients.add(recipient.person)
     self.assertContentEqual([maintainer], recipients)
    def test_NonGPGAuthenticatedNewBug(self):
        """Mail authenticated other than by gpg can create bugs.

        The incoming mail layer is responsible for authenticating the mail,
        and setting the current principal to the sender of the mail, either
        weakly or non-weakly authenticated.  At the layer of the handler,
        which this class is testing, we shouldn't care by what mechanism we
        decided to act on behalf of the mail sender, only that we did.

        In bug 643219, Launchpad had a problem where the MaloneHandler code
        was puncturing that abstraction and directly looking at the GPG
        signature; this test checks it's fixed.
        """
        # NB SignedMessage by default isn't actually signed, it just has the
        # capability of knowing about signing.
        message = self.factory.makeSignedMessage(body='  affects malone\nhi!')
        self.assertEquals(message.signature, None)

        # Pretend that the mail auth has given us a logged-in user.
        handler = MaloneHandler()
        with person_logged_in(self.factory.makePerson()):
            mail_handled, add_comment_to_bug, commands = \
                handler.extractAndAuthenticateCommands(message,
                    '*****@*****.**')
        self.assertEquals(mail_handled, None)
        self.assertEquals(map(str, commands), [
            'bug new',
            'affects malone',
            ])
 def test_new_security_bug(self):
     # Structural subscribers are not notified of security bugs.
     maintainer = self.factory.makePerson(name='maintainer')
     project = self.factory.makeProduct(name='fnord', owner=maintainer)
     subscriber = self.factory.makePerson(name='subscriber')
     with person_logged_in(subscriber):
         project.addBugSubscription(subscriber, subscriber)
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='bad thing\n security yes\n affects fnord',
             subject='security issue',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('security issue', bug.title)
     self.assertTrue(bug.security_related)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
     recipients = set()
     for notification in BugNotification.select():
         for recipient in notification.recipients:
             recipients.add(recipient.person)
     self.assertContentEqual([maintainer], recipients)
    def test_NonGPGAuthenticatedNewBug(self):
        """Mail authenticated other than by gpg can create bugs.

        The incoming mail layer is responsible for authenticating the mail,
        and setting the current principal to the sender of the mail, either
        weakly or non-weakly authenticated.  At the layer of the handler,
        which this class is testing, we shouldn't care by what mechanism we
        decided to act on behalf of the mail sender, only that we did.

        In bug 643219, Launchpad had a problem where the MaloneHandler code
        was puncturing that abstraction and directly looking at the GPG
        signature; this test checks it's fixed.
        """
        # NB SignedMessage by default isn't actually signed, it just has the
        # capability of knowing about signing.
        message = self.factory.makeSignedMessage(body='  affects malone\nhi!')
        self.assertEqual(message.signature, None)

        # Pretend that the mail auth has given us a logged-in user.
        handler = MaloneHandler()
        with person_logged_in(self.factory.makePerson()):
            mail_handled, add_comment_to_bug, commands = \
                handler.extractAndAuthenticateCommands(message,
                    '*****@*****.**')
        self.assertEqual(mail_handled, None)
        self.assertEqual(map(str, commands), [
            'bug new',
            'affects malone',
        ])
 def test_getCommandsBug(self):
     """getCommands returns a reasonable list if commands are specified."""
     message = self.factory.makeSignedMessage(body=' bug foo')
     handler = MaloneHandler()
     commands = handler.getCommands(message)
     self.assertEqual(1, len(commands))
     self.assertTrue(isinstance(commands[0], BugEmailCommand))
     self.assertEqual('bug', commands[0].name)
     self.assertEqual(['foo'], commands[0].string_args)
 def test_getCommandsBug(self):
     """getCommands returns a reasonable list if commands are specified."""
     message = self.factory.makeSignedMessage(body=' bug foo')
     handler = MaloneHandler()
     commands = handler.getCommands(message)
     self.assertEqual(1, len(commands))
     self.assertTrue(isinstance(commands[0], BugEmailCommand))
     self.assertEqual('bug', commands[0].name)
     self.assertEqual(['foo'], commands[0].string_args)
 def test_mailToHelpFromUnknownUser(self):
     """Mail from people of no account to help@ is simply dropped.
     """
     message = self.factory.makeSignedMessage(
         email_address='*****@*****.**')
     handler = MaloneHandler()
     mail_handled, add_comment_to_bug, commands = \
         handler.extractAndAuthenticateCommands(message,
             '*****@*****.**')
     self.assertEquals(mail_handled, True)
     self.assertEquals(self.getSentMail(), [])
 def test_mailToHelpFromUnknownUser(self):
     """Mail from people of no account to help@ is simply dropped.
     """
     message = self.factory.makeSignedMessage(
         email_address='*****@*****.**')
     handler = MaloneHandler()
     mail_handled, add_comment_to_bug, commands = \
         handler.extractAndAuthenticateCommands(message,
             '*****@*****.**')
     self.assertTrue(mail_handled)
     self.assertEmailQueueLength(0)
 def test_mailToHelpFromNonActiveUser(self):
     """Mail from people without a preferred email get a help message."""
     self.factory.makePerson(email='*****@*****.**',
                             email_address_status=EmailAddressStatus.NEW)
     message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     handler = MaloneHandler()
     response = handler.extractAndAuthenticateCommands(
         message, '*****@*****.**')
     mail_handled, add_comment_to_bug, commands = response
     self.assertTrue(mail_handled)
     emails = self.assertEmailQueueLength(1)
     self.assertEqual('*****@*****.**', emails[0]['X-Envelope-To'])
     self.assertEqual('Launchpad Bug Tracker Email Interface Help',
                      emails[0]['Subject'])
 def test_good_signature_timestamp(self):
     # An email message's GPG signature's timestamp checked to be sure it
     # isn't too far in the future or past.  This test shows that a
     # signature with a timestamp of appxoimately now will be accepted.
     signing_context = GPGSigningContext(import_secret_test_key(),
                                         password='******')
     msg = self.factory.makeSignedMessage(body=' security no',
                                          signing_context=signing_context)
     handler = MaloneHandler()
     with person_logged_in(self.factory.makePerson()):
         handler.process(msg, msg['To'])
     # Since there were no commands in the poorly-timestamped message, no
     # error emails were generated.
     self.assertEmailQueueLength(0)
 def test_mailToHelp(self):
     """Mail to help@ generates a help command."""
     user = self.factory.makePerson(email='*****@*****.**')
     message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     handler = MaloneHandler()
     with person_logged_in(user):
         mail_handled, add_comment_to_bug, commands = \
             handler.extractAndAuthenticateCommands(message,
                 '*****@*****.**')
     self.assertEqual(mail_handled, True)
     emails = self.assertEmailQueueLength(1)
     self.assertEqual(message['From'], emails[0]['X-Envelope-To'])
     self.assertEqual('Launchpad Bug Tracker Email Interface Help',
                      emails[0]['Subject'])
 def test_mailToHelp(self):
     """Mail to help@ generates a help command."""
     user = self.factory.makePerson(email='*****@*****.**')
     message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     handler = MaloneHandler()
     with person_logged_in(user):
         mail_handled, add_comment_to_bug, commands = \
             handler.extractAndAuthenticateCommands(message,
                 '*****@*****.**')
     self.assertEquals(mail_handled, True)
     emails = self.getSentMail()
     self.assertEquals(1, len(emails))
     self.assertEquals([message['From']], emails[0][1])
     self.assertTrue(
         'Subject: Launchpad Bug Tracker Email Interface' in emails[0][2])
 def test_good_signature_timestamp(self):
     # An email message's GPG signature's timestamp checked to be sure it
     # isn't too far in the future or past.  This test shows that a
     # signature with a timestamp of appxoimately now will be accepted.
     signing_context = GPGSigningContext(
         import_secret_test_key().fingerprint, password='******')
     msg = self.factory.makeSignedMessage(
         body=' security no', signing_context=signing_context)
     handler = MaloneHandler()
     with person_logged_in(self.factory.makePerson()):
         handler.process(msg, msg['To'])
     transaction.commit()
     # Since there were no commands in the poorly-timestamped message, no
     # error emails were generated.
     self.assertEqual(stub.test_emails, [])
 def test_mailToHelpFromNonActiveUser(self):
     """Mail from people without a preferred email get a help message."""
     self.factory.makePerson(
         email='*****@*****.**',
         email_address_status=EmailAddressStatus.NEW)
     message = self.factory.makeSignedMessage(email_address='*****@*****.**')
     handler = MaloneHandler()
     response = handler.extractAndAuthenticateCommands(
         message, '*****@*****.**')
     mail_handled, add_comment_to_bug, commands = response
     self.assertEquals(mail_handled, True)
     emails = self.getSentMail()
     self.assertEquals(1, len(emails))
     self.assertEquals(['*****@*****.**'], emails[0][1])
     self.assertTrue(
         'Subject: Launchpad Bug Tracker Email Interface' in emails[0][2])
 def test_information_type(self):
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='unsecure\n informationtype userdata\n affects fnord',
             subject='unsecure code',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('unsecure code', bug.title)
     self.assertEqual(InformationType.USERDATA, bug.information_type)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
 def test_information_type(self):
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='unsecure\n informationtype userdata\n affects fnord',
             subject='unsecure code',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('unsecure code', bug.title)
     self.assertEqual(InformationType.USERDATA, bug.information_type)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
 def test_new_bug_with_sharing_policy_proprietary(self):
     project = self.factory.makeProduct(name='fnord')
     self.factory.makeCommercialSubscription(product=project)
     with person_logged_in(project.owner):
         project.setBugSharingPolicy(BugSharingPolicy.PROPRIETARY)
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n affects fnord',
             subject='subject borked',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual([project.owner], list(bug.getDirectSubscribers()))
     self.assertEqual(InformationType.PROPRIETARY, bug.information_type)
 def test_new_bug_with_sharing_policy_proprietary(self):
     project = self.factory.makeProduct(name='fnord')
     self.factory.makeCommercialSubscription(product=project)
     with person_logged_in(project.owner):
         project.setBugSharingPolicy(BugSharingPolicy.PROPRIETARY)
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n affects fnord',
             subject='subject borked',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual([project.owner], list(bug.getDirectSubscribers()))
     self.assertEqual(InformationType.PROPRIETARY, bug.information_type)
    def test_bad_timestamp_but_no_commands(self):
        # If an email message's GPG signature's timestamp is too far in the
        # future or past but it doesn't contain any commands, the email is
        # processed anyway.

        msg = self.factory.makeSignedMessage(
            body='I really hope this bug gets fixed.')
        now = time.time()
        one_week = 60 * 60 * 24 * 7
        msg.signature = FakeSignature(timestamp=now + one_week)
        handler = MaloneHandler()
        # Clear old emails before potentially generating more.
        pop_notifications()
        with person_logged_in(self.factory.makePerson()):
            handler.process(msg, msg['To'])
        # Since there were no commands in the poorly-timestamped message, no
        # error emails were generated.
        self.assertEmailQueueLength(0)
 def test_new_affect_command_interleaved_with_bug_commands(self):
     # The bug commands can appear before and after the affects command.
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='unsecure\n security yes\n affects fnord\n tag ajax',
             subject='unsecure code',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('unsecure code', bug.title)
     self.assertTrue(bug.security_related)
     self.assertEqual(['ajax'], bug.tags)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
 def test_new_affect_command_interleaved_with_bug_commands(self):
     # The bug commands can appear before and after the affects command.
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='unsecure\n security yes\n affects fnord\n tag ajax',
             subject='unsecure code',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('unsecure code', bug.title)
     self.assertTrue(bug.security_related)
     self.assertEqual(['ajax'], bug.tags)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
 def test_new_bug(self):
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n affects fnord',
             subject='subject borked',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual([project.owner], list(bug.getDirectSubscribers()))
     self.assertEqual(project.owner, bug.owner)
     self.assertEqual('subject borked', bug.title)
     self.assertEqual(1, bug.messages.count())
     self.assertEqual('borked\n affects fnord', bug.description)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
    def test_bad_timestamp_but_no_commands(self):
        # If an email message's GPG signature's timestamp is too far in the
        # future or past but it doesn't contain any commands, the email is
        # processed anyway.

        msg = self.factory.makeSignedMessage(
            body='I really hope this bug gets fixed.')
        now = time.time()
        one_week = 60 * 60 * 24 * 7
        msg.signature = FakeSignature(timestamp=now + one_week)
        handler = MaloneHandler()
        # Clear old emails before potentially generating more.
        del stub.test_emails[:]
        with person_logged_in(self.factory.makePerson()):
            handler.process(msg, msg['To'])
        transaction.commit()
        # Since there were no commands in the poorly-timestamped message, no
        # error emails were generated.
        self.assertEqual(stub.test_emails, [])
 def test_new_bug_with_one_misplaced_affects_line(self):
     # Affects commands in the wrong position are processed as the user
     # intended when the bug is new and there is only one affects.
     project = self.factory.makeProduct(name='fnord')
     assignee = self.factory.makePerson(name='pting')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n assignee pting\n affects fnord',
             subject='affects after assignee',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('affects after assignee', bug.title)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
     self.assertEqual(assignee, bug.bugtasks[0].assignee)
 def test_new_bug_with_one_misplaced_affects_line(self):
     # Affects commands in the wrong position are processed as the user
     # intended when the bug is new and there is only one affects.
     project = self.factory.makeProduct(name='fnord')
     assignee = self.factory.makePerson(name='pting')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n assignee pting\n affects fnord',
             subject='affects after assignee',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual('affects after assignee', bug.title)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
     self.assertEqual(assignee, bug.bugtasks[0].assignee)
 def getFailureForMessage(self, to_address, from_address=None, body=None):
     mail = self.factory.makeSignedMessage(
         body=body, email_address=from_address)
     switch_dbuser(config.processmail.dbuser)
     # Rejection email goes to the preferred email of the current user.
     # The current user is extracted from the current interaction, which is
     # set up using the authenticateEmail method.  However that expects
     # real GPG signed emails, which we are faking here.
     login(mail['from'])
     handler = MaloneHandler()
     self.assertTrue(handler.process(mail, to_address, None))
     notifications = pop_notifications()
     if not notifications:
         return None
     notification = notifications[0]
     self.assertEqual('Submit Request Failure', notification['subject'])
     # The returned message is a multipart message, the first part is
     # the message, and the second is the original message.
     message, original = notification.get_payload()
     return message.get_payload(decode=True)
 def getFailureForMessage(self, to_address, from_address=None, body=None):
     mail = self.factory.makeSignedMessage(body=body,
                                           email_address=from_address)
     switch_dbuser(config.processmail.dbuser)
     # Rejection email goes to the preferred email of the current user.
     # The current user is extracted from the current interaction, which is
     # set up using the authenticateEmail method.  However that expects
     # real GPG signed emails, which we are faking here.
     login(mail['from'])
     handler = MaloneHandler()
     self.assertTrue(handler.process(mail, to_address, None))
     notifications = pop_notifications()
     if not notifications:
         return None
     notification = notifications[0]
     self.assertEqual('Submit Request Failure', notification['subject'])
     # The returned message is a multipart message, the first part is
     # the message, and the second is the original message.
     message, original = notification.get_payload()
     return message.get_payload(decode=True)
 def test_new_bug(self):
     project = self.factory.makeProduct(name='fnord')
     transaction.commit()
     handler = MaloneHandler()
     with person_logged_in(project.owner):
         msg = self.factory.makeSignedMessage(
             body='borked\n affects fnord',
             subject='subject borked',
             to_address='*****@*****.**')
         handler.process(msg, msg['To'])
     notification = self.getLatestBugNotification()
     bug = notification.bug
     self.assertEqual(
         [project.owner], list(bug.getDirectSubscribers()))
     self.assertEqual(project.owner, bug.owner)
     self.assertEqual('subject borked', bug.title)
     self.assertEqual(1, bug.messages.count())
     self.assertEqual('borked\n affects fnord', bug.description)
     self.assertEqual(1, len(bug.bugtasks))
     self.assertEqual(project, bug.bugtasks[0].target)
 def test_getCommandsEmpty(self):
     """getCommands returns an empty list for messages with no command."""
     message = self.factory.makeSignedMessage()
     handler = MaloneHandler()
     self.assertEqual([], handler.getCommands(message))
 def test_getCommandsEmpty(self):
     """getCommands returns an empty list for messages with no command."""
     message = self.factory.makeSignedMessage()
     handler = MaloneHandler()
     self.assertEqual([], handler.getCommands(message))