PYTHON-552: Manipulate user objects exclusively via commands for 2.6
This commit is contained in:
parent
6bd590abba
commit
a02e37ba5e
@ -615,6 +615,50 @@ class Database(common.BaseObject):
|
||||
def next(self):
|
||||
raise TypeError("'Database' object is not iterable")
|
||||
|
||||
def _create_user(self, name, password, **kwargs):
|
||||
"""Uses v2 commands for creating a new user.
|
||||
"""
|
||||
create_opts = {}
|
||||
if password is not None:
|
||||
create_opts["pwd"] = password
|
||||
if "roles" not in kwargs:
|
||||
create_opts["roles"] = []
|
||||
create_opts["writeConcern"] = self._get_wc_override()
|
||||
create_opts.update(kwargs)
|
||||
|
||||
self.command("createUser", name, **create_opts)
|
||||
|
||||
def _update_user(self, name, password, **kwargs):
|
||||
"""Uses v2 commands for updating a user.
|
||||
"""
|
||||
update_opts = {}
|
||||
if password is not None:
|
||||
update_opts["pwd"] = password
|
||||
update_opts["writeConcern"] = self._get_wc_override()
|
||||
update_opts.update(kwargs)
|
||||
|
||||
self.command("updateUser", name, **update_opts)
|
||||
|
||||
def _legacy_add_user(self, name, password, read_only, **kwargs):
|
||||
"""Uses v1 system to add users, i.e. saving to system.users.
|
||||
"""
|
||||
user = self.system.users.find_one({"user": name}) or {"user": name}
|
||||
if password is not None:
|
||||
user["pwd"] = auth._password_digest(name, password)
|
||||
if read_only is not None:
|
||||
user["readOnly"] = common.validate_boolean('read_only', read_only)
|
||||
user.update(kwargs)
|
||||
|
||||
try:
|
||||
self.system.users.save(user, **self._get_wc_override())
|
||||
except OperationFailure, e:
|
||||
# First admin user add fails gle in MongoDB >= 2.1.2
|
||||
# See SERVER-4225 for more information.
|
||||
if 'login' in str(e):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
def add_user(self, name, password=None, read_only=None, **kwargs):
|
||||
"""Create user `name` with password `password`.
|
||||
|
||||
@ -644,22 +688,19 @@ class Database(common.BaseObject):
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
|
||||
user = self.system.users.find_one({"user": name}) or {"user": name}
|
||||
if password is not None:
|
||||
user["pwd"] = auth._password_digest(name, password)
|
||||
if read_only is not None:
|
||||
user["readOnly"] = common.validate_boolean('read_only', read_only)
|
||||
user.update(kwargs)
|
||||
|
||||
try:
|
||||
self.system.users.save(user, **self._get_wc_override())
|
||||
except OperationFailure, e:
|
||||
# First admin user add fails gle in MongoDB >= 2.1.2
|
||||
# See SERVER-4225 for more information.
|
||||
if 'login' in str(e):
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
uinfo = self.command("usersInfo", name)
|
||||
|
||||
except OperationFailure, exc:
|
||||
if exc.code is None:
|
||||
self._legacy_add_user(name, password, read_only, **kwargs)
|
||||
return
|
||||
raise
|
||||
|
||||
if uinfo["users"]:
|
||||
self._update_user(name, password, **kwargs)
|
||||
else:
|
||||
self._create_user(name, password, **kwargs)
|
||||
|
||||
def remove_user(self, name):
|
||||
"""Remove user `name` from this :class:`Database`.
|
||||
@ -672,7 +713,16 @@ class Database(common.BaseObject):
|
||||
|
||||
.. versionadded:: 1.4
|
||||
"""
|
||||
self.system.users.remove({"user": name}, **self._get_wc_override())
|
||||
|
||||
try:
|
||||
self.command("removeUser", name,
|
||||
writeConcern=self._get_wc_override())
|
||||
except OperationFailure, exc:
|
||||
if exc.code is None:
|
||||
self.system.users.remove({"user": name},
|
||||
**self._get_wc_override())
|
||||
return
|
||||
raise
|
||||
|
||||
def authenticate(self, name, password=None,
|
||||
source=None, mechanism='MONGODB-CR', **kwargs):
|
||||
|
||||
@ -201,8 +201,14 @@ class TestAuthURIOptions(unittest.TestCase):
|
||||
raise SkipTest('Authentication is not enabled on server')
|
||||
response = client.admin.command('ismaster')
|
||||
self.set_name = str(response.get('setName', ''))
|
||||
client.pymongo_test.add_user('user', 'pass')
|
||||
client.admin.add_user('admin', 'pass')
|
||||
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'])
|
||||
|
||||
if self.set_name:
|
||||
# GLE requires authentication.
|
||||
client.admin.authenticate('admin', 'pass')
|
||||
@ -214,8 +220,9 @@ class TestAuthURIOptions(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
self.client.admin.authenticate('admin', 'pass')
|
||||
self.client.pymongo_test.system.users.remove()
|
||||
self.client.admin.system.users.remove()
|
||||
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
|
||||
|
||||
@ -275,7 +282,9 @@ class TestDelegatedAuth(unittest.TestCase):
|
||||
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')
|
||||
# Give admin all priviledges.
|
||||
if version.at_least(self.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',
|
||||
@ -285,10 +294,10 @@ class TestDelegatedAuth(unittest.TestCase):
|
||||
|
||||
def tearDown(self):
|
||||
self.client.admin.authenticate('admin', 'pass')
|
||||
self.client.pymongo_test.system.users.remove()
|
||||
self.client.pymongo_test2.system.users.remove()
|
||||
self.client.pymongo_test.remove_user('user')
|
||||
self.client.pymongo_test2.remove_user('user')
|
||||
self.client.pymongo_test2.foo.remove()
|
||||
self.client.admin.system.users.remove()
|
||||
self.client.admin.remove_user('admin')
|
||||
self.client.admin.logout()
|
||||
self.client = None
|
||||
|
||||
|
||||
@ -43,6 +43,7 @@ from test import version, host, port
|
||||
from test.utils import (assertRaisesExactly,
|
||||
delay,
|
||||
is_mongos,
|
||||
remove_all_users,
|
||||
server_is_master_with_slave,
|
||||
server_started_with_auth,
|
||||
TestRequestMixin)
|
||||
@ -238,7 +239,8 @@ class TestClient(unittest.TestCase, 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)):
|
||||
if (version.at_least(c, (1, 3, 3, 1))
|
||||
and not version.at_least(c, (2, 5, 3, -1))):
|
||||
c.drop_database("pymongo_test1")
|
||||
|
||||
c.pymongo_test.add_user("mike", "password")
|
||||
@ -315,13 +317,17 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
if is_mongos(c) and not version.at_least(c, (2, 0, 0)):
|
||||
raise SkipTest("Auth with sharding requires MongoDB >= 2.0.0")
|
||||
|
||||
c.admin.system.users.remove({})
|
||||
c.pymongo_test.system.users.remove({})
|
||||
remove_all_users(c.pymongo_test)
|
||||
remove_all_users(c.admin)
|
||||
|
||||
try:
|
||||
c.admin.add_user("admin", "pass")
|
||||
c.admin.add_user("admin", "pass",
|
||||
roles=['readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase',
|
||||
'dbAdminAnyDatabase',
|
||||
'userAdmin'])
|
||||
c.admin.authenticate("admin", "pass")
|
||||
c.pymongo_test.add_user("user", "pass")
|
||||
c.pymongo_test.add_user("user", "pass", roles=['userAdmin', 'readWrite'])
|
||||
|
||||
self.assertRaises(ConfigurationError, MongoClient,
|
||||
"mongodb://foo:bar@%s:%d" % (host, port))
|
||||
@ -358,8 +364,8 @@ class TestClient(unittest.TestCase, TestRequestMixin):
|
||||
|
||||
finally:
|
||||
# Clean up.
|
||||
c.admin.system.users.remove({})
|
||||
c.pymongo_test.system.users.remove({})
|
||||
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.
|
||||
|
||||
@ -41,7 +41,8 @@ from pymongo.son_manipulator import (AutoReference,
|
||||
NamespaceInjector,
|
||||
ObjectIdShuffler)
|
||||
from test import version
|
||||
from test.utils import is_mongos, server_started_with_auth
|
||||
from test.utils import (is_mongos, server_started_with_auth,
|
||||
remove_all_users)
|
||||
from test.test_client import get_client
|
||||
|
||||
|
||||
@ -330,6 +331,8 @@ class TestDatabase(unittest.TestCase):
|
||||
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 version.at_least(self.client, (2, 5, 3, -1)):
|
||||
raise SkipTest("Old auth requires MongoDB < 2.5.3")
|
||||
db = self.client.pymongo_test
|
||||
db.system.users.remove({})
|
||||
db.remove_user("mike")
|
||||
@ -373,13 +376,36 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertTrue(db.system.users.find({"readOnly": True}).count())
|
||||
db.logout()
|
||||
|
||||
def test_new_user_cmds(self):
|
||||
if not version.at_least(self.client, (2, 5, 3, -1)):
|
||||
raise SkipTest("User manipulation commands "
|
||||
"require MongoDB >= 2.5.3")
|
||||
|
||||
db = self.client.pymongo_test
|
||||
remove_all_users(db)
|
||||
db.add_user("amalia", "password", roles=["userAdmin"])
|
||||
db.authenticate("amalia", "password")
|
||||
# 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["name"], "amalia")
|
||||
self.assertEqual(amalia_user["customData"], {"secret": "koalas"})
|
||||
|
||||
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")
|
||||
db = self.client.auth_test
|
||||
db.system.users.remove({})
|
||||
db.add_user("bernie", "password")
|
||||
remove_all_users(db)
|
||||
|
||||
db.add_user("bernie", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
db.authenticate("bernie", "password")
|
||||
|
||||
db.test.remove({})
|
||||
@ -394,8 +420,8 @@ class TestDatabase(unittest.TestCase):
|
||||
db.test.remove({}).get('n'))
|
||||
|
||||
self.assertEqual(0, db.test.count())
|
||||
self.client.drop_database("auth_test")
|
||||
|
||||
db.remove_user("bernie")
|
||||
db.logout()
|
||||
|
||||
def test_authenticate_and_request(self):
|
||||
if (is_mongos(self.client) and not
|
||||
@ -407,9 +433,9 @@ class TestDatabase(unittest.TestCase):
|
||||
# (in or not in a request) properly when it's finished.
|
||||
self.assertFalse(self.client.auto_start_request)
|
||||
db = self.client.pymongo_test
|
||||
db.system.users.remove({})
|
||||
db.remove_user("mike")
|
||||
db.add_user("mike", "password")
|
||||
remove_all_users(db)
|
||||
db.add_user("mike", "password",
|
||||
roles=["userAdmin", "dbAdmin", "readWrite"])
|
||||
self.assertFalse(self.client.in_request())
|
||||
self.assertTrue(db.authenticate("mike", "password"))
|
||||
self.assertFalse(self.client.in_request())
|
||||
@ -421,10 +447,9 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertTrue(request_cx.in_request())
|
||||
|
||||
# just make sure there are no exceptions here
|
||||
db.remove_user("mike")
|
||||
db.logout()
|
||||
db.collection.find_one()
|
||||
request_db.logout()
|
||||
request_db.collection.find_one()
|
||||
|
||||
def test_authenticate_multiple(self):
|
||||
client = get_client()
|
||||
@ -438,16 +463,25 @@ class TestDatabase(unittest.TestCase):
|
||||
users_db = client.pymongo_test
|
||||
admin_db = client.admin
|
||||
other_db = client.pymongo_test1
|
||||
users_db.system.users.remove()
|
||||
admin_db.system.users.remove()
|
||||
remove_all_users(users_db)
|
||||
remove_all_users(admin_db)
|
||||
remove_all_users(other_db)
|
||||
users_db.test.remove()
|
||||
other_db.test.remove()
|
||||
|
||||
admin_db.add_user('admin', 'pass')
|
||||
admin_db.add_user('admin', 'pass',
|
||||
roles=["userAdminAnyDatabase", "dbAdmin",
|
||||
"clusterAdmin", "readWrite"])
|
||||
self.assertTrue(admin_db.authenticate('admin', 'pass'))
|
||||
|
||||
admin_db.add_user('ro-admin', 'pass', read_only=True)
|
||||
users_db.add_user('user', '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)
|
||||
@ -480,9 +514,9 @@ class TestDatabase(unittest.TestCase):
|
||||
admin_db.logout()
|
||||
users_db.logout()
|
||||
self.assertTrue(admin_db.authenticate('admin', 'pass'))
|
||||
self.assertTrue(admin_db.system.users.remove())
|
||||
self.assertEqual(0, admin_db.system.users.count())
|
||||
self.assertTrue(users_db.system.users.remove())
|
||||
users_db.remove_user('user')
|
||||
admin_db.remove_user('ro-admin')
|
||||
admin_db.remove_user('admin')
|
||||
|
||||
def test_id_ordering(self):
|
||||
# PyMongo attempts to have _id show up first
|
||||
@ -761,6 +795,5 @@ class TestDatabase(unittest.TestCase):
|
||||
self.assertEqual([], db.outgoing_manipulators)
|
||||
self.assertEqual(['AutoReference'], db.outgoing_copying_manipulators)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@ -20,7 +20,8 @@ import traceback
|
||||
|
||||
from nose.plugins.skip import SkipTest
|
||||
|
||||
from test.utils import server_started_with_auth, joinall, RendezvousThread
|
||||
from test.utils import (joinall, remove_all_users,
|
||||
server_started_with_auth, RendezvousThread)
|
||||
from test.test_client import get_client
|
||||
from pymongo.mongo_client import MongoClient
|
||||
from pymongo.replica_set_connection import MongoReplicaSetClient
|
||||
@ -330,18 +331,23 @@ class BaseTestThreadsAuth(object):
|
||||
if not server_started_with_auth(client):
|
||||
raise SkipTest("Authentication is not enabled on server")
|
||||
self.client = client
|
||||
self.client.admin.system.users.remove({})
|
||||
self.client.admin.add_user('admin-user', 'password')
|
||||
remove_all_users(self.client.admin)
|
||||
self.client.admin.add_user('admin-user', 'password',
|
||||
roles=['clusterAdmin',
|
||||
'dbAdminAnyDatabase',
|
||||
'readWriteAnyDatabase',
|
||||
'userAdminAnyDatabase'])
|
||||
self.client.admin.authenticate("admin-user", "password")
|
||||
self.client.auth_test.system.users.remove({})
|
||||
self.client.auth_test.add_user("test-user", "password")
|
||||
remove_all_users(self.client.auth_test)
|
||||
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.admin.system.users.remove({})
|
||||
self.client.auth_test.system.users.remove({})
|
||||
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
|
||||
|
||||
@ -19,7 +19,7 @@ import threading
|
||||
from pymongo import MongoClient, MongoReplicaSetClient
|
||||
from pymongo.errors import AutoReconnect
|
||||
from pymongo.pool import NO_REQUEST, NO_SOCKET_YET, SocketInfo
|
||||
from test import host, port
|
||||
from test import host, port, version
|
||||
|
||||
|
||||
def one(s):
|
||||
@ -50,6 +50,13 @@ def drop_collections(db):
|
||||
if not coll.startswith('system'):
|
||||
db.drop_collection(coll)
|
||||
|
||||
def remove_all_users(db):
|
||||
if version.at_least(db.connection, (2, 5, 3, -1)):
|
||||
db.command({"removeUsersFromDatabase": 1})
|
||||
else:
|
||||
db.system.users.remove({})
|
||||
|
||||
|
||||
def joinall(threads):
|
||||
"""Join threads with a 5-minute timeout, assert joins succeeded"""
|
||||
for t in threads:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user