PYTHON-552: Manipulate user objects exclusively via commands for 2.6

This commit is contained in:
hawka 2013-09-03 16:45:31 -04:00 committed by behackett
parent 6bd590abba
commit a02e37ba5e
6 changed files with 169 additions and 58 deletions

View File

@ -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):

View File

@ -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

View File

@ -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.

View File

@ -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()

View File

@ -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

View File

@ -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: