mongo 1.3.0 → 1.12.5
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data/{LICENSE.txt → LICENSE} +1 -1
- data/README.md +122 -271
- data/Rakefile +25 -209
- data/VERSION +1 -0
- data/bin/mongo_console +31 -9
- data/lib/mongo/bulk_write_collection_view.rb +387 -0
- data/lib/mongo/collection.rb +576 -269
- data/lib/mongo/collection_writer.rb +364 -0
- data/lib/mongo/connection/node.rb +249 -0
- data/lib/mongo/connection/pool.rb +340 -0
- data/lib/mongo/connection/pool_manager.rb +320 -0
- data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
- data/lib/mongo/connection/socket/socket_util.rb +37 -0
- data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
- data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
- data/lib/mongo/connection/socket/unix_socket.rb +39 -0
- data/lib/mongo/connection/socket.rb +18 -0
- data/lib/mongo/connection.rb +7 -875
- data/lib/mongo/cursor.rb +403 -117
- data/lib/mongo/db.rb +444 -243
- data/lib/mongo/exception.rb +145 -0
- data/lib/mongo/functional/authentication.rb +455 -0
- data/lib/mongo/functional/logging.rb +85 -0
- data/lib/mongo/functional/read_preference.rb +183 -0
- data/lib/mongo/functional/scram.rb +556 -0
- data/lib/mongo/functional/uri_parser.rb +409 -0
- data/lib/mongo/functional/write_concern.rb +66 -0
- data/lib/mongo/functional.rb +20 -0
- data/lib/mongo/gridfs/grid.rb +30 -24
- data/lib/mongo/gridfs/grid_ext.rb +6 -10
- data/lib/mongo/gridfs/grid_file_system.rb +38 -20
- data/lib/mongo/gridfs/grid_io.rb +84 -75
- data/lib/mongo/gridfs.rb +18 -0
- data/lib/mongo/legacy.rb +140 -0
- data/lib/mongo/mongo_client.rb +697 -0
- data/lib/mongo/mongo_replica_set_client.rb +535 -0
- data/lib/mongo/mongo_sharded_client.rb +159 -0
- data/lib/mongo/networking.rb +372 -0
- data/lib/mongo/{util → utils}/conversions.rb +29 -8
- data/lib/mongo/{util → utils}/core_ext.rb +28 -18
- data/lib/mongo/{util → utils}/server_version.rb +4 -6
- data/lib/mongo/{util → utils}/support.rb +29 -31
- data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
- data/lib/mongo/utils.rb +19 -0
- data/lib/mongo.rb +51 -50
- data/mongo.gemspec +29 -32
- data/test/functional/authentication_test.rb +39 -0
- data/test/functional/bulk_api_stress_test.rb +133 -0
- data/test/functional/bulk_write_collection_view_test.rb +1198 -0
- data/test/functional/client_test.rb +627 -0
- data/test/functional/collection_test.rb +2175 -0
- data/test/functional/collection_writer_test.rb +83 -0
- data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
- data/test/functional/cursor_fail_test.rb +57 -0
- data/test/functional/cursor_message_test.rb +56 -0
- data/test/functional/cursor_test.rb +683 -0
- data/test/functional/db_api_test.rb +835 -0
- data/test/functional/db_connection_test.rb +25 -0
- data/test/functional/db_test.rb +348 -0
- data/test/functional/grid_file_system_test.rb +285 -0
- data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
- data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
- data/test/functional/pool_test.rb +136 -0
- data/test/functional/safe_test.rb +98 -0
- data/test/functional/ssl_test.rb +29 -0
- data/test/functional/support_test.rb +62 -0
- data/test/functional/timeout_test.rb +60 -0
- data/test/functional/uri_test.rb +446 -0
- data/test/functional/write_concern_test.rb +118 -0
- data/test/helpers/general.rb +50 -0
- data/test/helpers/test_unit.rb +476 -0
- data/test/replica_set/authentication_test.rb +37 -0
- data/test/replica_set/basic_test.rb +189 -0
- data/test/replica_set/client_test.rb +393 -0
- data/test/replica_set/connection_test.rb +138 -0
- data/test/replica_set/count_test.rb +66 -0
- data/test/replica_set/cursor_test.rb +220 -0
- data/test/replica_set/insert_test.rb +157 -0
- data/test/replica_set/max_values_test.rb +151 -0
- data/test/replica_set/pinning_test.rb +105 -0
- data/test/replica_set/query_test.rb +73 -0
- data/test/replica_set/read_preference_test.rb +219 -0
- data/test/replica_set/refresh_test.rb +211 -0
- data/test/replica_set/replication_ack_test.rb +95 -0
- data/test/replica_set/ssl_test.rb +32 -0
- data/test/sharded_cluster/basic_test.rb +203 -0
- data/test/shared/authentication/basic_auth_shared.rb +260 -0
- data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
- data/test/shared/authentication/gssapi_shared.rb +176 -0
- data/test/shared/authentication/sasl_plain_shared.rb +96 -0
- data/test/shared/authentication/scram_shared.rb +92 -0
- data/test/shared/ssl_shared.rb +235 -0
- data/test/test_helper.rb +53 -94
- data/test/threading/basic_test.rb +120 -0
- data/test/tools/mongo_config.rb +708 -0
- data/test/tools/mongo_config_test.rb +160 -0
- data/test/unit/client_test.rb +381 -0
- data/test/unit/collection_test.rb +89 -53
- data/test/unit/connection_test.rb +282 -32
- data/test/unit/cursor_test.rb +206 -8
- data/test/unit/db_test.rb +55 -13
- data/test/unit/grid_test.rb +43 -16
- data/test/unit/mongo_sharded_client_test.rb +48 -0
- data/test/unit/node_test.rb +93 -0
- data/test/unit/pool_manager_test.rb +111 -0
- data/test/unit/read_pref_test.rb +406 -0
- data/test/unit/read_test.rb +159 -0
- data/test/unit/safe_test.rb +69 -36
- data/test/unit/sharding_pool_manager_test.rb +84 -0
- data/test/unit/write_concern_test.rb +175 -0
- data.tar.gz.sig +3 -0
- metadata +227 -216
- metadata.gz.sig +0 -0
- data/docs/CREDITS.md +0 -123
- data/docs/FAQ.md +0 -116
- data/docs/GridFS.md +0 -158
- data/docs/HISTORY.md +0 -244
- data/docs/RELEASES.md +0 -33
- data/docs/REPLICA_SETS.md +0 -72
- data/docs/TUTORIAL.md +0 -247
- data/docs/WRITE_CONCERN.md +0 -28
- data/lib/mongo/exceptions.rb +0 -71
- data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
- data/lib/mongo/repl_set_connection.rb +0 -342
- data/lib/mongo/test.rb +0 -20
- data/lib/mongo/util/pool.rb +0 -177
- data/lib/mongo/util/uri_parser.rb +0 -185
- data/test/async/collection_test.rb +0 -224
- data/test/async/connection_test.rb +0 -24
- data/test/async/cursor_test.rb +0 -162
- data/test/async/worker_pool_test.rb +0 -99
- data/test/auxillary/1.4_features.rb +0 -166
- data/test/auxillary/authentication_test.rb +0 -68
- data/test/auxillary/autoreconnect_test.rb +0 -41
- data/test/auxillary/fork_test.rb +0 -30
- data/test/auxillary/repl_set_auth_test.rb +0 -58
- data/test/auxillary/slave_connection_test.rb +0 -36
- data/test/auxillary/threaded_authentication_test.rb +0 -101
- data/test/bson/binary_test.rb +0 -15
- data/test/bson/bson_test.rb +0 -649
- data/test/bson/byte_buffer_test.rb +0 -208
- data/test/bson/hash_with_indifferent_access_test.rb +0 -38
- data/test/bson/json_test.rb +0 -17
- data/test/bson/object_id_test.rb +0 -154
- data/test/bson/ordered_hash_test.rb +0 -204
- data/test/bson/timestamp_test.rb +0 -24
- data/test/collection_test.rb +0 -910
- data/test/connection_test.rb +0 -309
- data/test/cursor_fail_test.rb +0 -75
- data/test/cursor_message_test.rb +0 -43
- data/test/cursor_test.rb +0 -483
- data/test/db_api_test.rb +0 -726
- data/test/db_connection_test.rb +0 -15
- data/test/db_test.rb +0 -287
- data/test/grid_file_system_test.rb +0 -243
- data/test/load/resque/load.rb +0 -21
- data/test/load/resque/processor.rb +0 -26
- data/test/load/thin/load.rb +0 -24
- data/test/load/unicorn/load.rb +0 -23
- data/test/load/unicorn/unicorn.rb +0 -29
- data/test/replica_sets/connect_test.rb +0 -94
- data/test/replica_sets/connection_string_test.rb +0 -32
- data/test/replica_sets/count_test.rb +0 -35
- data/test/replica_sets/insert_test.rb +0 -53
- data/test/replica_sets/pooled_insert_test.rb +0 -55
- data/test/replica_sets/query_secondaries.rb +0 -96
- data/test/replica_sets/query_test.rb +0 -51
- data/test/replica_sets/replication_ack_test.rb +0 -66
- data/test/replica_sets/rs_test_helper.rb +0 -27
- data/test/safe_test.rb +0 -68
- data/test/support/hash_with_indifferent_access.rb +0 -186
- data/test/support/keys.rb +0 -45
- data/test/support_test.rb +0 -18
- data/test/threading/threading_with_large_pool_test.rb +0 -90
- data/test/threading_test.rb +0 -87
- data/test/tools/auth_repl_set_manager.rb +0 -14
- data/test/tools/load.rb +0 -58
- data/test/tools/repl_set_manager.rb +0 -266
- data/test/tools/sharding_manager.rb +0 -202
- data/test/tools/test.rb +0 -4
- data/test/unit/pool_test.rb +0 -9
- data/test/unit/repl_set_connection_test.rb +0 -59
- data/test/uri_test.rb +0 -91
@@ -0,0 +1,145 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
# Generic Mongo Ruby Driver exception class.
|
17
|
+
class MongoRubyError < StandardError; end
|
18
|
+
|
19
|
+
# Raised when MongoDB itself has returned an error.
|
20
|
+
class MongoDBError < RuntimeError
|
21
|
+
|
22
|
+
# @return The entire failed command's response object, if available.
|
23
|
+
attr_reader :result
|
24
|
+
|
25
|
+
# @return The failed command's error code, if availab.e
|
26
|
+
attr_reader :error_code
|
27
|
+
|
28
|
+
def initialize(message=nil, error_code=nil, result=nil)
|
29
|
+
@error_code = error_code
|
30
|
+
@result = result
|
31
|
+
super(message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Raised on fatal errors to GridFS.
|
36
|
+
class GridError < MongoRubyError; end
|
37
|
+
|
38
|
+
# Raised on fatal errors to GridFS.
|
39
|
+
class GridFileNotFound < GridError; end
|
40
|
+
|
41
|
+
# Raised on fatal errors to GridFS.
|
42
|
+
class GridMD5Failure < GridError; end
|
43
|
+
|
44
|
+
# Raised when invalid arguments are sent to Mongo Ruby methods.
|
45
|
+
class MongoArgumentError < MongoRubyError; end
|
46
|
+
|
47
|
+
# Raised on failures in connection to the database server.
|
48
|
+
class ConnectionError < MongoRubyError; end
|
49
|
+
|
50
|
+
# Raised on failures in connection to the database server.
|
51
|
+
class ReplicaSetConnectionError < ConnectionError; end
|
52
|
+
|
53
|
+
# Raised on failures in connection to the database server.
|
54
|
+
class ConnectionTimeoutError < MongoRubyError; end
|
55
|
+
|
56
|
+
# Raised when no tags in a read preference maps to a given connection.
|
57
|
+
class NodeWithTagsNotFound < MongoRubyError; end
|
58
|
+
|
59
|
+
# Raised when a connection operation fails.
|
60
|
+
class ConnectionFailure < MongoDBError; end
|
61
|
+
|
62
|
+
# Raised when authentication fails.
|
63
|
+
class AuthenticationError < MongoDBError; end
|
64
|
+
|
65
|
+
# Raised when a database operation fails.
|
66
|
+
class OperationFailure < MongoDBError; end
|
67
|
+
|
68
|
+
# Raised when a database operation exceeds maximum specified time.
|
69
|
+
class ExecutionTimeout < OperationFailure; end
|
70
|
+
|
71
|
+
# Raised when a database operation has a write concern error.
|
72
|
+
class WriteConcernError < OperationFailure; end
|
73
|
+
|
74
|
+
# Raised when a socket read operation times out.
|
75
|
+
class OperationTimeout < SocketError; end
|
76
|
+
|
77
|
+
# Raised when a client attempts to perform an invalid operation.
|
78
|
+
class InvalidOperation < MongoDBError; end
|
79
|
+
|
80
|
+
# Raised when an invalid collection or database name is used (invalid namespace name).
|
81
|
+
class InvalidNSName < RuntimeError; end
|
82
|
+
|
83
|
+
# Raised when the client supplies an invalid value to sort by.
|
84
|
+
class InvalidSortValueError < MongoRubyError; end
|
85
|
+
|
86
|
+
# Raised for bulk write errors.
|
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
|
145
|
+
end
|
@@ -0,0 +1,455 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'digest/md5'
|
16
|
+
|
17
|
+
module Mongo
|
18
|
+
module Authentication
|
19
|
+
|
20
|
+
DEFAULT_MECHANISM = 'MONGODB-CR'
|
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] }
|
26
|
+
|
27
|
+
# authentication module methods
|
28
|
+
class << self
|
29
|
+
# Helper to validate an authentication mechanism and optionally
|
30
|
+
# raise an error if invalid.
|
31
|
+
#
|
32
|
+
# @param mechanism [String] [description]
|
33
|
+
# @param raise_error [Boolean] [description]
|
34
|
+
#
|
35
|
+
# @raise [ArgumentError] if raise_error and not a valid auth mechanism.
|
36
|
+
# @return [Boolean] returns the validation result.
|
37
|
+
def validate_mechanism(mechanism, raise_error=false)
|
38
|
+
return true if MECHANISMS.include?(mechanism.upcase)
|
39
|
+
if raise_error
|
40
|
+
raise ArgumentError,
|
41
|
+
"Invalid authentication mechanism provided. Must be one of " +
|
42
|
+
"#{Mongo::Authentication::MECHANISMS.join(', ')}."
|
43
|
+
end
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Helper to validate and normalize credential sets.
|
49
|
+
#
|
50
|
+
# @param auth [Hash] A hash containing the credential set.
|
51
|
+
#
|
52
|
+
# @raise [MongoArgumentError] if the credential set is invalid.
|
53
|
+
# @return [Hash] The validated credential set.
|
54
|
+
def validate_credentials(auth)
|
55
|
+
# set the default auth source if not defined
|
56
|
+
auth[:source] = auth[:source] || auth[:db_name] || 'admin'
|
57
|
+
|
58
|
+
if password_required?(auth[:mechanism]) && !auth[:password]
|
59
|
+
raise MongoArgumentError,
|
60
|
+
"When using the authentication mechanism " +
|
61
|
+
"#{auth[:mechanism].nil? ? 'MONGODB-CR or SCRAM-SHA-1' : auth[:mechanism]} " +
|
62
|
+
"both username and password are required."
|
63
|
+
end
|
64
|
+
# if extra opts exist, validate them
|
65
|
+
allowed_keys = EXTRA[auth[:mechanism]]
|
66
|
+
if auth[:extra] && !auth[:extra].empty?
|
67
|
+
invalid_opts = []
|
68
|
+
auth[:extra].keys.each { |k| invalid_opts << k unless allowed_keys.include?(k) }
|
69
|
+
raise MongoArgumentError,
|
70
|
+
"Invalid extra option(s): #{invalid_opts} found. Please check the extra options" +
|
71
|
+
" passed and try again." unless invalid_opts.empty?
|
72
|
+
end
|
73
|
+
auth
|
74
|
+
end
|
75
|
+
|
76
|
+
# Generate an MD5 for authentication.
|
77
|
+
#
|
78
|
+
# @param username [String] The username.
|
79
|
+
# @param password [String] The user's password.
|
80
|
+
# @param nonce [String] The nonce value.
|
81
|
+
#
|
82
|
+
# @return [String] MD5 key for db authentication.
|
83
|
+
def auth_key(username, password, nonce)
|
84
|
+
Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return a hashed password for auth.
|
88
|
+
#
|
89
|
+
# @param username [String] The username.
|
90
|
+
# @param password [String] The users's password.
|
91
|
+
#
|
92
|
+
# @return [String] The hashed password value.
|
93
|
+
def hash_password(username, password)
|
94
|
+
Digest::MD5.hexdigest("#{username}:mongo:#{password}")
|
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
|
109
|
+
end
|
110
|
+
|
111
|
+
# Saves a cache of authentication credentials to the current
|
112
|
+
# client instance. This method is called automatically by DB#authenticate.
|
113
|
+
#
|
114
|
+
# @param db_name [String] The current database name.
|
115
|
+
# @param username [String] The current username.
|
116
|
+
# @param password [String] (nil) The users's password (not required for
|
117
|
+
# all authentication mechanisms).
|
118
|
+
# @param source [String] (nil) The authentication source database
|
119
|
+
# (if different than the current database).
|
120
|
+
# @param mechanism [String] (nil) The authentication mechanism being used
|
121
|
+
# (default: 'MONGODB-CR' or 'SCRAM-SHA-1' if server version >= 2.7.8).
|
122
|
+
# @param extra [Hash] (nil) A optional hash of extra options to be stored with
|
123
|
+
# the credential set.
|
124
|
+
#
|
125
|
+
# @raise [MongoArgumentError] Raised if the database has already been used
|
126
|
+
# for authentication. A log out is required before additional auths can
|
127
|
+
# be issued against a given database.
|
128
|
+
# @raise [AuthenticationError] Raised if authentication fails.
|
129
|
+
# @return [Hash] a hash representing the authentication just added.
|
130
|
+
def add_auth(db_name, username, password=nil, source=nil, mechanism=nil, extra=nil)
|
131
|
+
auth = Authentication.validate_credentials({
|
132
|
+
:db_name => db_name,
|
133
|
+
:username => username,
|
134
|
+
:password => password,
|
135
|
+
:source => source,
|
136
|
+
:mechanism => mechanism,
|
137
|
+
:extra => extra
|
138
|
+
})
|
139
|
+
|
140
|
+
if @auths.any? {|a| a[:source] == auth[:source]}
|
141
|
+
raise MongoArgumentError,
|
142
|
+
"Another user has already authenticated to the database " +
|
143
|
+
"'#{auth[:source]}' and multiple authentications are not " +
|
144
|
+
"permitted. Please logout first."
|
145
|
+
end
|
146
|
+
|
147
|
+
begin
|
148
|
+
socket = checkout_reader(:mode => :primary_preferred)
|
149
|
+
issue_authentication(auth, :socket => socket)
|
150
|
+
ensure
|
151
|
+
socket.checkin if socket
|
152
|
+
end
|
153
|
+
|
154
|
+
@auths << auth
|
155
|
+
auth
|
156
|
+
end
|
157
|
+
|
158
|
+
# Remove a saved authentication for this connection.
|
159
|
+
#
|
160
|
+
# @param db_name [String] The database name.
|
161
|
+
#
|
162
|
+
# @return [Boolean] The result of the operation.
|
163
|
+
def remove_auth(db_name)
|
164
|
+
return false unless @auths
|
165
|
+
auths = @auths.to_a
|
166
|
+
removed = auths.reject! { |a| a[:source] == db_name }
|
167
|
+
@auths = Set.new(auths)
|
168
|
+
!!removed
|
169
|
+
end
|
170
|
+
|
171
|
+
# Remove all authentication information stored in this connection.
|
172
|
+
#
|
173
|
+
# @return [Boolean] result of the operation.
|
174
|
+
def clear_auths
|
175
|
+
@auths = Set.new
|
176
|
+
true
|
177
|
+
end
|
178
|
+
|
179
|
+
# Method to handle and issue logout commands.
|
180
|
+
#
|
181
|
+
# @note This method should not be called directly. Use DB#logout.
|
182
|
+
#
|
183
|
+
# @param db_name [String] The database name.
|
184
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
185
|
+
#
|
186
|
+
# @option opts [Socket] socket Socket instance to use.
|
187
|
+
#
|
188
|
+
# @raise [MongoDBError] Raised if the logout operation fails.
|
189
|
+
# @return [Boolean] The result of the logout operation.
|
190
|
+
def issue_logout(db_name, opts={})
|
191
|
+
doc = auth_command({:logout => 1}, opts[:socket], db_name).first
|
192
|
+
unless Support.ok?(doc)
|
193
|
+
raise MongoDBError, "Error logging out on DB #{db_name}."
|
194
|
+
end
|
195
|
+
true # somewhat pointless, but here to preserve the existing API
|
196
|
+
end
|
197
|
+
|
198
|
+
# Method to handle and issue authentication commands.
|
199
|
+
#
|
200
|
+
# @note This method should not be called directly. Use DB#authenticate.
|
201
|
+
#
|
202
|
+
# @param auth [Hash] The authentication credentials to be used.
|
203
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
204
|
+
#
|
205
|
+
# @option opts [Socket] socket Socket instance to use.
|
206
|
+
#
|
207
|
+
# @raise [AuthenticationError] Raised if the authentication fails.
|
208
|
+
# @return [Boolean] Result of the authentication operation.
|
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])
|
215
|
+
result = case auth[:mechanism]
|
216
|
+
when 'MONGODB-CR'
|
217
|
+
issue_cr(auth, opts)
|
218
|
+
when 'MONGODB-X509'
|
219
|
+
issue_x509(auth, opts)
|
220
|
+
when 'PLAIN'
|
221
|
+
issue_plain(auth, opts)
|
222
|
+
when 'GSSAPI'
|
223
|
+
issue_gssapi(auth, opts)
|
224
|
+
when 'SCRAM-SHA-1'
|
225
|
+
issue_scram(auth, opts)
|
226
|
+
end
|
227
|
+
|
228
|
+
unless Support.ok?(result)
|
229
|
+
raise AuthenticationError,
|
230
|
+
"Failed to authenticate user '#{auth[:username]}' " +
|
231
|
+
"on db '#{auth[:source]}'."
|
232
|
+
end
|
233
|
+
|
234
|
+
true
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
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
|
+
|
319
|
+
# Handles issuing authentication commands for the MONGODB-CR auth mechanism.
|
320
|
+
#
|
321
|
+
# @param auth [Hash] The authentication credentials to be used.
|
322
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
323
|
+
#
|
324
|
+
# @option opts [Socket] socket Socket instance to use.
|
325
|
+
#
|
326
|
+
# @return [Boolean] Result of the authentication operation.
|
327
|
+
#
|
328
|
+
# @private
|
329
|
+
def issue_cr(auth, opts={})
|
330
|
+
db_name = auth[:source]
|
331
|
+
nonce = get_nonce(auth[:source], opts)
|
332
|
+
|
333
|
+
# build auth command document
|
334
|
+
cmd = BSON::OrderedHash.new
|
335
|
+
cmd['authenticate'] = 1
|
336
|
+
cmd['user'] = auth[:username]
|
337
|
+
cmd['nonce'] = nonce
|
338
|
+
cmd['key'] = Authentication.auth_key(auth[:username],
|
339
|
+
auth[:password],
|
340
|
+
nonce)
|
341
|
+
auth_command(cmd, opts[:socket], db_name).first
|
342
|
+
end
|
343
|
+
|
344
|
+
# Handles issuing authentication commands for the MONGODB-X509 auth mechanism.
|
345
|
+
#
|
346
|
+
# @param auth [Hash] The authentication credentials to be used.
|
347
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
348
|
+
#
|
349
|
+
# @private
|
350
|
+
def issue_x509(auth, opts={})
|
351
|
+
db_name = '$external'
|
352
|
+
|
353
|
+
cmd = BSON::OrderedHash.new
|
354
|
+
cmd[:authenticate] = 1
|
355
|
+
cmd[:mechanism] = auth[:mechanism]
|
356
|
+
cmd[:user] = auth[:username]
|
357
|
+
|
358
|
+
auth_command(cmd, opts[:socket], db_name).first
|
359
|
+
end
|
360
|
+
|
361
|
+
# Handles issuing authentication commands for the PLAIN auth mechanism.
|
362
|
+
#
|
363
|
+
# @param auth [Hash] The authentication credentials to be used.
|
364
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
365
|
+
#
|
366
|
+
# @option opts [Socket] socket Socket instance to use.
|
367
|
+
#
|
368
|
+
# @return [Boolean] Result of the authentication operation.
|
369
|
+
#
|
370
|
+
# @private
|
371
|
+
def issue_plain(auth, opts={})
|
372
|
+
db_name = auth[:source]
|
373
|
+
payload = "\x00#{auth[:username]}\x00#{auth[:password]}"
|
374
|
+
|
375
|
+
cmd = BSON::OrderedHash.new
|
376
|
+
cmd[:saslStart] = 1
|
377
|
+
cmd[:mechanism] = auth[:mechanism]
|
378
|
+
cmd[:payload] = BSON::Binary.new(payload)
|
379
|
+
cmd[:autoAuthorize] = 1
|
380
|
+
|
381
|
+
auth_command(cmd, opts[:socket], db_name).first
|
382
|
+
end
|
383
|
+
|
384
|
+
# Handles issuing authentication commands for the GSSAPI auth mechanism.
|
385
|
+
#
|
386
|
+
# @param auth [Hash] The authentication credentials to be used.
|
387
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
388
|
+
#
|
389
|
+
# @private
|
390
|
+
def issue_gssapi(auth, opts={})
|
391
|
+
raise "In order to use Kerberos, please add the mongo-kerberos gem to your dependencies"
|
392
|
+
end
|
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
|
+
|
417
|
+
# Helper to fetch a nonce value from a given database instance.
|
418
|
+
#
|
419
|
+
# @param database [Mongo::DB] The DB instance to use for issue the nonce command.
|
420
|
+
# @param opts [Hash] Hash of optional settings and configuration values.
|
421
|
+
#
|
422
|
+
# @option opts [Socket] socket Socket instance to use.
|
423
|
+
#
|
424
|
+
# @raise [MongoDBError] Raised if there is an error executing the command.
|
425
|
+
# @return [String] Returns the nonce value.
|
426
|
+
#
|
427
|
+
# @private
|
428
|
+
def get_nonce(db_name, opts={})
|
429
|
+
cmd = BSON::OrderedHash.new
|
430
|
+
cmd[:getnonce] = 1
|
431
|
+
doc = auth_command(cmd, opts[:socket], db_name).first
|
432
|
+
|
433
|
+
unless Support.ok?(doc)
|
434
|
+
raise MongoDBError, "Error retrieving nonce: #{doc}"
|
435
|
+
end
|
436
|
+
doc['nonce']
|
437
|
+
end
|
438
|
+
|
439
|
+
def auth_command(selector, socket, db_name)
|
440
|
+
begin
|
441
|
+
message = build_command_message(db_name, selector)
|
442
|
+
request_id = add_message_headers(message, Mongo::Constants::OP_QUERY)
|
443
|
+
packed_message = message.to_s
|
444
|
+
|
445
|
+
send_message_on_socket(packed_message, socket)
|
446
|
+
receive(socket, request_id).shift
|
447
|
+
rescue OperationFailure => ex
|
448
|
+
return ex.result
|
449
|
+
rescue ConnectionFailure, OperationTimeout => ex
|
450
|
+
socket.close
|
451
|
+
raise ex
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Copyright (C) 2009-2013 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module Mongo
|
16
|
+
module Logging
|
17
|
+
|
18
|
+
module Instrumenter
|
19
|
+
def self.instrument(name, payload = {})
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
@instrumenter = Instrumenter
|
25
|
+
|
26
|
+
def write_logging_startup_message
|
27
|
+
log(:debug, "Logging level is currently :debug which could negatively impact " +
|
28
|
+
"client-side performance. You should set your logging level no lower than " +
|
29
|
+
":info in production.")
|
30
|
+
end
|
31
|
+
|
32
|
+
# Log a message with the given level.
|
33
|
+
def log(level, msg)
|
34
|
+
return unless @logger
|
35
|
+
case level
|
36
|
+
when :fatal then
|
37
|
+
@logger.fatal "MONGODB [FATAL] #{msg}"
|
38
|
+
when :error then
|
39
|
+
@logger.error "MONGODB [ERROR] #{msg}"
|
40
|
+
when :warn then
|
41
|
+
@logger.warn "MONGODB [WARNING] #{msg}"
|
42
|
+
when :info then
|
43
|
+
@logger.info "MONGODB [INFO] #{msg}"
|
44
|
+
when :debug then
|
45
|
+
@logger.debug "MONGODB [DEBUG] #{msg}"
|
46
|
+
else
|
47
|
+
@logger.debug "MONGODB [DEBUG] #{msg}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Execute the block and log the operation described by name and payload.
|
52
|
+
def instrument(name, payload = {})
|
53
|
+
start_time = Time.now
|
54
|
+
res = Logging.instrumenter.instrument(name, payload) do
|
55
|
+
yield
|
56
|
+
end
|
57
|
+
duration = Time.now - start_time
|
58
|
+
log_operation(name, payload, duration)
|
59
|
+
res
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.instrumenter
|
63
|
+
@instrumenter
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.instrumenter=(instrumenter)
|
67
|
+
@instrumenter = instrumenter
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def log_operation(name, payload, duration)
|
73
|
+
@logger && @logger.debug do
|
74
|
+
msg = "MONGODB "
|
75
|
+
msg << "(%.1fms) " % (duration * 1000)
|
76
|
+
msg << "#{payload[:database]}['#{payload[:collection]}'].#{name}("
|
77
|
+
msg << payload.values_at(:selector, :document, :documents, :fields ).compact.map(&:inspect).join(', ') + ")"
|
78
|
+
msg << ".skip(#{payload[:skip]})" if payload[:skip]
|
79
|
+
msg << ".limit(#{payload[:limit]})" if payload[:limit]
|
80
|
+
msg << ".sort(#{payload[:order]})" if payload[:order]
|
81
|
+
msg
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|