PYTHON-1158 - Protect pool init and fix auth test

This commit is contained in:
Bernie Hackett 2017-02-07 12:52:07 -08:00
parent 8e57445794
commit 39cf311c74
3 changed files with 65 additions and 22 deletions

View File

@ -201,10 +201,18 @@ class Pool:
# match_hostname directly disable this explicitly.
if hasattr(self.ssl_ctx, "check_hostname"):
self.ssl_ctx.check_hostname = False
# load_cert_chain and load_verify_locations can fail
# if the file contents are invalid.
if ssl_certfile is not None:
self.ssl_ctx.load_cert_chain(ssl_certfile, ssl_keyfile)
try:
self.ssl_ctx.load_cert_chain(ssl_certfile, ssl_keyfile)
except ssl.SSLError:
pass
if ssl_ca_certs is not None:
self.ssl_ctx.load_verify_locations(ssl_ca_certs)
try:
self.ssl_ctx.load_verify_locations(ssl_ca_certs)
except ssl.SSLError:
pass
# PROTOCOL_TLS_CLIENT sets verify_mode to CERT_REQUIRED so
# we always have to set this explicitly.
if ssl_cert_reqs is not None:

View File

@ -76,11 +76,11 @@ pair = '%s:%d' % (host, port)
class AuthContext(object):
def __init__(self):
def __init__(self, **kwargs):
self.auth_enabled = False
self.restricted_localhost = False
try:
self.client = pymongo.MongoClient(host, port)
self.client = pymongo.MongoClient(host, port, **kwargs)
except ConnectionFailure:
self.client = None
else:

View File

@ -36,8 +36,8 @@ from pymongo.common import HAS_SSL, validate_cert_reqs
from pymongo.errors import (ConfigurationError,
ConnectionFailure,
OperationFailure)
from test import host, port, pair, version
from test.utils import server_started_with_auth, remove_all_users
from test import host, port, pair, version, db_user, db_pwd, AuthContext
from test.utils import remove_all_users
CERT_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)),
@ -99,6 +99,13 @@ if HAS_SSL:
if CERT_SSL:
SERVER_IS_RESOLVABLE = is_server_resolvable()
if SIMPLE_SSL or CERT_SSL:
ssl_auth_ctx = AuthContext(ssl=True, ssl_certfile=CLIENT_PEM)
if ssl_auth_ctx.auth_enabled:
ssl_auth_ctx.add_user_and_log_in()
else:
ssl_auth_ctx = None
class TestClientSSL(unittest.TestCase):
@ -245,6 +252,9 @@ class TestSSL(unittest.TestCase):
raise SkipTest("Python 3.0.x has problems "
"with SSL and socket timeouts.")
if ssl_auth_ctx and ssl_auth_ctx.auth_enabled:
raise SkipTest("Can't test with auth enabled.")
def test_simple_ssl(self):
# Expects the server to be running with ssl and with
# no --sslPEMKeyFile or with --sslWeakCertificateValidation
@ -486,6 +496,17 @@ class TestSSL(unittest.TestCase):
MongoClient(uri + "&ssl_match_hostname=false")
class TestX509Auth(unittest.TestCase):
def setUp(self):
if not HAS_SSL:
raise SkipTest("The ssl module is not available.")
if sys.version.startswith('3.0'):
raise SkipTest("Python 3.0.x has problems "
"with SSL and socket timeouts.")
def test_mongodb_x509_auth(self):
# Expects the server to be running with the server.pem, ca.pem
# and crl.pem provided in mongodb and the server tests as well as
@ -498,50 +519,64 @@ class TestSSL(unittest.TestCase):
if not CERT_SSL:
raise SkipTest("No mongod available over SSL with certs")
client = MongoClient(host, port, ssl=True, ssl_certfile=CLIENT_PEM)
if not version.at_least(client, (2, 5, 3, -1)):
if not version.at_least(ssl_auth_ctx.client, (2, 5, 3, -1)):
raise SkipTest("MONGODB-X509 tests require MongoDB 2.5.3 or newer")
if not server_started_with_auth(client):
if not ssl_auth_ctx.auth_enabled:
raise SkipTest('Authentication is not enabled on server')
# This does two things:
# 1. Ensures we test against both client classes.
# 2. Ensures the tests pass regardless of what replica
# set member became primary before the tests run.
ismaster = ssl_auth_ctx.client.admin.command('ismaster')
if 'setName' in ismaster:
get_client = lambda hst: MongoReplicaSetClient(
hst,
ssl=True,
ssl_certfile=CLIENT_PEM,
replicaSet=ismaster['setName'])
else:
get_client = lambda hst: MongoClient(
hst,
ssl=True,
ssl_certfile=CLIENT_PEM)
client = get_client(pair)
client.admin.authenticate(db_user, db_pwd)
# Give admin all necessary privileges.
client['$external'].add_user(MONGODB_X509_USERNAME, roles=[
{'role': 'readWriteAnyDatabase', 'db': 'admin'},
{'role': 'userAdminAnyDatabase', 'db': 'admin'}])
client.admin.logout()
coll = client.pymongo_test.test
self.assertRaises(OperationFailure, coll.count)
self.assertTrue(client.admin.authenticate(MONGODB_X509_USERNAME,
mechanism='MONGODB-X509'))
self.assertTrue(coll.remove())
client.admin.logout()
uri = ('mongodb://%s@%s:%d/?authMechanism='
'MONGODB-X509' % (quote_plus(MONGODB_X509_USERNAME), host, port))
# SSL options aren't supported in the URI...
self.assertTrue(MongoClient(uri, ssl=True, ssl_certfile=CLIENT_PEM))
self.assertTrue(get_client(uri))
# Should require a username
uri = ('mongodb://%s:%d/?authMechanism=MONGODB-X509' % (host, port))
client_bad = MongoClient(uri, ssl=True, ssl_certfile=CLIENT_PEM)
client_bad = get_client(uri)
self.assertRaises(OperationFailure, client_bad.pymongo_test.test.remove)
# Auth should fail if username and certificate do not match
uri = ('mongodb://%s@%s:%d/?authMechanism='
'MONGODB-X509' % (quote_plus("not the username"), host, port))
self.assertRaises(ConfigurationError, MongoClient, uri,
ssl=True, ssl_certfile=CLIENT_PEM)
self.assertRaises(ConfigurationError, get_client, uri)
self.assertRaises(OperationFailure, client.admin.authenticate,
"not the username",
mechanism="MONGODB-X509")
# Invalid certificate (using CA certificate as client certificate)
uri = ('mongodb://%s@%s:%d/?authMechanism='
'MONGODB-X509' % (quote_plus(MONGODB_X509_USERNAME), host, port))
self.assertRaises(ConnectionFailure, MongoClient, uri,
ssl=True, ssl_certfile=CA_PEM)
self.assertRaises(ConnectionFailure, MongoClient, pair,
ssl=True, ssl_certfile=CA_PEM)
# Cleanup
client.admin.authenticate(db_user, db_pwd)
remove_all_users(client['$external'])
client['$external'].logout()
if __name__ == "__main__":
unittest.main()