Deprecate db.authenticate and db.eval

PYTHON-1313, and PYTHON-1315.
This commit is contained in:
A. Jesse Jiryu Davis 2017-08-01 10:13:20 -04:00
parent 9ca77901ab
commit 72e61277b2
10 changed files with 278 additions and 290 deletions

View File

@ -22,6 +22,13 @@ Changes and Deprecations:
was deprecated in MongoDB 3.4 and is expected to be removed in MongoDB 3.6.
Applications should use :meth:`~pymongo.collection.Collection.aggregate`
with the `$group` pipeline stage instead.
- Deprecated :meth:`~pymongo.database.Database.authenticate`. Authenticating
multiple users conflicts with support for logical sessions in MongoDB 3.6.
To authenticate as multiple users, create multiple instances of
:class:`~pymongo.mongo_client.MongoClient`.
- Deprecated :meth:`~pymongo.database.Database.eval`. The eval command
was deprecated in MongoDB 3.0 and will be removed in a future server version.
- Deprecated :class:`~pymongo.database.SystemJS`.
- Deprecated :meth:`~pymongo.mongo_client.MongoClient.get_default_database`.
Applications should use
:meth:`~pymongo.mongo_client.MongoClient.get_database` without the `name`

View File

@ -28,15 +28,21 @@ SCRAM-SHA-1 (RFC 5802)
.. versionadded:: 2.8
SCRAM-SHA-1 is the default authentication mechanism supported by a cluster
configured for authentication with MongoDB 3.0 or later. Authentication is
per-database and credentials can be specified through the MongoDB URI or
passed to the :meth:`~pymongo.database.Database.authenticate` method::
configured for authentication with MongoDB 3.0 or later. Authentication
requires a username, a password, and a database name. The default database
name is "admin", this can be overidden with the ``authSource`` option.
Credentials can be specified as arguments to
:class:`~pymongo.mongo_client.MongoClient`::
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com')
>>> client.the_database.authenticate('user', 'password', mechanism='SCRAM-SHA-1')
True
>>>
>>> client = MongoClient('example.com',
... user='user',
... password='password',
... authSource='the_database',
... authMechanism='SCRAM-SHA-1')
Or through the MongoDB URI::
>>> uri = "mongodb://user:password@example.com/the_database?authMechanism=SCRAM-SHA-1"
>>> client = MongoClient(uri)
@ -52,9 +58,10 @@ Before MongoDB 3.0 the default authentication mechanism was MONGODB-CR,
the "MongoDB Challenge-Response" protocol::
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com')
>>> client.the_database.authenticate('user', 'password', mechanism='MONGODB-CR')
True
>>> client = MongoClient('example.com',
... user='user',
... password='password',
... authMechanism='MONGODB-CR')
>>>
>>> uri = "mongodb://user:password@example.com/the_database?authMechanism=MONGODB-CR"
>>> client = MongoClient(uri)
@ -66,24 +73,22 @@ If no mechanism is specified, PyMongo automatically uses MONGODB-CR when
connected to a pre-3.0 version of MongoDB, and SCRAM-SHA-1 when connected to
a recent version.
Delegated Authentication
------------------------
.. versionadded: 2.5
Default Database and "authSource"
---------------------------------
If your user is defined in one database with a role that gives it
authorization on a different database use the `source` option to specify
the database in which the user is defined::
You can specify both a default database and the authentication database in the
URI::
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com')
>>> db = client.the_database
>>> db.authenticate('user', 'password', source='source_database')
True
>>> uri = "mongodb://user:password@example.com/default_db?authSource=admin"
>>> client = MongoClient(uri)
Or the `authSource` URI option::
PyMongo will authenticate on the "admin" database, but the default database
will be "default_db"::
>>> uri = "mongodb://user:password@example.com/?authSource=source_database"
>>> db = MongoClient(uri).the_database
>>> # get_database with no "name" argument chooses the DB from the URI
>>> db = MongoClient(uri).get_database()
>>> print(db.name)
'default_db'
MONGODB-X509
------------
@ -98,14 +103,12 @@ and newer::
>>> import ssl
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com',
... username="<X.509 derived username>"
... authMechanism="MONGODB-X509",
... ssl=True,
... ssl_certfile='/path/to/client.pem',
... ssl_cert_reqs=ssl.CERT_REQUIRED,
... ssl_ca_certs='/path/to/ca.pem')
>>> client.the_database.authenticate("<X.509 derived username>",
... mechanism='MONGODB-X509')
True
>>>
MONGODB-X509 authenticates against the $external virtual database, so you
do not have to specify a database in the URI::
@ -156,27 +159,12 @@ URI::
>>> client = MongoClient(uri)
>>>
or using :meth:`~pymongo.database.Database.authenticate`::
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com')
>>> db = client.test
>>> db.authenticate('mongodbuser@EXAMPLE.COM', mechanism='GSSAPI')
True
The default service name used by MongoDB and PyMongo is `mongodb`. You can
specify a custom service name with the ``authMechanismProperties`` option::
>>> from pymongo import MongoClient
>>> uri = "mongodb://mongodbuser%40EXAMPLE.COM@example.com/?authMechanism=GSSAPI&authMechanismProperties=SERVICE_NAME:myservicename"
>>> client = MongoClient(uri)
>>>
>>> client = MongoClient('example.com')
>>> db = client.test
>>> db.authenticate(
... 'mongodbuser@EXAMPLE.COM', mechanism='GSSAPI',
... authMechanismProperties='SERVICE_NAME:myservicename')
True
Windows (SSPI)
~~~~~~~~~~~~~~
@ -215,13 +203,6 @@ to an LDAP server. Using the PLAIN mechanism is very similar to MONGODB-CR.
These examples use the $external virtual database for LDAP support::
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com')
>>> client.the_database.authenticate('user',
... 'password',
... source='$external',
... mechanism='PLAIN')
True
>>>
>>> uri = "mongodb://user:password@example.com/?authMechanism=PLAIN&authSource=$external"
>>> client = MongoClient(uri)
>>>
@ -232,17 +213,6 @@ the SASL PLAIN mechanism::
>>> import ssl
>>> from pymongo import MongoClient
>>> client = MongoClient('example.com',
... ssl=True,
... ssl_certfile='/path/to/client.pem',
... ssl_cert_reqs=ssl.CERT_REQUIRED,
... ssl_ca_certs='/path/to/ca.pem')
>>> client.the_database.authenticate('user',
... 'password',
... source='$external',
... mechanism='PLAIN')
True
>>>
>>> uri = "mongodb://user:password@example.com/?authMechanism=PLAIN&authSource=$external"
>>> client = MongoClient(uri,
... ssl=True,

View File

@ -19,10 +19,11 @@ To copy from a different mongod server that is not password-protected::
fromhost='source.example.com')
If the target server is password-protected, authenticate to the "admin"
database first::
database::
>>> client.admin.authenticate('administrator', 'pwd')
True
>>> client = MongoClient('target.example.com',
... username='administrator',
... password='pwd')
>>> client.admin.command('copydb',
fromdb='source_db_name',
todb='target_db_name',

View File

@ -142,7 +142,7 @@ class Database(common.BaseObject):
@property
def system_js(self):
"""A :class:`SystemJS` helper for this :class:`Database`.
"""**DEPRECATED**: :class:`SystemJS` helper for this :class:`Database`.
See the documentation for :class:`SystemJS` for more details.
"""
@ -990,7 +990,7 @@ class Database(common.BaseObject):
def authenticate(self, name=None, password=None,
source=None, mechanism='DEFAULT', **kwargs):
"""Authenticate to use this database.
"""**DEPRECATED**: Authenticate to use this database.
Authentication lasts for the life of the underlying client
instance, or until :meth:`logout` is called.
@ -1030,6 +1030,11 @@ class Database(common.BaseObject):
name for GSSAPI authentication pass
authMechanismProperties='SERVICE_NAME:<service name>'
.. 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.
@ -1072,7 +1077,10 @@ class Database(common.BaseObject):
return True
def logout(self):
"""Deauthorize use of this database for this client instance."""
"""**DEPRECATED**: Deauthorize use of this database."""
warnings.warn("Database.logout() is deprecated",
DeprecationWarning, stacklevel=2)
# Sockets will be deauthenticated as they are used.
self.client._purge_credentials(self.name)
@ -1101,18 +1109,7 @@ class Database(common.BaseObject):
return self[dbref.collection].find_one({"_id": dbref.id}, **kwargs)
def eval(self, code, *args):
"""Evaluate a JavaScript expression in MongoDB.
Useful if you need to touch a lot of data lightly; in such a
scenario the network transfer of the data could be a
bottleneck. The `code` argument must be a JavaScript
function. Additional positional arguments will be passed to
that function when it is run on the server.
Raises :class:`TypeError` if `code` is not an instance of
:class:`basestring` (:class:`str` in python 3) or `Code`.
Raises :class:`~pymongo.errors.OperationFailure` if the eval
fails. Returns the result of the evaluation.
"""**DEPRECATED**: Evaluate a JavaScript expression in MongoDB.
:Parameters:
- `code`: string representation of JavaScript code to be
@ -1123,6 +1120,9 @@ class Database(common.BaseObject):
.. warning:: the eval command is deprecated in MongoDB 3.0 and
will be removed in a future server version.
"""
warnings.warn("Database.eval() is deprecated",
DeprecationWarning, stacklevel=2)
if not isinstance(code, Code):
code = Code(code)
@ -1139,30 +1139,17 @@ class Database(common.BaseObject):
class SystemJS(object):
"""Helper class for dealing with stored JavaScript.
"""**DEPRECATED**: Helper class for dealing with stored JavaScript.
"""
def __init__(self, database):
"""Get a system js helper for the database `database`.
"""**DEPRECATED**: Get a system js helper for the database `database`.
An instance of :class:`SystemJS` can be created with an instance
of :class:`Database` through :attr:`Database.system_js`,
manual instantiation of this class should not be necessary.
:class:`SystemJS` instances allow for easy manipulation and
access to server-side JavaScript:
.. doctest::
>>> db.system_js.add1 = "function (x) { return x + 1; }"
>>> db.system.js.find({"_id": "add1"}).count()
1
>>> db.system_js.add1(5)
6.0
>>> del db.system_js.add1
>>> db.system.js.find({"_id": "add1"}).count()
0
SystemJS will be removed in PyMongo 4.0.
"""
warnings.warn("SystemJS is deprecated",
DeprecationWarning, stacklevel=2)
if not database.write_concern.acknowledged:
database = database.client.get_database(
database.name, write_concern=WriteConcern())

View File

@ -146,6 +146,14 @@ class client_knobs(object):
self.disable()
def _all_users(db):
if Version.from_client(db.client).at_least(2, 5, 3, -1):
return set(u['user']
for u in db.command('usersInfo').get('users', []))
else:
return set(u['user'] for u in db.system.users.find())
class ClientContext(object):
def __init__(self):
@ -226,7 +234,12 @@ class ClientContext(object):
if self.version.at_least(2, 5, 3, -1):
roles = {'roles': ['root']}
self.client.admin.add_user(db_user, db_pwd, **roles)
self.client.admin.authenticate(db_user, db_pwd)
self.client = _connect(host,
port,
username=db_user,
password=db_pwd,
**self.ssl_client_options)
# May not have this if OperationFailure was raised earlier.
self.cmd_line = self.client.admin.command('getCmdLineOpts')
@ -270,9 +283,16 @@ class ClientContext(object):
return bool(len(self.client.secondaries))
def _check_user_provided(self):
"""Return True if db_user/db_password is already an admin user."""
client = pymongo.MongoClient(
host, port,
username=db_user,
password=db_pwd,
serverSelectionTimeoutMS=100,
**self.ssl_client_options)
try:
self.client.admin.authenticate(db_user, db_pwd)
return True
return db_user in _all_users(client.admin)
except pymongo.errors.OperationFailure as e:
msg = e.details.get('errmsg', '')
if e.code == 18 or 'auth fails' in msg:

View File

@ -31,7 +31,10 @@ from pymongo.auth import HAVE_KERBEROS, _build_credentials_tuple
from pymongo.errors import OperationFailure
from pymongo.read_preferences import ReadPreference
from test import client_context, SkipTest, unittest, Version
from test.utils import delay, rs_or_single_client_noauth, single_client_noauth
from test.utils import (delay,
ignore_deprecations,
rs_or_single_client_noauth,
single_client_noauth)
# YOU MUST RUN KINIT BEFORE RUNNING GSSAPI TESTS ON UNIX.
GSSAPI_HOST = os.environ.get('GSSAPI_HOST')
@ -109,9 +112,8 @@ class TestGSSAPI(unittest.TestCase):
self.assertEqual(1, len(set([creds1, creds2])))
self.assertEqual(3, len(set([creds0, creds1, creds2, creds3])))
@ignore_deprecations
def test_gssapi_simple(self):
client = MongoClient(GSSAPI_HOST, GSSAPI_PORT)
db = client[GSSAPI_DB]
if GSSAPI_PASS is not None:
uri = ('mongodb://%s:%s@%s:%d/?authMechanism='
'GSSAPI' % (quote_plus(GSSAPI_PRINCIPAL),
@ -125,25 +127,28 @@ class TestGSSAPI(unittest.TestCase):
GSSAPI_PORT))
if not self.service_realm_required:
# Call authenticate() without authMechanismProperties.
self.assertTrue(db.authenticate(GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI'))
db.collection.find_one()
# Without authMechanismProperties.
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI')
client[GSSAPI_DB].collection.find_one()
# Log in using URI, without authMechanismProperties.
client = MongoClient(uri)
db = client[GSSAPI_DB]
db.collection.find_one()
client[GSSAPI_DB].collection.find_one()
# Authenticate with authMechanismProperties.
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI',
authMechanismProperties=self.mech_properties)
# Call authenticate() with authMechanismProperties.
self.assertTrue(db.authenticate(
GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI',
authMechanismProperties=self.mech_properties))
db.collection.find_one()
client[GSSAPI_DB].collection.find_one()
# Log in using URI, with authMechanismProperties.
mech_uri = uri + '&authMechanismProperties=%s' % (self.mech_properties,)
@ -152,43 +157,48 @@ class TestGSSAPI(unittest.TestCase):
set_name = client.admin.command('ismaster').get('setName')
if set_name:
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
replicaSet=set_name)
db = client[GSSAPI_DB]
if not self.service_realm_required:
# Without authMechanismProperties
self.assertTrue(db.authenticate(GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI'))
db.collection_names()
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI',
replicaSet=set_name)
client[GSSAPI_DB].collection_names()
uri = uri + '&replicaSet=%s' % (str(set_name),)
client = MongoClient(uri)
db = client[GSSAPI_DB]
db.collection_names()
client[GSSAPI_DB].collection_names()
# With authMechanismProperties
self.assertTrue(db.authenticate(
GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI',
authMechanismProperties=self.mech_properties))
db.collection_names()
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI',
authMechanismProperties=self.mech_properties,
replicaSet=set_name)
client[GSSAPI_DB].collection_names()
mech_uri = mech_uri + '&replicaSet=%s' % (str(set_name),)
client = MongoClient(mech_uri)
client[GSSAPI_DB].collection_names()
@ignore_deprecations
def test_gssapi_threaded(self):
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI',
authMechanismProperties=self.mech_properties)
client = MongoClient(GSSAPI_HOST, GSSAPI_PORT)
# Authentication succeeded?
client.server_info()
db = client[GSSAPI_DB]
self.assertTrue(
db.authenticate(GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI',
authMechanismProperties=self.mech_properties))
# Need one document in the collection. AutoAuthenticateThread does
# collection.find_one with a 1-second delay, forcing it to check out
@ -215,14 +225,14 @@ class TestGSSAPI(unittest.TestCase):
if set_name:
client = MongoClient(GSSAPI_HOST,
GSSAPI_PORT,
replicaSet=set_name,
readPreference='secondary')
db = client[GSSAPI_DB]
self.assertTrue(
db.authenticate(GSSAPI_PRINCIPAL,
GSSAPI_PASS,
mechanism='GSSAPI',
authMechanismProperties=self.mech_properties))
username=GSSAPI_PRINCIPAL,
password=GSSAPI_PASS,
authMechanism='GSSAPI',
authMechanismProperties=self.mech_properties,
replicaSet=set_name)
# Succeeded?
client.server_info()
threads = []
for _ in range(4):
@ -244,9 +254,12 @@ class TestSASLPlain(unittest.TestCase):
def test_sasl_plain(self):
client = MongoClient(SASL_HOST, SASL_PORT)
self.assertTrue(client.ldap.authenticate(SASL_USER, SASL_PASS,
SASL_DB, 'PLAIN'))
client = MongoClient(SASL_HOST,
SASL_PORT,
username=SASL_USER,
password=SASL_PASS,
authSource=SASL_DB,
authMechanism='PLAIN')
client.ldap.test.find_one()
uri = ('mongodb://%s:%s@%s:%d/?authMechanism=PLAIN;'
@ -259,10 +272,12 @@ class TestSASLPlain(unittest.TestCase):
set_name = client.admin.command('ismaster').get('setName')
if set_name:
client = MongoClient(SASL_HOST,
port=SASL_PORT,
replicaSet=set_name)
self.assertTrue(client.ldap.authenticate(SASL_USER, SASL_PASS,
SASL_DB, 'PLAIN'))
SASL_PORT,
replicaSet=set_name,
username=SASL_USER,
password=SASL_PASS,
authSource=SASL_DB,
authMechanism='PLAIN')
client.ldap.test.find_one()
uri = ('mongodb://%s:%s@%s:%d/?authMechanism=PLAIN;'
@ -275,21 +290,22 @@ class TestSASLPlain(unittest.TestCase):
def test_sasl_plain_bad_credentials(self):
client = MongoClient(SASL_HOST, SASL_PORT)
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 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})
# 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;'
@ -326,10 +342,12 @@ class TestSCRAMSHA1(unittest.TestCase):
def test_scram_sha1(self):
host, port = client_context.host, client_context.port
client = rs_or_single_client_noauth()
self.assertTrue(client.pymongo_test.authenticate(
'user', 'pass', mechanism='SCRAM-SHA-1'))
client.pymongo_test.command('dbstats')
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'
@ -355,13 +373,12 @@ class TestAuthURIOptions(unittest.TestCase):
@client_context.require_auth
def setUp(self):
client = rs_or_single_client_noauth()
client_context.client.admin.add_user('admin', 'pass',
roles=['userAdminAnyDatabase',
'dbAdminAnyDatabase',
'readWriteAnyDatabase',
'clusterAdmin'])
client.admin.authenticate('admin', 'pass')
client = rs_or_single_client_noauth(username='admin', password='pass')
client.pymongo_test.add_user('user', 'pass',
roles=['userAdmin', 'readWrite'])
@ -372,11 +389,8 @@ class TestAuthURIOptions(unittest.TestCase):
self.client = client
def tearDown(self):
self.client.admin.authenticate('admin', 'pass')
self.client.pymongo_test.remove_user('user')
self.client.admin.remove_user('admin')
self.client.pymongo_test.logout()
self.client.admin.logout()
self.client = None
def test_uri_options(self):
@ -433,50 +447,5 @@ class TestAuthURIOptions(unittest.TestCase):
self.assertTrue(db.command('dbstats'))
class TestDelegatedAuth(unittest.TestCase):
@client_context.require_auth
@client_context.require_version_max(2, 5, 3)
@client_context.require_version_min(2, 4, 0)
def setUp(self):
self.client = client_context.client
def tearDown(self):
self.client.pymongo_test.remove_user('user')
self.client.pymongo_test2.remove_user('user')
self.client.pymongo_test2.foo.drop()
def test_delegated_auth(self):
self.client.pymongo_test2.foo.drop()
self.client.pymongo_test2.foo.insert_one({})
# User definition with no roles in pymongo_test.
self.client.pymongo_test.add_user('user', 'pass', roles=[])
# Delegate auth to pymongo_test.
self.client.pymongo_test2.add_user('user',
userSource='pymongo_test',
roles=['read'])
auth_c = rs_or_single_client_noauth()
self.assertRaises(OperationFailure,
auth_c.pymongo_test2.foo.find_one)
# Auth must occur on the db where the user is defined.
self.assertRaises(OperationFailure,
auth_c.pymongo_test2.authenticate,
'user', 'pass')
# Auth directly
self.assertTrue(auth_c.pymongo_test.authenticate('user', 'pass'))
self.assertTrue(auth_c.pymongo_test2.foo.find_one())
auth_c.pymongo_test.logout()
self.assertRaises(OperationFailure,
auth_c.pymongo_test2.foo.find_one)
# Auth using source
self.assertTrue(auth_c.pymongo_test2.authenticate(
'user', 'pass', source='pymongo_test'))
self.assertTrue(auth_c.pymongo_test2.foo.find_one())
# Must logout from the db authenticate was called on.
auth_c.pymongo_test2.logout()
self.assertRaises(OperationFailure,
auth_c.pymongo_test2.foo.find_one)
if __name__ == "__main__":
unittest.main()

