Skip to content

Commit 40bc55c

Browse files
committed
Merge branch 'master' into fork
2 parents 5ed5227 + af7bf38 commit 40bc55c

File tree

9 files changed

+88
-27
lines changed

9 files changed

+88
-27
lines changed

bson/py3compat.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
if PY3:
2222
import codecs
23+
24+
from io import BytesIO as StringIO
25+
2326
def b(s):
2427
# BSON and socket operations deal in binary data. In
2528
# python 3 that means instances of `bytes`. In python
@@ -37,6 +40,11 @@ def bytes_from_hex(h):
3740
text_type = str
3841

3942
else:
43+
try:
44+
from cStringIO import StringIO
45+
except ImportError:
46+
from StringIO import StringIO
47+
4048
def b(s):
4149
# See comments above. In python 2.x b('foo') is just 'foo'.
4250
return s

bson/timestamp.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from bson.tz_util import utc
2222

23+
UPPERBOUND = 4294967296
2324

2425
class Timestamp(object):
2526
"""MongoDB internal timestamps used in the opLog.
@@ -54,9 +55,9 @@ def __init__(self, time, inc):
5455
raise TypeError("time must be an instance of int")
5556
if not isinstance(inc, (int, long)):
5657
raise TypeError("inc must be an instance of int")
57-
if not 0 <= time < 2 ** 32:
58+
if not 0 <= time < UPPERBOUND:
5859
raise ValueError("time must be contained in [0, 2**32)")
59-
if not 0 <= inc < 2 ** 32:
60+
if not 0 <= inc < UPPERBOUND:
6061
raise ValueError("inc must be contained in [0, 2**32)")
6162

6263
self.__time = time

gridfs/grid_file.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,10 @@
1717
import datetime
1818
import math
1919
import os
20-
import sys
21-
22-
if sys.version_info[0] == 3:
23-
from io import BytesIO as StringIO
24-
else:
25-
# 2to3 will turn cStringIO into io. That's okay
26-
# since we'll never get here under python3.
27-
from cStringIO import StringIO
2820

2921
from bson.binary import Binary
3022
from bson.objectid import ObjectId
31-
from bson.py3compat import b, binary_type, string_types, text_type
23+
from bson.py3compat import b, binary_type, string_types, text_type, StringIO
3224
from gridfs.errors import (CorruptGridFile,
3325
FileExists,
3426
NoFile,

pymongo/database.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -677,16 +677,22 @@ def authenticate(self, name, password):
677677
raise TypeError("password must be an instance "
678678
"of %s" % (basestring.__name__,))
679679

680+
# So we can authenticate during a failover. The start_request()
681+
# call below will pin the host used for getnonce so we use the
682+
# same host for authenticate.
683+
read_pref = rp.ReadPreference.PRIMARY_PREFERRED
684+
680685
in_request = self.connection.in_request()
681686
try:
682687
if not in_request:
683688
self.connection.start_request()
684689

685-
nonce = self.command("getnonce")["nonce"]
690+
nonce = self.command("getnonce",
691+
read_preference=read_pref)["nonce"]
686692
key = helpers._auth_key(nonce, name, password)
687693
try:
688694
self.command("authenticate", user=unicode(name),
689-
nonce=nonce, key=key)
695+
nonce=nonce, key=key, read_preference=read_pref)
690696
self.connection._cache_credentials(self.name,
691697
unicode(name),
692698
unicode(password))

pymongo/read_preferences.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,9 @@ def select_member(
172172
"""Commands that may be sent to replica-set secondaries, depending on
173173
ReadPreference and tags. All other commands are always run on the primary.
174174
"""
175-
secondary_ok_commands = set([
175+
secondary_ok_commands = frozenset([
176176
"group", "aggregate", "collstats", "dbstats", "count", "distinct",
177-
"geonear", "geosearch", "geowalk", "mapreduce",
177+
"geonear", "geosearch", "geowalk", "mapreduce", "getnonce", "authenticate",
178178
])
179179

180180

test/high_availability/ha_tools.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import sys
2424
import time
2525

26+
from stat import S_IRUSR
27+
2628
import pymongo
2729

2830
home = os.environ.get('HOME')
@@ -78,7 +80,7 @@ def wait_for(proc, port_num):
7880
return False
7981

8082

81-
def start_replica_set(members, fresh=True):
83+
def start_replica_set(members, auth=False, fresh=True):
8284
global cur_port
8385

8486
if fresh:
@@ -87,6 +89,17 @@ def start_replica_set(members, fresh=True):
8789
shutil.rmtree(dbpath)
8890
except OSError:
8991
pass
92+
os.makedirs(dbpath)
93+
94+
if auth:
95+
key_file = os.path.join(dbpath, 'key.txt')
96+
if not os.path.exists(key_file):
97+
f = open(key_file, 'w')
98+
try:
99+
f.write("my super secret system password")
100+
finally:
101+
f.close()
102+
os.chmod(key_file, S_IRUSR)
90103

91104
cur_port = port
92105

@@ -106,6 +119,8 @@ def start_replica_set(members, fresh=True):
106119
'--replSet', set_name,
107120
'--nojournal', '--oplogSize', '64',
108121
'--logappend', '--logpath', member_logpath]
122+
if auth:
123+
cmd += ['--keyFile', key_file]
109124
proc = subprocess.Popen(cmd,
110125
stdout=subprocess.PIPE,
111126
stderr=subprocess.STDOUT)

test/high_availability/test_ha.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
ReadPreference)
2626
from pymongo.replica_set_connection import Member, Monitor
2727
from pymongo.connection import Connection, _partition_node
28-
from pymongo.errors import AutoReconnect
28+
from pymongo.errors import AutoReconnect, OperationFailure
2929

3030
from test import utils
3131

@@ -609,6 +609,53 @@ def tearDown(self):
609609
self.clear_ping_times()
610610

611611

612+
class TestReplicaSetAuth(unittest.TestCase):
613+
def setUp(self):
614+
members = [
615+
{},
616+
{'priority': 0},
617+
{'priority': 0},
618+
]
619+
620+
res = ha_tools.start_replica_set(members, auth=True)
621+
self.c = ReplicaSetConnection(res[0], replicaSet=res[1],
622+
use_greenlets=use_greenlets)
623+
624+
# Add an admin user to enable auth
625+
try:
626+
self.c.admin.add_user('admin', 'adminpass')
627+
except:
628+
# SERVER-4225
629+
pass
630+
self.c.admin.authenticate('admin', 'adminpass')
631+
632+
self.db = self.c.pymongo_ha_auth
633+
self.db.add_user('user', 'userpass')
634+
self.c.admin.logout()
635+
636+
def test_auth_during_failover(self):
637+
self.assertTrue(self.db.authenticate('user', 'userpass'))
638+
self.assertTrue(self.db.foo.insert({'foo': 'bar'},
639+
safe=True, w=3, wtimeout=1000))
640+
self.db.logout()
641+
self.assertRaises(OperationFailure, self.db.foo.find_one)
642+
643+
primary = '%s:%d' % self.c.primary
644+
ha_tools.kill_members([primary], 2)
645+
646+
# Let monitor notice primary's gone
647+
sleep(2 * MONITOR_INTERVAL)
648+
649+
# Make sure we can still authenticate
650+
self.assertTrue(self.db.authenticate('user', 'userpass'))
651+
# And still query.
652+
self.db.read_preference = ReadPreference.PRIMARY_PREFERRED
653+
self.assertEqual('bar', self.db.foo.find_one()['foo'])
654+
655+
def tearDown(self):
656+
self.c.close()
657+
ha_tools.kill_all_members()
658+
612659
class TestMongosHighAvailability(unittest.TestCase):
613660
def setUp(self):
614661
seed_list = ha_tools.create_sharded_cluster()

test/test_grid_file.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
"""Tests for the grid_file module.
1818
"""
1919

20-
try:
21-
from io import BytesIO as StringIO
22-
except ImportError:
23-
from cStringIO import StringIO
2420
import datetime
2521
import os
2622
import sys
@@ -30,7 +26,7 @@
3026
from nose.plugins.skip import SkipTest
3127

3228
from bson.objectid import ObjectId
33-
from bson.py3compat import b
29+
from bson.py3compat import b, StringIO
3430
from gridfs.grid_file import (DEFAULT_CHUNK_SIZE,
3531
_SEEK_CUR,
3632
_SEEK_END,

test/test_gridfs.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@
2222
from pymongo.read_preferences import ReadPreference
2323
from test.test_replica_set_connection import TestConnectionReplicaSetBase
2424

25-
try:
26-
from io import BytesIO as StringIO
27-
except ImportError:
28-
from cStringIO import StringIO
2925
import datetime
3026
import unittest
3127
import threading
@@ -35,7 +31,7 @@
3531

3632
import gridfs
3733

38-
from bson.py3compat import b
34+
from bson.py3compat import b, StringIO
3935
from gridfs.errors import (FileExists,
4036
NoFile)
4137
from test.test_connection import get_connection

0 commit comments

Comments
 (0)