mongo 1.11.1 → 1.12.0.rc0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/VERSION +1 -1
- data/lib/mongo/collection.rb +8 -3
- data/lib/mongo/collection_writer.rb +1 -1
- data/lib/mongo/connection/socket/unix_socket.rb +1 -1
- data/lib/mongo/cursor.rb +8 -1
- data/lib/mongo/db.rb +61 -33
- data/lib/mongo/exception.rb +57 -0
- data/lib/mongo/functional.rb +1 -0
- data/lib/mongo/functional/authentication.rb +138 -11
- data/lib/mongo/functional/read_preference.rb +31 -22
- data/lib/mongo/functional/scram.rb +555 -0
- data/lib/mongo/functional/uri_parser.rb +107 -79
- data/lib/mongo/mongo_client.rb +19 -24
- data/lib/mongo/mongo_replica_set_client.rb +2 -1
- data/test/functional/authentication_test.rb +3 -0
- data/test/functional/client_test.rb +33 -0
- data/test/functional/collection_test.rb +29 -19
- data/test/functional/db_api_test.rb +16 -1
- data/test/functional/pool_test.rb +7 -6
- data/test/functional/uri_test.rb +111 -7
- data/test/helpers/test_unit.rb +17 -3
- data/test/replica_set/client_test.rb +31 -0
- data/test/replica_set/insert_test.rb +49 -32
- data/test/replica_set/pinning_test.rb +50 -0
- data/test/replica_set/query_test.rb +1 -1
- data/test/replica_set/replication_ack_test.rb +3 -3
- data/test/shared/authentication/basic_auth_shared.rb +14 -1
- data/test/shared/authentication/gssapi_shared.rb +13 -8
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/tools/mongo_config.rb +18 -6
- data/test/unit/client_test.rb +40 -6
- data/test/unit/connection_test.rb +15 -5
- data/test/unit/db_test.rb +1 -1
- data/test/unit/read_pref_test.rb +291 -0
- metadata +9 -6
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c376366d59154151291d12a0a859a6db8850416
|
4
|
+
data.tar.gz: 7d92d814592073a3f2548fea9b2960e241c98502
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc0c717e7741c0871d80af3b6f65be1bd8908a2e0364c6bf17106056f5d71ea4677f410a4cacf0a6ec85181486a2e3eb5392a1b09472c0d728ac9c3dcaecd027
|
7
|
+
data.tar.gz: fd7c88744f849a3cdec1a1e514a2028d4391615028dceb3c48edb0fbe5b6f87a1bceae1745cbc96ad2c0f96f0380897d6f1a9668d4a620adb9d52b0b32c67fe5
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.12.0.rc0
|
data/lib/mongo/collection.rb
CHANGED
@@ -717,11 +717,13 @@ module Mongo
|
|
717
717
|
|
718
718
|
if result.key?('cursor')
|
719
719
|
cursor_info = result['cursor']
|
720
|
+
pinned_pool = @connection.pinned_pool
|
721
|
+
pinned_pool = pinned_pool[:pool] if pinned_pool.respond_to?(:keys)
|
720
722
|
|
721
723
|
seed = {
|
722
724
|
:cursor_id => cursor_info['id'],
|
723
725
|
:first_batch => cursor_info['firstBatch'],
|
724
|
-
:pool =>
|
726
|
+
:pool => pinned_pool
|
725
727
|
}
|
726
728
|
|
727
729
|
return Cursor.new(self, seed.merge!(opts))
|
@@ -887,10 +889,13 @@ module Mongo
|
|
887
889
|
result = @db.command(cmd, command_options(opts))
|
888
890
|
|
889
891
|
result['cursors'].collect do |cursor_info|
|
892
|
+
pinned_pool = @connection.pinned_pool
|
893
|
+
pinned_pool = pinned_pool[:pool] if pinned_pool.respond_to?(:keys)
|
894
|
+
|
890
895
|
seed = {
|
891
896
|
:cursor_id => cursor_info['cursor']['id'],
|
892
897
|
:first_batch => cursor_info['cursor']['firstBatch'],
|
893
|
-
:pool =>
|
898
|
+
:pool => pinned_pool
|
894
899
|
}
|
895
900
|
Cursor.new(self, seed.merge!(opts))
|
896
901
|
end
|
@@ -1024,7 +1029,7 @@ module Mongo
|
|
1024
1029
|
#
|
1025
1030
|
# @return [Hash] options that apply to this collection.
|
1026
1031
|
def options
|
1027
|
-
@db.collections_info(@name).
|
1032
|
+
@db.collections_info(@name).first['options']
|
1028
1033
|
end
|
1029
1034
|
|
1030
1035
|
# Return stats on the collection. Uses MongoDB's collstats command.
|
@@ -55,7 +55,7 @@ module Mongo
|
|
55
55
|
@max_write_batch_size = @collection.db.connection.max_write_batch_size
|
56
56
|
docs = documents.dup
|
57
57
|
catch(:error) do
|
58
|
-
until docs.empty? || (!errors.empty? && !collect_on_error) # process documents a batch at a time
|
58
|
+
until docs.empty? || (!errors.empty? && !collect_on_error && !continue_on_error) # process documents a batch at a time
|
59
59
|
batch_docs = []
|
60
60
|
batch_message_initialize(message, op_type, continue_on_error, write_concern)
|
61
61
|
while !docs.empty? && batch_docs.size < @max_write_batch_size
|
data/lib/mongo/cursor.rb
CHANGED
@@ -564,9 +564,11 @@ module Mongo
|
|
564
564
|
ensure
|
565
565
|
socket.checkin unless @socket || socket.nil?
|
566
566
|
end
|
567
|
-
|
567
|
+
|
568
|
+
if pin_pool?(results.first)
|
568
569
|
@connection.pin_pool(socket.pool, read_preference)
|
569
570
|
end
|
571
|
+
|
570
572
|
@returned += @n_received
|
571
573
|
@cache += results
|
572
574
|
@query_run = true
|
@@ -728,5 +730,10 @@ module Mongo
|
|
728
730
|
end
|
729
731
|
indexes.join("_")
|
730
732
|
end
|
733
|
+
|
734
|
+
def pin_pool?(response)
|
735
|
+
( response && (response['cursor'] || response['cursors']) ) ||
|
736
|
+
( !@socket && !@command )
|
737
|
+
end
|
731
738
|
end
|
732
739
|
end
|
data/lib/mongo/db.rb
CHANGED
@@ -217,27 +217,25 @@ module Mongo
|
|
217
217
|
#
|
218
218
|
# @return [Hash] an object representing the user.
|
219
219
|
def add_user(username, password=nil, read_only=false, opts={})
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
raise ex
|
240
|
-
end
|
220
|
+
user_info = command(:usersInfo => username)
|
221
|
+
if user_info.key?('users') && !user_info['users'].empty?
|
222
|
+
create_or_update_user(:updateUser, username, password, read_only, opts)
|
223
|
+
else
|
224
|
+
create_or_update_user(:createUser, username, password, read_only, opts)
|
225
|
+
end
|
226
|
+
# MongoDB >= 2.5.3 requires the use of commands to manage users.
|
227
|
+
# "Command not found" error didn't return an error code (59) before
|
228
|
+
# MongoDB 2.4.7 so we assume that a nil error code means the usersInfo
|
229
|
+
# command doesn't exist and we should fall back to the legacy add user code.
|
230
|
+
rescue OperationFailure => ex
|
231
|
+
if Mongo::ErrorCode::COMMAND_NOT_FOUND_CODES.include?(ex.error_code)
|
232
|
+
legacy_add_user(username, password, read_only, opts)
|
233
|
+
elsif ex.error_code == Mongo::ErrorCode::UNAUTHORIZED
|
234
|
+
# In MongoDB > 2.7 the localhost exception was narrowed, and the usersInfo
|
235
|
+
# command is no longer allowed. In this case, add the first user.
|
236
|
+
create_or_update_user(:createUser, username, password, read_only, opts)
|
237
|
+
else
|
238
|
+
raise ex
|
241
239
|
end
|
242
240
|
end
|
243
241
|
|
@@ -261,9 +259,14 @@ module Mongo
|
|
261
259
|
#
|
262
260
|
# @return [Array]
|
263
261
|
def collection_names
|
264
|
-
|
265
|
-
|
266
|
-
|
262
|
+
if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_2_8)
|
263
|
+
names = collections_info.collect { |doc| doc['name'] || '' }
|
264
|
+
names.delete_if do |name|
|
265
|
+
name.index('$')
|
266
|
+
end
|
267
|
+
else
|
268
|
+
legacy_collection_names
|
269
|
+
end
|
267
270
|
end
|
268
271
|
|
269
272
|
# Get an array of Collection instances, one for each collection in this database.
|
@@ -281,11 +284,15 @@ module Mongo
|
|
281
284
|
#
|
282
285
|
# @param [String] coll_name return info for the specified collection only.
|
283
286
|
#
|
284
|
-
# @return [
|
287
|
+
# @return [Array] List of collection info.
|
285
288
|
def collections_info(coll_name=nil)
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
+
if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_2_8)
|
290
|
+
cmd = BSON::OrderedHash[:listCollections, 1]
|
291
|
+
cmd.merge!(:filter => { :name => coll_name }) if coll_name
|
292
|
+
self.command(cmd)['collections']
|
293
|
+
else
|
294
|
+
legacy_collections_info(coll_name).to_a
|
295
|
+
end
|
289
296
|
end
|
290
297
|
|
291
298
|
# Create a collection.
|
@@ -482,12 +489,14 @@ module Mongo
|
|
482
489
|
# @return [Hash] keys are index names and the values are lists of [key, type] pairs
|
483
490
|
# defining the index.
|
484
491
|
def index_information(collection_name)
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
492
|
+
if @client.wire_version_feature?(Mongo::MongoClient::MONGODB_2_8)
|
493
|
+
result = self.command(:listIndexes => collection_name)['indexes']
|
494
|
+
else
|
495
|
+
result = legacy_list_indexes(collection_name)
|
496
|
+
end
|
497
|
+
result.reduce({}) do |info, index|
|
498
|
+
info.merge!(index['name'] => index)
|
489
499
|
end
|
490
|
-
info
|
491
500
|
end
|
492
501
|
|
493
502
|
# Return stats on this database. Uses MongoDB's dbstats command.
|
@@ -736,5 +745,24 @@ module Mongo
|
|
736
745
|
end
|
737
746
|
user
|
738
747
|
end
|
748
|
+
|
749
|
+
def legacy_list_indexes(collection_name)
|
750
|
+
sel = {:ns => full_collection_name(collection_name)}
|
751
|
+
Cursor.new(Collection.new(SYSTEM_INDEX_COLLECTION, self), :selector => sel)
|
752
|
+
end
|
753
|
+
|
754
|
+
def legacy_collections_info(coll_name=nil)
|
755
|
+
selector = {}
|
756
|
+
selector[:name] = full_collection_name(coll_name) if coll_name
|
757
|
+
Cursor.new(Collection.new(SYSTEM_NAMESPACE_COLLECTION, self), :selector => selector)
|
758
|
+
end
|
759
|
+
|
760
|
+
def legacy_collection_names
|
761
|
+
names = legacy_collections_info.collect { |doc| doc['name'] || '' }
|
762
|
+
names = names.delete_if do |name|
|
763
|
+
name.index(@name).nil? || name.index('$')
|
764
|
+
end
|
765
|
+
names.map {|name| name.sub(@name + '.', '')}
|
766
|
+
end
|
739
767
|
end
|
740
768
|
end
|
data/lib/mongo/exception.rb
CHANGED
@@ -85,4 +85,61 @@ module Mongo
|
|
85
85
|
|
86
86
|
# Raised for bulk write errors.
|
87
87
|
class BulkWriteError < OperationFailure; end
|
88
|
+
|
89
|
+
# This exception is raised when the server nonce returned does not
|
90
|
+
# match the client nonce sent to it.
|
91
|
+
#
|
92
|
+
# @since 1.12.0
|
93
|
+
class InvalidNonce < OperationFailure
|
94
|
+
|
95
|
+
# @return [ String ] nonce The client nonce.
|
96
|
+
attr_reader :nonce
|
97
|
+
|
98
|
+
# @return [ String ] rnonce The server nonce.
|
99
|
+
attr_reader :rnonce
|
100
|
+
|
101
|
+
# Instantiate the new exception.
|
102
|
+
#
|
103
|
+
# @example Create the exception.
|
104
|
+
# InvalidNonce.new(nonce, rnonce)
|
105
|
+
#
|
106
|
+
# @param [ String ] nonce The client nonce.
|
107
|
+
# @param [ String ] rnonce The server nonce.
|
108
|
+
#
|
109
|
+
# @since 1.12.0
|
110
|
+
def initialize(nonce, rnonce)
|
111
|
+
@nonce = nonce
|
112
|
+
@rnonce = rnonce
|
113
|
+
super("Expected server rnonce '#{rnonce}' to start with client nonce '#{nonce}'.")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# This exception is raised when the server verifier does not match the
|
118
|
+
# expected signature on the client.
|
119
|
+
#
|
120
|
+
# @since 1.12.0
|
121
|
+
class InvalidSignature < OperationFailure
|
122
|
+
|
123
|
+
# @return [ String ] verifier The server verifier string.
|
124
|
+
attr_reader :verifier
|
125
|
+
|
126
|
+
# @return [ String ] server_signature The expected server signature.
|
127
|
+
attr_reader :server_signature
|
128
|
+
|
129
|
+
# Create the new exception.
|
130
|
+
#
|
131
|
+
# @example Create the new exception.
|
132
|
+
# InvalidSignature.new(verifier, server_signature)
|
133
|
+
#
|
134
|
+
# @param [ String ] verifier The verifier returned from the server.
|
135
|
+
# @param [ String ] server_signature The expected value from the
|
136
|
+
# server.
|
137
|
+
#
|
138
|
+
# @since 1.12.0
|
139
|
+
def initialize(verifier, server_signature)
|
140
|
+
@verifier = verifier
|
141
|
+
@server_signature = server_signature
|
142
|
+
super("Expected server verifier '#{verifier}' to match '#{server_signature}'.")
|
143
|
+
end
|
144
|
+
end
|
88
145
|
end
|
data/lib/mongo/functional.rb
CHANGED
@@ -18,8 +18,11 @@ module Mongo
|
|
18
18
|
module Authentication
|
19
19
|
|
20
20
|
DEFAULT_MECHANISM = 'MONGODB-CR'
|
21
|
-
MECHANISMS = ['GSSAPI', 'MONGODB-CR', 'MONGODB-X509', 'PLAIN']
|
22
|
-
|
21
|
+
MECHANISMS = ['GSSAPI', 'MONGODB-CR', 'MONGODB-X509', 'PLAIN', 'SCRAM-SHA-1']
|
22
|
+
MECHANISM_ERROR = "Must use one of #{MECHANISMS.join(', ')} " +
|
23
|
+
"authentication mechanisms."
|
24
|
+
EXTRA = { 'GSSAPI' => [:service_name, :canonicalize_host_name,
|
25
|
+
:service_realm] }
|
23
26
|
|
24
27
|
# authentication module methods
|
25
28
|
class << self
|
@@ -49,15 +52,13 @@ module Mongo
|
|
49
52
|
# @raise [MongoArgumentError] if the credential set is invalid.
|
50
53
|
# @return [Hash] The validated credential set.
|
51
54
|
def validate_credentials(auth)
|
52
|
-
# set the default auth mechanism if not defined
|
53
|
-
auth[:mechanism] ||= DEFAULT_MECHANISM
|
54
|
-
|
55
55
|
# set the default auth source if not defined
|
56
56
|
auth[:source] = auth[:source] || auth[:db_name] || 'admin'
|
57
57
|
|
58
|
-
if (auth[:mechanism]
|
58
|
+
if password_required?(auth[:mechanism]) && !auth[:password]
|
59
59
|
raise MongoArgumentError,
|
60
|
-
"When using the authentication mechanism
|
60
|
+
"When using the authentication mechanism " +
|
61
|
+
"#{auth[:mechanism].nil? ? 'MONGODB-CR or SCRAM-SHA-1' : auth[:mechanism]} " +
|
61
62
|
"both username and password are required."
|
62
63
|
end
|
63
64
|
# if extra opts exist, validate them
|
@@ -92,6 +93,19 @@ module Mongo
|
|
92
93
|
def hash_password(username, password)
|
93
94
|
Digest::MD5.hexdigest("#{username}:mongo:#{password}")
|
94
95
|
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
# Does the authentication require a password?
|
100
|
+
#
|
101
|
+
# @param [ String ] mech The authentication mechanism.
|
102
|
+
#
|
103
|
+
# @return [ true, false ] If a password is required.
|
104
|
+
#
|
105
|
+
# @since 1.12.0
|
106
|
+
def password_required?(mech)
|
107
|
+
mech == 'MONGODB-CR' || mech == 'PLAIN' || mech == 'SCRAM-SHA-1' || mech.nil?
|
108
|
+
end
|
95
109
|
end
|
96
110
|
|
97
111
|
# Saves a cache of authentication credentials to the current
|
@@ -104,7 +118,7 @@ module Mongo
|
|
104
118
|
# @param source [String] (nil) The authentication source database
|
105
119
|
# (if different than the current database).
|
106
120
|
# @param mechanism [String] (nil) The authentication mechanism being used
|
107
|
-
# (default: 'MONGODB-CR').
|
121
|
+
# (default: 'MONGODB-CR' or 'SCRAM-SHA-1' if server version >= 2.7.8).
|
108
122
|
# @param extra [Hash] (nil) A optional hash of extra options to be stored with
|
109
123
|
# the credential set.
|
110
124
|
#
|
@@ -131,8 +145,8 @@ module Mongo
|
|
131
145
|
end
|
132
146
|
|
133
147
|
begin
|
134
|
-
socket =
|
135
|
-
|
148
|
+
socket = checkout_reader(:mode => :primary_preferred)
|
149
|
+
issue_authentication(auth, :socket => socket)
|
136
150
|
ensure
|
137
151
|
socket.checkin if socket
|
138
152
|
end
|
@@ -148,7 +162,10 @@ module Mongo
|
|
148
162
|
# @return [Boolean] The result of the operation.
|
149
163
|
def remove_auth(db_name)
|
150
164
|
return false unless @auths
|
151
|
-
@auths.
|
165
|
+
auths = @auths.to_a
|
166
|
+
removed = auths.reject! { |a| a[:source] == db_name }
|
167
|
+
@auths = Set.new(auths)
|
168
|
+
!!removed
|
152
169
|
end
|
153
170
|
|
154
171
|
# Remove all authentication information stored in this connection.
|
@@ -190,6 +207,11 @@ module Mongo
|
|
190
207
|
# @raise [AuthenticationError] Raised if the authentication fails.
|
191
208
|
# @return [Boolean] Result of the authentication operation.
|
192
209
|
def issue_authentication(auth, opts={})
|
210
|
+
# set the default auth mechanism if not defined
|
211
|
+
auth[:mechanism] ||= default_mechanism
|
212
|
+
|
213
|
+
raise MongoArgumentError,
|
214
|
+
MECHANISM_ERROR unless MECHANISMS.include?(auth[:mechanism])
|
193
215
|
result = case auth[:mechanism]
|
194
216
|
when 'MONGODB-CR'
|
195
217
|
issue_cr(auth, opts)
|
@@ -199,6 +221,8 @@ module Mongo
|
|
199
221
|
issue_plain(auth, opts)
|
200
222
|
when 'GSSAPI'
|
201
223
|
issue_gssapi(auth, opts)
|
224
|
+
when 'SCRAM-SHA-1'
|
225
|
+
issue_scram(auth, opts)
|
202
226
|
end
|
203
227
|
|
204
228
|
unless Support.ok?(result)
|
@@ -212,6 +236,86 @@ module Mongo
|
|
212
236
|
|
213
237
|
private
|
214
238
|
|
239
|
+
def default_mechanism
|
240
|
+
max_wire_version >= 3 ? 'SCRAM-SHA-1' : DEFAULT_MECHANISM
|
241
|
+
end
|
242
|
+
|
243
|
+
# Handles copying a database with SCRAM-SHA-1 authentication.
|
244
|
+
#
|
245
|
+
# @api private
|
246
|
+
#
|
247
|
+
# @param [ String ] username The user to authenticate on the
|
248
|
+
# 'from' database.
|
249
|
+
# @param [ String ] password The password for the user authenticated
|
250
|
+
# on the 'from' database.
|
251
|
+
# @param [ String ] from_host The host of the 'from' database.
|
252
|
+
# @param [ String ] from_db Name of the database to copy from.
|
253
|
+
# @param [ String ] to_db Name of the database to copy to.
|
254
|
+
#
|
255
|
+
# @return [ Hash ] The result of the copydb operation.
|
256
|
+
#
|
257
|
+
# @since 1.12.0
|
258
|
+
def copy_db_scram(username, password, from_host, from_db, to_db)
|
259
|
+
auth = { :db_name => from_db,
|
260
|
+
:username => username,
|
261
|
+
:password => password }
|
262
|
+
|
263
|
+
socket = checkout_reader(:mode => :primary_preferred)
|
264
|
+
|
265
|
+
copy_db = { :from_host => from_host, :from_db => from_db, :to_db => to_db }
|
266
|
+
scram = SCRAM.new(auth, Authentication.hash_password(username, password),
|
267
|
+
{ :copy_db => copy_db })
|
268
|
+
result = auth_command(scram.copy_db_start, socket, 'admin').first
|
269
|
+
result = auth_command(scram.copy_db_continue(result), socket, 'admin').first
|
270
|
+
until result['done']
|
271
|
+
result = auth_command(scram.copy_db_continue(result), socket, 'admin').first
|
272
|
+
end
|
273
|
+
socket.checkin
|
274
|
+
result
|
275
|
+
end
|
276
|
+
|
277
|
+
# Handles copying a database with MONGODB-CR authentication.
|
278
|
+
#
|
279
|
+
# @api private
|
280
|
+
#
|
281
|
+
# @param [ String ] username The user to authenticate on the
|
282
|
+
# 'from' database.
|
283
|
+
# @param [ String ] password The password for the user authenticated
|
284
|
+
# on the 'from' database.
|
285
|
+
# @param [ String ] from_host The host of the 'from' database.
|
286
|
+
# @param [ String ] from_db Name of the database to copy from.
|
287
|
+
# @param [ String ] to_db Name of the database to copy to.
|
288
|
+
#
|
289
|
+
# @return [ Hash ] The result of the copydb operation.
|
290
|
+
#
|
291
|
+
# @since 1.12.0
|
292
|
+
def copy_db_mongodb_cr(username, password, from_host, from_db, to_db)
|
293
|
+
oh = BSON::OrderedHash.new
|
294
|
+
oh[:copydb] = 1
|
295
|
+
oh[:fromhost] = from_host
|
296
|
+
oh[:fromdb] = from_db
|
297
|
+
oh[:todb] = to_db
|
298
|
+
|
299
|
+
socket = checkout_reader(:mode => :primary_preferred)
|
300
|
+
|
301
|
+
if username || password
|
302
|
+
unless username && password
|
303
|
+
raise MongoArgumentError,
|
304
|
+
'Both username and password must be supplied for authentication.'
|
305
|
+
end
|
306
|
+
nonce_cmd = BSON::OrderedHash.new
|
307
|
+
nonce_cmd[:copydbgetnonce] = 1
|
308
|
+
nonce_cmd[:fromhost] = from_host
|
309
|
+
result = auth_command(nonce_cmd, socket, 'admin').first
|
310
|
+
oh[:nonce] = result['nonce']
|
311
|
+
oh[:username] = username
|
312
|
+
oh[:key] = Authentication.auth_key(username, password, oh[:nonce])
|
313
|
+
end
|
314
|
+
result = auth_command(oh, socket, 'admin').first
|
315
|
+
socket.checkin
|
316
|
+
result
|
317
|
+
end
|
318
|
+
|
215
319
|
# Handles issuing authentication commands for the MONGODB-CR auth mechanism.
|
216
320
|
#
|
217
321
|
# @param auth [Hash] The authentication credentials to be used.
|
@@ -287,6 +391,29 @@ module Mongo
|
|
287
391
|
raise "In order to use Kerberos, please add the mongo-kerberos gem to your dependencies"
|
288
392
|
end
|
289
393
|
|
394
|
+
# Handles issuing SCRAM-SHA-1 authentication.
|
395
|
+
#
|
396
|
+
# @api private
|
397
|
+
#
|
398
|
+
# @param [ Hash ] auth The authentication credentials.
|
399
|
+
# @param [ Hash ] opts The options.
|
400
|
+
#
|
401
|
+
# @options opts [ Socket ] socket The Socket instance to use.
|
402
|
+
#
|
403
|
+
# @return [ Hash ] The result of the authentication operation.
|
404
|
+
#
|
405
|
+
# @since 1.12.0
|
406
|
+
def issue_scram(auth, opts = {})
|
407
|
+
db_name = auth[:source]
|
408
|
+
scram = SCRAM.new(auth, Authentication.hash_password(auth[:username], auth[:password]))
|
409
|
+
result = auth_command(scram.start, opts[:socket], db_name).first
|
410
|
+
result = auth_command(scram.continue(result), opts[:socket], db_name).first
|
411
|
+
until result['done']
|
412
|
+
result = auth_command(scram.finalize(result), opts[:socket], db_name).first
|
413
|
+
end
|
414
|
+
result
|
415
|
+
end
|
416
|
+
|
290
417
|
# Helper to fetch a nonce value from a given database instance.
|
291
418
|
#
|
292
419
|
# @param database [Mongo::DB] The DB instance to use for issue the nonce command.
|