Skip to content

Commit 3badfce

Browse files
committed
Fix for Python 3.6, win32, pg11 and lack of TLSv1.1
1 parent 3ffdde5 commit 3badfce

File tree

2 files changed

+47
-31
lines changed

2 files changed

+47
-31
lines changed

asyncpg/connect_utils.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,10 @@ def _parse_hostlist(hostlist, port, *, unquote=False):
222222

223223

224224
def _parse_tls_version(tls_version):
225+
if not hasattr(ssl_module, 'TLSVersion'):
226+
raise ValueError(
227+
"TLSVersion is not supported in this version of Python"
228+
)
225229
if tls_version.startswith('SSL'):
226230
raise ValueError(
227231
f"Unsupported TLS version: {tls_version}"
@@ -234,6 +238,10 @@ def _parse_tls_version(tls_version):
234238
)
235239

236240

241+
def _dot_postgresql_path(filename) -> pathlib.Path:
242+
return (pathlib.Path.home() / '.postgresql' / filename).resolve()
243+
244+
237245
def _parse_connect_dsn_and_args(*, dsn, host, port, user,
238246
password, passfile, database, ssl,
239247
connect_timeout, server_settings):
@@ -485,7 +493,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
485493
ssl.load_verify_locations(cafile=sslrootcert)
486494
ssl.verify_mode = ssl_module.CERT_REQUIRED
487495
else:
488-
sslrootcert = os.path.expanduser('~/.postgresql/root.crt')
496+
sslrootcert = _dot_postgresql_path('root.crt')
489497
try:
490498
ssl.load_verify_locations(cafile=sslrootcert)
491499
except FileNotFoundError:
@@ -509,7 +517,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
509517
ssl.load_verify_locations(cafile=sslcrl)
510518
ssl.verify_flags |= ssl_module.VERIFY_CRL_CHECK_CHAIN
511519
else:
512-
sslcrl = os.path.expanduser('~/.postgresql/root.crl')
520+
sslcrl = _dot_postgresql_path('root.crl')
513521
try:
514522
ssl.load_verify_locations(cafile=sslcrl)
515523
except FileNotFoundError:
@@ -520,8 +528,8 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
520528
if sslkey is None:
521529
sslkey = os.getenv('PGSSLKEY')
522530
if not sslkey:
523-
sslkey = os.path.expanduser('~/.postgresql/postgresql.key')
524-
if not os.path.exists(sslkey):
531+
sslkey = _dot_postgresql_path('postgresql.key')
532+
if not sslkey.exists():
525533
sslkey = None
526534
if not sslpassword:
527535
sslpassword = ''
@@ -532,7 +540,7 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
532540
sslcert, keyfile=sslkey, password=lambda: sslpassword
533541
)
534542
else:
535-
sslcert = os.path.expanduser('~/.postgresql/postgresql.crt')
543+
sslcert = _dot_postgresql_path('postgresql.crt')
536544
try:
537545
ssl.load_cert_chain(
538546
sslcert, keyfile=sslkey, password=lambda: sslpassword
@@ -552,13 +560,17 @@ def _parse_connect_dsn_and_args(*, dsn, host, port, user,
552560
ssl.options &= ~ssl_module.OP_NO_COMPRESSION
553561

554562
if ssl_min_protocol_version is None:
555-
ssl_min_protocol_version = os.getenv(
556-
'PGSSLMINPROTOCOLVERSION', 'TLSv1.2'
557-
)
563+
ssl_min_protocol_version = os.getenv('PGSSLMINPROTOCOLVERSION')
558564
if ssl_min_protocol_version:
559565
ssl.minimum_version = _parse_tls_version(
560566
ssl_min_protocol_version
561567
)
568+
else:
569+
try:
570+
ssl.minimum_version = _parse_tls_version('TLSv1.2')
571+
except ValueError:
572+
# Python 3.6 does not have ssl.TLSVersion
573+
pass
562574

563575
if ssl_max_protocol_version is None:
564576
ssl_max_protocol_version = os.getenv('PGSSLMAXPROTOCOLVERSION')

tests/test_connect.py

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
import contextlib
1010
import ipaddress
1111
import os
12+
import pathlib
1213
import platform
1314
import shutil
1415
import ssl
1516
import stat
17+
import sys
1618
import tempfile
1719
import textwrap
1820
import unittest
@@ -46,31 +48,26 @@
4648
@contextlib.contextmanager
4749
def mock_dot_postgresql(*, ca=True, crl=False, client=False, protected=False):
4850
with tempfile.TemporaryDirectory() as temp_dir:
49-
pg_home = os.path.join(temp_dir, '.postgresql')
50-
os.mkdir(pg_home)
51+
home = pathlib.Path(temp_dir)
52+
pg_home = home / '.postgresql'
53+
pg_home.mkdir()
5154
if ca:
52-
shutil.copyfile(
53-
SSL_CA_CERT_FILE, os.path.join(pg_home, 'root.crt')
54-
)
55+
shutil.copyfile(SSL_CA_CERT_FILE, pg_home / 'root.crt')
5556
if crl:
56-
shutil.copyfile(
57-
SSL_CA_CRL_FILE, os.path.join(pg_home, 'root.crl')
58-
)
57+
shutil.copyfile(SSL_CA_CRL_FILE, pg_home / 'root.crl')
5958
if client:
60-
shutil.copyfile(
61-
CLIENT_SSL_CERT_FILE, os.path.join(pg_home, 'postgresql.crt')
62-
)
59+
shutil.copyfile(CLIENT_SSL_CERT_FILE, pg_home / 'postgresql.crt')
6360
if protected:
6461
shutil.copyfile(
65-
CLIENT_SSL_PROTECTED_KEY_FILE,
66-
os.path.join(pg_home, 'postgresql.key'),
62+
CLIENT_SSL_PROTECTED_KEY_FILE, pg_home / 'postgresql.key'
6763
)
6864
else:
6965
shutil.copyfile(
70-
CLIENT_SSL_KEY_FILE,
71-
os.path.join(pg_home, 'postgresql.key'),
66+
CLIENT_SSL_KEY_FILE, pg_home / 'postgresql.key'
7267
)
73-
with unittest.mock.patch.dict('os.environ', {'HOME': temp_dir}):
68+
with unittest.mock.patch(
69+
'pathlib.Path.home', unittest.mock.Mock(return_value=home)
70+
):
7471
yield
7572

7673

@@ -1213,9 +1210,10 @@ def get_server_settings(cls):
12131210
'ssl_cert_file': SSL_CERT_FILE,
12141211
'ssl_key_file': SSL_KEY_FILE,
12151212
'ssl_ca_file': CLIENT_CA_CERT_FILE,
1216-
'ssl_min_protocol_version': 'TLSv1.2',
1217-
'ssl_max_protocol_version': 'TLSv1.2',
12181213
})
1214+
if cls.cluster.get_pg_version() >= (12, 0):
1215+
conf['ssl_min_protocol_version'] = 'TLSv1.2'
1216+
conf['ssl_max_protocol_version'] = 'TLSv1.2'
12191217

