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
1=== modified file 'lib/lp/code/mail/tests/test_codehandler.py'
2--- lib/lp/code/mail/tests/test_codehandler.py 2009-07-17 00:26:05 +0000
3+++ lib/lp/code/mail/tests/test_codehandler.py 2009-07-23 17:47:50 +0000
4@@ -732,6 +732,7 @@
5 )
6 self.assertEqual(notification['to'],
7 mail['from'])
8+ self.assertEqual(0, bmp.all_comments.count())
9
10
11 class TestCodeHandlerProcessMergeDirective(TestCaseWithFactory):
12
13=== modified file 'lib/lp/code/model/branchmergeproposal.py'
14--- lib/lp/code/model/branchmergeproposal.py 2009-07-17 00:26:05 +0000
15+++ lib/lp/code/model/branchmergeproposal.py 2009-07-23 17:54:31 +0000
16@@ -44,13 +44,15 @@
17 from lp.code.interfaces.branchmergeproposal import (
18 BadBranchMergeProposalSearchContext, BadStateTransition,
19 BRANCH_MERGE_PROPOSAL_FINAL_STATES as FINAL_STATES,
20- IBranchMergeProposal, IBranchMergeProposalGetter, UserNotBranchReviewer, WrongBranchMergeProposal)
21+ IBranchMergeProposal, IBranchMergeProposalGetter, UserNotBranchReviewer,
22+ WrongBranchMergeProposal)
23 from lp.code.interfaces.branchtarget import IHasBranchTarget
24 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
25 from lp.registry.interfaces.person import IPerson
26 from lp.registry.interfaces.product import IProduct
27 from lp.code.mail.branch import RecipientReason
28 from lp.registry.interfaces.person import validate_public_person
29+from lp.services.mail.sendmail import validate_message
30
31
32 def is_valid_transition(proposal, from_state, next_state, user=None):
33@@ -558,7 +560,8 @@
34 subject=subject, datecreated=_date_created)
35 chunk = MessageChunk(message=message, content=content, sequence=1)
36 return self.createCommentFromMessage(
37- message, vote, review_type, _notify_listeners=_notify_listeners)
38+ message, vote, review_type, original_email=None,
39+ _notify_listeners=_notify_listeners, _validate=False)
40
41 def getUsersVoteReference(self, user, review_type=None):
42 """Get the existing vote reference for the given user."""
43@@ -624,14 +627,17 @@
44 review_type=review_type)
45
46 def createCommentFromMessage(self, message, vote, review_type,
47- original_email=None, _notify_listeners=True):
48+ original_email, _notify_listeners=True,
49+ _validate=True):
50 """See `IBranchMergeProposal`."""
51+ if _validate:
52+ validate_message(original_email)
53 # Lower case the review type.
54 if review_type is not None:
55 review_type = review_type.lower()
56 code_review_message = CodeReviewComment(
57 branch_merge_proposal=self, message=message, vote=vote,
58- vote_tag=review_type)
59+ vote_tag=review_type, original_email=original_email)
60 # Get the appropriate CodeReviewVoteReference for the reviewer.
61 # If there isn't one, then create one, otherwise set the comment
62 # reference.
63
64=== modified file 'lib/lp/code/model/tests/test_codereviewcomment.py'
65--- lib/lp/code/model/tests/test_codereviewcomment.py 2009-06-25 04:06:00 +0000
66+++ lib/lp/code/model/tests/test_codereviewcomment.py 2009-07-23 17:47:50 +0000
67@@ -96,7 +96,8 @@
68 def test_createCommentFromMessage(self):
69 """Creating a CodeReviewComment from a message works."""
70 message = self.factory.makeMessage(owner=self.submitter)
71- comment = self.bmp.createCommentFromMessage(message, None, None)
72+ comment = self.bmp.createCommentFromMessage(
73+ message, None, None, original_email=None, _validate=False)
74 self.assertEqual(message, comment.message)
75
76 def test_createCommentFromMessageNotifies(self):
77@@ -104,7 +105,7 @@
78 message = self.factory.makeMessage()
79 self.assertNotifies(
80 NewCodeReviewCommentEvent, self.bmp.createCommentFromMessage,
81- message, None, None)
82+ message, None, None, original_email=None, _validate=False)
83
84
85 class TestCodeReviewCommentGetAttachments(TestCaseWithFactory):
86
87=== modified file 'lib/lp/services/mail/sendmail.py'
88--- lib/lp/services/mail/sendmail.py 2009-07-17 18:46:25 +0000
89+++ lib/lp/services/mail/sendmail.py 2009-07-23 17:47:50 +0000
90@@ -292,6 +292,12 @@
91 formataddr((name, address))
92 for name, address in getaddresses([email_header])]
93
94+def validate_message(message):
95+ assert isinstance(message, Message), 'Not an email.Message.Message'
96+ assert 'to' in message and bool(message['to']), 'No To: header'
97+ assert 'from' in message and bool(message['from']), 'No From: header'
98+ assert 'subject' in message and bool(message['subject']), \
99+ 'No Subject: header'
100
101 def sendmail(message, to_addrs=None, bulk=True):
102 """Send an email.Message.Message
103@@ -316,12 +322,7 @@
104
105 Returns the Message-Id
106 """
107- assert isinstance(message, Message), 'Not an email.Message.Message'
108- assert 'to' in message and bool(message['to']), 'No To: header'
109- assert 'from' in message and bool(message['from']), 'No From: header'
110- assert 'subject' in message and bool(message['subject']), \
111- 'No Subject: header'
112-
113+ validate_message(message)
114 if to_addrs is None:
115 to_addrs = get_addresses_from_header(message['to'])
116 if message['cc']:
117
118=== modified file 'lib/lp/testing/factory.py'
119--- lib/lp/testing/factory.py 2009-07-17 00:26:05 +0000
120+++ lib/lp/testing/factory.py 2009-07-23 17:47:50 +0000
121@@ -1014,6 +1014,7 @@
122 person = self.makePerson()
123 email_address = person.preferredemail.email
124 mail['From'] = email_address
125+ mail['To'] = self.makePerson().preferredemail.email
126 if subject is None:
127 subject = self.getUniqueString('subject')
128 mail['Subject'] = subject