Browse files

Allow auth with RSC during failover PYTHON-388

  • Loading branch information...
1 parent d79c438 commit 03593d512cced629a56a0d20f35eca16aab482d2 @behackett behackett committed Aug 13, 2012
Showing with 74 additions and 6 deletions.
  1. +8 −2 pymongo/database.py
  2. +2 −2 pymongo/read_preferences.py
  3. +16 −1 test/high_availability/ha_tools.py
  4. +48 −1 test/high_availability/test_ha.py
View
10 pymongo/database.py
@@ -675,16 +675,22 @@ def authenticate(self, name, password):
raise TypeError("password must be an instance "
"of %s" % (basestring.__name__,))
+ # So we can authenticate during a failover. The start_request()
+ # call below will pin the host used for getnonce so we use the
+ # same host for authenticate.
+ read_pref = rp.ReadPreference.PRIMARY_PREFERRED
+
in_request = self.connection.in_request()
try:
if not in_request:
self.connection.start_request()
- nonce = self.command("getnonce")["nonce"]
+ nonce = self.command("getnonce",
+ read_preference=read_pref)["nonce"]
key = helpers._auth_key(nonce, name, password)
try:
self.command("authenticate", user=unicode(name),
- nonce=nonce, key=key)
+ nonce=nonce, key=key, read_preference=read_pref)
self.connection._cache_credentials(self.name,
unicode(name),
unicode(password))
View
4 pymongo/read_preferences.py
@@ -172,9 +172,9 @@ def select_member(
"""Commands that may be sent to replica-set secondaries, depending on
ReadPreference and tags. All other commands are always run on the primary.
"""
-secondary_ok_commands = set([
+secondary_ok_commands = frozenset([
"group", "aggregate", "collstats", "dbstats", "count", "distinct",
- "geonear", "geosearch", "geowalk", "mapreduce",
+ "geonear", "geosearch", "geowalk", "mapreduce", "getnonce", "authenticate",
])
View
17 test/high_availability/ha_tools.py
@@ -23,6 +23,8 @@
import sys
import time
+from stat import S_IRUSR
+
import pymongo
home = os.environ.get('HOME')
@@ -78,7 +80,7 @@ def wait_for(proc, port_num):
return False
-def start_replica_set(members, fresh=True):
+def start_replica_set(members, auth=False, fresh=True):
global cur_port
if fresh:
@@ -87,6 +89,17 @@ def start_replica_set(members, fresh=True):
shutil.rmtree(dbpath)
except OSError:
pass
+ os.makedirs(dbpath)
+
+ if auth:
+ key_file = os.path.join(dbpath, 'key.txt')
+ if not os.path.exists(key_file):
+ f = open(key_file, 'w')
+ try:
+ f.write("my super secret system password")
+ finally:
+ f.close()
+ os.chmod(key_file, S_IRUSR)
cur_port = port
@@ -106,6 +119,8 @@ def start_replica_set(members, fresh=True):
'--replSet', set_name,
'--nojournal', '--oplogSize', '64',
'--logappend', '--logpath', member_logpath]
+ if auth:
+ cmd += ['--keyFile', key_file]
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
View
49 test/high_availability/test_ha.py
@@ -25,7 +25,7 @@
ReadPreference)
from pymongo.replica_set_connection import Member, Monitor
from pymongo.connection import Connection, _partition_node
-from pymongo.errors import AutoReconnect
+from pymongo.errors import AutoReconnect, OperationFailure
from test import utils
@@ -609,6 +609,53 @@ def tearDown(self):
self.clear_ping_times()
+class TestReplicaSetAuth(unittest.TestCase):
+ def setUp(self):
+ members = [
+ {},
+ {'priority': 0},
+ {'priority': 0},
+ ]
+
+ res = ha_tools.start_replica_set(members, auth=True)
+ self.c = ReplicaSetConnection(res[0], replicaSet=res[1],
+ use_greenlets=use_greenlets)
+
+ # Add an admin user to enable auth
+ try:
+ self.c.admin.add_user('admin', 'adminpass')
+ except:
+ # SERVER-4225
+ pass
+ self.c.admin.authenticate('admin', 'adminpass')
+
+ self.db = self.c.pymongo_ha_auth
+ self.db.add_user('user', 'userpass')
+ self.c.admin.logout()
+
+ def test_auth_during_failover(self):
+ self.assertTrue(self.db.authenticate('user', 'userpass'))
+ self.assertTrue(self.db.foo.insert({'foo': 'bar'},
+ safe=True, w=3, wtimeout=1000))
+ self.db.logout()
+ self.assertRaises(OperationFailure, self.db.foo.find_one)
+
+ primary = '%s:%d' % self.c.primary
+ ha_tools.kill_members([primary], 2)
+
+ # Let monitor notice primary's gone
+ sleep(2 * MONITOR_INTERVAL)
+
+ # Make sure we can still authenticate
+ self.assertTrue(self.db.authenticate('user', 'userpass'))
+ # And still query.
+ self.db.read_preference = ReadPreference.PRIMARY_PREFERRED
+ self.assertEqual('bar', self.db.foo.find_one()['foo'])
+
+ def tearDown(self):
+ self.c.close()
+ ha_tools.kill_all_members()
+
class TestMongosHighAvailability(unittest.TestCase):
def setUp(self):
seed_list = ha_tools.create_sharded_cluster()

0 comments on commit 03593d5

Please sign in to comment.