View File

@ -67,6 +67,7 @@ from test.utils import (assertRaisesExactly,
connected,
delay,
get_pool,
ignore_deprecations,
is_greenthread_patched,
lazy_client_trial,
NTHREADS,
@ -611,6 +612,7 @@ class TestClient(IntegrationTest):
rs_or_single_client(username="ad min", password="foo").server_info()
@client_context.require_auth
@ignore_deprecations
def test_multiple_logins(self):
self.client.pymongo_test.add_user('user1', 'pass', roles=['readWrite'])
self.client.pymongo_test.add_user('user2', 'pass', roles=['readWrite'])

View File

@ -388,7 +388,14 @@ class TestDatabase(IntegrationTest):
def test_authenticate_add_remove_user(self):
# "self.client" is logged in as root.
auth_db = self.client.pymongo_test
db = rs_or_single_client_noauth().pymongo_test
def check_auth(username, password):
c = rs_or_single_client_noauth(
username=username,
password=password,
authSource="pymongo_test")
c.pymongo_test.collection.find_one()
# Configuration errors
self.assertRaises(ValueError, auth_db.add_user, "user", '')
@ -411,45 +418,38 @@ class TestDatabase(IntegrationTest):
"user", "password", digestPassword=True)
# Add / authenticate / remove
auth_db.add_user("mike", "password", roles=["dbOwner"])
auth_db.add_user("mike", "password", roles=["read"])
self.addCleanup(remove_all_users, auth_db)
self.assertRaises(TypeError, db.authenticate, 5, "password")
self.assertRaises(TypeError, db.authenticate, "mike", 5)
self.assertRaises(TypeError, check_auth, 5, "password")
self.assertRaises(TypeError, check_auth, "mike", 5)
self.assertRaises(OperationFailure,
db.authenticate, "mike", "not a real password")
self.assertRaises(OperationFailure,
db.authenticate, "faker", "password")
db.authenticate("mike", "password")
db.logout()
check_auth, "mike", "not a real password")
self.assertRaises(OperationFailure, check_auth, "faker", "password")
check_auth("mike", "password")
# Unicode name and password.
db.authenticate(u"mike", u"password")
db.logout()
check_auth(u"mike", u"password")
auth_db.remove_user("mike")
self.assertRaises(OperationFailure,
db.authenticate, "mike", "password")
check_auth, "mike", "password")
# Add / authenticate / change password
self.assertRaises(OperationFailure,
db.authenticate, "Gustave", u"Dor\xe9")
auth_db.add_user("Gustave", u"Dor\xe9", roles=["dbOwner"])
db.authenticate("Gustave", u"Dor\xe9")
self.assertRaises(OperationFailure, check_auth, "Gustave", u"Dor\xe9")
auth_db.add_user("Gustave", u"Dor\xe9", roles=["read"])
check_auth("Gustave", u"Dor\xe9")
# Change password.
auth_db.add_user("Gustave", "password", roles=["dbOwner"])
db.logout()
self.assertRaises(OperationFailure,
db.authenticate, "Gustave", u"Dor\xe9")
self.assertTrue(db.authenticate("Gustave", u"password"))
auth_db.add_user("Gustave", "password", roles=["read"])
self.assertRaises(OperationFailure, check_auth, "Gustave", u"Dor\xe9")
check_auth("Gustave", u"password")
if not client_context.version.at_least(2, 5, 3, -1):
# Add a readOnly user
with ignore_deprecations():
auth_db.add_user("Ross", "password", read_only=True)
db.logout()
db.authenticate("Ross", u"password")
check_auth("Ross", u"password")
self.assertTrue(
auth_db.system.users.find({"readOnly": True}).count())
@ -457,22 +457,28 @@ class TestDatabase(IntegrationTest):
def test_make_user_readonly(self):
# "self.client" is logged in as root.
auth_db = self.client.pymongo_test
db = rs_or_single_client_noauth().pymongo_test
# Make a read-write user.
auth_db.add_user('jesse', 'pw')
self.addCleanup(remove_all_users, auth_db)
# Check that we're read-write by default.
db.authenticate('jesse', 'pw')
db.collection.insert_one({})
db.logout()
c = rs_or_single_client_noauth(username='jesse',
password='pw',
authSource='pymongo_test')
c.pymongo_test.collection.insert_one({})
# Make the user read-only.
auth_db.add_user('jesse', 'pw', read_only=True)
db.authenticate('jesse', 'pw')
self.assertRaises(OperationFailure, db.collection.insert_one, {})
c = rs_or_single_client_noauth(username='jesse',
password='pw',
authSource='pymongo_test')
self.assertRaises(OperationFailure,
c.pymongo_test.collection.insert_one,
{})
@client_context.require_version_min(2, 5, 3, -1)
@client_context.require_auth
@ -512,8 +518,9 @@ class TestDatabase(IntegrationTest):
auth_db.add_user("amalia", "password", roles=["userAdmin"])
self.addCleanup(auth_db.remove_user, "amalia")
db = rs_or_single_client_noauth().pymongo_test
db.authenticate("amalia", "password")
db = rs_or_single_client_noauth(username="amalia",
password="password",
authSource="pymongo_test").pymongo_test
# This tests the ability to update user attributes.
db.add_user("amalia", "new_password",
@ -526,6 +533,7 @@ class TestDatabase(IntegrationTest):
self.assertEqual(amalia_user["customData"], {"secret": "koalas"})
@client_context.require_auth
@ignore_deprecations
def test_authenticate_multiple(self):
# "self.client" is logged in as root.
self.client.drop_database("pymongo_test")
@ -636,27 +644,28 @@ class TestDatabase(IntegrationTest):
db = self.client.pymongo_test
db.test.drop()
self.assertRaises(TypeError, db.eval, None)
self.assertRaises(TypeError, db.eval, 5)
self.assertRaises(TypeError, db.eval, [])
with ignore_deprecations():
self.assertRaises(TypeError, db.eval, None)
self.assertRaises(TypeError, db.eval, 5)
self.assertRaises(TypeError, db.eval, [])
self.assertEqual(3, db.eval("function (x) {return x;}", 3))
self.assertEqual(3, db.eval(u"function (x) {return x;}", 3))
self.assertEqual(3, db.eval("function (x) {return x;}", 3))
self.assertEqual(3, db.eval(u"function (x) {return x;}", 3))
self.assertEqual(None,
db.eval("function (x) {db.test.save({y:x});}", 5))
self.assertEqual(db.test.find_one()["y"], 5)
self.assertEqual(None,
db.eval("function (x) {db.test.save({y:x});}", 5))
self.assertEqual(db.test.find_one()["y"], 5)
self.assertEqual(5, db.eval("function (x, y) {return x + y;}", 2, 3))
self.assertEqual(5, db.eval("function () {return 5;}"))
self.assertEqual(5, db.eval("2 + 3;"))
self.assertEqual(5, db.eval("function (x, y) {return x + y;}", 2, 3))
self.assertEqual(5, db.eval("function () {return 5;}"))
self.assertEqual(5, db.eval("2 + 3;"))
self.assertEqual(5, db.eval(Code("2 + 3;")))
self.assertRaises(OperationFailure, db.eval, Code("return i;"))
self.assertEqual(2, db.eval(Code("return i;", {"i": 2})))
self.assertEqual(5, db.eval(Code("i + 3;", {"i": 2})))
self.assertEqual(5, db.eval(Code("2 + 3;")))
self.assertRaises(OperationFailure, db.eval, Code("return i;"))
self.assertEqual(2, db.eval(Code("return i;", {"i": 2})))
self.assertEqual(5, db.eval(Code("i + 3;", {"i": 2})))
self.assertRaises(OperationFailure, db.eval, "5 ++ 5;")
self.assertRaises(OperationFailure, db.eval, "5 ++ 5;")
# TODO some of these tests belong in the collection level testing.
def test_insert_find_one(self):

View File

@ -507,28 +507,28 @@ class TestSSL(IntegrationTest):
{'role': 'readWriteAnyDatabase', 'db': 'admin'},
{'role': 'userAdminAnyDatabase', 'db': 'admin'}])
ssl_client.admin.logout()
noauth = MongoClient(
client_context.pair,
ssl=True,
ssl_cert_reqs=ssl.CERT_NONE,
ssl_certfile=CLIENT_PEM)
coll = ssl_client.pymongo_test.test
self.assertRaises(OperationFailure, coll.count)
self.assertRaises(OperationFailure, noauth.pymongo_test.test.count)
auth = MongoClient(
client_context.pair,
authMechanism='MONGODB-X509',
ssl=True,
ssl_cert_reqs=ssl.CERT_NONE,
ssl_certfile=CLIENT_PEM)
if client_context.version.at_least(3, 3, 12):
self.assertTrue(
ssl_client.admin.authenticate(mechanism='MONGODB-X509'))
# No error
coll.find_one()
# MONGODB_X509_USERNAME and None aren't the same user, so we
# have to log out before continuing.
ssl_client.admin.logout()
auth.pymongo_test.test.find_one()
else:
# Should require a username
with self.assertRaises(ConfigurationError):
ssl_client.admin.authenticate(mechanism='MONGODB-X509')
self.assertTrue(ssl_client.admin.authenticate(
MONGODB_X509_USERNAME, mechanism='MONGODB-X509'))
# No error
coll.find_one()
auth.pymongo_test.test.find_one()
uri = ('mongodb://%s@%s:%d/?authMechanism='
'MONGODB-X509' % (
@ -560,12 +560,20 @@ class TestSSL(IntegrationTest):
bad_client = MongoClient(
uri, ssl=True, ssl_cert_reqs="CERT_NONE", ssl_certfile=CLIENT_PEM)
with self.assertRaises(OperationFailure):
bad_client.pymongo_test.test.find_one()
self.assertRaises(OperationFailure, ssl_client.admin.authenticate,
"not the username",
mechanism="MONGODB-X509")
bad_client = MongoClient(
client_context.pair,
username="not the username",
authMechanism='MONGODB-X509',
ssl=True,
ssl_cert_reqs=ssl.CERT_NONE,
ssl_certfile=CLIENT_PEM)
with self.assertRaises(OperationFailure):
bad_client.pymongo_test.test.find_one()
# Invalid certificate (using CA certificate as client certificate)
uri = ('mongodb://%s@%s:%d/?authMechanism='

View File

@ -16,6 +16,7 @@
"""
import contextlib
import functools
import os
import struct
import sys
@ -348,12 +349,26 @@ def assertRaisesExactly(cls, fn, *args, **kwargs):
@contextlib.contextmanager
def ignore_deprecations():
def _ignore_deprecations():
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
yield
def ignore_deprecations(wrapped=None):
"""A context manager or a decorator."""
if wrapped:
@functools.wraps(wrapped)
def wrapper(*args, **kwargs):
with _ignore_deprecations():
return wrapped(*args, **kwargs)
return wrapper
else:
return _ignore_deprecations()
def read_from_which_host(
client,
pref,