@@ -123,43 +123,55 @@ def initialize(name, client, opts={})
123123 # @param [String] password
124124 # @param [Boolean] save_auth
125125 # Save this authentication to the client object using MongoClient#add_auth. This
126- # will ensure that the authentication will be applied on database reconnect. Note
127- # that this value must be true when using connection pooling.
126+ # will ensure that the authentication will be applied to all sockets and upon
127+ # database reconnect.
128+ # @param source [String] Database with user credentials. This should be used to
129+ # authenticate against a database when the credentials exist elsewhere.
130+ #
131+ # @note save_auth must be true when using connection pooling or providing a source
132+ # for credentials.
128133 #
129134 # @return [Boolean]
130135 #
131136 # @raise [AuthenticationError]
132137 #
133138 # @core authenticate authenticate-instance_method
134- def authenticate ( username , password , save_auth = true )
135- if @connection . pool_size > 1 && !save_auth
136- raise MongoArgumentError , "If using connection pooling, :save_auth must be set to true."
139+ def authenticate ( username , password = nil , save_auth = true , source = nil )
140+ if ( @connection . pool_size > 1 || source ) && !save_auth
141+ raise MongoArgumentError , "If using connection pooling or delegated auth, " +
142+ ":save_auth must be set to true."
137143 end
138144
139145 begin
140146 socket = @connection . checkout_reader ( :mode => :primary_preferred )
141- issue_authentication ( username , password , save_auth , :socket => socket )
147+ issue_authentication ( username , password , save_auth ,
148+ :socket => socket , :source => source )
142149 ensure
143150 socket . checkin if socket
144151 end
145152
146153 @connection . authenticate_pools
154+ true
147155 end
148156
149157 def issue_authentication ( username , password , save_auth = true , opts = { } )
150158 doc = command ( { :getnonce => 1 } , :check_response => false , :socket => opts [ :socket ] )
151159 raise MongoDBError , "Error retrieving nonce: #{ doc } " unless ok? ( doc )
152160 nonce = doc [ 'nonce' ]
153161
162+ # issue authentication against this database if source option not provided
163+ source = opts [ :source ]
164+ db = source ? @connection [ source ] : self
165+
154166 auth = BSON ::OrderedHash . new
155167 auth [ 'authenticate' ] = 1
156168 auth [ 'user' ] = username
157169 auth [ 'nonce' ] = nonce
158170 auth [ 'key' ] = Mongo ::Support . auth_key ( username , password , nonce )
159- if ok? ( doc = self . command ( auth , :check_response => false , :socket => opts [ :socket ] ) )
160- @connection . add_auth ( @ name, username , password ) if save_auth
171+ if ok? ( doc = db . command ( auth , :check_response => false , :socket => opts [ :socket ] ) )
172+ @connection . add_auth ( name , username , password , source ) if save_auth
161173 else
162- message = "Failed to authenticate user '#{ username } ' on db '#{ self . name } '"
174+ message = "Failed to authenticate user '#{ username } ' on db '#{ db . name } '"
163175 raise Mongo ::AuthenticationError . new ( message , doc [ 'code' ] , doc )
164176 end
165177 true
@@ -248,14 +260,14 @@ def remove_user(username)
248260 #
249261 # @return [Boolean]
250262 def logout ( opts = { } )
251- @connection . logout_pools ( @name ) if @connection . pool_size > 1
252- issue_logout ( opts )
263+ auth = @connection . auths . find { |a | a [ :db_name ] == name }
264+ db = auth && auth [ :source ] ? @connection [ auth [ :source ] ] : self
265+ auth ? @connection . logout_pools ( db . name ) : db . issue_logout ( opts )
266+ @connection . remove_auth ( db . name )
253267 end
254268
255269 def issue_logout ( opts = { } )
256- if ok? ( doc = command ( { :logout => 1 } , :socket => opts [ :socket ] ) )
257- @connection . remove_auth ( @name )
258- else
270+ unless ok? ( doc = command ( { :logout => 1 } , :socket => opts [ :socket ] ) )
259271 raise MongoDBError , "Error logging out: #{ doc . inspect } "
260272 end
261273 true
0 commit comments