Deprecate db.authenticate and db.eval
PYTHON-1313, and PYTHON-1315.
This commit is contained in:
parent
9ca77901ab
commit
72e61277b2
@ -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`
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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='
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user