PYTHON-751 Move all auth tests to the test_auth module.
This commit is contained in:
parent
33d0c702a5
commit
7decdd8a40
@ -19,7 +19,8 @@ import os
|
||||
import warnings
|
||||
|
||||
import pymongo
|
||||
from pymongo.errors import ConnectionFailure
|
||||
from nose.plugins.skip import SkipTest
|
||||
from pymongo.errors import OperationFailure
|
||||
|
||||
# hostnames retrieved by MongoReplicaSetClient from isMaster will be of unicode
|
||||
# type in Python 2, so ensure these hostnames are unicodes, too. It makes tests
|
||||
@ -34,6 +35,66 @@ port2 = int(os.environ.get("DB_PORT2", 27018))
|
||||
host3 = unicode(os.environ.get("DB_IP3", 'localhost'))
|
||||
port3 = int(os.environ.get("DB_PORT3", 27019))
|
||||
|
||||
db_user = unicode(os.environ.get("DB_USER", "administrator"))
|
||||
db_pwd = unicode(os.environ.get("DB_PWD", "password"))
|
||||
|
||||
|
||||
class AuthContext(object):
|
||||
|
||||
def __init__(self):
|
||||
self.client = pymongo.MongoClient(host, port)
|
||||
self.auth_enabled = False
|
||||
self.restricted_localhost = False
|
||||
try:
|
||||
command_line = self.client.admin.command('getCmdLineOpts')
|
||||
if self._server_started_with_auth(command_line):
|
||||
self.auth_enabled = True
|
||||
except OperationFailure, e:
|
||||
if e.code == 13:
|
||||
self.auth_enabled = True
|
||||
self.restricted_localhost = True
|
||||
else:
|
||||
raise
|
||||
|
||||
def _server_started_with_auth(self, command_line):
|
||||
# MongoDB >= 2.0
|
||||
if 'parsed' in command_line:
|
||||
parsed = command_line['parsed']
|
||||
# MongoDB >= 2.6
|
||||
if 'security' in parsed:
|
||||
security = parsed['security']
|
||||
if 'authorization' in security:
|
||||
return security['authorization'] == 'enabled'
|
||||
return security.get('auth', bool(security.get('keyFile')))
|
||||
return parsed.get('auth', bool(parsed.get('keyFile')))
|
||||
# Legacy
|
||||
argv = command_line['argv']
|
||||
return '--auth' in argv or '--keyFile' in argv
|
||||
|
||||
def add_user_and_log_in(self):
|
||||
self.client.admin.add_user(db_user, db_pwd,
|
||||
roles=('userAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'clusterAdmin'))
|
||||
self.client.admin.authenticate(db_user, db_pwd)
|
||||
|
||||
def remove_user_and_log_out(self):
|
||||
self.client.admin.remove_user(db_user)
|
||||
self.client.admin.logout()
|
||||
self.client.disconnect()
|
||||
|
||||
|
||||
auth_context = AuthContext()
|
||||
|
||||
|
||||
def skip_restricted_localhost():
|
||||
"""Skip tests when the localhost exception is restricted (SERVER-12621)."""
|
||||
if auth_context.restricted_localhost:
|
||||
raise SkipTest("Cannot test with restricted localhost exception "
|
||||
"(SERVER-12621).")
|
||||
|
||||
|
||||
# Make sure warnings are always raised, regardless of
|
||||
# python version.
|
||||
def setup():
|
||||
@ -42,16 +103,16 @@ def setup():
|
||||
|
||||
|
||||
def teardown():
|
||||
try:
|
||||
c = pymongo.MongoClient(host, port)
|
||||
except ConnectionFailure:
|
||||
# Tests where ssl=True can cause connection failures here.
|
||||
# Ignore and continue.
|
||||
return
|
||||
client = auth_context.client
|
||||
if auth_context.auth_enabled:
|
||||
auth_context.add_user_and_log_in()
|
||||
|
||||
c.drop_database("pymongo-pooling-tests")
|
||||
c.drop_database("pymongo_test")
|
||||
c.drop_database("pymongo_test1")
|
||||
c.drop_database("pymongo_test2")
|
||||
c.drop_database("pymongo_test_mike")
|
||||
c.drop_database("pymongo_test_bernie")
|
||||
client.drop_database("pymongo-pooling-tests")
|
||||
client.drop_database("pymongo_test")
|
||||
client.drop_database("pymongo_test1")
|
||||
client.drop_database("pymongo_test2")
|
||||
client.drop_database("pymongo_test_mike")
|
||||
client.drop_database("pymongo_test_bernie")
|
||||
|
||||
if auth_context.auth_enabled:
|
||||
auth_context.remove_user_and_log_out()
|
||||
|
||||
@ -15,9 +15,11 @@
|
||||
"""Authentication Tests."""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
from urllib import quote_plus
|
||||
|
||||
@ -25,12 +27,28 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from pymongo import MongoClient, MongoReplicaSetClient
|
||||
from pymongo import (MongoClient,
|
||||
MongoReplicaSetClient,
|
||||
auth)
|
||||
from pymongo.auth import HAVE_KERBEROS
|
||||
from pymongo.errors import OperationFailure, ConfigurationError
|
||||
from pymongo.errors import (OperationFailure,
|
||||
ConfigurationError,
|
||||
ConnectionFailure,
|
||||
AutoReconnect)
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from test import version, host, port
|
||||
from test.utils import is_mongos, server_started_with_auth
|
||||
from test import version, host, port, pair, auth_context
|
||||
from test.test_bulk import BulkTestBase
|
||||
from test.test_client import get_client
|
||||
from test.test_pooling_base import get_pool
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.test_threads import AutoAuthenticateThreads
|
||||
from test.utils import (is_mongos,
|
||||
remove_all_users,
|
||||
assertRaisesExactly,
|
||||
one,
|
||||
catch_warnings,
|
||||
TestRequestMixin,
|
||||
joinall)
|
||||
|
||||
# YOU MUST RUN KINIT BEFORE RUNNING GSSAPI TESTS.
|
||||
GSSAPI_HOST = os.environ.get('GSSAPI_HOST')
|
||||
@ -44,6 +62,20 @@ SASL_PASS = os.environ.get('SASL_PASS')
|
||||
SASL_DB = os.environ.get('SASL_DB', '$external')
|
||||
|
||||
|
||||
def setUpModule():
|
||||
if not auth_context.auth_enabled:
|
||||
raise SkipTest("Server not started with --auth.")
|
||||
if (is_mongos(auth_context.client) and
|
||||
not version.at_least(auth_context.client, (2, 0, 0))):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
auth_context.add_user_and_log_in()
|
||||
|
||||
|
||||
def tearDownModule():
|
||||
if auth_context.auth_enabled:
|
||||
auth_context.remove_user_and_log_out()
|
||||
|
||||
|
||||
class AutoAuthenticateThread(threading.Thread):
|
||||
"""Used in testing threaded authentication.
|
||||
"""
|
||||
@ -230,17 +262,13 @@ class TestAuthURIOptions(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
client = MongoClient(host, port)
|
||||
# Sharded auth not supported before MongoDB 2.0
|
||||
if is_mongos(client) and not version.at_least(client, (2, 0, 0)):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
if not server_started_with_auth(client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
response = client.admin.command('ismaster')
|
||||
self.set_name = str(response.get('setName', ''))
|
||||
client.admin.add_user('admin', 'pass', roles=['userAdminAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'clusterAdmin'])
|
||||
auth_context.client.admin.add_user('admin', 'pass',
|
||||
roles=['userAdminAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'clusterAdmin'])
|
||||
client.admin.authenticate('admin', 'pass')
|
||||
client.pymongo_test.add_user('user', 'pass',
|
||||
roles=['userAdmin', 'readWrite'])
|
||||
@ -314,19 +342,18 @@ class TestDelegatedAuth(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.client = MongoClient(host, port)
|
||||
if not version.at_least(self.client, (2, 4, 0)):
|
||||
authed_client = auth_context.client
|
||||
if not version.at_least(authed_client, (2, 4, 0)):
|
||||
raise SkipTest('Delegated authentication requires MongoDB >= 2.4.0')
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
if version.at_least(self.client, (2, 5, 3, -1)):
|
||||
if version.at_least(authed_client, (2, 5, 3, -1)):
|
||||
raise SkipTest('Delegated auth does not exist in MongoDB >= 2.5.3')
|
||||
# Give admin all privileges.
|
||||
self.client.admin.add_user('admin', 'pass',
|
||||
roles=['readAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'clusterAdmin'])
|
||||
authed_client.admin.add_user('admin', 'pass',
|
||||
roles=['readAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'clusterAdmin'])
|
||||
|
||||
def tearDown(self):
|
||||
self.client.admin.authenticate('admin', 'pass')
|
||||
@ -370,5 +397,730 @@ class TestDelegatedAuth(unittest.TestCase):
|
||||
self.client.pymongo_test2.foo.find_one)
|
||||
|
||||
|
||||
class TestClientAuth(unittest.TestCase):
|
||||
|
||||
def test_copy_db(self):
|
||||
authed_client = auth_context.client
|
||||
if is_mongos(authed_client):
|
||||
raise SkipTest("SERVER-6427")
|
||||
|
||||
c = MongoClient(host, port)
|
||||
|
||||
authed_client.admin.add_user("admin", "password")
|
||||
c.admin.authenticate("admin", "password")
|
||||
c.drop_database("pymongo_test1")
|
||||
c.pymongo_test.test.insert({"foo": "bar"})
|
||||
|
||||
try:
|
||||
c.pymongo_test.add_user("mike", "password")
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="foo", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="mike", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
c.copy_database("pymongo_test", "pymongo_test1",
|
||||
username="mike", password="password")
|
||||
self.assertTrue("pymongo_test1" in c.database_names())
|
||||
self.assertEqual("bar", c.pymongo_test1.test.find_one()["foo"])
|
||||
finally:
|
||||
# Cleanup
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user("admin")
|
||||
c.disconnect()
|
||||
|
||||
def test_auth_from_uri(self):
|
||||
c = MongoClient(host, port)
|
||||
auth_context.client.admin.add_user("admin", "pass")
|
||||
c.admin.authenticate("admin", "pass")
|
||||
try:
|
||||
c.pymongo_test.add_user("user", "pass",
|
||||
roles=['userAdmin', 'readWrite'])
|
||||
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://foo:bar@%s:%d" % (host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://admin:bar@%s:%d" % (host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://user:pass@%s:%d" % (host, port))
|
||||
MongoClient("mongodb://admin:pass@%s:%d" % (host, port))
|
||||
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://admin:pass@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://user:foo@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
MongoClient("mongodb://user:pass@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
|
||||
# Auth with lazy connection.
|
||||
MongoClient(
|
||||
"mongodb://user:pass@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False).pymongo_test.test.find_one()
|
||||
|
||||
# Wrong password.
|
||||
bad_client = MongoClient(
|
||||
"mongodb://user:wrong@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False)
|
||||
|
||||
self.assertRaises(OperationFailure,
|
||||
bad_client.pymongo_test.test.find_one)
|
||||
|
||||
finally:
|
||||
# Clean up.
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user('admin')
|
||||
|
||||
def test_lazy_auth_raises_operation_failure(self):
|
||||
lazy_client = MongoClient(
|
||||
"mongodb://user:wrong@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False)
|
||||
|
||||
assertRaisesExactly(
|
||||
OperationFailure, lazy_client.test.collection.find_one)
|
||||
|
||||
def test_unix_socket(self):
|
||||
authed_client = auth_context.client
|
||||
if not hasattr(socket, "AF_UNIX"):
|
||||
raise SkipTest("UNIX-sockets are not supported on this system")
|
||||
if (sys.platform == 'darwin' and
|
||||
not version.at_least(authed_client, (2, 7, 1))):
|
||||
raise SkipTest("SERVER-8492")
|
||||
|
||||
mongodb_socket = '/tmp/mongodb-27017.sock'
|
||||
if not os.access(mongodb_socket, os.R_OK):
|
||||
raise SkipTest("Socket file is not accessable")
|
||||
|
||||
self.assertTrue(MongoClient("mongodb://%s" % mongodb_socket))
|
||||
|
||||
authed_client.admin.add_user('admin', 'pass')
|
||||
|
||||
try:
|
||||
client = MongoClient("mongodb://%s" % mongodb_socket)
|
||||
client.admin.authenticate('admin', 'pass')
|
||||
client.pymongo_test.test.save({"dummy": "object"})
|
||||
|
||||
# Confirm we can read via the socket
|
||||
dbs = client.database_names()
|
||||
self.assertTrue("pymongo_test" in dbs)
|
||||
|
||||
# Confirm it fails with a missing socket
|
||||
self.assertRaises(ConnectionFailure, MongoClient,
|
||||
"mongodb:///tmp/none-existent.sock")
|
||||
finally:
|
||||
authed_client.admin.remove_user('admin')
|
||||
|
||||
def test_auth_network_error(self):
|
||||
# Make sure there's no semaphore leak if we get a network error
|
||||
# when authenticating a new socket with cached credentials.
|
||||
auth_client = get_client()
|
||||
|
||||
auth_context.client.admin.add_user('admin', 'password')
|
||||
auth_client.admin.authenticate('admin', 'password')
|
||||
try:
|
||||
# Get a client with one socket so we detect if it's leaked.
|
||||
c = get_client(max_pool_size=1, waitQueueTimeoutMS=1)
|
||||
|
||||
# Simulate an authenticate() call on a different socket.
|
||||
credentials = auth._build_credentials_tuple(
|
||||
'MONGODB-CR', 'admin',
|
||||
unicode('admin'), unicode('password'),
|
||||
{})
|
||||
|
||||
c._cache_credentials('test', credentials, connect=False)
|
||||
|
||||
# Cause a network error on the actual socket.
|
||||
pool = get_pool(c)
|
||||
socket_info = one(pool.sockets)
|
||||
socket_info.sock.close()
|
||||
|
||||
# In __check_auth, the client authenticates its socket with the
|
||||
# new credential, but gets a socket.error. Should be reraised as
|
||||
# AutoReconnect.
|
||||
self.assertRaises(AutoReconnect, c.test.collection.find_one)
|
||||
|
||||
# No semaphore leak, the pool is allowed to make a new socket.
|
||||
c.test.collection.find_one()
|
||||
finally:
|
||||
auth_client.admin.remove_user('admin')
|
||||
|
||||
|
||||
class TestDatabaseAuth(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = MongoClient(host, port)
|
||||
|
||||
def test_authenticate_add_remove_user(self):
|
||||
authed_client = auth_context.client
|
||||
db = authed_client.pymongo_test
|
||||
|
||||
# Configuration errors
|
||||
self.assertRaises(ValueError, db.add_user, "user", '')
|
||||
self.assertRaises(TypeError, db.add_user, "user", 'password', 15)
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", 'password', 'True')
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", 'password', True, roles=['read'])
|
||||
|
||||
if version.at_least(authed_client, (2, 5, 3, -1)):
|
||||
ctx = catch_warnings()
|
||||
try:
|
||||
warnings.simplefilter("error", DeprecationWarning)
|
||||
self.assertRaises(DeprecationWarning, db.add_user,
|
||||
"user", "password")
|
||||
self.assertRaises(DeprecationWarning, db.add_user,
|
||||
"user", "password", True)
|
||||
finally:
|
||||
ctx.exit()
|
||||
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", "password", digestPassword=True)
|
||||
|
||||
authed_client.admin.add_user("admin", "password")
|
||||
self.client.admin.authenticate("admin", "password")
|
||||
db = self.client.pymongo_test
|
||||
|
||||
try:
|
||||
# Add / authenticate / remove
|
||||
db.add_user("mike", "password")
|
||||
self.assertRaises(TypeError, db.authenticate, 5, "password")
|
||||
self.assertRaises(TypeError, db.authenticate, "mike", 5)
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "mike", "not a real password")
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "faker", "password")
|
||||
self.assertTrue(db.authenticate("mike", "password"))
|
||||
db.logout()
|
||||
self.assertTrue(db.authenticate(u"mike", u"password"))
|
||||
db.remove_user("mike")
|
||||
db.logout()
|
||||
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "mike", "password")
|
||||
|
||||
# Add / authenticate / change password
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "Gustave", u"Dor\xe9")
|
||||
db.add_user("Gustave", u"Dor\xe9")
|
||||
self.assertTrue(db.authenticate("Gustave", u"Dor\xe9"))
|
||||
db.add_user("Gustave", "password")
|
||||
db.logout()
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "Gustave", u"Dor\xe9")
|
||||
self.assertTrue(db.authenticate("Gustave", u"password"))
|
||||
|
||||
if not version.at_least(authed_client, (2, 5, 3, -1)):
|
||||
# Add a readOnly user
|
||||
db.add_user("Ross", "password", read_only=True)
|
||||
db.logout()
|
||||
self.assertTrue(db.authenticate("Ross", u"password"))
|
||||
self.assertTrue(
|
||||
db.system.users.find({"readOnly": True}).count())
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
remove_all_users(db)
|
||||
self.client.admin.remove_user("admin")
|
||||
self.client.admin.logout()
|
||||
|
||||
def test_make_user_readonly(self):
|
||||
admin = self.client.admin
|
||||
auth_context.client.admin.add_user('admin', 'pw')
|
||||
admin.authenticate('admin', 'pw')
|
||||
|
||||
db = self.client.pymongo_test
|
||||
|
||||
try:
|
||||
# Make a read-write user.
|
||||
db.add_user('jesse', 'pw')
|
||||
admin.logout()
|
||||
|
||||
# Check that we're read-write by default.
|
||||
db.authenticate('jesse', 'pw')
|
||||
db.collection.insert({})
|
||||
db.logout()
|
||||
|
||||
# Make the user read-only.
|
||||
admin.authenticate('admin', 'pw')
|
||||
db.add_user('jesse', 'pw', read_only=True)
|
||||
admin.logout()
|
||||
|
||||
db.authenticate('jesse', 'pw')
|
||||
self.assertRaises(OperationFailure, db.collection.insert, {})
|
||||
finally:
|
||||
# Cleanup
|
||||
admin.authenticate('admin', 'pw')
|
||||
remove_all_users(db)
|
||||
admin.remove_user("admin")
|
||||
admin.logout()
|
||||
|
||||
def test_default_roles(self):
|
||||
authed_client = auth_context.client
|
||||
if not version.at_least(authed_client, (2, 5, 3, -1)):
|
||||
raise SkipTest("Default roles only exist in MongoDB >= 2.5.3")
|
||||
|
||||
# "Admin" user
|
||||
db = self.client.admin
|
||||
authed_client.admin.add_user('admin', 'pass')
|
||||
try:
|
||||
db.authenticate('admin', 'pass')
|
||||
info = db.command('usersInfo', 'admin')['users'][0]
|
||||
self.assertEqual("root", info['roles'][0]['role'])
|
||||
|
||||
# Read only "admin" user
|
||||
db.add_user('ro-admin', 'pass', read_only=True)
|
||||
db.logout()
|
||||
db.authenticate('ro-admin', 'pass')
|
||||
info = db.command('usersInfo', 'ro-admin')['users'][0]
|
||||
self.assertEqual("readAnyDatabase", info['roles'][0]['role'])
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
db.authenticate('admin', 'pass')
|
||||
db.remove_user('ro-admin')
|
||||
db.remove_user('admin')
|
||||
db.logout()
|
||||
|
||||
db.connection.disconnect()
|
||||
|
||||
# "Non-admin" user
|
||||
db = self.client.pymongo_test
|
||||
authed_client.pymongo_test.add_user('user', 'pass')
|
||||
try:
|
||||
db.authenticate('user', 'pass')
|
||||
info = db.command('usersInfo', 'user')['users'][0]
|
||||
self.assertEqual("dbOwner", info['roles'][0]['role'])
|
||||
|
||||
# Read only "Non-admin" user
|
||||
db.add_user('ro-user', 'pass', read_only=True)
|
||||
db.logout()
|
||||
db.authenticate('ro-user', 'pass')
|
||||
info = db.command('usersInfo', 'ro-user')['users'][0]
|
||||
self.assertEqual("read", info['roles'][0]['role'])
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
db.authenticate('user', 'pass')
|
||||
remove_all_users(db)
|
||||
db.logout()
|
||||
|
||||
def test_new_user_cmds(self):
|
||||
authed_client = auth_context.client
|
||||
if not version.at_least(authed_client, (2, 5, 3, -1)):
|
||||
raise SkipTest("User manipulation through commands "
|
||||
"requires MongoDB >= 2.5.3")
|
||||
|
||||
db = self.client.pymongo_test
|
||||
authed_client.pymongo_test.add_user("amalia", "password",
|
||||
roles=["userAdmin"])
|
||||
db.authenticate("amalia", "password")
|
||||
try:
|
||||
# This tests the ability to update user attributes.
|
||||
db.add_user("amalia", "new_password",
|
||||
customData={"secret": "koalas"})
|
||||
|
||||
user_info = db.command("usersInfo", "amalia")
|
||||
self.assertTrue(user_info["users"])
|
||||
amalia_user = user_info["users"][0]
|
||||
self.assertEqual(amalia_user["user"], "amalia")
|
||||
self.assertEqual(amalia_user["customData"], {"secret": "koalas"})
|
||||
finally:
|
||||
db.remove_user("amalia")
|
||||
db.logout()
|
||||
|
||||
def test_authenticate_and_safe(self):
|
||||
db = auth_context.client.auth_test
|
||||
|
||||
db.add_user("bernie", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
db.authenticate("bernie", "password")
|
||||
try:
|
||||
db.test.remove({})
|
||||
self.assertTrue(db.test.insert({"bim": "baz"}))
|
||||
self.assertEqual(1, db.test.count())
|
||||
|
||||
self.assertEqual(1,
|
||||
db.test.update({"bim": "baz"},
|
||||
{"$set": {"bim": "bar"}}).get('n'))
|
||||
|
||||
self.assertEqual(1,
|
||||
db.test.remove({}).get('n'))
|
||||
|
||||
self.assertEqual(0, db.test.count())
|
||||
finally:
|
||||
db.remove_user("bernie")
|
||||
db.logout()
|
||||
|
||||
def test_authenticate_and_request(self):
|
||||
# Database.authenticate() needs to be in a request - check that it
|
||||
# always runs in a request, and that it restores the request state
|
||||
# (in or not in a request) properly when it's finished.
|
||||
self.assertFalse(self.client.auto_start_request)
|
||||
db = self.client.pymongo_test
|
||||
auth_context.client.pymongo_test.add_user(
|
||||
"mike", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
try:
|
||||
self.assertFalse(self.client.in_request())
|
||||
self.assertTrue(db.authenticate("mike", "password"))
|
||||
self.assertFalse(self.client.in_request())
|
||||
|
||||
request_cx = get_client(auto_start_request=True)
|
||||
request_db = request_cx.pymongo_test
|
||||
self.assertTrue(request_db.authenticate("mike", "password"))
|
||||
self.assertTrue(request_cx.in_request())
|
||||
finally:
|
||||
db.authenticate("mike", "password")
|
||||
db.remove_user("mike")
|
||||
db.logout()
|
||||
request_db.logout()
|
||||
|
||||
def test_authenticate_multiple(self):
|
||||
client = get_client()
|
||||
authed_client = auth_context.client
|
||||
if (is_mongos(authed_client) and not
|
||||
version.at_least(authed_client, (2, 2, 0))):
|
||||
raise SkipTest("Need mongos >= 2.2.0")
|
||||
|
||||
# Setup
|
||||
authed_client.pymongo_test.test.drop()
|
||||
authed_client.pymongo_test1.test.drop()
|
||||
users_db = client.pymongo_test
|
||||
admin_db = client.admin
|
||||
other_db = client.pymongo_test1
|
||||
|
||||
authed_client.admin.add_user('admin', 'pass',
|
||||
roles=["userAdminAnyDatabase", "dbAdmin",
|
||||
"clusterAdmin", "readWrite"])
|
||||
try:
|
||||
self.assertTrue(admin_db.authenticate('admin', 'pass'))
|
||||
|
||||
if version.at_least(self.client, (2, 5, 3, -1)):
|
||||
admin_db.add_user('ro-admin', 'pass',
|
||||
roles=["userAdmin", "readAnyDatabase"])
|
||||
else:
|
||||
admin_db.add_user('ro-admin', 'pass', read_only=True)
|
||||
|
||||
users_db.add_user('user', 'pass',
|
||||
roles=["userAdmin", "readWrite"])
|
||||
|
||||
admin_db.logout()
|
||||
self.assertRaises(OperationFailure, users_db.test.find_one)
|
||||
|
||||
# Regular user should be able to query its own db, but
|
||||
# no other.
|
||||
users_db.authenticate('user', 'pass')
|
||||
self.assertEqual(0, users_db.test.count())
|
||||
self.assertRaises(OperationFailure, other_db.test.find_one)
|
||||
|
||||
# Admin read-only user should be able to query any db,
|
||||
# but not write.
|
||||
admin_db.authenticate('ro-admin', 'pass')
|
||||
self.assertEqual(0, other_db.test.count())
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert, {})
|
||||
|
||||
# Force close all sockets
|
||||
client.disconnect()
|
||||
|
||||
# We should still be able to write to the regular user's db
|
||||
self.assertTrue(users_db.test.remove())
|
||||
# And read from other dbs...
|
||||
self.assertEqual(0, other_db.test.count())
|
||||
# But still not write to other dbs...
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert, {})
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
admin_db.logout()
|
||||
users_db.logout()
|
||||
admin_db.authenticate('admin', 'pass')
|
||||
remove_all_users(users_db)
|
||||
admin_db.remove_user('ro-admin')
|
||||
admin_db.remove_user('admin')
|
||||
|
||||
|
||||
class TestReplicaSetClientAuth(TestReplicaSetClientBase, TestRequestMixin):
|
||||
|
||||
def test_init_disconnected_with_auth_failure(self):
|
||||
c = MongoReplicaSetClient(
|
||||
"mongodb://user:pass@somedomainthatdoesntexist", replicaSet="rs",
|
||||
connectTimeoutMS=1, _connect=False)
|
||||
|
||||
self.assertRaises(ConnectionFailure, c.pymongo_test.test.find_one)
|
||||
|
||||
def test_init_disconnected_with_auth(self):
|
||||
c = self._get_client()
|
||||
|
||||
auth_context.client.admin.add_user("admin", "pass")
|
||||
c.admin.authenticate("admin", "pass")
|
||||
try:
|
||||
c.pymongo_test.add_user("user", "pass",
|
||||
roles=['readWrite', 'userAdmin'])
|
||||
|
||||
# Auth with lazy connection.
|
||||
host = one(self.hosts)
|
||||
uri = "mongodb://user:pass@%s:%d/pymongo_test?replicaSet=%s" % (
|
||||
host[0], host[1], self.name)
|
||||
|
||||
authenticated_client = MongoReplicaSetClient(uri, _connect=False)
|
||||
authenticated_client.pymongo_test.test.find_one()
|
||||
|
||||
# Wrong password.
|
||||
bad_uri = ("mongodb://user:wrong@%s:%d/pymongo_test?replicaSet=%s"
|
||||
% (host[0], host[1], self.name))
|
||||
|
||||
bad_client = MongoReplicaSetClient(bad_uri, _connect=False)
|
||||
self.assertRaises(
|
||||
OperationFailure, bad_client.pymongo_test.test.find_one)
|
||||
|
||||
finally:
|
||||
# Clean up.
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user('admin')
|
||||
|
||||
def test_lazy_auth_raises_operation_failure(self):
|
||||
lazy_client = MongoReplicaSetClient(
|
||||
"mongodb://user:wrong@%s/pymongo_test" % pair,
|
||||
replicaSet=self.name,
|
||||
_connect=False)
|
||||
|
||||
assertRaisesExactly(
|
||||
OperationFailure, lazy_client.test.collection.find_one)
|
||||
|
||||
def test_copy_db(self):
|
||||
c = self._get_client()
|
||||
|
||||
auth_context.client.admin.add_user("admin", "password")
|
||||
c.admin.authenticate("admin", "password")
|
||||
c.drop_database("pymongo_test1")
|
||||
c.pymongo_test.test.insert({"foo": "bar"})
|
||||
|
||||
try:
|
||||
c.pymongo_test.add_user("mike", "password")
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="foo", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="mike", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
c.copy_database("pymongo_test", "pymongo_test1",
|
||||
username="mike", password="password")
|
||||
self.assertTrue("pymongo_test1" in c.database_names())
|
||||
res = c.pymongo_test1.test.find_one(_must_use_master=True)
|
||||
self.assertEqual("bar", res["foo"])
|
||||
finally:
|
||||
# Cleanup
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user("admin")
|
||||
c.close()
|
||||
|
||||
def test_auth_network_error(self):
|
||||
# Make sure there's no semaphore leak if we get a network error
|
||||
# when authenticating a new socket with cached credentials.
|
||||
auth_client = self._get_client()
|
||||
|
||||
auth_context.client.admin.add_user('admin', 'password')
|
||||
auth_client.admin.authenticate('admin', 'password')
|
||||
try:
|
||||
# Get a client with one socket so we detect if it's leaked.
|
||||
c = self._get_client(max_pool_size=1, waitQueueTimeoutMS=1)
|
||||
|
||||
# Simulate an authenticate() call on a different socket.
|
||||
credentials = auth._build_credentials_tuple(
|
||||
'MONGODB-CR', 'admin',
|
||||
unicode('admin'), unicode('password'),
|
||||
{})
|
||||
|
||||
c._cache_credentials('test', credentials, connect=False)
|
||||
|
||||
# Cause a network error on the actual socket.
|
||||
pool = get_pool(c)
|
||||
socket_info = one(pool.sockets)
|
||||
socket_info.sock.close()
|
||||
|
||||
# In __check_auth, the client authenticates its socket with the
|
||||
# new credential, but gets a socket.error. Should be reraised as
|
||||
# AutoReconnect.
|
||||
self.assertRaises(AutoReconnect, c.test.collection.find_one)
|
||||
|
||||
# No semaphore leak, the pool is allowed to make a new socket.
|
||||
c.test.collection.find_one()
|
||||
finally:
|
||||
auth_client.admin.remove_user('admin')
|
||||
|
||||
|
||||
class TestBulkAuthorization(BulkTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBulkAuthorization, self).setUp()
|
||||
self.client = client = get_client()
|
||||
authed_client = auth_context.client
|
||||
if not version.at_least(authed_client, (2, 5, 3)):
|
||||
raise SkipTest('Need at least MongoDB 2.5.3 with auth')
|
||||
|
||||
db = client.pymongo_test
|
||||
self.coll = db.test
|
||||
|
||||
authed_client.pymongo_test.test.drop()
|
||||
authed_client.pymongo_test.add_user('dbOwner', 'pw', roles=['dbOwner'])
|
||||
db.authenticate('dbOwner', 'pw')
|
||||
db.add_user('readonly', 'pw', roles=['read'])
|
||||
db.command(
|
||||
'createRole', 'noremove',
|
||||
privileges=[{
|
||||
'actions': ['insert', 'update', 'find'],
|
||||
'resource': {'db': 'pymongo_test', 'collection': 'test'}
|
||||
}],
|
||||
roles=[])
|
||||
|
||||
db.add_user('noremove', 'pw', roles=['noremove'])
|
||||
db.logout()
|
||||
|
||||
def test_readonly(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
db = self.client.pymongo_test
|
||||
db.authenticate('readonly', 'pw')
|
||||
bulk = self.coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
self.assertRaises(OperationFailure, bulk.execute)
|
||||
|
||||
def test_no_remove(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
db = self.client.pymongo_test
|
||||
db.authenticate('noremove', 'pw')
|
||||
bulk = self.coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
bulk.find({'x': 2}).upsert().replace_one({'x': 2})
|
||||
bulk.find({}).remove() # Prohibited.
|
||||
bulk.insert({'x': 3}) # Never attempted.
|
||||
self.assertRaises(OperationFailure, bulk.execute)
|
||||
self.assertEqual(set([1, 2]), set(self.coll.distinct('x')))
|
||||
|
||||
def tearDown(self):
|
||||
db = self.client.pymongo_test
|
||||
db.logout()
|
||||
db.authenticate('dbOwner', 'pw')
|
||||
db.command('dropRole', 'noremove')
|
||||
remove_all_users(db)
|
||||
db.logout()
|
||||
|
||||
|
||||
class BaseTestThreadsAuth(object):
|
||||
"""
|
||||
Base test class for TestThreadsAuth and TestThreadsAuthReplicaSet. (This is
|
||||
not itself a unittest.TestCase, otherwise it'd be run twice -- once when
|
||||
nose imports this module, and once when nose imports
|
||||
test_threads_replica_set_connection.py, which imports this module.)
|
||||
"""
|
||||
def _get_client(self):
|
||||
"""
|
||||
Intended for overriding in TestThreadsAuthReplicaSet. This method
|
||||
returns a MongoClient here, and a MongoReplicaSetClient in
|
||||
test_threads_replica_set_connection.py.
|
||||
"""
|
||||
# Regular test client
|
||||
return get_client()
|
||||
|
||||
def setUp(self):
|
||||
client = self._get_client()
|
||||
self.client = client
|
||||
auth_context.client.admin.add_user('admin-user', 'password',
|
||||
roles=['clusterAdmin',
|
||||
'dbAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase'])
|
||||
self.client.admin.authenticate("admin-user", "password")
|
||||
self.client.auth_test.add_user("test-user", "password",
|
||||
roles=['readWrite'])
|
||||
|
||||
def tearDown(self):
|
||||
# Remove auth users from databases
|
||||
self.client.admin.authenticate("admin-user", "password")
|
||||
self.client.drop_database('auth_test')
|
||||
remove_all_users(self.client.auth_test)
|
||||
self.client.admin.remove_user('admin-user')
|
||||
# Clear client reference so that RSC's monitor thread
|
||||
# dies.
|
||||
self.client = None
|
||||
|
||||
def test_auto_auth_login(self):
|
||||
client = self._get_client()
|
||||
self.assertRaises(OperationFailure, client.auth_test.test.find_one)
|
||||
|
||||
# Admin auth
|
||||
client = self._get_client()
|
||||
client.admin.authenticate("admin-user", "password")
|
||||
|
||||
nthreads = 10
|
||||
threads = []
|
||||
for _ in xrange(nthreads):
|
||||
t = AutoAuthenticateThreads(client.auth_test.test, 100)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
joinall(threads)
|
||||
|
||||
for t in threads:
|
||||
self.assertTrue(t.success)
|
||||
|
||||
# Database-specific auth
|
||||
client = self._get_client()
|
||||
client.auth_test.authenticate("test-user", "password")
|
||||
|
||||
threads = []
|
||||
for _ in xrange(nthreads):
|
||||
t = AutoAuthenticateThreads(client.auth_test.test, 100)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
joinall(threads)
|
||||
|
||||
for t in threads:
|
||||
self.assertTrue(t.success)
|
||||
|
||||
|
||||
class TestThreadsAuth(BaseTestThreadsAuth, unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestThreadsAuthReplicaSet(TestReplicaSetClientBase, BaseTestThreadsAuth):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Prepare to test all the same things that TestThreads tests, but do it
|
||||
with a replica-set client
|
||||
"""
|
||||
TestReplicaSetClientBase.setUp(self)
|
||||
BaseTestThreadsAuth.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
TestReplicaSetClientBase.tearDown(self)
|
||||
BaseTestThreadsAuth.tearDown(self)
|
||||
|
||||
def _get_client(self):
|
||||
"""
|
||||
Override TestThreadsAuth, so its tests run on a MongoReplicaSetClient
|
||||
instead of a regular MongoClient.
|
||||
"""
|
||||
return MongoReplicaSetClient(pair, replicaSet=self.name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -33,9 +33,14 @@ from bson.binary import *
|
||||
from bson.py3compat import b, binary_type
|
||||
from bson.son import SON
|
||||
from nose.plugins.skip import SkipTest
|
||||
from test import skip_restricted_localhost
|
||||
from test.test_client import get_client
|
||||
from pymongo.mongo_client import MongoClient
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestBinary(unittest.TestCase):
|
||||
def test_binary(self):
|
||||
a_string = "hello world"
|
||||
|
||||
@ -23,12 +23,13 @@ sys.path[0:0] = [""]
|
||||
|
||||
from bson import InvalidDocument, SON
|
||||
from pymongo.errors import BulkWriteError, InvalidOperation, OperationFailure
|
||||
from test import version
|
||||
from test import version, skip_restricted_localhost
|
||||
from test.test_client import get_client
|
||||
from test.utils import (oid_generated_on_client,
|
||||
remove_all_users,
|
||||
server_started_with_auth,
|
||||
server_started_with_nojournal)
|
||||
from test.utils import oid_generated_on_client
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class BulkTestBase(unittest.TestCase):
|
||||
|
||||
@ -1135,63 +1136,5 @@ class TestBulkNoResults(BulkTestBase):
|
||||
self.assertTrue(self.coll.find_one({'_id': 1}) is None)
|
||||
|
||||
|
||||
class TestBulkAuthorization(BulkTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestBulkAuthorization, self).setUp()
|
||||
self.client = client = get_client()
|
||||
if (not server_started_with_auth(client)
|
||||
or not version.at_least(client, (2, 5, 3))):
|
||||
raise SkipTest('Need at least MongoDB 2.5.3 with auth')
|
||||
|
||||
db = client.pymongo_test
|
||||
self.coll = db.test
|
||||
self.coll.remove()
|
||||
|
||||
db.add_user('dbOwner', 'pw', roles=['dbOwner'])
|
||||
db.authenticate('dbOwner', 'pw')
|
||||
db.add_user('readonly', 'pw', roles=['read'])
|
||||
db.command(
|
||||
'createRole', 'noremove',
|
||||
privileges=[{
|
||||
'actions': ['insert', 'update', 'find'],
|
||||
'resource': {'db': 'pymongo_test', 'collection': 'test'}
|
||||
}],
|
||||
roles=[])
|
||||
|
||||
db.add_user('noremove', 'pw', roles=['noremove'])
|
||||
db.logout()
|
||||
|
||||
def test_readonly(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
db = self.client.pymongo_test
|
||||
db.authenticate('readonly', 'pw')
|
||||
bulk = self.coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
self.assertRaises(OperationFailure, bulk.execute)
|
||||
|
||||
def test_no_remove(self):
|
||||
# We test that an authorization failure aborts the batch and is raised
|
||||
# as OperationFailure.
|
||||
db = self.client.pymongo_test
|
||||
db.authenticate('noremove', 'pw')
|
||||
bulk = self.coll.initialize_ordered_bulk_op()
|
||||
bulk.insert({'x': 1})
|
||||
bulk.find({'x': 2}).upsert().replace_one({'x': 2})
|
||||
bulk.find({}).remove() # Prohibited.
|
||||
bulk.insert({'x': 3}) # Never attempted.
|
||||
self.assertRaises(OperationFailure, bulk.execute)
|
||||
self.assertEqual(set([1, 2]), set(self.coll.distinct('x')))
|
||||
|
||||
def tearDown(self):
|
||||
db = self.client.pymongo_test
|
||||
db.logout()
|
||||
db.authenticate('dbOwner', 'pw')
|
||||
db.command('dropRole', 'noremove')
|
||||
remove_all_users(db)
|
||||
db.logout()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -34,20 +34,18 @@ from bson.tz_util import utc
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.database import Database
|
||||
from pymongo.pool import SocketInfo
|
||||
from pymongo import auth, thread_util, common
|
||||
from pymongo import thread_util
|
||||
from pymongo.errors import (AutoReconnect,
|
||||
ConfigurationError,
|
||||
ConnectionFailure,
|
||||
InvalidName,
|
||||
OperationFailure,
|
||||
PyMongoError)
|
||||
from test import version, host, port, pair
|
||||
OperationFailure)
|
||||
from test import version, host, port, pair, skip_restricted_localhost
|
||||
from test.pymongo_mocks import MockClient
|
||||
from test.utils import (assertRaisesExactly,
|
||||
catch_warnings,
|
||||
delay,
|
||||
is_mongos,
|
||||
remove_all_users,
|
||||
server_is_master_with_slave,
|
||||
server_started_with_auth,
|
||||
TestRequestMixin,
|
||||
@ -55,8 +53,9 @@ from test.utils import (assertRaisesExactly,
|
||||
_TestExhaustCursorMixin,
|
||||
lazy_client_trial,
|
||||
NTHREADS,
|
||||
get_pool,
|
||||
one)
|
||||
get_pool)
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
def get_client(*args, **kwargs):
|
||||
@ -266,36 +265,6 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
self.assertTrue("pymongo_test2" in c.database_names())
|
||||
self.assertEqual("bar", c.pymongo_test2.test.find_one()["foo"])
|
||||
|
||||
# See SERVER-6427 for mongos
|
||||
if not is_mongos(c) and server_started_with_auth(c):
|
||||
|
||||
c.drop_database("pymongo_test1")
|
||||
|
||||
c.admin.add_user("admin", "password")
|
||||
c.admin.authenticate("admin", "password")
|
||||
try:
|
||||
c.pymongo_test.add_user("mike", "password")
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="foo", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="mike", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
c.copy_database("pymongo_test", "pymongo_test1",
|
||||
username="mike", password="password")
|
||||
self.assertTrue("pymongo_test1" in c.database_names())
|
||||
self.assertEqual("bar", c.pymongo_test1.test.find_one()["foo"])
|
||||
finally:
|
||||
# Cleanup
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user("admin")
|
||||
c.disconnect()
|
||||
|
||||
def test_iteration(self):
|
||||
client = MongoClient(host, port)
|
||||
|
||||
@ -347,75 +316,13 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
c = MongoClient(uri, _connect=False)
|
||||
self.assertEqual(Database(c, 'foo'), c.get_default_database())
|
||||
|
||||
def test_auth_from_uri(self):
|
||||
c = MongoClient(host, port)
|
||||
# Sharded auth not supported before MongoDB 2.0
|
||||
if is_mongos(c) and not version.at_least(c, (2, 0, 0)):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
if not server_started_with_auth(c):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
c.admin.add_user("admin", "pass")
|
||||
c.admin.authenticate("admin", "pass")
|
||||
try:
|
||||
c.pymongo_test.add_user("user", "pass", roles=['userAdmin', 'readWrite'])
|
||||
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://foo:bar@%s:%d" % (host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://admin:bar@%s:%d" % (host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://user:pass@%s:%d" % (host, port))
|
||||
MongoClient("mongodb://admin:pass@%s:%d" % (host, port))
|
||||
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://admin:pass@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://user:foo@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
MongoClient("mongodb://user:pass@%s:%d/pymongo_test" %
|
||||
(host, port))
|
||||
|
||||
# Auth with lazy connection.
|
||||
MongoClient(
|
||||
"mongodb://user:pass@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False).pymongo_test.test.find_one()
|
||||
|
||||
# Wrong password.
|
||||
bad_client = MongoClient(
|
||||
"mongodb://user:wrong@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False)
|
||||
|
||||
self.assertRaises(OperationFailure,
|
||||
bad_client.pymongo_test.test.find_one)
|
||||
|
||||
finally:
|
||||
# Clean up.
|
||||
remove_all_users(c.pymongo_test)
|
||||
remove_all_users(c.admin)
|
||||
|
||||
def test_lazy_auth_raises_operation_failure(self):
|
||||
# Check if we have the prerequisites to run this test.
|
||||
c = MongoClient(host, port)
|
||||
if not server_started_with_auth(c):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
if is_mongos(c) and not version.at_least(c, (2, 0, 0)):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
|
||||
lazy_client = MongoClient(
|
||||
"mongodb://user:wrong@%s:%d/pymongo_test" % (host, port),
|
||||
_connect=False)
|
||||
|
||||
assertRaisesExactly(
|
||||
OperationFailure, lazy_client.test.collection.find_one)
|
||||
|
||||
def test_unix_socket(self):
|
||||
if not hasattr(socket, "AF_UNIX"):
|
||||
raise SkipTest("UNIX-sockets are not supported on this system")
|
||||
client = MongoClient(host, port)
|
||||
if (sys.platform == 'darwin' and
|
||||
server_started_with_auth(MongoClient(host, port))):
|
||||
server_started_with_auth(client) and
|
||||
not version.at_least(client, (2, 7, 1))):
|
||||
raise SkipTest("SERVER-8492")
|
||||
|
||||
mongodb_socket = '/tmp/mongodb-27017.sock'
|
||||
@ -1001,42 +908,6 @@ with client.start_request() as request:
|
||||
client = get_client(_connect=False)
|
||||
client.pymongo_test.test.remove(w=0)
|
||||
|
||||
def test_auth_network_error(self):
|
||||
# Make sure there's no semaphore leak if we get a network error
|
||||
# when authenticating a new socket with cached credentials.
|
||||
auth_client = get_client()
|
||||
if not server_started_with_auth(auth_client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
auth_client.admin.add_user('admin', 'password')
|
||||
auth_client.admin.authenticate('admin', 'password')
|
||||
try:
|
||||
# Get a client with one socket so we detect if it's leaked.
|
||||
c = get_client(max_pool_size=1, waitQueueTimeoutMS=1)
|
||||
|
||||
# Simulate an authenticate() call on a different socket.
|
||||
credentials = auth._build_credentials_tuple(
|
||||
'MONGODB-CR', 'admin',
|
||||
unicode('admin'), unicode('password'),
|
||||
{})
|
||||
|
||||
c._cache_credentials('test', credentials, connect=False)
|
||||
|
||||
# Cause a network error on the actual socket.
|
||||
pool = get_pool(c)
|
||||
socket_info = one(pool.sockets)
|
||||
socket_info.sock.close()
|
||||
|
||||
# In __check_auth, the client authenticates its socket with the
|
||||
# new credential, but gets a socket.error. Should be reraised as
|
||||
# AutoReconnect.
|
||||
self.assertRaises(AutoReconnect, c.test.collection.find_one)
|
||||
|
||||
# No semaphore leak, the pool is allowed to make a new socket.
|
||||
c.test.collection.find_one()
|
||||
finally:
|
||||
remove_all_users(auth_client.admin)
|
||||
|
||||
|
||||
class TestClientLazyConnect(unittest.TestCase, _TestLazyConnectMixin):
|
||||
def _get_client(self, **kwargs):
|
||||
|
||||
@ -53,8 +53,7 @@ from pymongo.errors import (DocumentTooLarge,
|
||||
from test.test_client import get_client
|
||||
from test.utils import (catch_warnings, enable_text_search,
|
||||
get_pool, is_mongos, joinall, oid_generated_on_client)
|
||||
from test import (qcheck,
|
||||
version)
|
||||
from test import qcheck, version, skip_restricted_localhost
|
||||
|
||||
have_uuid = True
|
||||
try:
|
||||
@ -63,6 +62,9 @@ except ImportError:
|
||||
have_uuid = False
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestCollection(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
@ -30,7 +30,7 @@ from pymongo.connection import Connection
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.errors import ConfigurationError, OperationFailure
|
||||
from test import host, port, pair, version
|
||||
from test import host, port, pair, version, skip_restricted_localhost
|
||||
from test.utils import catch_warnings, drop_collections
|
||||
|
||||
have_uuid = True
|
||||
@ -40,6 +40,9 @@ except ImportError:
|
||||
have_uuid = False
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestCommon(unittest.TestCase):
|
||||
|
||||
def test_baseobject(self):
|
||||
|
||||
@ -37,12 +37,15 @@ from pymongo.database import Database
|
||||
from pymongo.errors import (InvalidOperation,
|
||||
OperationFailure,
|
||||
ExecutionTimeout)
|
||||
from test import version
|
||||
from test import version, skip_restricted_localhost
|
||||
from test.test_client import get_client
|
||||
from test.utils import (catch_warnings, is_mongos,
|
||||
get_command_line, server_started_with_auth)
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestCursor(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"""Test the database module."""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
@ -39,7 +38,6 @@ from pymongo import (ALL,
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.database import Database
|
||||
from pymongo.errors import (CollectionInvalid,
|
||||
ConfigurationError,
|
||||
ExecutionTimeout,
|
||||
InvalidName,
|
||||
OperationFailure)
|
||||
@ -47,12 +45,15 @@ from pymongo.son_manipulator import (AutoReference,
|
||||
NamespaceInjector,
|
||||
SONManipulator,
|
||||
ObjectIdShuffler)
|
||||
from test import version
|
||||
from test import version, skip_restricted_localhost
|
||||
from test.utils import (catch_warnings, get_command_line,
|
||||
is_mongos, remove_all_users, server_started_with_auth)
|
||||
is_mongos, server_started_with_auth)
|
||||
from test.test_client import get_client
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestDatabase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -358,319 +359,6 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertEqual(auth._password_digest("Gustave", u"Dor\xe9"),
|
||||
u"81e0e2364499209f466e75926a162d73")
|
||||
|
||||
def test_authenticate_add_remove_user(self):
|
||||
if (is_mongos(self.client) and not
|
||||
version.at_least(self.client, (2, 0, 0))):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
db = self.client.pymongo_test
|
||||
|
||||
# Configuration errors
|
||||
self.assertRaises(ValueError, db.add_user, "user", '')
|
||||
self.assertRaises(TypeError, db.add_user, "user", 'password', 15)
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", 'password', 'True')
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", 'password', True, roles=['read'])
|
||||
|
||||
if version.at_least(self.client, (2, 5, 3, -1)):
|
||||
ctx = catch_warnings()
|
||||
try:
|
||||
warnings.simplefilter("error", DeprecationWarning)
|
||||
self.assertRaises(DeprecationWarning, db.add_user,
|
||||
"user", "password")
|
||||
self.assertRaises(DeprecationWarning, db.add_user,
|
||||
"user", "password", True)
|
||||
finally:
|
||||
ctx.exit()
|
||||
|
||||
self.assertRaises(ConfigurationError, db.add_user,
|
||||
"user", "password", digestPassword=True)
|
||||
|
||||
self.client.admin.add_user("admin", "password")
|
||||
self.client.admin.authenticate("admin", "password")
|
||||
|
||||
try:
|
||||
# Add / authenticate / remove
|
||||
db.add_user("mike", "password")
|
||||
self.assertRaises(TypeError, db.authenticate, 5, "password")
|
||||
self.assertRaises(TypeError, db.authenticate, "mike", 5)
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "mike", "not a real password")
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "faker", "password")
|
||||
self.assertTrue(db.authenticate("mike", "password"))
|
||||
db.logout()
|
||||
self.assertTrue(db.authenticate(u"mike", u"password"))
|
||||
db.remove_user("mike")
|
||||
db.logout()
|
||||
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "mike", "password")
|
||||
|
||||
# Add / authenticate / change password
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "Gustave", u"Dor\xe9")
|
||||
db.add_user("Gustave", u"Dor\xe9")
|
||||
self.assertTrue(db.authenticate("Gustave", u"Dor\xe9"))
|
||||
db.add_user("Gustave", "password")
|
||||
db.logout()
|
||||
self.assertRaises(OperationFailure,
|
||||
db.authenticate, "Gustave", u"Dor\xe9")
|
||||
self.assertTrue(db.authenticate("Gustave", u"password"))
|
||||
|
||||
if not version.at_least(self.client, (2, 5, 3, -1)):
|
||||
# Add a readOnly user
|
||||
db.add_user("Ross", "password", read_only=True)
|
||||
db.logout()
|
||||
self.assertTrue(db.authenticate("Ross", u"password"))
|
||||
self.assertTrue(db.system.users.find({"readOnly": True}).count())
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
remove_all_users(db)
|
||||
self.client.admin.remove_user("admin")
|
||||
self.client.admin.logout()
|
||||
|
||||
def test_make_user_readonly(self):
|
||||
if (is_mongos(self.client)
|
||||
and not version.at_least(self.client, (2, 0, 0))):
|
||||
raise SkipTest('Auth with sharding requires MongoDB >= 2.0.0')
|
||||
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
admin = self.client.admin
|
||||
admin.add_user('admin', 'pw')
|
||||
admin.authenticate('admin', 'pw')
|
||||
|
||||
db = self.client.pymongo_test
|
||||
|
||||
try:
|
||||
# Make a read-write user.
|
||||
db.add_user('jesse', 'pw')
|
||||
admin.logout()
|
||||
|
||||
# Check that we're read-write by default.
|
||||
db.authenticate('jesse', 'pw')
|
||||
db.collection.insert({})
|
||||
db.logout()
|
||||
|
||||
# Make the user read-only.
|
||||
admin.authenticate('admin', 'pw')
|
||||
db.add_user('jesse', 'pw', read_only=True)
|
||||
admin.logout()
|
||||
|
||||
db.authenticate('jesse', 'pw')
|
||||
self.assertRaises(OperationFailure, db.collection.insert, {})
|
||||
finally:
|
||||
# Cleanup
|
||||
admin.authenticate('admin', 'pw')
|
||||
remove_all_users(db)
|
||||
admin.remove_user("admin")
|
||||
admin.logout()
|
||||
|
||||
def test_default_roles(self):
|
||||
if not version.at_least(self.client, (2, 5, 3, -1)):
|
||||
raise SkipTest("Default roles only exist in MongoDB >= 2.5.3")
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
# "Admin" user
|
||||
db = self.client.admin
|
||||
db.add_user('admin', 'pass')
|
||||
try:
|
||||
db.authenticate('admin', 'pass')
|
||||
info = db.command('usersInfo', 'admin')['users'][0]
|
||||
self.assertEqual("root", info['roles'][0]['role'])
|
||||
|
||||
# Read only "admin" user
|
||||
db.add_user('ro-admin', 'pass', read_only=True)
|
||||
db.logout()
|
||||
db.authenticate('ro-admin', 'pass')
|
||||
info = db.command('usersInfo', 'ro-admin')['users'][0]
|
||||
self.assertEqual("readAnyDatabase", info['roles'][0]['role'])
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
db.authenticate('admin', 'pass')
|
||||
remove_all_users(db)
|
||||
db.logout()
|
||||
|
||||
db.connection.disconnect()
|
||||
|
||||
# "Non-admin" user
|
||||
db = self.client.pymongo_test
|
||||
db.add_user('user', 'pass')
|
||||
try:
|
||||
db.authenticate('user', 'pass')
|
||||
info = db.command('usersInfo', 'user')['users'][0]
|
||||
self.assertEqual("dbOwner", info['roles'][0]['role'])
|
||||
|
||||
# Read only "Non-admin" user
|
||||
db.add_user('ro-user', 'pass', read_only=True)
|
||||
db.logout()
|
||||
db.authenticate('ro-user', 'pass')
|
||||
info = db.command('usersInfo', 'ro-user')['users'][0]
|
||||
self.assertEqual("read", info['roles'][0]['role'])
|
||||
db.logout()
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
db.authenticate('user', 'pass')
|
||||
remove_all_users(db)
|
||||
db.logout()
|
||||
|
||||
def test_new_user_cmds(self):
|
||||
if not version.at_least(self.client, (2, 5, 3, -1)):
|
||||
raise SkipTest("User manipulation through commands "
|
||||
"requires MongoDB >= 2.5.3")
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
db = self.client.pymongo_test
|
||||
db.add_user("amalia", "password", roles=["userAdmin"])
|
||||
db.authenticate("amalia", "password")
|
||||
try:
|
||||
# This tests the ability to update user attributes.
|
||||
db.add_user("amalia", "new_password",
|
||||
customData={"secret": "koalas"})
|
||||
|
||||
user_info = db.command("usersInfo", "amalia")
|
||||
self.assertTrue(user_info["users"])
|
||||
amalia_user = user_info["users"][0]
|
||||
self.assertEqual(amalia_user["user"], "amalia")
|
||||
self.assertEqual(amalia_user["customData"], {"secret": "koalas"})
|
||||
finally:
|
||||
db.remove_user("amalia")
|
||||
db.logout()
|
||||
|
||||
def test_authenticate_and_safe(self):
|
||||
if (is_mongos(self.client) and not
|
||||
version.at_least(self.client, (2, 0, 0))):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
db = self.client.auth_test
|
||||
|
||||
db.add_user("bernie", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
db.authenticate("bernie", "password")
|
||||
try:
|
||||
db.test.remove({})
|
||||
self.assertTrue(db.test.insert({"bim": "baz"}))
|
||||
self.assertEqual(1, db.test.count())
|
||||
|
||||
self.assertEqual(1,
|
||||
db.test.update({"bim": "baz"},
|
||||
{"$set": {"bim": "bar"}}).get('n'))
|
||||
|
||||
self.assertEqual(1,
|
||||
db.test.remove({}).get('n'))
|
||||
|
||||
self.assertEqual(0, db.test.count())
|
||||
finally:
|
||||
db.remove_user("bernie")
|
||||
db.logout()
|
||||
|
||||
def test_authenticate_and_request(self):
|
||||
if (is_mongos(self.client) and not
|
||||
version.at_least(self.client, (2, 0, 0))):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
if not server_started_with_auth(self.client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
# Database.authenticate() needs to be in a request - check that it
|
||||
# always runs in a request, and that it restores the request state
|
||||
# (in or not in a request) properly when it's finished.
|
||||
self.assertFalse(self.client.auto_start_request)
|
||||
db = self.client.pymongo_test
|
||||
db.add_user("mike", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
try:
|
||||
self.assertFalse(self.client.in_request())
|
||||
self.assertTrue(db.authenticate("mike", "password"))
|
||||
self.assertFalse(self.client.in_request())
|
||||
|
||||
request_cx = get_client(auto_start_request=True)
|
||||
request_db = request_cx.pymongo_test
|
||||
self.assertTrue(request_db.authenticate("mike", "password"))
|
||||
self.assertTrue(request_cx.in_request())
|
||||
finally:
|
||||
db.authenticate("mike", "password")
|
||||
db.remove_user("mike")
|
||||
db.logout()
|
||||
request_db.logout()
|
||||
|
||||
def test_authenticate_multiple(self):
|
||||
client = get_client()
|
||||
if (is_mongos(client) and not
|
||||
version.at_least(self.client, (2, 2, 0))):
|
||||
raise SkipTest("Need mongos >= 2.2.0")
|
||||
if not server_started_with_auth(client):
|
||||
raise SkipTest("Authentication is not enabled on server")
|
||||
|
||||
# Setup
|
||||
users_db = client.pymongo_test
|
||||
admin_db = client.admin
|
||||
other_db = client.pymongo_test1
|
||||
users_db.test.remove()
|
||||
other_db.test.remove()
|
||||
|
||||
admin_db.add_user('admin', 'pass',
|
||||
roles=["userAdminAnyDatabase", "dbAdmin",
|
||||
"clusterAdmin", "readWrite"])
|
||||
try:
|
||||
self.assertTrue(admin_db.authenticate('admin', 'pass'))
|
||||
|
||||
if version.at_least(self.client, (2, 5, 3, -1)):
|
||||
admin_db.add_user('ro-admin', 'pass',
|
||||
roles=["userAdmin", "readAnyDatabase"])
|
||||
else:
|
||||
admin_db.add_user('ro-admin', 'pass', read_only=True)
|
||||
|
||||
users_db.add_user('user', 'pass',
|
||||
roles=["userAdmin", "readWrite"])
|
||||
|
||||
admin_db.logout()
|
||||
self.assertRaises(OperationFailure, users_db.test.find_one)
|
||||
|
||||
# Regular user should be able to query its own db, but
|
||||
# no other.
|
||||
users_db.authenticate('user', 'pass')
|
||||
self.assertEqual(0, users_db.test.count())
|
||||
self.assertRaises(OperationFailure, other_db.test.find_one)
|
||||
|
||||
# Admin read-only user should be able to query any db,
|
||||
# but not write.
|
||||
admin_db.authenticate('ro-admin', 'pass')
|
||||
self.assertEqual(0, other_db.test.count())
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert, {})
|
||||
|
||||
# Force close all sockets
|
||||
client.disconnect()
|
||||
|
||||
# We should still be able to write to the regular user's db
|
||||
self.assertTrue(users_db.test.remove())
|
||||
# And read from other dbs...
|
||||
self.assertEqual(0, other_db.test.count())
|
||||
# But still not write to other dbs...
|
||||
self.assertRaises(OperationFailure,
|
||||
other_db.test.insert, {})
|
||||
|
||||
# Cleanup
|
||||
finally:
|
||||
admin_db.logout()
|
||||
users_db.logout()
|
||||
admin_db.authenticate('admin', 'pass')
|
||||
remove_all_users(users_db)
|
||||
remove_all_users(admin_db)
|
||||
|
||||
def test_id_ordering(self):
|
||||
# PyMongo attempts to have _id show up first
|
||||
# when you iterate key/value pairs in a document.
|
||||
@ -1020,6 +708,5 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertEqual('value', out.get('value'))
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -39,7 +39,10 @@ from gridfs.errors import (NoFile,
|
||||
from pymongo import MongoClient
|
||||
from pymongo.errors import ConnectionFailure
|
||||
from test.test_client import get_client
|
||||
from test import qcheck
|
||||
from test import qcheck, skip_restricted_localhost
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestGridFile(unittest.TestCase):
|
||||
|
||||
@ -32,11 +32,15 @@ from gridfs.errors import (FileExists, NoFile)
|
||||
from pymongo.errors import ConnectionFailure
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from test import skip_restricted_localhost
|
||||
from test.test_client import get_client
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.utils import catch_warnings, joinall
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class JustWrite(threading.Thread):
|
||||
|
||||
def __init__(self, fs, n):
|
||||
|
||||
@ -37,6 +37,7 @@ from bson.son import RE_TYPE
|
||||
from bson.timestamp import Timestamp
|
||||
from bson.tz_util import utc
|
||||
|
||||
from test import skip_restricted_localhost
|
||||
from test.test_client import get_client
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
@ -221,6 +222,7 @@ class TestJsonUtil(unittest.TestCase):
|
||||
self.assertEqual('{"$code": "return z", "$scope": {"z": 2}}', res)
|
||||
|
||||
def test_cursor(self):
|
||||
skip_restricted_localhost()
|
||||
db = self.db
|
||||
|
||||
db.drop_collection("test")
|
||||
|
||||
@ -27,11 +27,14 @@ import pymongo
|
||||
from pymongo.connection import Connection
|
||||
from pymongo.replica_set_connection import ReplicaSetConnection
|
||||
from pymongo.errors import ConfigurationError
|
||||
from test import host, port, pair
|
||||
from test import host, port, pair, skip_restricted_localhost
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.utils import catch_warnings, get_pool
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestConnection(unittest.TestCase):
|
||||
def test_connection(self):
|
||||
c = Connection(host, port)
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"""Test for master slave connections."""
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
@ -36,10 +35,16 @@ from pymongo.database import Database
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.collection import Collection
|
||||
from pymongo.master_slave_connection import MasterSlaveConnection
|
||||
from test import host, port, host2, port2, host3, port3
|
||||
from test import (host, port,
|
||||
host2, port2,
|
||||
host3, port3,
|
||||
skip_restricted_localhost)
|
||||
from test.utils import TestRequestMixin, catch_warnings, get_pool
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestMasterSlaveConnection(unittest.TestCase, TestRequestMixin):
|
||||
|
||||
def setUp(self):
|
||||
|
||||
@ -21,9 +21,13 @@ import unittest
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.errors import AutoReconnect
|
||||
from test import skip_restricted_localhost
|
||||
from test.pymongo_mocks import MockClient
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class FindOne(threading.Thread):
|
||||
def __init__(self, client):
|
||||
super(FindOne, self).__init__()
|
||||
|
||||
@ -18,14 +18,13 @@ import datetime
|
||||
import pickle
|
||||
import unittest
|
||||
import sys
|
||||
import time
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from bson.errors import InvalidId
|
||||
from bson.objectid import ObjectId
|
||||
from bson.py3compat import b, binary_type
|
||||
from bson.py3compat import b
|
||||
from bson.tz_util import (FixedOffset,
|
||||
utc)
|
||||
|
||||
|
||||
@ -23,13 +23,16 @@ sys.path[0:0] = [""]
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from test import host, port
|
||||
from test import host, port, skip_restricted_localhost
|
||||
from test.test_pooling_base import (
|
||||
_TestPooling, _TestMaxPoolSize, _TestMaxOpenSockets,
|
||||
_TestPoolSocketSharing, _TestWaitQueueMultiple, one)
|
||||
from test.utils import get_pool
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestPoolingThreads(_TestPooling, unittest.TestCase):
|
||||
use_greenlets = False
|
||||
|
||||
|
||||
@ -23,13 +23,16 @@ from nose.plugins.skip import SkipTest
|
||||
|
||||
from pymongo import pool
|
||||
from pymongo.errors import ConfigurationError
|
||||
from test import host, port
|
||||
from test import host, port, skip_restricted_localhost
|
||||
from test.utils import looplet
|
||||
from test.test_pooling_base import (
|
||||
_TestPooling, _TestMaxPoolSize, _TestMaxOpenSockets,
|
||||
_TestPoolSocketSharing, _TestWaitQueueMultiple, has_gevent)
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestPoolingGevent(_TestPooling, unittest.TestCase):
|
||||
"""Apply all the standard pool tests with greenlets and Gevent"""
|
||||
use_greenlets = True
|
||||
|
||||
@ -15,7 +15,6 @@
|
||||
"""Test the pymongo module itself."""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import sys
|
||||
sys.path[0:0] = [""]
|
||||
|
||||
|
||||
@ -31,10 +31,13 @@ from pymongo.errors import ConfigurationError
|
||||
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
from test.test_client import get_client
|
||||
from test import version, utils, host, port
|
||||
from test import version, utils, host, port, skip_restricted_localhost
|
||||
from test.utils import catch_warnings
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestReadPreferencesBase(TestReplicaSetClientBase):
|
||||
def setUp(self):
|
||||
super(TestReadPreferencesBase, self).setUp()
|
||||
|
||||
@ -35,7 +35,7 @@ from bson.son import SON
|
||||
from bson.tz_util import utc
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.read_preferences import ReadPreference
|
||||
from pymongo.member import PRIMARY, SECONDARY, OTHER
|
||||
from pymongo.member import SECONDARY
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from pymongo.mongo_replica_set_client import _partition_node, have_gevent
|
||||
from pymongo.database import Database
|
||||
@ -45,16 +45,17 @@ from pymongo.errors import (AutoReconnect,
|
||||
ConnectionFailure,
|
||||
InvalidName,
|
||||
OperationFailure, InvalidOperation)
|
||||
from pymongo import auth
|
||||
from test import version, port, pair
|
||||
from test import version, port, pair, skip_restricted_localhost, auth_context
|
||||
from test.pymongo_mocks import MockReplicaSetClient
|
||||
from test.utils import (
|
||||
delay, assertReadFrom, assertReadFromAll, read_from_which_host,
|
||||
remove_all_users, assertRaisesExactly, TestRequestMixin, one,
|
||||
server_started_with_auth, pools_from_rs_client, get_pool,
|
||||
assertRaisesExactly, TestRequestMixin, one, pools_from_rs_client, get_pool,
|
||||
_TestLazyConnectMixin, _TestExhaustCursorMixin)
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestReplicaSetClientAgainstStandalone(unittest.TestCase):
|
||||
"""This is a funny beast -- we want to run tests for MongoReplicaSetClient
|
||||
but only if the database at DB_IP and DB_PORT is a standalone.
|
||||
@ -83,7 +84,10 @@ class TestReplicaSetClientBase(unittest.TestCase):
|
||||
self.arbiters = set([_partition_node(h)
|
||||
for h in response.get("arbiters", [])])
|
||||
|
||||
repl_set_status = client.admin.command('replSetGetStatus')
|
||||
# Cannot run replSetGetStatus in MongoDB >= 2.7.1 under auth once a
|
||||
# user has been added.
|
||||
repl_set_status = auth_context.client.admin.command(
|
||||
'replSetGetStatus')
|
||||
primary_info = [
|
||||
m for m in repl_set_status['members']
|
||||
if m['stateStr'] == 'PRIMARY'
|
||||
@ -168,44 +172,6 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
|
||||
self.assertRaises(ConnectionFailure, c.pymongo_test.test.find_one)
|
||||
|
||||
def test_init_disconnected_with_auth_failure(self):
|
||||
c = MongoReplicaSetClient(
|
||||
"mongodb://user:pass@somedomainthatdoesntexist", replicaSet="rs",
|
||||
connectTimeoutMS=1, _connect=False)
|
||||
|
||||
self.assertRaises(ConnectionFailure, c.pymongo_test.test.find_one)
|
||||
|
||||
def test_init_disconnected_with_auth(self):
|
||||
c = self._get_client()
|
||||
if not server_started_with_auth(c):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
c.admin.add_user("admin", "pass")
|
||||
c.admin.authenticate("admin", "pass")
|
||||
try:
|
||||
c.pymongo_test.add_user("user", "pass", roles=['readWrite', 'userAdmin'])
|
||||
|
||||
# Auth with lazy connection.
|
||||
host = one(self.hosts)
|
||||
uri = "mongodb://user:pass@%s:%d/pymongo_test?replicaSet=%s" % (
|
||||
host[0], host[1], self.name)
|
||||
|
||||
authenticated_client = MongoReplicaSetClient(uri, _connect=False)
|
||||
authenticated_client.pymongo_test.test.find_one()
|
||||
|
||||
# Wrong password.
|
||||
bad_uri = "mongodb://user:wrong@%s:%d/pymongo_test?replicaSet=%s" % (
|
||||
host[0], host[1], self.name)
|
||||
|
||||
bad_client = MongoReplicaSetClient(bad_uri, _connect=False)
|
||||
self.assertRaises(
|
||||
OperationFailure, bad_client.pymongo_test.test.find_one)
|
||||
|
||||
finally:
|
||||
# Clean up.
|
||||
remove_all_users(c.pymongo_test)
|
||||
remove_all_users(c.admin)
|
||||
|
||||
def test_connect(self):
|
||||
assertRaisesExactly(ConnectionFailure, MongoReplicaSetClient,
|
||||
"somedomainthatdoesntexist.org:27017",
|
||||
@ -341,20 +307,6 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
finally:
|
||||
socket.socket.sendall = old_sendall
|
||||
|
||||
def test_lazy_auth_raises_operation_failure(self):
|
||||
# Check if we have the prerequisites to run this test.
|
||||
c = self._get_client()
|
||||
if not server_started_with_auth(c):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
lazy_client = MongoReplicaSetClient(
|
||||
"mongodb://user:wrong@%s/pymongo_test" % pair,
|
||||
replicaSet=self.name,
|
||||
_connect=False)
|
||||
|
||||
assertRaisesExactly(
|
||||
OperationFailure, lazy_client.test.collection.find_one)
|
||||
|
||||
def test_operations(self):
|
||||
c = self._get_client()
|
||||
|
||||
@ -466,35 +418,6 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
self.assertTrue("pymongo_test2" in c.database_names())
|
||||
self.assertEqual("bar", c.pymongo_test2.test.find_one()["foo"])
|
||||
|
||||
if version.at_least(c, (1, 3, 3, 1)) and server_started_with_auth(c):
|
||||
c.drop_database("pymongo_test1")
|
||||
|
||||
c.admin.add_user("admin", "password")
|
||||
c.admin.authenticate("admin", "password")
|
||||
try:
|
||||
c.pymongo_test.add_user("mike", "password")
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="foo", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
self.assertRaises(OperationFailure, c.copy_database,
|
||||
"pymongo_test", "pymongo_test1",
|
||||
username="mike", password="bar")
|
||||
self.assertFalse("pymongo_test1" in c.database_names())
|
||||
|
||||
c.copy_database("pymongo_test", "pymongo_test1",
|
||||
username="mike", password="password")
|
||||
self.assertTrue("pymongo_test1" in c.database_names())
|
||||
res = c.pymongo_test1.test.find_one(_must_use_master=True)
|
||||
self.assertEqual("bar", res["foo"])
|
||||
finally:
|
||||
# Cleanup
|
||||
remove_all_users(c.pymongo_test)
|
||||
c.admin.remove_user("admin")
|
||||
c.close()
|
||||
|
||||
def test_get_default_database(self):
|
||||
host = one(self.hosts)
|
||||
uri = "mongodb://%s:%d/foo?replicaSet=%s" % (
|
||||
@ -1127,42 +1050,6 @@ class TestReplicaSetClient(TestReplicaSetClientBase, TestRequestMixin):
|
||||
|
||||
self.assertFalse(client.alive())
|
||||
|
||||
def test_auth_network_error(self):
|
||||
# Make sure there's no semaphore leak if we get a network error
|
||||
# when authenticating a new socket with cached credentials.
|
||||
auth_client = self._get_client()
|
||||
if not server_started_with_auth(auth_client):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
|
||||
auth_client.admin.add_user('admin', 'password')
|
||||
auth_client.admin.authenticate('admin', 'password')
|
||||
try:
|
||||
# Get a client with one socket so we detect if it's leaked.
|
||||
c = self._get_client(max_pool_size=1, waitQueueTimeoutMS=1)
|
||||
|
||||
# Simulate an authenticate() call on a different socket.
|
||||
credentials = auth._build_credentials_tuple(
|
||||
'MONGODB-CR', 'admin',
|
||||
unicode('admin'), unicode('password'),
|
||||
{})
|
||||
|
||||
c._cache_credentials('test', credentials, connect=False)
|
||||
|
||||
# Cause a network error on the actual socket.
|
||||
pool = get_pool(c)
|
||||
socket_info = one(pool.sockets)
|
||||
socket_info.sock.close()
|
||||
|
||||
# In __check_auth, the client authenticates its socket with the
|
||||
# new credential, but gets a socket.error. Should be reraised as
|
||||
# AutoReconnect.
|
||||
self.assertRaises(AutoReconnect, c.test.collection.find_one)
|
||||
|
||||
# No semaphore leak, the pool is allowed to make a new socket.
|
||||
c.test.collection.find_one()
|
||||
finally:
|
||||
remove_all_users(auth_client.admin)
|
||||
|
||||
|
||||
class TestReplicaSetWireVersion(unittest.TestCase):
|
||||
def test_wire_version(self):
|
||||
|
||||
@ -21,9 +21,13 @@ sys.path[0:0] = [""]
|
||||
|
||||
from pymongo.errors import ConfigurationError, ConnectionFailure
|
||||
from pymongo import ReadPreference
|
||||
from test import skip_restricted_localhost
|
||||
from test.pymongo_mocks import MockClient, MockReplicaSetClient
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestSecondaryBecomesStandalone(unittest.TestCase):
|
||||
# An administrator removes a secondary from a 3-node set and
|
||||
# brings it back up as standalone, without updating the other
|
||||
|
||||
@ -18,14 +18,15 @@ import unittest
|
||||
import threading
|
||||
import traceback
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from test.utils import (joinall, remove_all_users,
|
||||
server_started_with_auth, RendezvousThread)
|
||||
from test import skip_restricted_localhost
|
||||
from test.utils import joinall, RendezvousThread
|
||||
from test.test_client import get_client
|
||||
from test.utils import get_pool
|
||||
from pymongo.pool import SocketInfo, _closed
|
||||
from pymongo.errors import AutoReconnect, OperationFailure
|
||||
from pymongo.errors import AutoReconnect
|
||||
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class AutoAuthenticateThreads(threading.Thread):
|
||||
@ -299,87 +300,9 @@ class BaseTestThreads(object):
|
||||
self.assertTrue(t.passed, "%s threw exception" % t)
|
||||
|
||||
|
||||
class BaseTestThreadsAuth(object):
|
||||
"""
|
||||
Base test class for TestThreadsAuth and TestThreadsAuthReplicaSet. (This is
|
||||
not itself a unittest.TestCase, otherwise it'd be run twice -- once when
|
||||
nose imports this module, and once when nose imports
|
||||
test_threads_replica_set_connection.py, which imports this module.)
|
||||
"""
|
||||
def _get_client(self):
|
||||
"""
|
||||
Intended for overriding in TestThreadsAuthReplicaSet. This method
|
||||
returns a MongoClient here, and a MongoReplicaSetClient in
|
||||
test_threads_replica_set_connection.py.
|
||||
"""
|
||||
# Regular test client
|
||||
return get_client()
|
||||
|
||||
def setUp(self):
|
||||
client = self._get_client()
|
||||
if not server_started_with_auth(client):
|
||||
raise SkipTest("Authentication is not enabled on server")
|
||||
self.client = client
|
||||
self.client.admin.add_user('admin-user', 'password',
|
||||
roles=['clusterAdmin',
|
||||
'dbAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase'])
|
||||
self.client.admin.authenticate("admin-user", "password")
|
||||
self.client.auth_test.add_user("test-user", "password",
|
||||
roles=['readWrite'])
|
||||
|
||||
def tearDown(self):
|
||||
# Remove auth users from databases
|
||||
self.client.admin.authenticate("admin-user", "password")
|
||||
remove_all_users(self.client.auth_test)
|
||||
self.client.drop_database('auth_test')
|
||||
remove_all_users(self.client.admin)
|
||||
# Clear client reference so that RSC's monitor thread
|
||||
# dies.
|
||||
self.client = None
|
||||
|
||||
def test_auto_auth_login(self):
|
||||
client = self._get_client()
|
||||
self.assertRaises(OperationFailure, client.auth_test.test.find_one)
|
||||
|
||||
# Admin auth
|
||||
client = self._get_client()
|
||||
client.admin.authenticate("admin-user", "password")
|
||||
|
||||
nthreads = 10
|
||||
threads = []
|
||||
for _ in xrange(nthreads):
|
||||
t = AutoAuthenticateThreads(client.auth_test.test, 100)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
joinall(threads)
|
||||
|
||||
for t in threads:
|
||||
self.assertTrue(t.success)
|
||||
|
||||
# Database-specific auth
|
||||
client = self._get_client()
|
||||
client.auth_test.authenticate("test-user", "password")
|
||||
|
||||
threads = []
|
||||
for _ in xrange(nthreads):
|
||||
t = AutoAuthenticateThreads(client.auth_test.test, 100)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
joinall(threads)
|
||||
|
||||
for t in threads:
|
||||
self.assertTrue(t.success)
|
||||
|
||||
class TestThreads(BaseTestThreads, unittest.TestCase):
|
||||
pass
|
||||
|
||||
class TestThreadsAuth(BaseTestThreadsAuth, unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -16,10 +16,12 @@
|
||||
|
||||
import unittest
|
||||
|
||||
from pymongo.mongo_replica_set_client import MongoReplicaSetClient
|
||||
from test import skip_restricted_localhost
|
||||
from test.test_threads import BaseTestThreads
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase
|
||||
|
||||
from test.test_threads import BaseTestThreads, BaseTestThreadsAuth
|
||||
from test.test_replica_set_client import TestReplicaSetClientBase, pair
|
||||
|
||||
setUpModule = skip_restricted_localhost
|
||||
|
||||
|
||||
class TestThreadsReplicaSet(TestReplicaSetClientBase, BaseTestThreads):
|
||||
@ -39,31 +41,6 @@ class TestThreadsReplicaSet(TestReplicaSetClientBase, BaseTestThreads):
|
||||
return TestReplicaSetClientBase._get_client(self, **kwargs)
|
||||
|
||||
|
||||
class TestThreadsAuthReplicaSet(TestReplicaSetClientBase, BaseTestThreadsAuth):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Prepare to test all the same things that TestThreads tests, but do it
|
||||
with a replica-set client
|
||||
"""
|
||||
TestReplicaSetClientBase.setUp(self)
|
||||
BaseTestThreadsAuth.setUp(self)
|
||||
|
||||
def tearDown(self):
|
||||
TestReplicaSetClientBase.tearDown(self)
|
||||
BaseTestThreadsAuth.tearDown(self)
|
||||
|
||||
def _get_client(self):
|
||||
"""
|
||||
Override TestThreadsAuth, so its tests run on a MongoReplicaSetClient
|
||||
instead of a regular MongoClient.
|
||||
"""
|
||||
return MongoReplicaSetClient(pair, replicaSet=self.name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
suite = unittest.TestSuite([
|
||||
unittest.makeSuite(TestThreadsReplicaSet),
|
||||
unittest.makeSuite(TestThreadsAuthReplicaSet)
|
||||
])
|
||||
suite = unittest.makeSuite(TestThreadsReplicaSet)
|
||||
unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
|
||||
@ -92,10 +92,8 @@ def server_started_with_auth(client):
|
||||
# MongoDB >= 2.6
|
||||
if 'security' in parsed:
|
||||
security = parsed['security']
|
||||
# >= rc3
|
||||
if 'authorization' in security:
|
||||
return security['authorization'] == 'enabled'
|
||||
# < rc3
|
||||
return security.get('auth', False) or bool(security.get('keyFile'))
|
||||
return parsed.get('auth', False) or bool(parsed.get('keyFile'))
|
||||
# Legacy
|
||||
|
||||
Loading…
Reference in New Issue
Block a user