Skip to content

Commit

Permalink
MongoClient respects _connect=False even with auth PYTHON-516
Browse files Browse the repository at this point in the history
  • Loading branch information
ajdavis committed May 10, 2013
1 parent 81b6e1f commit 7667c10
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 34 deletions.
24 changes: 13 additions & 11 deletions pymongo/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ def __init__(self, host=None, port=None, max_pool_size=10,
credentials = (source, unicode(username),
unicode(password), mechanism)
try:
self._cache_credentials(source, credentials)
self._cache_credentials(source, credentials, _connect)
except OperationFailure, exc:
raise ConfigurationError(str(exc))

Expand Down Expand Up @@ -407,9 +407,10 @@ def _purge_index(self, database_name,
if index_name in self.__index_cache[database_name][collection_name]:
del self.__index_cache[database_name][collection_name][index_name]

def _cache_credentials(self, source, credentials):
def _cache_credentials(self, source, credentials, connect=True):
"""Add credentials to the database authentication cache
for automatic login when a socket is created.
for automatic login when a socket is created. If `connect` is True,
verify the credentials on the server first.
"""
if source in self.__auth_credentials:
# Nothing to do if we already have these credentials.
Expand All @@ -418,14 +419,15 @@ def _cache_credentials(self, source, credentials):
raise OperationFailure('Another user is already authenticated '
'to this database. You must logout first.')

sock_info = self.__socket()
try:
# Since __check_auth was called in __socket
# there is no need to call it here.
auth.authenticate(credentials, sock_info, self.__simple_command)
sock_info.authset.add(credentials)
finally:
self.__pool.maybe_return_socket(sock_info)
if connect:
sock_info = self.__socket()
try:
# Since __check_auth was called in __socket
# there is no need to call it here.
auth.authenticate(credentials, sock_info, self.__simple_command)
sock_info.authset.add(credentials)
finally:
self.__pool.maybe_return_socket(sock_info)

self.__auth_credentials[source] = credentials

Expand Down
96 changes: 73 additions & 23 deletions test/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
from pymongo.errors import (ConfigurationError,
ConnectionFailure,
InvalidName,
OperationFailure)
OperationFailure,
PyMongoError)
from test import version, host, port
from test.utils import (assertRaisesExactly,
delay,
Expand Down Expand Up @@ -76,6 +77,35 @@ def test_constants(self):
MongoClient.PORT = port
self.assertTrue(MongoClient())

def test_init_disconnected(self):
c = MongoClient(host, port, _connect=False)

# No errors
c.is_primary
c.is_mongos
c.max_pool_size
c.use_greenlets
c.nodes
c.auto_start_request
c.get_document_class()
c.tz_aware
c.max_bson_size
self.assertEqual(None, c.host)
self.assertEqual(None, c.port)

c.pymongo_test.test.find_one() # Auto-connect.
self.assertEqual(host, c.host)
self.assertEqual(port, c.port)

bad_host = "somedomainthatdoesntexist.org"
c = MongoClient(bad_host, port, connectTimeoutMS=1,_connect=False)
self.assertRaises(ConnectionFailure, c.pymongo_test.test.find_one)

def test_init_disconnected_with_auth(self):
uri = "mongodb://user:pass@somedomainthatdoesntexist"
c = MongoClient(uri, connectTimeoutMS=1, _connect=False)
self.assertRaises(ConnectionFailure, c.pymongo_test.test.find_one)

def test_connect(self):
# Check that the exception is a ConnectionFailure, not a subclass like
# AutoReconnect
Expand Down Expand Up @@ -272,29 +302,49 @@ def test_auth_from_uri(self):

c.admin.system.users.remove({})
c.pymongo_test.system.users.remove({})
c.admin.add_user("admin", "pass")
c.admin.authenticate("admin", "pass")
c.pymongo_test.add_user("user", "pass")

self.assertRaises(ConfigurationError, MongoClient,
"mongodb://foo:bar@%s:%d" % (host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://admin:bar@%s:%d" % (host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://user:pass@%s:%d" % (host, port))
MongoClient("mongodb://admin:pass@%s:%d" % (host, port))

self.assertRaises(ConfigurationError, MongoClient,
"mongodb://admin:pass@%s:%d/pymongo_test" %
(host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://user:foo@%s:%d/pymongo_test" %
(host, port))
MongoClient("mongodb://user:pass@%s:%d/pymongo_test" %
(host, port))

c.admin.system.users.remove({})
c.pymongo_test.system.users.remove({})
try:
c.admin.add_user("admin", "pass")
c.admin.authenticate("admin", "pass")
c.pymongo_test.add_user("user", "pass")

self.assertRaises(ConfigurationError, MongoClient,
"mongodb://foo:bar@%s:%d" % (host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://admin:bar@%s:%d" % (host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://user:pass@%s:%d" % (host, port))
MongoClient("mongodb://admin:pass@%s:%d" % (host, port))

self.assertRaises(ConfigurationError, MongoClient,
"mongodb://admin:pass@%s:%d/pymongo_test" %
(host, port))
self.assertRaises(ConfigurationError, MongoClient,
"mongodb://user:foo@%s:%d/pymongo_test" %
(host, port))
MongoClient("mongodb://user:pass@%s:%d/pymongo_test" %
(host, port))

# Auth with lazy connection.
MongoClient(
"mongodb://user:pass@%s:%d/pymongo_test" % (host, port),
_connect=False).pymongo_test.test.find_one()

# Wrong password.
bad_client = MongoClient(
"mongodb://user:wrong@%s:%d/pymongo_test" % (host, port),
_connect=False)

# If auth fails with lazy connection, MongoClient raises
# AutoReconnect instead of the more appropriate OperationFailure,
# PYTHON-517.
self.assertRaises(
PyMongoError, bad_client.pymongo_test.test.find_one)

finally:
# Clean up.
c.admin.system.users.remove({})
c.pymongo_test.system.users.remove({})

def test_unix_socket(self):
if not hasattr(socket, "AF_UNIX"):
Expand Down

0 comments on commit 7667c10

Please sign in to comment.