From 7667c10deda73084ec956ea0661ac3bdf14a1de2 Mon Sep 17 00:00:00 2001 From: "A. Jesse Jiryu Davis" Date: Fri, 10 May 2013 14:32:17 -0400 Subject: [PATCH] MongoClient respects _connect=False even with auth PYTHON-516 --- pymongo/mongo_client.py | 24 ++++++----- test/test_client.py | 96 +++++++++++++++++++++++++++++++---------- 2 files changed, 86 insertions(+), 34 deletions(-) diff --git a/pymongo/mongo_client.py b/pymongo/mongo_client.py index 1d743df9bb..88a52ee95e 100644 --- a/pymongo/mongo_client.py +++ b/pymongo/mongo_client.py @@ -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)) @@ -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. @@ -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 diff --git a/test/test_client.py b/test/test_client.py index 807869ab29..4b6743559a 100644 --- a/test/test_client.py +++ b/test/test_client.py @@ -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, @@ -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 @@ -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"):