PYTHON-1314 Remove Database.authenticate and Database.logout (#568)
This commit is contained in:
parent
c15028a6c7
commit
95974617bd
@ -23,6 +23,8 @@ Breaking Changes in 4.0
|
||||
:attr:`pymongo.mongo_client.MongoClient.is_locked`.
|
||||
- Removed :meth:`pymongo.mongo_client.MongoClient.database_names`.
|
||||
- Removed :meth:`pymongo.database.Database.collection_names`.
|
||||
- Removed :meth:`pymongo.database.Database.authenticate` and
|
||||
:meth:`pymongo.database.Database.logout`.
|
||||
- Removed :meth:`pymongo.database.Database.error`,
|
||||
:meth:`pymongo.database.Database.last_status`,
|
||||
:meth:`pymongo.database.Database.previous_error`,
|
||||
|
||||
@ -121,6 +121,27 @@ can be changed to this::
|
||||
Database
|
||||
--------
|
||||
|
||||
Database.authenticate and Database.logout are removed
|
||||
.....................................................
|
||||
|
||||
Removed :meth:`pymongo.database.Database.authenticate` and
|
||||
:meth:`pymongo.database.Database.logout`. Authenticating multiple users
|
||||
on the same client conflicts with support for logical sessions in MongoDB 3.6+.
|
||||
To authenticate as multiple users, create multiple instances of
|
||||
:class:`~pymongo.mongo_client.MongoClient`. Code like this::
|
||||
|
||||
client = MongoClient()
|
||||
client.admin.authenticate('user1', 'pass1')
|
||||
client.admin.authenticate('user2', 'pass2')
|
||||
|
||||
can be changed to this::
|
||||
|
||||
client1 = MongoClient(username='user1', password='pass1')
|
||||
client2 = MongoClient(username='user2', password='pass2')
|
||||
|
||||
Alternatively, create a single user that contains all the authentication privileges
|
||||
required by your application.
|
||||
|
||||
Database.collection_names is removed
|
||||
....................................
|
||||
|
||||
|
||||
@ -580,7 +580,3 @@ def authenticate(credentials, sock_info):
|
||||
auth_func = _AUTH_MAP.get(mechanism)
|
||||
auth_func(credentials, sock_info)
|
||||
|
||||
|
||||
def logout(source, sock_info):
|
||||
"""Log out from a database."""
|
||||
sock_info.command(source, {'logout': 1})
|
||||
|
||||
@ -351,12 +351,11 @@ class ClientSession(object):
|
||||
:class:`ClientSession`, call
|
||||
:meth:`~pymongo.mongo_client.MongoClient.start_session`.
|
||||
"""
|
||||
def __init__(self, client, server_session, options, authset, implicit):
|
||||
def __init__(self, client, server_session, options, implicit):
|
||||
# A MongoClient, a _ServerSession, a SessionOptions, and a set.
|
||||
self._client = client
|
||||
self._server_session = server_session
|
||||
self._options = options
|
||||
self._authset = authset
|
||||
self._cluster_time = None
|
||||
self._operation_time = None
|
||||
# Is this an implicitly created session?
|
||||
|
||||
@ -980,116 +980,6 @@ class Database(common.BaseObject):
|
||||
|
||||
next = __next__
|
||||
|
||||
def authenticate(self, name=None, password=None,
|
||||
source=None, mechanism='DEFAULT', **kwargs):
|
||||
"""**DEPRECATED**: Authenticate to use this database.
|
||||
|
||||
.. warning:: Starting in MongoDB 3.6, calling :meth:`authenticate`
|
||||
invalidates all existing cursors. It may also leave logical sessions
|
||||
open on the server for up to 30 minutes until they time out.
|
||||
|
||||
Authentication lasts for the life of the underlying client
|
||||
instance, or until :meth:`logout` is called.
|
||||
|
||||
Raises :class:`TypeError` if (required) `name`, (optional) `password`,
|
||||
or (optional) `source` is not an instance of :class:`basestring`
|
||||
(:class:`str` in python 3).
|
||||
|
||||
.. note::
|
||||
- This method authenticates the current connection, and
|
||||
will also cause all new :class:`~socket.socket` connections
|
||||
in the underlying client instance to be authenticated automatically.
|
||||
|
||||
- Authenticating more than once on the same database with different
|
||||
credentials is not supported. You must call :meth:`logout` before
|
||||
authenticating with new credentials.
|
||||
|
||||
- When sharing a client instance between multiple threads, all
|
||||
threads will share the authentication. If you need different
|
||||
authentication profiles for different purposes you must use
|
||||
distinct client instances.
|
||||
|
||||
:Parameters:
|
||||
- `name`: the name of the user to authenticate. Optional when
|
||||
`mechanism` is MONGODB-X509 and the MongoDB server version is
|
||||
>= 3.4.
|
||||
- `password` (optional): the password of the user to authenticate.
|
||||
Not used with GSSAPI or MONGODB-X509 authentication.
|
||||
- `source` (optional): the database to authenticate on. If not
|
||||
specified the current database is used.
|
||||
- `mechanism` (optional): See :data:`~pymongo.auth.MECHANISMS` for
|
||||
options. If no mechanism is specified, PyMongo automatically uses
|
||||
MONGODB-CR when connected to a pre-3.0 version of MongoDB,
|
||||
SCRAM-SHA-1 when connected to MongoDB 3.0 through 3.6, and
|
||||
negotiates the mechanism to use (SCRAM-SHA-1 or SCRAM-SHA-256) when
|
||||
connected to MongoDB 4.0+.
|
||||
- `authMechanismProperties` (optional): Used to specify
|
||||
authentication mechanism specific options. To specify the service
|
||||
name for GSSAPI authentication pass
|
||||
``authMechanismProperties='SERVICE_NAME:<service name>'``.
|
||||
To specify the session token for MONGODB-AWS authentication pass
|
||||
``authMechanismProperties='AWS_SESSION_TOKEN:<session token>'``.
|
||||
|
||||
.. versionchanged:: 3.7
|
||||
Added support for SCRAM-SHA-256 with MongoDB 4.0 and later.
|
||||
|
||||
.. versionchanged:: 3.5
|
||||
Deprecated. Authenticating multiple users conflicts with support for
|
||||
logical sessions in MongoDB 3.6. To authenticate as multiple users,
|
||||
create multiple instances of MongoClient.
|
||||
|
||||
.. versionadded:: 2.8
|
||||
Use SCRAM-SHA-1 with MongoDB 3.0 and later.
|
||||
|
||||
.. versionchanged:: 2.5
|
||||
Added the `source` and `mechanism` parameters. :meth:`authenticate`
|
||||
now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
|
||||
authentication fails due to invalid credentials or configuration
|
||||
issues.
|
||||
|
||||
.. mongodoc:: authenticate
|
||||
"""
|
||||
if name is not None and not isinstance(name, str):
|
||||
raise TypeError("name must be an instance of str")
|
||||
if password is not None and not isinstance(password, str):
|
||||
raise TypeError("password must be an instance of str")
|
||||
if source is not None and not isinstance(source, str):
|
||||
raise TypeError("source must be an instance of str")
|
||||
common.validate_auth_mechanism('mechanism', mechanism)
|
||||
|
||||
validated_options = common._CaseInsensitiveDictionary()
|
||||
for option, value in kwargs.items():
|
||||
normalized, val = common.validate_auth_option(option, value)
|
||||
validated_options[normalized] = val
|
||||
|
||||
credentials = auth._build_credentials_tuple(
|
||||
mechanism,
|
||||
source,
|
||||
name,
|
||||
password,
|
||||
validated_options,
|
||||
self.name)
|
||||
|
||||
self.client._cache_credentials(
|
||||
self.name,
|
||||
credentials,
|
||||
connect=True)
|
||||
|
||||
return True
|
||||
|
||||
def logout(self):
|
||||
"""**DEPRECATED**: Deauthorize use of this database.
|
||||
|
||||
.. warning:: Starting in MongoDB 3.6, calling :meth:`logout`
|
||||
invalidates all existing cursors. It may also leave logical sessions
|
||||
open on the server for up to 30 minutes until they time out.
|
||||
"""
|
||||
warnings.warn("Database.logout() is deprecated",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
|
||||
# Sockets will be deauthenticated as they are used.
|
||||
self.client._purge_credentials(self.name)
|
||||
|
||||
def dereference(self, dbref, session=None, **kwargs):
|
||||
"""Dereference a :class:`~bson.dbref.DBRef`, getting the
|
||||
document it points to.
|
||||
|
||||
@ -708,7 +708,7 @@ class MongoClient(common.BaseObject):
|
||||
self.__all_credentials = {}
|
||||
creds = options.credentials
|
||||
if creds:
|
||||
self._cache_credentials(creds.source, creds)
|
||||
self.__all_credentials[creds.source] = creds
|
||||
|
||||
self._topology_settings = TopologySettings(
|
||||
seeds=seeds,
|
||||
@ -753,38 +753,6 @@ class MongoClient(common.BaseObject):
|
||||
self._encrypter = _Encrypter.create(
|
||||
self, self.__options.auto_encryption_opts)
|
||||
|
||||
def _cache_credentials(self, source, credentials, connect=False):
|
||||
"""Save a set of authentication credentials.
|
||||
|
||||
The credentials are used to login a socket whenever one is created.
|
||||
If `connect` is True, verify the credentials on the server first.
|
||||
"""
|
||||
# Don't let other threads affect this call's data.
|
||||
all_credentials = self.__all_credentials.copy()
|
||||
|
||||
if source in all_credentials:
|
||||
# Nothing to do if we already have these credentials.
|
||||
if credentials == all_credentials[source]:
|
||||
return
|
||||
raise OperationFailure('Another user is already authenticated '
|
||||
'to this database. You must logout first.')
|
||||
|
||||
if connect:
|
||||
server = self._get_topology().select_server(
|
||||
writable_preferred_server_selector)
|
||||
|
||||
# get_socket() logs out of the database if logged in with old
|
||||
# credentials, and logs in with new ones.
|
||||
with server.get_socket(all_credentials) as sock_info:
|
||||
sock_info.authenticate(credentials)
|
||||
|
||||
# If several threads run _cache_credentials at once, last one wins.
|
||||
self.__all_credentials[source] = credentials
|
||||
|
||||
def _purge_credentials(self, source):
|
||||
"""Purge credentials from the authentication cache."""
|
||||
self.__all_credentials.pop(source, None)
|
||||
|
||||
def _server_property(self, attr_name):
|
||||
"""An attribute of the current server's description.
|
||||
|
||||
@ -1594,19 +1562,11 @@ class MongoClient(common.BaseObject):
|
||||
helpers._handle_exception()
|
||||
|
||||
def __start_session(self, implicit, **kwargs):
|
||||
# Driver Sessions Spec: "If startSession is called when multiple users
|
||||
# are authenticated drivers MUST raise an error with the error message
|
||||
# 'Cannot call startSession when multiple users are authenticated.'"
|
||||
authset = set(self.__all_credentials.values())
|
||||
if len(authset) > 1:
|
||||
raise InvalidOperation("Cannot call start_session when"
|
||||
" multiple users are authenticated")
|
||||
|
||||
# Raises ConfigurationError if sessions are not supported.
|
||||
server_session = self._get_server_session()
|
||||
opts = client_session.SessionOptions(**kwargs)
|
||||
return client_session.ClientSession(
|
||||
self, server_session, opts, authset, implicit)
|
||||
self, server_session, opts, implicit)
|
||||
|
||||
def start_session(self,
|
||||
causal_consistency=True,
|
||||
@ -1617,9 +1577,7 @@ class MongoClient(common.BaseObject):
|
||||
:class:`~pymongo.client_session.SessionOptions`. See the
|
||||
:mod:`~pymongo.client_session` module for details and examples.
|
||||
|
||||
Requires MongoDB 3.6. It is an error to call :meth:`start_session`
|
||||
if this client has been authenticated to multiple databases using the
|
||||
deprecated method :meth:`~pymongo.database.Database.authenticate`.
|
||||
Requires MongoDB 3.6.
|
||||
|
||||
A :class:`~pymongo.client_session.ClientSession` may only be used with
|
||||
the MongoClient that started it. :class:`ClientSession` instances are
|
||||
@ -1655,7 +1613,7 @@ class MongoClient(common.BaseObject):
|
||||
# should always opt-in.
|
||||
return self.__start_session(True, causal_consistency=False)
|
||||
except (ConfigurationError, InvalidOperation):
|
||||
# Sessions not supported, or multiple users authenticated.
|
||||
# Sessions not supported.
|
||||
return None
|
||||
|
||||
@contextlib.contextmanager
|
||||
|
||||
@ -771,17 +771,10 @@ class SocketInfo(object):
|
||||
:Parameters:
|
||||
- `all_credentials`: dict, maps auth source to MongoCredential.
|
||||
"""
|
||||
if all_credentials or self.authset:
|
||||
cached = set(all_credentials.values())
|
||||
authset = self.authset.copy()
|
||||
|
||||
# Logout any credentials that no longer exist in the cache.
|
||||
for credentials in authset - cached:
|
||||
auth.logout(credentials.source, self)
|
||||
self.authset.discard(credentials)
|
||||
|
||||
for credentials in cached - authset:
|
||||
self.authenticate(credentials)
|
||||
if all_credentials:
|
||||
for credentials in all_credentials.values():
|
||||
if credentials not in self.authset:
|
||||
self.authenticate(credentials)
|
||||
|
||||
# CMAP spec says to publish the ready event only after authenticating
|
||||
# the connection.
|
||||
@ -807,18 +800,13 @@ class SocketInfo(object):
|
||||
def validate_session(self, client, session):
|
||||
"""Validate this session before use with client.
|
||||
|
||||
Raises error if this session is logged in as a different user or
|
||||
the client is not the one that created the session.
|
||||
Raises error if the client is not the one that created the session.
|
||||
"""
|
||||
if session:
|
||||
if session._client is not client:
|
||||
raise InvalidOperation(
|
||||
'Can only use session with the MongoClient that'
|
||||
' started it')
|
||||
if session._authset != self.authset:
|
||||
raise InvalidOperation(
|
||||
'Cannot use session after authenticating with different'
|
||||
' credentials')
|
||||
|
||||
def close_socket(self, reason):
|
||||
"""Close this connection with a reason."""
|
||||
|
||||
@ -222,6 +222,17 @@ class ClientContext(object):
|
||||
server_api = ServerApi(MONGODB_API_VERSION)
|
||||
self.default_client_options["server_api"] = server_api
|
||||
|
||||
@property
|
||||
def client_options(self):
|
||||
"""Return the MongoClient options for creating a duplicate client."""
|
||||
opts = client_context.default_client_options.copy()
|
||||
if client_context.auth_enabled:
|
||||
opts['username'] = db_user
|
||||
opts['password'] = db_pwd
|
||||
if self.replica_set_name:
|
||||
opts['replicaSet'] = self.replica_set_name
|
||||
return opts
|
||||
|
||||
@property
|
||||
def ismaster(self):
|
||||
return self.client.admin.command('isMaster')
|
||||
|
||||
@ -73,31 +73,6 @@ class AutoAuthenticateThread(threading.Thread):
|
||||
self.success = True
|
||||
|
||||
|
||||
class DBAuthenticateThread(threading.Thread):
|
||||
"""Used in testing threaded authentication.
|
||||
|
||||
This does db.test.find_one() with a 1-second delay to ensure it must
|
||||
check out and authenticate multiple sockets from the pool concurrently.
|
||||
|
||||
:Parameters:
|
||||
`db`: An auth-protected db with a 'test' collection containing one
|
||||
document.
|
||||
"""
|
||||
|
||||
def __init__(self, db, username, password):
|
||||
super(DBAuthenticateThread, self).__init__()
|
||||
self.db = db
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.success = False
|
||||
|
||||
def run(self):
|
||||
self.db.authenticate(self.username, self.password)
|
||||
assert self.db.test.find_one({'$where': delay(1)}) is not None
|
||||
self.success = True
|
||||
|
||||
|
||||
|
||||
class TestGSSAPI(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
@ -314,24 +289,6 @@ class TestSASLPlain(unittest.TestCase):
|
||||
client.ldap.test.find_one()
|
||||
|
||||
def test_sasl_plain_bad_credentials(self):
|
||||
|
||||
with ignore_deprecations():
|
||||
client = MongoClient(SASL_HOST, SASL_PORT)
|
||||
|
||||
# Bad username
|
||||
self.assertRaises(OperationFailure, client.ldap.authenticate,
|
||||
'not-user', SASL_PASS, SASL_DB, 'PLAIN')
|
||||
self.assertRaises(OperationFailure, client.ldap.test.find_one)
|
||||
self.assertRaises(OperationFailure, client.ldap.test.insert_one,
|
||||
{"failed": True})
|
||||
|
||||
# Bad password
|
||||
self.assertRaises(OperationFailure, client.ldap.authenticate,
|
||||
SASL_USER, 'not-pwd', SASL_DB, 'PLAIN')
|
||||
self.assertRaises(OperationFailure, client.ldap.test.find_one)
|
||||
self.assertRaises(OperationFailure, client.ldap.test.insert_one,
|
||||
{"failed": True})
|
||||
|
||||
def auth_string(user, password):
|
||||
uri = ('mongodb://%s:%s@%s:%d/?authMechanism=PLAIN;'
|
||||
'authSource=%s' % (quote_plus(user),
|
||||
@ -368,12 +325,6 @@ class TestSCRAMSHA1(unittest.TestCase):
|
||||
def test_scram_sha1(self):
|
||||
host, port = client_context.host, client_context.port
|
||||
|
||||
with ignore_deprecations():
|
||||
client = rs_or_single_client_noauth()
|
||||
self.assertTrue(client.pymongo_test.authenticate(
|
||||
'user', 'pass', mechanism='SCRAM-SHA-1'))
|
||||
client.pymongo_test.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://user:pass@%s:%d/pymongo_test?authMechanism=SCRAM-SHA-1'
|
||||
% (host, port))
|
||||
@ -391,6 +342,7 @@ class TestSCRAMSHA1(unittest.TestCase):
|
||||
db.command('dbstats')
|
||||
|
||||
|
||||
# https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#scram-sha-256-and-mechanism-negotiation
|
||||
class TestSCRAM(unittest.TestCase):
|
||||
|
||||
@client_context.require_auth
|
||||
@ -432,148 +384,50 @@ class TestSCRAM(unittest.TestCase):
|
||||
self.assertEqual(
|
||||
started, ['saslStart', 'saslContinue', 'saslContinue'])
|
||||
|
||||
@ignore_deprecations
|
||||
def test_scram(self):
|
||||
host, port = client_context.host, client_context.port
|
||||
|
||||
# Step 1: create users
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
'sha1',
|
||||
'pwd',
|
||||
roles=['dbOwner'],
|
||||
'testscram', 'sha1', 'pwd', roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-1'])
|
||||
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
'sha256',
|
||||
'pwd',
|
||||
roles=['dbOwner'],
|
||||
'testscram', 'sha256', 'pwd', roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
'both',
|
||||
'pwd',
|
||||
roles=['dbOwner'],
|
||||
'testscram', 'both', 'pwd', roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-1', 'SCRAM-SHA-256'])
|
||||
|
||||
# Step 2: verify auth success cases
|
||||
client = rs_or_single_client_noauth(
|
||||
event_listeners=[self.listener])
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('sha1', 'pwd'))
|
||||
username='sha1', password='pwd', authSource='testscram')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'sha1', 'pwd', mechanism='SCRAM-SHA-1'))
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='sha1', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-1')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertRaises(
|
||||
OperationFailure,
|
||||
client.testscram.authenticate,
|
||||
'sha1', 'pwd', mechanism='SCRAM-SHA-256')
|
||||
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('sha256', 'pwd'))
|
||||
client = rs_or_single_client_noauth(
|
||||
username='sha256', password='pwd', authSource='testscram')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'sha256', 'pwd', mechanism='SCRAM-SHA-256'))
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='sha256', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertRaises(
|
||||
OperationFailure,
|
||||
client.testscram.authenticate,
|
||||
'sha256', 'pwd', mechanism='SCRAM-SHA-1')
|
||||
|
||||
self.listener.results.clear()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('both', 'pwd'))
|
||||
started = self.listener.results['started'][0]
|
||||
self.assertEqual(started.command.get('mechanism'), 'SCRAM-SHA-256')
|
||||
# Step 2: SCRAM-SHA-1 and SCRAM-SHA-256
|
||||
client = rs_or_single_client_noauth(
|
||||
username='both', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-1')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'both', 'pwd', mechanism='SCRAM-SHA-256'))
|
||||
client = rs_or_single_client_noauth(
|
||||
username='both', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'both', 'pwd', mechanism='SCRAM-SHA-1'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
|
||||
self.assertRaises(
|
||||
OperationFailure,
|
||||
client.testscram.authenticate,
|
||||
'not-a-user', 'pwd')
|
||||
|
||||
if HAVE_STRINGPREP:
|
||||
# Test the use of SASLprep on passwords. For example,
|
||||
# saslprep('\u2136') becomes 'IV' and saslprep('I\u00ADX')
|
||||
# becomes 'IX'. SASLprep is only supported when the standard
|
||||
# library provides stringprep.
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
'\u2168',
|
||||
'\u2163',
|
||||
roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
client_context.create_user(
|
||||
'testscram',
|
||||
'IX',
|
||||
'IX',
|
||||
roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('\u2168', '\u2163'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'\u2168', '\u2163', mechanism='SCRAM-SHA-256'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('\u2168', 'IV'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('IX', 'I\u00ADX'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate(
|
||||
'IX', 'I\u00ADX', mechanism='SCRAM-SHA-256'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
self.assertTrue(
|
||||
client.testscram.authenticate('IX', 'IX'))
|
||||
client.testscram.command('dbstats')
|
||||
client.testscram.logout()
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://\u2168:\u2163@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://\u2168:IV@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://IX:I\u00ADX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://IX:IX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
self.listener.results.clear()
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://both:pwd@%s:%d/testscram' % (host, port),
|
||||
username='both', password='pwd', authSource='testscram',
|
||||
event_listeners=[self.listener])
|
||||
client.testscram.command('dbstats')
|
||||
if client_context.version.at_least(4, 4, -1):
|
||||
@ -584,17 +438,26 @@ class TestSCRAM(unittest.TestCase):
|
||||
started = self.listener.results['started'][0]
|
||||
self.assertEqual(started.command.get('mechanism'), 'SCRAM-SHA-256')
|
||||
|
||||
# Step 3: verify auth failure conditions
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://both:pwd@%s:%d/testscram?authMechanism=SCRAM-SHA-1'
|
||||
% (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
username='sha1', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://both:pwd@%s:%d/testscram?authMechanism=SCRAM-SHA-256'
|
||||
% (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
username='sha256', password='pwd', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-1')
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='not-a-user', password='pwd', authSource='testscram')
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
if client_context.is_rs:
|
||||
host, port = client_context.host, client_context.port
|
||||
uri = ('mongodb://both:pwd@%s:%d/testscram'
|
||||
'?replicaSet=%s' % (host, port,
|
||||
client_context.replica_set_name))
|
||||
@ -604,6 +467,62 @@ class TestSCRAM(unittest.TestCase):
|
||||
'testscram', read_preference=ReadPreference.SECONDARY)
|
||||
db.command('dbstats')
|
||||
|
||||
@unittest.skipUnless(HAVE_STRINGPREP, 'Cannot test without stringprep')
|
||||
def test_scram_saslprep(self):
|
||||
# Step 4: test SASLprep
|
||||
host, port = client_context.host, client_context.port
|
||||
# Test the use of SASLprep on passwords. For example,
|
||||
# saslprep('\u2136') becomes 'IV' and saslprep('I\u00ADX')
|
||||
# becomes 'IX'. SASLprep is only supported when the standard
|
||||
# library provides stringprep.
|
||||
client_context.create_user(
|
||||
'testscram', '\u2168', '\u2163', roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
client_context.create_user(
|
||||
'testscram', 'IX', 'IX', roles=['dbOwner'],
|
||||
mechanisms=['SCRAM-SHA-256'])
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='\u2168', password='\u2163', authSource='testscram')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='\u2168', password='\u2163', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='\u2168', password='IV', authSource='testscram')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='IX', password='I\u00ADX', authSource='testscram')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='IX', password='I\u00ADX', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
username='IX', password='IX', authSource='testscram',
|
||||
authMechanism='SCRAM-SHA-256')
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://\u2168:\u2163@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://\u2168:IV@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://IX:I\u00ADX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
client = rs_or_single_client_noauth(
|
||||
'mongodb://IX:IX@%s:%d/testscram' % (host, port))
|
||||
client.testscram.command('dbstats')
|
||||
|
||||
def test_cache(self):
|
||||
client = single_client()
|
||||
# Force authentication.
|
||||
@ -651,38 +570,6 @@ class TestSCRAM(unittest.TestCase):
|
||||
thread.join()
|
||||
self.assertTrue(thread.success)
|
||||
|
||||
class TestThreadedAuth(unittest.TestCase):
|
||||
|
||||
@client_context.require_auth
|
||||
def test_db_authenticate_threaded(self):
|
||||
|
||||
db = client_context.client.db
|
||||
coll = db.test
|
||||
coll.drop()
|
||||
coll.insert_one({'_id': 1})
|
||||
|
||||
client_context.create_user(
|
||||
'db',
|
||||
'user',
|
||||
'pass',
|
||||
roles=['dbOwner'])
|
||||
self.addCleanup(db.command, 'dropUser', 'user')
|
||||
|
||||
db = rs_or_single_client_noauth().db
|
||||
db.authenticate('user', 'pass')
|
||||
# No error.
|
||||
db.authenticate('user', 'pass')
|
||||
|
||||
db = rs_or_single_client_noauth().db
|
||||
threads = []
|
||||
for _ in range(4):
|
||||
threads.append(DBAuthenticateThread(db, 'user', 'pass'))
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
for thread in threads:
|
||||
thread.join()
|
||||
self.assertTrue(thread.success)
|
||||
|
||||
|
||||
class TestAuthURIOptions(unittest.TestCase):
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ from bson.codec_options import CodecOptions, TypeEncoder, TypeRegistry
|
||||
from bson.son import SON
|
||||
from bson.tz_util import utc
|
||||
import pymongo
|
||||
from pymongo import auth, message
|
||||
from pymongo import message
|
||||
from pymongo.common import CONNECT_TIMEOUT, _UUID_REPRESENTATIONS
|
||||
from pymongo.command_cursor import CommandCursor
|
||||
from pymongo.compression_support import _HAVE_SNAPPY, _HAVE_ZSTD
|
||||
@ -80,7 +80,6 @@ from test.utils import (assertRaisesExactly,
|
||||
FunctionCallRecorder,
|
||||
get_pool,
|
||||
gevent_monkey_patched,
|
||||
ignore_deprecations,
|
||||
is_greenthread_patched,
|
||||
lazy_client_trial,
|
||||
NTHREADS,
|
||||
@ -908,37 +907,6 @@ class TestClient(IntegrationTest):
|
||||
with self.assertRaises(OperationFailure):
|
||||
rs_or_single_client(username="ad min", password="foo").server_info()
|
||||
|
||||
@client_context.require_auth
|
||||
@ignore_deprecations
|
||||
def test_multiple_logins(self):
|
||||
client_context.create_user(
|
||||
'pymongo_test', 'user1', 'pass', roles=['readWrite'])
|
||||
client_context.create_user(
|
||||
'pymongo_test', 'user2', 'pass', roles=['readWrite'])
|
||||
self.addCleanup(remove_all_users, self.client.pymongo_test)
|
||||
|
||||
client = rs_or_single_client_noauth(
|
||||
"mongodb://user1:pass@%s:%d/pymongo_test" % (
|
||||
client_context.host, client_context.port))
|
||||
|
||||
client.pymongo_test.test.find_one()
|
||||
with self.assertRaises(OperationFailure):
|
||||
# Can't log in to the same database with multiple users.
|
||||
client.pymongo_test.authenticate('user2', 'pass')
|
||||
|
||||
client.pymongo_test.test.find_one()
|
||||
client.pymongo_test.logout()
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.pymongo_test.test.find_one()
|
||||
|
||||
client.pymongo_test.authenticate('user2', 'pass')
|
||||
client.pymongo_test.test.find_one()
|
||||
|
||||
with self.assertRaises(OperationFailure):
|
||||
client.pymongo_test.authenticate('user1', 'pass')
|
||||
|
||||
client.pymongo_test.test.find_one()
|
||||
|
||||
@client_context.require_auth
|
||||
def test_lazy_auth_raises_operation_failure(self):
|
||||
lazy_client = rs_or_single_client_noauth(
|
||||
@ -1306,12 +1274,6 @@ class TestClient(IntegrationTest):
|
||||
waitQueueTimeoutMS=1,
|
||||
retryReads=False))
|
||||
|
||||
# Simulate an authenticate() call on a different socket.
|
||||
credentials = auth._build_credentials_tuple(
|
||||
'DEFAULT', 'admin', db_user, db_pwd, {}, None)
|
||||
|
||||
c._cache_credentials('test', credentials, connect=False)
|
||||
|
||||
# Cause a network error on the actual socket.
|
||||
pool = get_pool(c)
|
||||
socket_info = one(pool.sockets)
|
||||
|
||||
@ -17,7 +17,6 @@
|
||||
import datetime
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
@ -43,17 +42,12 @@ from pymongo.errors import (CollectionInvalid,
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.read_concern import ReadConcern
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.saslprep import HAVE_STRINGPREP
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import (client_context,
|
||||
SkipTest,
|
||||
unittest,
|
||||
IntegrationTest)
|
||||
from test.utils import (EventListener,
|
||||
ignore_deprecations,
|
||||
remove_all_users,
|
||||
rs_or_single_client_noauth,
|
||||
rs_or_single_client,
|
||||
from test.utils import (rs_or_single_client,
|
||||
server_started_with_auth,
|
||||
wait_until,
|
||||
IMPOSSIBLE_WRITE_CONCERN,
|
||||
@ -469,87 +463,6 @@ class TestDatabase(IntegrationTest):
|
||||
self.assertEqual(auth._password_digest("Gustave", "Dor\xe9"),
|
||||
"81e0e2364499209f466e75926a162d73")
|
||||
|
||||
@client_context.require_auth
|
||||
@ignore_deprecations
|
||||
def test_authenticate_multiple(self):
|
||||
# "self.client" is logged in as root.
|
||||
self.client.drop_database("pymongo_test")
|
||||
self.client.drop_database("pymongo_test1")
|
||||
users_db_auth = self.client.pymongo_test
|
||||
|
||||
client_context.create_user(
|
||||
'admin',
|
||||
'ro-admin',
|
||||
'pass',
|
||||
roles=["userAdmin", "readAnyDatabase"])
|
||||
|
||||
self.addCleanup(client_context.drop_user, 'admin', 'ro-admin')
|
||||
client_context.create_user(
|
||||
'pymongo_test', 'user', 'pass', roles=["userAdmin", "readWrite"])
|
||||
self.addCleanup(remove_all_users, users_db_auth)
|
||||
|
||||
# Non-root client.
|
||||
listener = EventListener()
|
||||
client = rs_or_single_client_noauth(event_listeners=[listener])
|
||||
admin_db = client.admin
|
||||
users_db = client.pymongo_test
|
||||
other_db = client.pymongo_test1
|
||||
|
||||
self.assertRaises(OperationFailure, users_db.test.find_one)
|
||||
self.assertEqual(listener.started_command_names(), ['find'])
|
||||
listener.reset()
|
||||
|
||||
# Regular user should be able to query its own db, but
|
||||
# no other.
|
||||
users_db.authenticate('user', 'pass')
|
||||
if client_context.version.at_least(3, 0):
|
||||
self.assertEqual(listener.started_command_names()[0], 'saslStart')
|
||||
else:
|
||||
self.assertEqual(listener.started_command_names()[0], 'getnonce')
|
||||
|
||||
self.assertEqual(0, users_db.test.count_documents({}))
|
||||
self.assertRaises(OperationFailure, other_db.test.find_one)
|
||||
|
||||
listener.reset()
|
||||
# Admin read-only user should be able to query any db,
|
||||
# but not write.
|
||||
admin_db.authenticate('ro-admin', 'pass')
|
||||
if client_context.version.at_least(3, 0):
|
||||
self.assertEqual(listener.started_command_names()[0], 'saslStart')
|
||||
else:
|
||||
self.assertEqual(listener.started_command_names()[0], 'getnonce')
|
||||
self.assertEqual(None, other_db.test.find_one())
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert_one, {})
|
||||
|
||||
# Close all sockets.
|
||||
client.close()
|
||||
|
||||
listener.reset()
|
||||
# We should still be able to write to the regular user's db.
|
||||
self.assertTrue(users_db.test.delete_many({}))
|
||||
names = listener.started_command_names()
|
||||
if client_context.version.at_least(4, 4, -1):
|
||||
# No speculation with multiple users (but we do skipEmptyExchange).
|
||||
self.assertEqual(
|
||||
names, ['saslStart', 'saslContinue', 'saslStart',
|
||||
'saslContinue', 'delete'])
|
||||
elif client_context.version.at_least(3, 0):
|
||||
self.assertEqual(
|
||||
names, ['saslStart', 'saslContinue', 'saslContinue',
|
||||
'saslStart', 'saslContinue', 'saslContinue', 'delete'])
|
||||
else:
|
||||
self.assertEqual(
|
||||
names, ['getnonce', 'authenticate',
|
||||
'getnonce', 'authenticate', 'delete'])
|
||||
|
||||
# And read from other dbs...
|
||||
self.assertEqual(0, other_db.test.count_documents({}))
|
||||
|
||||
# But still not write to other dbs.
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert_one, {})
|
||||
|
||||
def test_id_ordering(self):
|
||||
# PyMongo attempts to have _id show up first
|
||||
# when you iterate key/value pairs in a document.
|
||||
|
||||
@ -15,13 +15,9 @@
|
||||
"""Test various legacy / deprecated API features."""
|
||||
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from bson.binary import PYTHON_LEGACY, STANDARD
|
||||
from bson.code import Code
|
||||
from bson.codec_options import CodecOptions
|
||||
from bson.son import SON
|
||||
from pymongo import ASCENDING, GEOHAYSTACK
|
||||
from pymongo.common import partition_node
|
||||
@ -1113,10 +1109,9 @@ class TestLegacyBulkAuthorization(BulkAuthorizationTestBase):
|
||||
def test_readonly(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
cli = rs_or_single_client_noauth()
|
||||
db = cli.pymongo_test
|
||||
coll = db.test
|
||||
db.authenticate('readonly', 'pw')
|
||||
cli = rs_or_single_client_noauth(
|
||||
username='readonly', password='pw', authSource='pymongo_test')
|
||||
coll = cli.pymongo_test.test
|
||||
bulk = coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
self.assertRaises(OperationFailure, bulk.execute)
|
||||
@ -1124,10 +1119,9 @@ class TestLegacyBulkAuthorization(BulkAuthorizationTestBase):
|
||||
def test_no_remove(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
cli = rs_or_single_client_noauth()
|
||||
db = cli.pymongo_test
|
||||
coll = db.test
|
||||
db.authenticate('noremove', 'pw')
|
||||
cli = rs_or_single_client_noauth(
|
||||
username='noremove', password='pw', authSource='pymongo_test')
|
||||
coll = cli.pymongo_test.test
|
||||
bulk = coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
bulk.find({'x': 2}).upsert().replace_one({'x': 2})
|
||||
|
||||
@ -19,7 +19,6 @@ import copy
|
||||
import pickle
|
||||
import random
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
@ -39,9 +38,7 @@ from pymongo.write_concern import WriteConcern
|
||||
from test import (SkipTest,
|
||||
client_context,
|
||||
IntegrationTest,
|
||||
unittest,
|
||||
db_user,
|
||||
db_pwd)
|
||||
unittest)
|
||||
from test.utils import (connected,
|
||||
ignore_deprecations,
|
||||
one,
|
||||
@ -312,7 +309,7 @@ class TestReadPreferences(TestReadPreferencesBase):
|
||||
class ReadPrefTester(MongoClient):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.has_read_from = set()
|
||||
client_options = client_context.default_client_options.copy()
|
||||
client_options = client_context.client_options
|
||||
client_options.update(kwargs)
|
||||
super(ReadPrefTester, self).__init__(*args, **client_options)
|
||||
|
||||
@ -354,11 +351,8 @@ class TestCommandAndReadPreference(IntegrationTest):
|
||||
super(TestCommandAndReadPreference, cls).setUpClass()
|
||||
cls.c = ReadPrefTester(
|
||||
client_context.pair,
|
||||
replicaSet=client_context.replica_set_name,
|
||||
# Ignore round trip times, to test ReadPreference modes only.
|
||||
localThresholdMS=1000*1000)
|
||||
if client_context.auth_enabled:
|
||||
cls.c.admin.authenticate(db_user, db_pwd)
|
||||
cls.client_version = Version.from_client(cls.c)
|
||||
# mapReduce fails if the collection does not exist.
|
||||
coll = cls.c.pymongo_test.get_collection(
|
||||
|
||||
@ -29,7 +29,7 @@ from pymongo.errors import (ConfigurationError,
|
||||
InvalidOperation,
|
||||
OperationFailure)
|
||||
from pymongo.read_concern import ReadConcern
|
||||
from test import IntegrationTest, client_context, db_user, db_pwd, unittest, SkipTest
|
||||
from test import IntegrationTest, client_context, unittest, SkipTest
|
||||
from test.utils import (ignore_deprecations,
|
||||
rs_or_single_client,
|
||||
EventListener,
|
||||
@ -1040,112 +1040,6 @@ class TestCausalConsistency(unittest.TestCase):
|
||||
self.assertIsNone(after_cluster_time)
|
||||
|
||||
|
||||
class TestSessionsMultiAuth(IntegrationTest):
|
||||
@client_context.require_auth
|
||||
@client_context.require_sessions
|
||||
def setUp(self):
|
||||
super(TestSessionsMultiAuth, self).setUp()
|
||||
|
||||
client_context.create_user(
|
||||
'pymongo_test', 'second-user', 'pass', roles=['readWrite'])
|
||||
self.addCleanup(client_context.drop_user, 'pymongo_test','second-user')
|
||||
|
||||
@ignore_deprecations
|
||||
def test_session_authenticate_multiple(self):
|
||||
listener = SessionTestListener()
|
||||
# Logged in as root.
|
||||
client = rs_or_single_client(event_listeners=[listener])
|
||||
db = client.pymongo_test
|
||||
db.authenticate('second-user', 'pass')
|
||||
|
||||
with self.assertRaises(InvalidOperation):
|
||||
client.start_session()
|
||||
|
||||
# No implicit sessions.
|
||||
listener.results.clear()
|
||||
db.collection.find_one()
|
||||
event = listener.first_command_started()
|
||||
self.assertNotIn(
|
||||
'lsid', event.command,
|
||||
"find_one with multi-auth shouldn't have sent lsid with %s" % (
|
||||
event.command_name))
|
||||
|
||||
@ignore_deprecations
|
||||
def test_explicit_session_logout(self):
|
||||
listener = SessionTestListener()
|
||||
|
||||
# Changing auth invalidates the session. Start as root.
|
||||
client = rs_or_single_client(event_listeners=[listener])
|
||||
db = client.pymongo_test
|
||||
db.collection.insert_many([{} for _ in range(10)])
|
||||
self.addCleanup(db.collection.drop)
|
||||
|
||||
with client.start_session() as s:
|
||||
listener.results.clear()
|
||||
cursor = db.collection.find(session=s).batch_size(2)
|
||||
next(cursor)
|
||||
event = listener.first_command_started()
|
||||
self.assertEqual(event.command_name, 'find')
|
||||
self.assertEqual(
|
||||
s.session_id, event.command.get('lsid'),
|
||||
"find() sent wrong lsid with %s cmd" % (event.command_name,))
|
||||
|
||||
client.admin.logout()
|
||||
db.authenticate('second-user', 'pass')
|
||||
|
||||
err = ('Cannot use session after authenticating with different'
|
||||
' credentials')
|
||||
|
||||
with self.assertRaisesRegex(InvalidOperation, err):
|
||||
# Auth has changed between find and getMore.
|
||||
list(cursor)
|
||||
|
||||
with self.assertRaisesRegex(InvalidOperation, err):
|
||||
db.collection.bulk_write([InsertOne({})], session=s)
|
||||
|
||||
with self.assertRaisesRegex(InvalidOperation, err):
|
||||
db.list_collection_names(session=s)
|
||||
|
||||
with self.assertRaisesRegex(InvalidOperation, err):
|
||||
db.collection.find_one(session=s)
|
||||
|
||||
with self.assertRaisesRegex(InvalidOperation, err):
|
||||
list(db.collection.aggregate([], session=s))
|
||||
|
||||
@ignore_deprecations
|
||||
def test_implicit_session_logout(self):
|
||||
listener = SessionTestListener()
|
||||
|
||||
# Changing auth doesn't invalidate the session. Start as root.
|
||||
client = rs_or_single_client(event_listeners=[listener])
|
||||
db = client.pymongo_test
|
||||
|
||||
for name, f in [
|
||||
('bulk_write', lambda: db.collection.bulk_write([InsertOne({})])),
|
||||
('list_collection_names', db.list_collection_names),
|
||||
('find_one', db.collection.find_one),
|
||||
('aggregate', lambda: list(db.collection.aggregate([])))
|
||||
]:
|
||||
def sub_test():
|
||||
listener.results.clear()
|
||||
f()
|
||||
for event in listener.results['started']:
|
||||
self.assertIn(
|
||||
'lsid', event.command,
|
||||
"%s sent no lsid with %s" % (
|
||||
name, event.command_name))
|
||||
|
||||
# We switch auth without clearing the pool of session ids. The
|
||||
# server considers these to be new sessions since it's a new user.
|
||||
# The old sessions time out on the server after 30 minutes.
|
||||
client.admin.logout()
|
||||
db.authenticate('second-user', 'pass')
|
||||
sub_test()
|
||||
db.logout()
|
||||
client.admin.authenticate(db_user, db_pwd)
|
||||
sub_test()
|
||||
|
||||
|
||||
class TestSessionsNotSupported(IntegrationTest):
|
||||
@client_context.require_version_max(3, 5, 10)
|
||||
def test_sessions_not_supported(self):
|
||||
|
||||
@ -30,8 +30,6 @@ from pymongo.ssl_support import HAVE_SSL, get_ssl_context, validate_cert_reqs, _
|
||||
from pymongo.write_concern import WriteConcern
|
||||
from test import (IntegrationTest,
|
||||
client_context,
|
||||
db_pwd,
|
||||
db_user,
|
||||
SkipTest,
|
||||
unittest,
|
||||
HAVE_IPADDRESS)
|
||||
@ -548,14 +546,7 @@ class TestSSL(IntegrationTest):
|
||||
@client_context.require_ssl_certfile
|
||||
def test_mongodb_x509_auth(self):
|
||||
host, port = client_context.host, client_context.port
|
||||
ssl_client = MongoClient(
|
||||
client_context.pair,
|
||||
ssl=True,
|
||||
ssl_cert_reqs=ssl.CERT_NONE,
|
||||
ssl_certfile=CLIENT_PEM)
|
||||
self.addCleanup(remove_all_users, ssl_client['$external'])
|
||||
|
||||
ssl_client.admin.authenticate(db_user, db_pwd)
|
||||
self.addCleanup(remove_all_users, client_context.client['$external'])
|
||||
|
||||
# Give x509 user all necessary privileges.
|
||||
client_context.create_user('$external', MONGODB_X509_USERNAME, roles=[
|
||||
|
||||
@ -17,13 +17,10 @@
|
||||
import threading
|
||||
|
||||
from test import (client_context,
|
||||
db_user,
|
||||
db_pwd,
|
||||
IntegrationTest,
|
||||
unittest)
|
||||
from test.utils import rs_or_single_client_noauth, rs_or_single_client
|
||||
from test.utils import rs_or_single_client
|
||||
from test.utils import joinall
|
||||
from pymongo.errors import OperationFailure
|
||||
|
||||
|
||||
@client_context.require_connection
|
||||
@ -203,34 +200,5 @@ class TestThreads(IntegrationTest):
|
||||
self.assertTrue(t.passed)
|
||||
|
||||
|
||||
class TestThreadsAuth(IntegrationTest):
|
||||
@classmethod
|
||||
@client_context.require_auth
|
||||
def setUpClass(cls):
|
||||
super(TestThreadsAuth, cls).setUpClass()
|
||||
|
||||
def test_auto_auth_login(self):
|
||||
# Create the database upfront to workaround SERVER-39167.
|
||||
self.client.auth_test.test.insert_one({})
|
||||
self.addCleanup(self.client.drop_database, "auth_test")
|
||||
client = rs_or_single_client_noauth()
|
||||
self.assertRaises(OperationFailure, client.auth_test.test.find_one)
|
||||
|
||||
# Admin auth
|
||||
client.admin.authenticate(db_user, db_pwd)
|
||||
|
||||
nthreads = 10
|
||||
threads = []
|
||||
for _ in range(nthreads):
|
||||
t = AutoAuthenticateThreads(client.auth_test.test, 10)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
joinall(threads)
|
||||
|
||||
for t in threads:
|
||||
self.assertTrue(t.success)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user