12201218
return conf
12211219

@@ -1340,13 +1338,13 @@ async def verify_fails(sslmode, *, host='localhost', exn_type):
13401338
await verify_works('allow')
13411339
await verify_works('prefer')
13421340
await verify_fails('require',
1343-
exn_type=ssl.CertificateError)
1341+
exn_type=ssl.SSLError)
13441342
await verify_fails('verify-ca',
1345-
exn_type=ssl.CertificateError)
1343+
exn_type=ssl.SSLError)
13461344
await verify_fails('verify-ca', host='127.0.0.1',
1347-
exn_type=ssl.CertificateError)
1345+
exn_type=ssl.SSLError)
13481346
await verify_fails('verify-full',
1349-
exn_type=ssl.CertificateError)
1347+
exn_type=ssl.SSLError)
13501348

13511349
async def test_ssl_connection_default_context(self):
13521350
# XXX: uvloop artifact
@@ -1410,7 +1408,13 @@ async def test_executemany_uvloop_ssl_issue_700(self):
14101408
finally:
14111409
await con.close()
14121410

1411+
@unittest.skipIf(
1412+
sys.version_info < (3, 7), "Python < 3.7 doesn't have ssl.TLSVersion"
1413+
)
14131414
async def test_tls_version(self):
1415+
if self.cluster.get_pg_version() < (12, 0):
1416+
self.skipTest("PostgreSQL < 12 cannot set ssl protocol version")
1417+
14141418
# XXX: uvloop artifact
14151419
old_handler = self.loop.get_exception_handler()
14161420
try:
@@ -1420,7 +1424,7 @@ async def test_tls_version(self):
14201424
dsn='postgresql://ssl_user@localhost/postgres'
14211425
'?sslmode=require&ssl_min_protocol_version=TLSv1.3'
14221426
)
1423-
with self.assertRaisesRegex(ssl.SSLError, 'protocol version'):
1427+
with self.assertRaises(ssl.SSLError):
14241428
await self.connect(
14251429
dsn='postgresql://ssl_user@localhost/postgres'
14261430
'?sslmode=require'

0 commit comments

Comments
 (0)