Merge lp:~dreamhosters/txaws/920309-fix-ca-certs into lp:txaws
- 920309-fix-ca-certs
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Jamu Kakar | ||||
Approved revision: | 130 | ||||
Merge reported by: | Duncan McGreggor | ||||
Merged at revision: | not available | ||||
Proposed branch: | lp:~dreamhosters/txaws/920309-fix-ca-certs | ||||
Merge into: | lp:txaws | ||||
Diff against target: |
541 lines (+263/-146) 7 files modified
Makefile (+1/-1) txaws/client/ssl.py (+44/-16) txaws/client/tests/test_base.py (+8/-125) txaws/client/tests/test_ssl.py (+199/-0) txaws/exception.py (+6/-0) txaws/s3/client.py (+1/-2) txaws/s3/tests/test_acls.py (+4/-2) |
||||
To merge this branch: | bzr merge lp:~dreamhosters/txaws/920309-fix-ca-certs | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Arsene Rei | Approve | ||
Jamu Kakar | Approve | ||
Review via email: mp+90325@code.launchpad.net |
Commit message
Description of the change
Arsene Rei (arsene-rei) wrote : | # |
Jamu Kakar (jkakar) wrote : | # |
[1]
+ CERTS_PATH environment variable that should point to a directory containing
Is CERTS_PATH a commonly used environment variable...? If it isn't,
it might be better to use TXAWS_CERTS_PATH.
Nice work, +1!
Duncan McGreggor (oubiwann) wrote : | # |
[1] Yeah, the files parameter wasn't used by anything (not even unit tests). Not only that, nothing in the code calls get_ca_certs except for get_global_
[2] Function/
Duncan McGreggor (oubiwann) wrote : | # |
Ah, in a private IRC chat, rei clarified:
"But, regarding C{files} I'm just saying it should be removed from the docstring."
Good catch. I'm on it.
Duncan McGreggor (oubiwann) wrote : | # |
Done, and ready for your review.
- 131. By Duncan McGreggor
-
Fixed docstring from review feedback (by Stephon Striplin).
Arsene Rei (arsene-rei) wrote : | # |
Yup. Looks solid. :)
Duncan McGreggor (oubiwann) wrote : | # |
> [1]
>
> + CERTS_PATH environment variable that should point to a directory containing
>
> Is CERTS_PATH a commonly used environment variable...? If it isn't,
> it might be better to use TXAWS_CERTS_PATH.
Good call -- done and thanks!
- 132. By Duncan McGreggor
-
Per Jamu's review comment, made the shell env var something distinctly
txAWS-ey. - 133. By Duncan McGreggor
-
Fixed pep8 and pyflakes from some recent commits.
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2012-01-25 18:03:05 +0000 |
3 | +++ Makefile 2012-01-26 23:08:23 +0000 |
4 | @@ -48,7 +48,7 @@ |
5 | |
6 | virtual-pep8: VERSION ?= 2.7 |
7 | virtual-pep8: |
8 | - -. .venv-$(VERSION)/bin/activate && pep8 ./txaws |
9 | + -. .venv-$(VERSION)/bin/activate && pep8 --repeat ./txaws |
10 | |
11 | |
12 | virtual-pyflakes: VERSION ?= 2.7 |
13 | |
14 | === modified file 'txaws/client/ssl.py' |
15 | --- txaws/client/ssl.py 2011-11-29 18:42:09 +0000 |
16 | +++ txaws/client/ssl.py 2012-01-26 23:08:23 +0000 |
17 | @@ -1,16 +1,28 @@ |
18 | from glob import glob |
19 | import os |
20 | import re |
21 | +import sys |
22 | |
23 | from OpenSSL import SSL |
24 | from OpenSSL.crypto import load_certificate, FILETYPE_PEM |
25 | |
26 | from twisted.internet.ssl import CertificateOptions |
27 | |
28 | +from txaws import exception |
29 | + |
30 | |
31 | __all__ = ["VerifyingContextFactory", "get_ca_certs"] |
32 | |
33 | |
34 | +# Multiple defaults are supported; just add more paths, separated by colons. |
35 | +if sys.platform == "darwin": |
36 | + DEFAULT_CERTS_PATH = "/System/Library/OpenSSL/certs/:" |
37 | +# XXX Windows users can file a bug to add theirs, since we don't know what |
38 | +# the right path is |
39 | +else: |
40 | + DEFAULT_CERTS_PATH = "/etc/ssl/certs/:" |
41 | + |
42 | + |
43 | class VerifyingContextFactory(CertificateOptions): |
44 | """ |
45 | A SSL context factory to pass to C{connectSSL} to check for hostname |
46 | @@ -71,22 +83,38 @@ |
47 | return context |
48 | |
49 | |
50 | -def get_ca_certs(files="/etc/ssl/certs/*.pem"): |
51 | - """Retrieve a list of CAs pointed by C{files}.""" |
52 | - certificateAuthorityMap = {} |
53 | - for certFileName in glob(files): |
54 | - # There might be some dead symlinks in there, so let's make sure it's |
55 | - # real. |
56 | - if not os.path.exists(certFileName): |
57 | - continue |
58 | - certFile = open(certFileName) |
59 | - data = certFile.read() |
60 | - certFile.close() |
61 | - x509 = load_certificate(FILETYPE_PEM, data) |
62 | - digest = x509.digest("sha1") |
63 | - # Now, de-duplicate in case the same cert has multiple names. |
64 | - certificateAuthorityMap[digest] = x509 |
65 | - return certificateAuthorityMap.values() |
66 | +def get_ca_certs(): |
67 | + """ |
68 | + Retrieve a list of CAs at either the DEFAULT_CERTS_PATH or the env |
69 | + override, TXAWS_CERTS_PATH. |
70 | + |
71 | + In order to find .pem files, this function checks first for presence of the |
72 | + TXAWS_CERTS_PATH environment variable that should point to a directory |
73 | + containing cert files. In the absense of this variable, the module-level |
74 | + DEFAULT_CERTS_PATH will be used instead. |
75 | + |
76 | + Note that both of these variables have have multiple paths in them, just |
77 | + like the familiar PATH environment variable (separated by colons). |
78 | + """ |
79 | + cert_paths = os.getenv("TXAWS_CERTS_PATH", DEFAULT_CERTS_PATH).split(":") |
80 | + certificate_authority_map = {} |
81 | + for path in cert_paths: |
82 | + for cert_file_name in glob(os.path.join(path, "*.pem")): |
83 | + # There might be some dead symlinks in there, so let's make sure |
84 | + # it's real. |
85 | + if not os.path.exists(cert_file_name): |
86 | + continue |
87 | + cert_file = open(cert_file_name) |
88 | + data = cert_file.read() |
89 | + cert_file.close() |
90 | + x509 = load_certificate(FILETYPE_PEM, data) |
91 | + digest = x509.digest("sha1") |
92 | + # Now, de-duplicate in case the same cert has multiple names. |
93 | + certificate_authority_map[digest] = x509 |
94 | + values = certificate_authority_map.values() |
95 | + if len(values) == 0: |
96 | + raise exception.CertsNotFoundError("Could not find any .pem files.") |
97 | + return values |
98 | |
99 | |
100 | _ca_certs = None |
101 | |
102 | === renamed file 'txaws/client/tests/test_client.py' => 'txaws/client/tests/test_base.py' |
103 | --- txaws/client/tests/test_client.py 2011-11-29 18:47:00 +0000 |
104 | +++ txaws/client/tests/test_base.py 2012-01-26 23:08:23 +0000 |
105 | @@ -1,38 +1,22 @@ |
106 | import os |
107 | |
108 | -from OpenSSL.crypto import load_certificate, FILETYPE_PEM |
109 | -from OpenSSL.SSL import Error as SSLError |
110 | -from OpenSSL.version import __version__ as pyopenssl_version |
111 | - |
112 | from twisted.internet import reactor |
113 | -from twisted.internet.ssl import DefaultOpenSSLContextFactory |
114 | from twisted.internet.error import ConnectionRefusedError |
115 | from twisted.protocols.policies import WrappingFactory |
116 | from twisted.python import log |
117 | from twisted.python.filepath import FilePath |
118 | from twisted.python.failure import Failure |
119 | +from twisted.test.test_sslverify import makeCertificate |
120 | from twisted.web import server, static |
121 | from twisted.web.client import HTTPClientFactory |
122 | from twisted.web.error import Error as TwistedWebError |
123 | |
124 | +from txaws.client import ssl |
125 | from txaws.client.base import BaseClient, BaseQuery, error_wrapper |
126 | -from txaws.client.ssl import VerifyingContextFactory |
127 | from txaws.service import AWSServiceEndpoint |
128 | from txaws.testing.base import TXAWSTestCase |
129 | |
130 | |
131 | -def sibpath(path): |
132 | - return os.path.join(os.path.dirname(__file__), path) |
133 | - |
134 | - |
135 | -PRIVKEY = sibpath("private.ssl") |
136 | -PUBKEY = sibpath("public.ssl") |
137 | -BADPRIVKEY = sibpath("badprivate.ssl") |
138 | -BADPUBKEY = sibpath("badpublic.ssl") |
139 | -PRIVSANKEY = sibpath("private_san.ssl") |
140 | -PUBSANKEY = sibpath("public_san.ssl") |
141 | - |
142 | - |
143 | class ErrorWrapperTestCase(TXAWSTestCase): |
144 | |
145 | def test_204_no_content(self): |
146 | @@ -168,6 +152,9 @@ |
147 | d.addCallback(query.get_response_headers) |
148 | return d.addCallback(check_results) |
149 | |
150 | + # XXX for systems that don't have certs in the DEFAULT_CERT_PATH, this test |
151 | + # will fail; instead, let's create some certs in a temp directory and set |
152 | + # the DEFAULT_CERT_PATH to point there. |
153 | def test_ssl_hostname_verification(self): |
154 | """ |
155 | If the endpoint passed to L{BaseQuery} has C{ssl_hostname_verification} |
156 | @@ -183,6 +170,8 @@ |
157 | def connectSSL(self, host, port, client, factory): |
158 | self.connects.append((host, port, client, factory)) |
159 | |
160 | + certs = makeCertificate(O="Test Certificate", CN="something")[1] |
161 | + self.patch(ssl, "_ca_certs", certs) |
162 | fake_reactor = FakeReactor() |
163 | endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
164 | query = BaseQuery("an action", "creds", endpoint, fake_reactor) |
165 | @@ -190,112 +179,6 @@ |
166 | [(host, port, client, factory)] = fake_reactor.connects |
167 | self.assertEqual("example.com", host) |
168 | self.assertEqual(443, port) |
169 | - self.assertTrue(isinstance(factory, VerifyingContextFactory)) |
170 | + self.assertTrue(isinstance(factory, ssl.VerifyingContextFactory)) |
171 | self.assertEqual("example.com", factory.host) |
172 | self.assertNotEqual([], factory.caCerts) |
173 | - |
174 | - |
175 | -class BaseQuerySSLTestCase(TXAWSTestCase): |
176 | - |
177 | - def setUp(self): |
178 | - self.cleanupServerConnections = 0 |
179 | - name = self.mktemp() |
180 | - os.mkdir(name) |
181 | - FilePath(name).child("file").setContent("0123456789") |
182 | - r = static.File(name) |
183 | - self.site = server.Site(r, timeout=None) |
184 | - self.wrapper = WrappingFactory(self.site) |
185 | - from txaws.client import ssl |
186 | - pub_key = file(PUBKEY) |
187 | - pub_key_data = pub_key.read() |
188 | - pub_key.close() |
189 | - pub_key_san = file(PUBSANKEY) |
190 | - pub_key_san_data = pub_key_san.read() |
191 | - pub_key_san.close() |
192 | - ssl._ca_certs = [load_certificate(FILETYPE_PEM, pub_key_data), |
193 | - load_certificate(FILETYPE_PEM, pub_key_san_data)] |
194 | - |
195 | - def tearDown(self): |
196 | - from txaws.client import ssl |
197 | - ssl._ca_certs = None |
198 | - # If the test indicated it might leave some server-side connections |
199 | - # around, clean them up. |
200 | - connections = self.wrapper.protocols.keys() |
201 | - # If there are fewer server-side connections than requested, |
202 | - # that's okay. Some might have noticed that the client closed |
203 | - # the connection and cleaned up after themselves. |
204 | - for n in range(min(len(connections), self.cleanupServerConnections)): |
205 | - proto = connections.pop() |
206 | - log.msg("Closing %r" % (proto,)) |
207 | - proto.transport.loseConnection() |
208 | - if connections: |
209 | - log.msg("Some left-over connections; this test is probably buggy.") |
210 | - return self.port.stopListening() |
211 | - |
212 | - def _get_url(self, path): |
213 | - return "https://localhost:%d/%s" % (self.portno, path) |
214 | - |
215 | - def test_ssl_verification_positive(self): |
216 | - """ |
217 | - The L{VerifyingContextFactory} properly allows to connect to the |
218 | - endpoint if the certificates match. |
219 | - """ |
220 | - context_factory = DefaultOpenSSLContextFactory(PRIVKEY, PUBKEY) |
221 | - self.port = reactor.listenSSL( |
222 | - 0, self.site, context_factory, interface="127.0.0.1") |
223 | - self.portno = self.port.getHost().port |
224 | - |
225 | - endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
226 | - query = BaseQuery("an action", "creds", endpoint) |
227 | - d = query.get_page(self._get_url("file")) |
228 | - return d.addCallback(self.assertEquals, "0123456789") |
229 | - |
230 | - def test_ssl_verification_negative(self): |
231 | - """ |
232 | - The L{VerifyingContextFactory} fails with a SSL error the certificates |
233 | - can't be checked. |
234 | - """ |
235 | - context_factory = DefaultOpenSSLContextFactory(BADPRIVKEY, BADPUBKEY) |
236 | - self.port = reactor.listenSSL( |
237 | - 0, self.site, context_factory, interface="127.0.0.1") |
238 | - self.portno = self.port.getHost().port |
239 | - |
240 | - endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
241 | - query = BaseQuery("an action", "creds", endpoint) |
242 | - d = query.get_page(self._get_url("file")) |
243 | - return self.assertFailure(d, SSLError) |
244 | - |
245 | - def test_ssl_verification_bypassed(self): |
246 | - """ |
247 | - L{BaseQuery} doesn't use L{VerifyingContextFactory} |
248 | - if C{ssl_hostname_verification} is C{False}, thus allowing to connect |
249 | - to non-secure endpoints. |
250 | - """ |
251 | - context_factory = DefaultOpenSSLContextFactory(BADPRIVKEY, BADPUBKEY) |
252 | - self.port = reactor.listenSSL( |
253 | - 0, self.site, context_factory, interface="127.0.0.1") |
254 | - self.portno = self.port.getHost().port |
255 | - |
256 | - endpoint = AWSServiceEndpoint(ssl_hostname_verification=False) |
257 | - query = BaseQuery("an action", "creds", endpoint) |
258 | - d = query.get_page(self._get_url("file")) |
259 | - return d.addCallback(self.assertEquals, "0123456789") |
260 | - |
261 | - def test_ssl_subject_alt_name(self): |
262 | - """ |
263 | - L{VerifyingContextFactory} supports checking C{subjectAltName} in the |
264 | - certificate if it's available. |
265 | - """ |
266 | - context_factory = DefaultOpenSSLContextFactory(PRIVSANKEY, PUBSANKEY) |
267 | - self.port = reactor.listenSSL( |
268 | - 0, self.site, context_factory, interface="127.0.0.1") |
269 | - self.portno = self.port.getHost().port |
270 | - |
271 | - endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
272 | - query = BaseQuery("an action", "creds", endpoint) |
273 | - d = query.get_page("https://127.0.0.1:%d/file" % (self.portno,)) |
274 | - return d.addCallback(self.assertEquals, "0123456789") |
275 | - |
276 | - if pyopenssl_version < "0.12": |
277 | - test_ssl_subject_alt_name.skip = ( |
278 | - "subjectAltName not supported by older PyOpenSSL") |
279 | |
280 | === added file 'txaws/client/tests/test_ssl.py' |
281 | --- txaws/client/tests/test_ssl.py 1970-01-01 00:00:00 +0000 |
282 | +++ txaws/client/tests/test_ssl.py 2012-01-26 23:08:23 +0000 |
283 | @@ -0,0 +1,199 @@ |
284 | +import os |
285 | +import tempfile |
286 | + |
287 | +from OpenSSL.crypto import dump_certificate, load_certificate, FILETYPE_PEM |
288 | +from OpenSSL.SSL import Error as SSLError |
289 | +from OpenSSL.version import __version__ as pyopenssl_version |
290 | + |
291 | +from twisted.internet import reactor |
292 | +from twisted.internet.ssl import DefaultOpenSSLContextFactory |
293 | +from twisted.protocols.policies import WrappingFactory |
294 | +from twisted.python import log |
295 | +from twisted.python.filepath import FilePath |
296 | +from twisted.test.test_sslverify import makeCertificate |
297 | +from twisted.web import server, static |
298 | + |
299 | +from txaws import exception |
300 | +from txaws.client import ssl |
301 | +from txaws.client.base import BaseQuery |
302 | +from txaws.service import AWSServiceEndpoint |
303 | +from txaws.testing.base import TXAWSTestCase |
304 | + |
305 | + |
306 | +def sibpath(path): |
307 | + return os.path.join(os.path.dirname(__file__), path) |
308 | + |
309 | + |
310 | +PRIVKEY = sibpath("private.ssl") |
311 | +PUBKEY = sibpath("public.ssl") |
312 | +BADPRIVKEY = sibpath("badprivate.ssl") |
313 | +BADPUBKEY = sibpath("badpublic.ssl") |
314 | +PRIVSANKEY = sibpath("private_san.ssl") |
315 | +PUBSANKEY = sibpath("public_san.ssl") |
316 | + |
317 | + |
318 | +class BaseQuerySSLTestCase(TXAWSTestCase): |
319 | + |
320 | + def setUp(self): |
321 | + self.cleanupServerConnections = 0 |
322 | + name = self.mktemp() |
323 | + os.mkdir(name) |
324 | + FilePath(name).child("file").setContent("0123456789") |
325 | + r = static.File(name) |
326 | + self.site = server.Site(r, timeout=None) |
327 | + self.wrapper = WrappingFactory(self.site) |
328 | + pub_key = file(PUBKEY) |
329 | + pub_key_data = pub_key.read() |
330 | + pub_key.close() |
331 | + pub_key_san = file(PUBSANKEY) |
332 | + pub_key_san_data = pub_key_san.read() |
333 | + pub_key_san.close() |
334 | + ssl._ca_certs = [load_certificate(FILETYPE_PEM, pub_key_data), |
335 | + load_certificate(FILETYPE_PEM, pub_key_san_data)] |
336 | + |
337 | + def tearDown(self): |
338 | + ssl._ca_certs = None |
339 | + # If the test indicated it might leave some server-side connections |
340 | + # around, clean them up. |
341 | + connections = self.wrapper.protocols.keys() |
342 | + # If there are fewer server-side connections than requested, |
343 | + # that's okay. Some might have noticed that the client closed |
344 | + # the connection and cleaned up after themselves. |
345 | + for n in range(min(len(connections), self.cleanupServerConnections)): |
346 | + proto = connections.pop() |
347 | + log.msg("Closing %r" % (proto,)) |
348 | + proto.transport.loseConnection() |
349 | + if connections: |
350 | + log.msg("Some left-over connections; this test is probably buggy.") |
351 | + return self.port.stopListening() |
352 | + |
353 | + def _get_url(self, path): |
354 | + return "https://localhost:%d/%s" % (self.portno, path) |
355 | + |
356 | + def test_ssl_verification_positive(self): |
357 | + """ |
358 | + The L{VerifyingContextFactory} properly allows to connect to the |
359 | + endpoint if the certificates match. |
360 | + """ |
361 | + context_factory = DefaultOpenSSLContextFactory(PRIVKEY, PUBKEY) |
362 | + self.port = reactor.listenSSL( |
363 | + 0, self.site, context_factory, interface="127.0.0.1") |
364 | + self.portno = self.port.getHost().port |
365 | + |
366 | + endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
367 | + query = BaseQuery("an action", "creds", endpoint) |
368 | + d = query.get_page(self._get_url("file")) |
369 | + return d.addCallback(self.assertEquals, "0123456789") |
370 | + |
371 | + def test_ssl_verification_negative(self): |
372 | + """ |
373 | + The L{VerifyingContextFactory} fails with a SSL error the certificates |
374 | + can't be checked. |
375 | + """ |
376 | + context_factory = DefaultOpenSSLContextFactory(BADPRIVKEY, BADPUBKEY) |
377 | + self.port = reactor.listenSSL( |
378 | + 0, self.site, context_factory, interface="127.0.0.1") |
379 | + self.portno = self.port.getHost().port |
380 | + |
381 | + endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
382 | + query = BaseQuery("an action", "creds", endpoint) |
383 | + d = query.get_page(self._get_url("file")) |
384 | + return self.assertFailure(d, SSLError) |
385 | + |
386 | + def test_ssl_verification_bypassed(self): |
387 | + """ |
388 | + L{BaseQuery} doesn't use L{VerifyingContextFactory} |
389 | + if C{ssl_hostname_verification} is C{False}, thus allowing to connect |
390 | + to non-secure endpoints. |
391 | + """ |
392 | + context_factory = DefaultOpenSSLContextFactory(BADPRIVKEY, BADPUBKEY) |
393 | + self.port = reactor.listenSSL( |
394 | + 0, self.site, context_factory, interface="127.0.0.1") |
395 | + self.portno = self.port.getHost().port |
396 | + |
397 | + endpoint = AWSServiceEndpoint(ssl_hostname_verification=False) |
398 | + query = BaseQuery("an action", "creds", endpoint) |
399 | + d = query.get_page(self._get_url("file")) |
400 | + return d.addCallback(self.assertEquals, "0123456789") |
401 | + |
402 | + def test_ssl_subject_alt_name(self): |
403 | + """ |
404 | + L{VerifyingContextFactory} supports checking C{subjectAltName} in the |
405 | + certificate if it's available. |
406 | + """ |
407 | + context_factory = DefaultOpenSSLContextFactory(PRIVSANKEY, PUBSANKEY) |
408 | + self.port = reactor.listenSSL( |
409 | + 0, self.site, context_factory, interface="127.0.0.1") |
410 | + self.portno = self.port.getHost().port |
411 | + |
412 | + endpoint = AWSServiceEndpoint(ssl_hostname_verification=True) |
413 | + query = BaseQuery("an action", "creds", endpoint) |
414 | + d = query.get_page("https://127.0.0.1:%d/file" % (self.portno,)) |
415 | + return d.addCallback(self.assertEquals, "0123456789") |
416 | + |
417 | + if pyopenssl_version < "0.12": |
418 | + test_ssl_subject_alt_name.skip = ( |
419 | + "subjectAltName not supported by older PyOpenSSL") |
420 | + |
421 | + |
422 | +class CertsFilesTestCase(TXAWSTestCase): |
423 | + |
424 | + def setUp(self): |
425 | + super(CertsFilesTestCase, self).setUp() |
426 | + # set up temp dir with no certs |
427 | + self.no_certs_dir = tempfile.mkdtemp() |
428 | + # create certs |
429 | + cert1 = makeCertificate(O="Server Certificate 1", CN="cn1") |
430 | + cert2 = makeCertificate(O="Server Certificate 2", CN="cn2") |
431 | + cert3 = makeCertificate(O="Server Certificate 3", CN="cn3") |
432 | + # set up temp dir with one cert |
433 | + self.one_cert_dir = tempfile.mkdtemp() |
434 | + self.cert1 = self._write_pem(cert1, self.one_cert_dir, "cert1.pem") |
435 | + # set up temp dir with two certs |
436 | + self.two_certs_dir = tempfile.mkdtemp() |
437 | + self.cert2 = self._write_pem(cert2, self.two_certs_dir, "cert2.pem") |
438 | + self.cert3 = self._write_pem(cert3, self.two_certs_dir, "cert3.pem") |
439 | + |
440 | + def tearDown(self): |
441 | + super(CertsFilesTestCase, self).tearDown() |
442 | + os.unlink(self.cert1) |
443 | + os.unlink(self.cert2) |
444 | + os.unlink(self.cert3) |
445 | + os.removedirs(self.no_certs_dir) |
446 | + os.removedirs(self.one_cert_dir) |
447 | + os.removedirs(self.two_certs_dir) |
448 | + |
449 | + def _write_pem(self, cert, dir, filename): |
450 | + data = dump_certificate(FILETYPE_PEM, cert[1]) |
451 | + full_path = os.path.join(dir, filename) |
452 | + fh = open(full_path, "w") |
453 | + fh.write(data) |
454 | + fh.close() |
455 | + return full_path |
456 | + |
457 | + def test_get_ca_certs_no_certs(self): |
458 | + os.environ["TXAWS_CERTS_PATH"] = self.no_certs_dir |
459 | + self.patch(ssl, "DEFAULT_CERTS_PATH", self.no_certs_dir) |
460 | + self.assertRaises(exception.CertsNotFoundError, ssl.get_ca_certs) |
461 | + |
462 | + def test_get_ca_certs_with_default_path(self): |
463 | + self.patch(ssl, "DEFAULT_CERTS_PATH", self.two_certs_dir) |
464 | + certs = ssl.get_ca_certs() |
465 | + self.assertEqual(len(certs), 2) |
466 | + |
467 | + def test_get_ca_certs_with_env_path(self): |
468 | + os.environ["TXAWS_CERTS_PATH"] = self.one_cert_dir |
469 | + certs = ssl.get_ca_certs() |
470 | + self.assertEqual(len(certs), 1) |
471 | + |
472 | + def test_get_ca_certs_multiple_paths(self): |
473 | + os.environ["TXAWS_CERTS_PATH"] = "%s:%s" % ( |
474 | + self.one_cert_dir, self.two_certs_dir) |
475 | + certs = ssl.get_ca_certs() |
476 | + self.assertEqual(len(certs), 3) |
477 | + |
478 | + def test_get_ca_certs_one_empty_path(self): |
479 | + os.environ["TXAWS_CERTS_PATH"] = "%s:%s" % ( |
480 | + self.no_certs_dir, self.one_cert_dir) |
481 | + certs = ssl.get_ca_certs() |
482 | + self.assertEqual(len(certs), 1) |
483 | |
484 | === modified file 'txaws/exception.py' |
485 | --- txaws/exception.py 2012-01-23 00:48:29 +0000 |
486 | +++ txaws/exception.py 2012-01-26 23:08:23 +0000 |
487 | @@ -126,3 +126,9 @@ |
488 | """ |
489 | txAWS was unable to parse the server response. |
490 | """ |
491 | + |
492 | + |
493 | +class CertsNotFoundError(Exception): |
494 | + """ |
495 | + txAWS was not able to find any SSL certificates. |
496 | + """ |
497 | |
498 | === modified file 'txaws/s3/client.py' |
499 | --- txaws/s3/client.py 2012-01-26 02:19:17 +0000 |
500 | +++ txaws/s3/client.py 2012-01-26 23:08:23 +0000 |
501 | @@ -198,7 +198,6 @@ |
502 | def _parse_lifecycle_config(self, xml_bytes): |
503 | """Parse a C{LifecycleConfiguration} XML document.""" |
504 | root = XML(xml_bytes) |
505 | - contents = [] |
506 | rules = [] |
507 | |
508 | for content_data in root.findall("Rule"): |
509 | @@ -209,7 +208,7 @@ |
510 | rules.append( |
511 | LifecycleConfigurationRule(id, prefix, status, expiration)) |
512 | |
513 | - return LifecycleConfiguration(rules) |
514 | + return LifecycleConfiguration(rules) |
515 | |
516 | def get_bucket_acl(self, bucket): |
517 | """ |
518 | |
519 | === modified file 'txaws/s3/tests/test_acls.py' |
520 | --- txaws/s3/tests/test_acls.py 2012-01-24 23:01:13 +0000 |
521 | +++ txaws/s3/tests/test_acls.py 2012-01-26 23:08:23 +0000 |
522 | @@ -41,7 +41,8 @@ |
523 | grantee = acls.Grantee(email_address="BucketOwnersEmail@amazon.com") |
524 | xml_bytes = grantee.to_xml() |
525 | self.assertEquals(xml_bytes, """\ |
526 | -<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="AmazonCustomerByEmail"> |
527 | +<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\ |
528 | + xsi:type="AmazonCustomerByEmail"> |
529 | <EmailAddress>BucketOwnersEmail@amazon.com</EmailAddress> |
530 | </Grantee> |
531 | """) |
532 | @@ -51,7 +52,8 @@ |
533 | uri='http://acs.amazonaws.com/groups/global/AuthenticatedUsers') |
534 | xml_bytes = grantee.to_xml() |
535 | self.assertEquals(xml_bytes, """\ |
536 | -<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group"> |
537 | +<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\ |
538 | + xsi:type="Group"> |
539 | <URI>http://acs.amazonaws.com/groups/global/AuthenticatedUsers</URI> |
540 | </Grantee> |
541 | """) |
+ Retrieve a list of CAs pointed by C{files}.
files is no longer an argument to get_ca_certs.
They had inline imports...?