13668.1.21
by Curtis Hovey
Updated copyrights. |
1 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.22
by Karl Fogel
Add the copyright header block to the remaining .py files. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
3 |
|
4 |
"""Test the SignedMessage class."""
|
|
5 |
||
6 |
__metaclass__ = type |
|
7 |
||
8 |
from email.Message import Message |
|
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
9 |
from email.MIMEMultipart import MIMEMultipart |
7675.60.10
by Tim Penhey
Add tests for signed message. |
10 |
from email.MIMEText import MIMEText |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
11 |
from email.Utils import ( |
12 |
formatdate, |
|
13 |
make_msgid, |
|
14 |
)
|
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
15 |
from textwrap import dedent |
16 |
||
17 |
import gpgme |
|
18 |
from zope.component import getUtility |
|
19 |
||
11678.2.5
by Gary Poster
move files as requested by review to lp/services; change things further as lint requests. |
20 |
from lp.registry.interfaces.person import IPersonSet |
14455.2.1
by William Grant
Move code and tests. |
21 |
from lp.services.gpg.interfaces import IGPGHandler |
11678.2.5
by Gary Poster
move files as requested by review to lp/services; change things further as lint requests. |
22 |
from lp.services.mail.incoming import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
23 |
authenticateEmail, |
24 |
canonicalise_line_endings, |
|
25 |
)
|
|
13668.1.22
by Curtis Hovey
Sorted imports. |
26 |
from lp.services.mail.interfaces import IWeaklyAuthenticatedPrincipal |
27 |
from lp.services.mail.signedmessage import signed_message_from_string |
|
8440.1.2
by Curtis Hovey
Updated lp.testing imports. Removed the shim |
28 |
from lp.testing import TestCaseWithFactory |
29 |
from lp.testing.factory import GPGSigningContext |
|
14455.2.4
by William Grant
Drop imports from canonical.launchpad.ftests. |
30 |
from lp.testing.gpgkeys import ( |
31 |
import_public_test_keys, |
|
32 |
import_secret_test_key, |
|
33 |
)
|
|
14612.2.1
by William Grant
format-imports on lib/. So many imports. |
34 |
from lp.testing.layers import DatabaseFunctionalLayer |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
35 |
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
36 |
|
37 |
class TestSignedMessage(TestCaseWithFactory): |
|
11678.2.5
by Gary Poster
move files as requested by review to lp/services; change things further as lint requests. |
38 |
"Test SignedMessage class correctly extracts and verifies GPG signatures."
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
39 |
|
40 |
layer = DatabaseFunctionalLayer |
|
41 |
||
42 |
def setUp(self): |
|
43 |
# Login with admin roles as we aren't testing access here.
|
|
44 |
TestCaseWithFactory.setUp(self, 'admin@canonical.com') |
|
45 |
import_public_test_keys() |
|
46 |
||
47 |
def test_unsigned_message(self): |
|
48 |
# An unsigned message will not have a signature nor signed content,
|
|
49 |
# and generates a weakly authenticated principle.
|
|
50 |
sender = self.factory.makePerson() |
|
51 |
email_message = self.factory.makeEmailMessage(sender=sender) |
|
52 |
msg = signed_message_from_string(email_message.as_string()) |
|
53 |
self.assertIs(None, msg.signedContent) |
|
54 |
self.assertIs(None, msg.signature) |
|
10856.2.29
by Stuart Bishop
Fix test_signedmessage tests |
55 |
principle = authenticateEmail(msg) |
7675.60.10
by Tim Penhey
Add tests for signed message. |
56 |
self.assertEqual(sender, principle.person) |
57 |
self.assertTrue( |
|
58 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |
|
59 |
self.assertIs(None, msg.signature) |
|
60 |
||
11658.1.1
by Benji York
add a fix and test for bug-612754 |
61 |
def _get_clearsigned_for_person(self, sender, body=None): |
7675.60.10
by Tim Penhey
Add tests for signed message. |
62 |
# Create a signed message for the sender specified with the test
|
63 |
# secret key.
|
|
64 |
key = import_secret_test_key() |
|
65 |
signing_context = GPGSigningContext(key.fingerprint, password='test') |
|
11658.1.1
by Benji York
add a fix and test for bug-612754 |
66 |
if body is None: |
67 |
body = dedent("""\ |
|
68 |
This is a multi-line body.
|
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
69 |
|
11658.1.1
by Benji York
add a fix and test for bug-612754 |
70 |
Sincerely,
|
71 |
Your friendly tester.
|
|
72 |
""") |
|
7675.60.10
by Tim Penhey
Add tests for signed message. |
73 |
msg = self.factory.makeSignedMessage( |
74 |
email_address=sender.preferredemail.email, |
|
75 |
body=body, signing_context=signing_context) |
|
76 |
self.assertFalse(msg.is_multipart()) |
|
77 |
return signed_message_from_string(msg.as_string()) |
|
78 |
||
79 |
def test_clearsigned_message_wrong_sender(self): |
|
80 |
# If the message is signed, but the key doesn't belong to the sender,
|
|
81 |
# the principle is set to the sender, but weakly authenticated.
|
|
82 |
sender = self.factory.makePerson() |
|
83 |
msg = self._get_clearsigned_for_person(sender) |
|
10856.2.29
by Stuart Bishop
Fix test_signedmessage tests |
84 |
principle = authenticateEmail(msg) |
7675.60.10
by Tim Penhey
Add tests for signed message. |
85 |
self.assertIsNot(None, msg.signature) |
86 |
self.assertEqual(sender, principle.person) |
|
87 |
self.assertTrue( |
|
88 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |
|
89 |
||
90 |
def test_clearsigned_message(self): |
|
91 |
# The test keys belong to Sample Person.
|
|
92 |
sender = getUtility(IPersonSet).getByEmail('test@canonical.com') |
|
93 |
msg = self._get_clearsigned_for_person(sender) |
|
10856.2.29
by Stuart Bishop
Fix test_signedmessage tests |
94 |
principle = authenticateEmail(msg) |
7675.60.10
by Tim Penhey
Add tests for signed message. |
95 |
self.assertIsNot(None, msg.signature) |
96 |
self.assertEqual(sender, principle.person) |
|
97 |
self.assertFalse( |
|
98 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |
|
99 |
||
11658.1.1
by Benji York
add a fix and test for bug-612754 |
100 |
def test_trailing_whitespace(self): |
101 |
# Trailing whitespace should be ignored when verifying a message's
|
|
102 |
# signature.
|
|
103 |
sender = getUtility(IPersonSet).getByEmail('test@canonical.com') |
|
11658.1.2
by Benji York
tabs too |
104 |
body = ( |
13668.1.18
by Curtis Hovey
Hushed lint. |
105 |
'A message with trailing spaces. \n' |
106 |
'And tabs\t\t\n' |
|
11658.1.2
by Benji York
tabs too |
107 |
'Also mixed. \t ') |
11658.1.1
by Benji York
add a fix and test for bug-612754 |
108 |
msg = self._get_clearsigned_for_person(sender, body) |
109 |
principle = authenticateEmail(msg) |
|
110 |
self.assertIsNot(None, msg.signature) |
|
111 |
self.assertEqual(sender, principle.person) |
|
112 |
self.assertFalse( |
|
113 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |
|
114 |
||
7675.60.10
by Tim Penhey
Add tests for signed message. |
115 |
def _get_detached_message_for_person(self, sender): |
116 |
# Return a signed message that contains a detached signature.
|
|
117 |
body = dedent("""\ |
|
118 |
This is a multi-line body.
|
|
119 |
||
120 |
Sincerely,
|
|
121 |
Your friendly tester.""") |
|
122 |
to = self.factory.getUniqueEmailAddress() |
|
123 |
||
124 |
msg = MIMEMultipart() |
|
125 |
msg['Message-Id'] = make_msgid('launchpad') |
|
126 |
msg['Date'] = formatdate() |
|
127 |
msg['To'] = to |
|
128 |
msg['From'] = sender.preferredemail.email |
|
129 |
msg['Subject'] = 'Sample' |
|
130 |
||
131 |
body_text = MIMEText(body) |
|
132 |
msg.attach(body_text) |
|
133 |
# A detached signature is calculated on the entire string content of
|
|
134 |
# the body message part.
|
|
135 |
key = import_secret_test_key() |
|
136 |
gpghandler = getUtility(IGPGHandler) |
|
137 |
signature = gpghandler.signContent( |
|
138 |
canonicalise_line_endings(body_text.as_string()), |
|
139 |
key.fingerprint, 'test', gpgme.SIG_MODE_DETACH) |
|
140 |
||
141 |
attachment = Message() |
|
142 |
attachment.set_payload(signature) |
|
143 |
attachment['Content-Type'] = 'application/pgp-signature' |
|
144 |
msg.attach(attachment) |
|
145 |
self.assertTrue(msg.is_multipart()) |
|
146 |
return signed_message_from_string(msg.as_string()) |
|
147 |
||
148 |
def test_detached_signature_message_wrong_sender(self): |
|
149 |
# If the message is signed, but the key doesn't belong to the sender,
|
|
150 |
# the principle is set to the sender, but weakly authenticated.
|
|
151 |
sender = self.factory.makePerson() |
|
152 |
msg = self._get_detached_message_for_person(sender) |
|
10856.2.29
by Stuart Bishop
Fix test_signedmessage tests |
153 |
principle = authenticateEmail(msg) |
7675.60.10
by Tim Penhey
Add tests for signed message. |
154 |
self.assertIsNot(None, msg.signature) |
155 |
self.assertEqual(sender, principle.person) |
|
156 |
self.assertTrue( |
|
157 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |
|
158 |
||
159 |
def test_detached_signature_message(self): |
|
160 |
# Test a detached correct signature.
|
|
161 |
sender = getUtility(IPersonSet).getByEmail('test@canonical.com') |
|
162 |
msg = self._get_detached_message_for_person(sender) |
|
10856.2.29
by Stuart Bishop
Fix test_signedmessage tests |
163 |
principle = authenticateEmail(msg) |
7675.60.10
by Tim Penhey
Add tests for signed message. |
164 |
self.assertIsNot(None, msg.signature) |
165 |
self.assertEqual(sender, principle.person) |
|
166 |
self.assertFalse( |
|
167 |
IWeaklyAuthenticatedPrincipal.providedBy(principle)) |