Skip to content

Commit 46d9ed5

Browse files
committed
RUBY-577: support for delegated authentication and logout
1 parent 567199f commit 46d9ed5

File tree

2 files changed

+30
-17
lines changed

2 files changed

+30
-17
lines changed

lib/mongo/db.rb

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/mongo/mongo_client.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ def apply_saved_authentication(opts={})
259259
return false if @auths.empty?
260260
@auths.each do |auth|
261261
self[auth[:db_name]].issue_authentication(auth[:username], auth[:password], false,
262-
:socket => opts[:socket])
262+
:source => auth[:source], :socket => opts[:socket])
263263
end
264264
true
265265
end
@@ -277,15 +277,16 @@ def apply_saved_authentication(opts={})
277277
# @param [String] password
278278
#
279279
# @return [Hash] a hash representing the authentication just added.
280-
def add_auth(db_name, username, password)
280+
def add_auth(db_name, username, password, source)
281281
if @auths.any? {|a| a[:db_name] == db_name}
282282
raise MongoArgumentError, "Cannot apply multiple authentications to database '#{db_name}'"
283283
end
284284

285285
auth = {
286286
:db_name => db_name,
287287
:username => username,
288-
:password => password
288+
:password => password,
289+
:source => source
289290
}
290291
@auths << auth
291292
auth

0 commit comments

Comments
 (0)