Merge lp:~abentley/launchpad/abort-failed-vote into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Merged at revision: not available
Proposed branch: lp:~abentley/launchpad/abort-failed-vote
Merge into: lp:launchpad
Diff against target: None lines
To merge this branch: bzr merge lp:~abentley/launchpad/abort-failed-vote
Reviewer Review Type Date Requested Status
Celso Providelo (community) Approve
Review via email: mp+9212@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

= Summary =
Fix bug #354544 by preventing creating an codereviewcomment from an
email whose subject line is empty

== Proposed fix ==
Until now, errors due to empty subject lines have been produced after
the CodeReviewComment was created, when sending the message to
recipients. Now, we perform the same validation used by
sendmail.sendmail to ensure that we never create an unsendable
codereviewcomment.

== Pre-implementation notes ==
None

== Implementation details ==
The orig_email parameter for createCommentFromMessage is now mandatory,
but may be supplied as None if _validate is supplied as False. This is
done by createComment, because we can assume that we create a valid
message that way.

Several tests had to be updated for to handle the new parameters, and
the makeSignedMessage had to be updated to create messages with a "to"
header.

== Tests ==
bin/test test_codehandler

== Demo and Q/A ==
Reply to a merge proposal with no subject. An error email should be
sent, and your comment should not appear on the merge proposal.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/code/model/branchmergeproposal.py
  lib/lp/code/mail/tests/test_codehandler.py
  lib/lp/testing/factory.py
  lib/lp/services/mail/sendmail.py
  lib/lp/code/model/tests/test_codereviewcomment.py
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkpopuMACgkQ0F+nu1YWqI320ACfcSEjLGHJUipn0sCeJ5mnJfnd
qtEAn1z1sphwJnCtsQgkG9iszb2k90HT
=h3uu
-----END PGP SIGNATURE-----

Revision history for this message
Celso Providelo (cprov) wrote :

Aaron,

We discussed your approach for being explicit about the validation, instead of validating any not-None messsage passed in. You convinced me that there is no reason to block internal callsites to pass not-None messages and skip validation if that's what they need to do.

We rely on the fact that external callsites should not mess with the 'private' argument.

