Mercurial > p > roundup > code
comparison test/test_mailgw.py @ 4541:62239a524beb
PGP support is again working (pyme API has changed significantly)...
...and we now have a regression test. We now take care that
bounce-messages for incoming encrypted mails or mails where the policy
dictates that outgoing traffic should be encrypted is actually
pgp-encrypted. Note that the new pgp encrypt option for outgoing mails
works only for bounces for now.
| author | Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net> |
|---|---|
| date | Fri, 07 Oct 2011 14:21:57 +0000 |
| parents | bf67fed13ef9 |
| children | 46239c21a1eb |
comparison
equal
deleted
inserted
replaced
| 4540:bf67fed13ef9 | 4541:62239a524beb |
|---|---|
| 12 # $Id: test_mailgw.py,v 1.96 2008-08-19 01:40:59 richard Exp $ | 12 # $Id: test_mailgw.py,v 1.96 2008-08-19 01:40:59 richard Exp $ |
| 13 | 13 |
| 14 # TODO: test bcc | 14 # TODO: test bcc |
| 15 | 15 |
| 16 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time | 16 import unittest, tempfile, os, shutil, errno, imp, sys, difflib, rfc822, time |
| 17 from email.parser import FeedParser | |
| 17 | 18 |
| 18 | 19 |
| 19 try: | 20 try: |
| 20 import pyme, pyme.core | 21 import pyme, pyme.core |
| 21 except ImportError: | 22 except ImportError: |
| 162 return self._handle_message(message) | 163 return self._handle_message(message) |
| 163 handler = MailGW(self.instance, args) | 164 handler = MailGW(self.instance, args) |
| 164 handler.db = self.db | 165 handler.db = self.db |
| 165 return handler | 166 return handler |
| 166 | 167 |
| 167 def _handle_mail(self, message, args=()): | 168 def _handle_mail(self, message, args=(), trap_exc=0): |
| 168 handler = self._create_mailgw(message, args) | 169 handler = self._create_mailgw(message, args) |
| 169 handler.trapExceptions = 0 | 170 handler.trapExceptions = trap_exc |
| 170 return handler.main(StringIO(message)) | 171 return handler.main(StringIO(message)) |
| 171 | 172 |
| 172 def _get_mail(self): | 173 def _get_mail(self): |
| 173 f = open(SENDMAILDEBUG) | 174 f = open(SENDMAILDEBUG) |
| 174 try: | 175 try: |
| 1873 Message-Id: <dummy_test_message_id> | 1874 Message-Id: <dummy_test_message_id> |
| 1874 Subject: [issue] Testing nonexisting user... | 1875 Subject: [issue] Testing nonexisting user... |
| 1875 | 1876 |
| 1876 This is a test submission of a new issue. | 1877 This is a test submission of a new issue. |
| 1877 ''' | 1878 ''' |
| 1878 handler = self._create_mailgw(message) | 1879 # trap_exc=1: we want a bounce message: |
| 1879 # we want a bounce message: | 1880 ret = self._handle_mail(message, trap_exc=1) |
| 1880 handler.trapExceptions = 1 | |
| 1881 ret = handler.main(StringIO(message)) | |
| 1882 self.compareMessages(self._get_mail(), | 1881 self.compareMessages(self._get_mail(), |
| 1883 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example> | 1882 '''FROM: Roundup issue tracker <roundup-admin@your.tracker.email.domain.example> |
| 1884 TO: nonexisting@bork.bork.bork | 1883 TO: nonexisting@bork.bork.bork |
| 1885 From nobody Tue Jul 14 12:04:11 2009 | 1884 From nobody Tue Jul 14 12:04:11 2009 |
| 1886 Content-Type: multipart/mixed; boundary="===============0639262320==" | 1885 Content-Type: multipart/mixed; boundary="===============0639262320==" |
| 3065 | 3064 |
| 3066 class MailgwPGPTestCase(MailgwTestAbstractBase): | 3065 class MailgwPGPTestCase(MailgwTestAbstractBase): |
| 3067 pgphome = 'pgp-test-home' | 3066 pgphome = 'pgp-test-home' |
| 3068 def setUp(self): | 3067 def setUp(self): |
| 3069 MailgwTestAbstractBase.setUp(self) | 3068 MailgwTestAbstractBase.setUp(self) |
| 3070 self.db.security.addRole (name = 'pgp', description = 'PGP Role') | 3069 self.db.security.addRole(name = 'pgp', description = 'PGP Role') |
| 3071 self.instance.config['PGP_HOMEDIR'] = self.pgphome | 3070 self.instance.config['PGP_HOMEDIR'] = self.pgphome |
| 3072 self.instance.config['PGP_ROLES'] = 'pgp' | 3071 self.instance.config['PGP_ROLES'] = 'pgp' |
| 3073 self.instance.config['PGP_ENABLE'] = True | 3072 self.instance.config['PGP_ENABLE'] = True |
| 3073 self.instance.config['MAIL_DOMAIN'] = 'example.com' | |
| 3074 self.instance.config['ADMIN_EMAIL'] = 'roundup-admin@example.com' | |
| 3074 self.db.user.set(self.john_id, roles='User,pgp') | 3075 self.db.user.set(self.john_id, roles='User,pgp') |
| 3075 os.mkdir(self.pgphome) | 3076 os.mkdir(self.pgphome) |
| 3076 os.environ['GNUPGHOME'] = self.pgphome | 3077 os.environ['GNUPGHOME'] = self.pgphome |
| 3077 ctx = pyme.core.Context() | 3078 ctx = pyme.core.Context() |
| 3078 key = pyme.core.Data(pgp_test_key) | 3079 key = pyme.core.Data(pgp_test_key) |
| 3088 def tearDown(self): | 3089 def tearDown(self): |
| 3089 MailgwTestAbstractBase.tearDown(self) | 3090 MailgwTestAbstractBase.tearDown(self) |
| 3090 if os.path.exists(self.pgphome): | 3091 if os.path.exists(self.pgphome): |
| 3091 shutil.rmtree(self.pgphome) | 3092 shutil.rmtree(self.pgphome) |
| 3092 | 3093 |
| 3093 def testUnsignedMessage(self): | 3094 def testPGPUnsignedMessage(self): |
| 3094 self.assertRaises(MailUsageError, self._handle_mail, | 3095 self.assertRaises(MailUsageError, self._handle_mail, |
| 3095 '''Content-Type: text/plain; | 3096 '''Content-Type: text/plain; |
| 3096 charset="iso-8859-1" | 3097 charset="iso-8859-1" |
| 3097 From: John Doe <john@test.test> | 3098 From: John Doe <john@test.test> |
| 3098 To: issue_tracker@your.tracker.email.domain.example | 3099 To: issue_tracker@your.tracker.email.domain.example |
| 3100 Subject: [issue] Testing non-signed message... | 3101 Subject: [issue] Testing non-signed message... |
| 3101 | 3102 |
| 3102 This is no pgp signed message. | 3103 This is no pgp signed message. |
| 3103 ''') | 3104 ''') |
| 3104 | 3105 |
| 3105 def testSignedMessage(self): | 3106 signed_msg = '''Content-Disposition: inline |
| 3106 nodeid = self._handle_mail('''Content-Disposition: inline | |
| 3107 From: John Doe <john@test.test> | 3107 From: John Doe <john@test.test> |
| 3108 To: issue_tracker@your.tracker.email.domain.example | 3108 To: issue_tracker@your.tracker.email.domain.example |
| 3109 Subject: [issue] Testing signed message... | 3109 Subject: [issue] Testing signed message... |
| 3110 Content-Type: multipart/signed; micalg=pgp-sha1; | 3110 Content-Type: multipart/signed; micalg=pgp-sha1; |
| 3111 protocol="application/pgp-signature"; boundary="cWoXeonUoKmBZSoM" | 3111 protocol="application/pgp-signature"; boundary="cWoXeonUoKmBZSoM" |
| 3131 ZQ4K6R3m3AOw7BLdvZs= | 3131 ZQ4K6R3m3AOw7BLdvZs= |
| 3132 =wpYk | 3132 =wpYk |
| 3133 -----END PGP SIGNATURE----- | 3133 -----END PGP SIGNATURE----- |
| 3134 | 3134 |
| 3135 --cWoXeonUoKmBZSoM-- | 3135 --cWoXeonUoKmBZSoM-- |
| 3136 ''') | 3136 ''' |
| 3137 m = self.db.issue.get (nodeid, 'messages') [0] | 3137 |
| 3138 def testPGPSignedMessage(self): | |
| 3139 nodeid = self._handle_mail(self.signed_msg) | |
| 3140 m = self.db.issue.get(nodeid, 'messages')[0] | |
| 3138 self.assertEqual(self.db.msg.get(m, 'content'), | 3141 self.assertEqual(self.db.msg.get(m, 'content'), |
| 3139 'This is a pgp signed message.') | 3142 'This is a pgp signed message.') |
| 3143 | |
| 3144 def testPGPSignedMessageFail(self): | |
| 3145 # require both, signing and encryption | |
| 3146 self.instance.config['PGP_REQUIRE_INCOMING'] = 'both' | |
| 3147 self.assertRaises(MailUsageError, self._handle_mail, self.signed_msg) | |
| 3148 | |
| 3149 encrypted_msg = '''Content-Disposition: inline | |
| 3150 From: John Doe <john@test.test> | |
| 3151 To: roundup-admin@example.com | |
| 3152 Subject: [issue] Testing encrypted message... | |
| 3153 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; | |
| 3154 boundary="d6Gm4EdcadzBjdND" | |
| 3155 | |
| 3156 --d6Gm4EdcadzBjdND | |
| 3157 Content-Type: application/pgp-encrypted | |
| 3158 Content-Disposition: attachment | |
| 3159 | |
| 3160 Version: 1 | |
| 3161 | |
| 3162 --d6Gm4EdcadzBjdND | |
| 3163 Content-Type: application/octet-stream | |
| 3164 Content-Disposition: inline; filename="msg.asc" | |
| 3165 | |
| 3166 -----BEGIN PGP MESSAGE----- | |
| 3167 Version: GnuPG v1.4.10 (GNU/Linux) | |
| 3168 | |
| 3169 hQEMAzfeQttq+Q2YAQf9FxCtZVgC7jAy6UkeAJ1imCpnh9DgKA5w40OFtrY4mVAp | |
| 3170 cL7kCkvGvJCW7uQZrmSgIiYaZGLI3GS42XutORC6E6PzBEW0fJUMIXYmoSd0OFeY | |
| 3171 3H2+854qu37W/uCOWM9OnPFIH8g8q8DgYy88i0goM+Ot9Q96yFfJ7QymanOZJgVa | |
| 3172 MNC+oKDiIZKiE3PCwtGr+8CHZN/9J6O4FeJijBlr09C5LXc+Nif5T0R0nt17MAns | |
| 3173 9g2UvGxW8U24NAS1mOg868U05hquLPIcFz9jGZGknJu7HBpOkQ9GjKqkzN8pgZVN | |
| 3174 VbN8IdDqi0QtRKE44jtWQlyNlESMjv6GtC2V9F6qKNK8AfHtBexDhyv4G9cPFFNO | |
| 3175 afQ6e4dPi89RYIQyydtwiqao8fj6jlAy2Z1cbr7YxwBG7BeUZv9yis7ShaAIo78S | |
| 3176 82MrCYpSjfHNwKiSfC5yITw22Uv4wWgixVdAsaSdtBqEKXJPG9LNey18ArsBjSM1 | |
| 3177 P81iDOWUp/uyIe5ZfvNI38BBxEYslPTUlDk2GB8J2Vun7IWHoj9a4tY3IotC9jBr | |
| 3178 5Qnigzqrt7cJZX6OrN0c+wnOjXbMGYXmgSs4jeM= | |
| 3179 =XX5Q | |
| 3180 -----END PGP MESSAGE----- | |
| 3181 | |
| 3182 --d6Gm4EdcadzBjdND-- | |
| 3183 ''' | |
| 3184 def testPGPEncryptedUnsignedMessageError(self): | |
| 3185 self.assertRaises(MailUsageError, self._handle_mail, self.encrypted_msg) | |
| 3186 | |
| 3187 def testPGPEncryptedUnsignedMessage(self): | |
| 3188 # no error if we don't require a signature: | |
| 3189 self.instance.config['PGP_REQUIRE_INCOMING'] = 'encrypted' | |
| 3190 nodeid = self._handle_mail (self.encrypted_msg) | |
| 3191 m = self.db.issue.get(nodeid, 'messages')[0] | |
| 3192 self.assertEqual(self.db.msg.get(m, 'content'), | |
| 3193 'This is the text to be encrypted') | |
| 3194 | |
| 3195 def testPGPEncryptedUnsignedMessageFromNonPGPUser(self): | |
| 3196 msg = self.encrypted_msg.replace('John Doe <john@test.test>', | |
| 3197 '"Contrary, Mary" <mary@test.test>') | |
| 3198 nodeid = self._handle_mail (msg) | |
| 3199 m = self.db.issue.get(nodeid, 'messages')[0] | |
| 3200 self.assertEqual(self.db.msg.get(m, 'content'), | |
| 3201 'This is the text to be encrypted') | |
| 3202 self.assertEqual(self.db.msg.get(m, 'author'), self.mary_id) | |
| 3203 | |
| 3204 # check that a bounce-message that is triggered *after* | |
| 3205 # decrypting is properly encrypted: | |
| 3206 def testPGPEncryptedUnsignedMessageCheckBounce(self): | |
| 3207 # allow non-signed msg | |
| 3208 self.instance.config['PGP_REQUIRE_INCOMING'] = 'encrypted' | |
| 3209 # don't allow creation of message, trigger error *after* decrypt | |
| 3210 self.db.user.set(self.john_id, roles='pgp') | |
| 3211 self.db.security.addPermissionToRole('pgp', 'Email Access') | |
| 3212 self.db.security.addPermissionToRole('pgp', 'Create', 'issue') | |
| 3213 # trap_exc=1: we want a bounce message: | |
| 3214 self._handle_mail(self.encrypted_msg, trap_exc=1) | |
| 3215 m = self._get_mail() | |
| 3216 fp = FeedParser() | |
| 3217 fp.feed(m) | |
| 3218 parts = fp.close().get_payload() | |
| 3219 self.assertEqual(len(parts),2) | |
| 3220 self.assertEqual(parts[0].get_payload().strip(), 'Version: 1') | |
| 3221 crypt = pyme.core.Data(parts[1].get_payload()) | |
| 3222 plain = pyme.core.Data() | |
| 3223 ctx = pyme.core.Context() | |
| 3224 res = ctx.op_decrypt(crypt, plain) | |
| 3225 self.assertEqual(res, None) | |
| 3226 plain.seek(0,0) | |
| 3227 fp = FeedParser() | |
| 3228 fp.feed(plain.read()) | |
| 3229 parts = fp.close().get_payload() | |
| 3230 self.assertEqual(len(parts),2) | |
| 3231 self.assertEqual(parts[0].get_payload().strip(), | |
| 3232 'You are not permitted to create messages.') | |
| 3233 self.assertEqual(parts[1].get_payload().strip(), | |
| 3234 '''Content-Type: text/plain; charset=us-ascii | |
| 3235 Content-Disposition: inline | |
| 3236 | |
| 3237 This is the text to be encrypted''') | |
| 3238 | |
| 3239 | |
| 3240 def testPGPEncryptedSignedMessage(self): | |
| 3241 # require both, signing and encryption | |
| 3242 self.instance.config['PGP_REQUIRE_INCOMING'] = 'both' | |
| 3243 nodeid = self._handle_mail('''Content-Disposition: inline | |
| 3244 From: John Doe <john@test.test> | |
| 3245 To: roundup-admin@example.com | |
| 3246 Subject: Testing encrypted and signed message | |
| 3247 MIME-Version: 1.0 | |
| 3248 Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; | |
| 3249 boundary="ReaqsoxgOBHFXBhH" | |
| 3250 | |
| 3251 --ReaqsoxgOBHFXBhH | |
| 3252 Content-Type: application/pgp-encrypted | |
| 3253 Content-Disposition: attachment | |
| 3254 | |
| 3255 Version: 1 | |
| 3256 | |
| 3257 --ReaqsoxgOBHFXBhH | |
| 3258 Content-Type: application/octet-stream | |
| 3259 Content-Disposition: inline; filename="msg.asc" | |
| 3260 | |
| 3261 -----BEGIN PGP MESSAGE----- | |
| 3262 Version: GnuPG v1.4.10 (GNU/Linux) | |
| 3263 | |
| 3264 hQEMAzfeQttq+Q2YAQf+NaC3r8qBURQqxHH9IAP4vg0QAP2yj3n0v6guo1lRf5BA | |
| 3265 EUfTQ3jc3chxLvzTgoUIuMOvhlNroqR1lgLwhfSTCyuKWDZa+aVNiSgsB2MD44Xd | |
| 3266 mAkKKmnmOGLmfbICbPQZxl4xNhCMTHiAy1xQE6mTj/+pEAq5XxjJUwn/gJ3O1Wmd | |
| 3267 NyWtJY2N+TRbxUVB2WhG1j9J1D2sjhG26TciE8JeuLDZzaiVNOW9YlX2Lw5KtlkR | |
| 3268 Hkgw6Xme06G0XXZUcm9JuBU/7oFP/tSrC1tBsnVlq1pZYf6AygIBdXWb9gD/WmXh | |
| 3269 7Eu/xCKrw4RFnXnTgmBz/NHRfVDkfdSscZqexnG1D9LAwQHSuVf8sxDPNesv0W+8 | |
| 3270 e49loVjvU+Y0BCFQAbWSW4iOEUYZpW/ITRE4+wIqMXZbAraeBV0KPZ4hAa3qSmf+ | |
| 3271 oZBRcbzssL163Odx/OHRuK2J2CHC654+crrlTBnxd/RUKgRbSUKwrZzB2G6OPcGv | |
| 3272 wfiqXsY+XvSZtTbWuvUJxePh8vhhhjpuo1JtlrYc3hZ9OYgoCoV1JiLl5c60U5Es | |
| 3273 oUT9GDl1Qsgb4dF4TJ1IBj+riYiocYpJxPhxzsy6liSLNy2OA6VEjG0FGk53+Ok9 | |
| 3274 7UzOA+WaHJHSXafZzrdP1TWJUFlOMA+dOgTKpH69eL1+IRfywOjEwp1UNSbLnJpc | |
| 3275 D0QQLwIFttplKvYkn0DZByJCVnIlGkl4s5LM5rnc8iecX8Jad0iRIlPV6CVM+Nso | |
| 3276 WdARUfyJfXAmz8uk4f2sVfeMu1gdMySdjvxwlgHDJdBPIG51r2b8L/NCTiC57YjF | |
| 3277 zGhS06FLl3V1xx6gBlpqQHjut3efrAGpXGBVpnTJMOcgYAk= | |
| 3278 =jt/n | |
| 3279 -----END PGP MESSAGE----- | |
| 3280 | |
| 3281 --ReaqsoxgOBHFXBhH-- | |
| 3282 ''') | |
| 3283 m = self.db.issue.get(nodeid, 'messages')[0] | |
| 3284 self.assertEqual(self.db.msg.get(m, 'content'), | |
| 3285 'This is the text of a signed and encrypted email.') | |
| 3286 | |
| 3140 | 3287 |
| 3141 def test_suite(): | 3288 def test_suite(): |
| 3142 suite = unittest.TestSuite() | 3289 suite = unittest.TestSuite() |
| 3143 suite.addTest(unittest.makeSuite(MailgwTestCase)) | 3290 suite.addTest(unittest.makeSuite(MailgwTestCase)) |
| 3144 if pyme is not None: | 3291 if pyme is not None: |