Please add 'validate_message' to the lib/lp/services/mail/sendmail.py __all__ and r=me.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/mail/tests/test_codehandler.py'
--- lib/lp/code/mail/tests/test_codehandler.py 2009-07-17 00:26:05 +0000
+++ lib/lp/code/mail/tests/test_codehandler.py 2009-07-23 17:47:50 +0000
@@ -732,6 +732,7 @@
732 )732 )
733 self.assertEqual(notification['to'],733 self.assertEqual(notification['to'],
734 mail['from'])734 mail['from'])
735 self.assertEqual(0, bmp.all_comments.count())
735736
736737
737class TestCodeHandlerProcessMergeDirective(TestCaseWithFactory):738class TestCodeHandlerProcessMergeDirective(TestCaseWithFactory):
738739
=== modified file 'lib/lp/code/model/branchmergeproposal.py'
--- lib/lp/code/model/branchmergeproposal.py 2009-07-17 00:26:05 +0000
+++ lib/lp/code/model/branchmergeproposal.py 2009-07-23 17:54:31 +0000
@@ -44,13 +44,15 @@
44from lp.code.interfaces.branchmergeproposal import (44from lp.code.interfaces.branchmergeproposal import (
45 BadBranchMergeProposalSearchContext, BadStateTransition,45 BadBranchMergeProposalSearchContext, BadStateTransition,
46 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,46 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
47 IBranchMergeProposal, IBranchMergeProposalGetter, UserNotBranchReviewer, WrongBranchMergeProposal)47 IBranchMergeProposal, IBranchMergeProposalGetter, UserNotBranchReviewer,
48 WrongBranchMergeProposal)
48from lp.code.interfaces.branchtarget import IHasBranchTarget49from lp.code.interfaces.branchtarget import IHasBranchTarget
49from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities50from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
50from lp.registry.interfaces.person import IPerson51from lp.registry.interfaces.person import IPerson
51from lp.registry.interfaces.product import IProduct52from lp.registry.interfaces.product import IProduct
52from lp.code.mail.branch import RecipientReason53from lp.code.mail.branch import RecipientReason
53from lp.registry.interfaces.person import validate_public_person54from lp.registry.interfaces.person import validate_public_person
55from lp.services.mail.sendmail import validate_message
5456
5557
56def is_valid_transition(proposal, from_state, next_state, user=None):58def is_valid_transition(proposal, from_state, next_state, user=None):
@@ -558,7 +560,8 @@
558 subject=subject, datecreated=_date_created)560 subject=subject, datecreated=_date_created)
559 chunk = MessageChunk(message=message, content=content, sequence=1)561 chunk = MessageChunk(message=message, content=content, sequence=1)
560 return self.createCommentFromMessage(562 return self.createCommentFromMessage(
561 message, vote, review_type, _notify_listeners=_notify_listeners)563 message, vote, review_type, original_email=None,
564 _notify_listeners=_notify_listeners, _validate=False)
562565
563 def getUsersVoteReference(self, user, review_type=None):566 def getUsersVoteReference(self, user, review_type=None):
564 """Get the existing vote reference for the given user."""567 """Get the existing vote reference for the given user."""
@@ -624,14 +627,17 @@
624 review_type=review_type)627 review_type=review_type)
625628
626 def createCommentFromMessage(self, message, vote, review_type,629 def createCommentFromMessage(self, message, vote, review_type,
627 original_email=None, _notify_listeners=True):630 original_email, _notify_listeners=True,
631 _validate=True):
628 """See `IBranchMergeProposal`."""632 """See `IBranchMergeProposal`."""
633 if _validate:
634 validate_message(original_email)
629 # Lower case the review type.635 # Lower case the review type.
630 if review_type is not None:636 if review_type is not None:
631 review_type = review_type.lower()637 review_type = review_type.lower()
632 code_review_message = CodeReviewComment(638 code_review_message = CodeReviewComment(
633 branch_merge_proposal=self, message=message, vote=vote,639 branch_merge_proposal=self, message=message, vote=vote,
634 vote_tag=review_type)640 vote_tag=review_type, original_email=original_email)
635 # Get the appropriate CodeReviewVoteReference for the reviewer.641 # Get the appropriate CodeReviewVoteReference for the reviewer.
636 # If there isn't one, then create one, otherwise set the comment642 # If there isn't one, then create one, otherwise set the comment
637 # reference.643 # reference.
638644
=== modified file 'lib/lp/code/model/tests/test_codereviewcomment.py'
--- lib/lp/code/model/tests/test_codereviewcomment.py 2009-06-25 04:06:00 +0000
+++ lib/lp/code/model/tests/test_codereviewcomment.py 2009-07-23 17:47:50 +0000
@@ -96,7 +96,8 @@
96 def test_createCommentFromMessage(self):96 def test_createCommentFromMessage(self):
97 """Creating a CodeReviewComment from a message works."""97 """Creating a CodeReviewComment from a message works."""
98 message = self.factory.makeMessage(owner=self.submitter)98 message = self.factory.makeMessage(owner=self.submitter)
99 comment = self.bmp.createCommentFromMessage(message, None, None)99 comment = self.bmp.createCommentFromMessage(
100 message, None, None, original_email=None, _validate=False)
100 self.assertEqual(message, comment.message)101 self.assertEqual(message, comment.message)
101102
102 def test_createCommentFromMessageNotifies(self):103 def test_createCommentFromMessageNotifies(self):
@@ -104,7 +105,7 @@
104 message = self.factory.makeMessage()105 message = self.factory.makeMessage()
105 self.assertNotifies(106 self.assertNotifies(
106 NewCodeReviewCommentEvent, self.bmp.createCommentFromMessage,107 NewCodeReviewCommentEvent, self.bmp.createCommentFromMessage,
107 message, None, None)108 message, None, None, original_email=None, _validate=False)
108109
109110
110class TestCodeReviewCommentGetAttachments(TestCaseWithFactory):111class TestCodeReviewCommentGetAttachments(TestCaseWithFactory):
111112
=== modified file 'lib/lp/services/mail/sendmail.py'
--- lib/lp/services/mail/sendmail.py 2009-07-17 18:46:25 +0000
+++ lib/lp/services/mail/sendmail.py 2009-07-23 17:47:50 +0000
@@ -292,6 +292,12 @@
292 formataddr((name, address))292 formataddr((name, address))
293 for name, address in getaddresses([email_header])]293 for name, address in getaddresses([email_header])]
294294
295def validate_message(message):
296 assert isinstance(message, Message), 'Not an email.Message.Message'
297 assert 'to' in message and bool(message['to']), 'No To: header'
298 assert 'from' in message and bool(message['from']), 'No From: header'
299 assert 'subject' in message and bool(message['subject']), \
300 'No Subject: header'
295301
296def sendmail(message, to_addrs=None, bulk=True):302def sendmail(message, to_addrs=None, bulk=True):
297 """Send an email.Message.Message303 """Send an email.Message.Message
@@ -316,12 +322,7 @@
316322
317 Returns the Message-Id323 Returns the Message-Id
318 """324 """
319 assert isinstance(message, Message), 'Not an email.Message.Message'325 validate_message(message)
320 assert 'to' in message and bool(message['to']), 'No To: header'
321 assert 'from' in message and bool(message['from']), 'No From: header'
322 assert 'subject' in message and bool(message['subject']), \
323 'No Subject: header'
324
325 if to_addrs is None:326 if to_addrs is None:
326 to_addrs = get_addresses_from_header(message['to'])327 to_addrs = get_addresses_from_header(message['to'])
327 if message['cc']:328 if message['cc']:
328329
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2009-07-17 00:26:05 +0000
+++ lib/lp/testing/factory.py 2009-07-23 17:47:50 +0000
@@ -1014,6 +1014,7 @@
1014 person = self.makePerson()1014 person = self.makePerson()
1015 email_address = person.preferredemail.email1015 email_address = person.preferredemail.email
1016 mail['From'] = email_address1016 mail['From'] = email_address
1017 mail['To'] = self.makePerson().preferredemail.email
1017 if subject is None:1018 if subject is None:
1018 subject = self.getUniqueString('subject')1019 subject = self.getUniqueString('subject')
1019 mail['Subject'] = subject1020 mail['Subject'] = subject