added authentication support for copy_database

This commit is contained in:
Kyle Banker 2010-03-16 13:56:30 -04:00
parent 38d6401d1a
commit 9dd1a5c2e3
6 changed files with 106 additions and 34 deletions

View File

@ -48,6 +48,7 @@ require 'mongo/types/regexp_of_holding'
require 'mongo/types/min_max_keys'
require 'mongo/util/support'
require 'mongo/util/core_ext'
require 'mongo/util/conversions'
require 'mongo/util/server_version'
require 'mongo/util/bson_ruby'

View File

@ -276,19 +276,33 @@ module Mongo
self[name].command(:dropDatabase => 1)
end
# Copy the database +from+ on the local server to +to+ on the specified +host+.
# +host+ defaults to 'localhost' if no value is provided.
# Copy the database +from+ to +to+ on localhost. The +from+ database is
# assumed to be on localhost, but an alternate host can be specified.
#
# @param [String] from name of the database to copy from.
# @param [String] to name of the database to copy to.
# @param [String] from_host host of the 'from' database.
def copy_database(from, to, from_host="localhost")
# @param [String] username username for authentication against from_db (>=1.3.x).
# @param [String] password password for authentication against from_db (>=1.3.x).
def copy_database(from, to, from_host="localhost", username=nil, password=nil)
oh = OrderedHash.new
oh[:copydb] = 1
oh[:fromhost] = from_host
oh[:fromdb] = from
oh[:todb] = to
self["admin"].command(oh, false, true)
if username || password
unless username && password
raise MongoArgumentError, "Both username and password must be supplied for authentication."
end
nonce_cmd = OrderedHash.new
nonce_cmd[:copydbgetnonce] = 1
nonce_cmd[:fromhost] = from_host
result = self["admin"].command(nonce_cmd, true, true)
oh[:nonce] = result["nonce"]
oh[:username] = username
oh[:key] = Mongo::Support.auth_key(username, password, oh[:nonce])
end
self["admin"].command(oh, true, true)
end
# Increment and return the next available request id.

View File

@ -16,7 +16,6 @@
require 'socket'
require 'timeout'
require 'digest/md5'
require 'thread'
module Mongo
@ -65,7 +64,7 @@ module Mongo
#
# @core databases constructor_details
def initialize(db_name, connection, options={})
@name = validate_db_name(db_name)
@name = Mongo::Support.validate_db_name(db_name)
@connection = connection
@strict = options[:strict]
@pk_factory = options[:pk]
@ -94,7 +93,7 @@ module Mongo
auth['authenticate'] = 1
auth['user'] = username
auth['nonce'] = nonce
auth['key'] = Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
auth['key'] = Mongo::Support.auth_key(username, password, nonce)
if ok?(command(auth))
if save_auth
@connection.add_auth(@name, username, password)
@ -115,7 +114,7 @@ module Mongo
def add_user(username, password)
users = self[SYSTEM_USER_COLLECTION]
user = users.find_one({:user => username}) || {:user => username}
user['pwd'] = hash_password(username, password)
user['pwd'] = Mongo::Support.hash_password(username, password)
users.save(user)
return user
end
@ -444,7 +443,7 @@ module Mongo
:limit => -1, :selector => selector, :socket => sock).next_document
if check_response && !ok?(result)
raise OperationFailure, "Database command '#{selector.keys.first}' failed."
raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{result.inspect}"
else
result
end
@ -546,26 +545,8 @@ module Mongo
private
def hash_password(username, plaintext)
Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
end
def system_command_collection
Collection.new(self, SYSTEM_COMMAND_COLLECTION)
end
def validate_db_name(db_name)
unless [String, Symbol].include?(db_name.class)
raise TypeError, "db_name must be a string or symbol"
end
[" ", ".", "$", "/", "\\"].each do |invalid_char|
if db_name.include? invalid_char
raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
end
end
raise InvalidName, "database name cannot be the empty string" if db_name.empty?
db_name
end
end
end

View File

@ -0,0 +1,26 @@
# --
# Copyright (C) 2008-2010 10gen Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ++
#:nodoc:
class Object
#:nodoc:
def returning(value)
yield value
value
end
end

View File

@ -14,13 +14,46 @@
# limitations under the License.
# ++
#:nodoc:
class Object
require 'digest/md5'
#:nodoc:
def returning(value)
yield value
value
module Mongo
module Support
extend self
# Generate an MD5 for authentication.
#
# @param [String] username
# @param [String] password
# @param [String] nonce
#
# @return [String] a key for db authentication.
def auth_key(username, password, nonce)
Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
end
# Return a hashed password for auth.
#
# @param [String] username
# @param [String] plaintext
#
# @return [String]
def hash_password(username, plaintext)
Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
end
def validate_db_name(db_name)
unless [String, Symbol].include?(db_name.class)
raise TypeError, "db_name must be a string or symbol"
end
[" ", ".", "$", "/", "\\"].each do |invalid_char|
if db_name.include? invalid_char
raise InvalidName, "database names cannot contain the character '#{invalid_char}'"
end
end
raise InvalidName, "database name cannot be the empty string" if db_name.empty?
db_name
end
end
end

View File

@ -58,6 +58,23 @@ class TestConnection < Test::Unit::TestCase
old_object = @mongo.db('old').collection('copy-test').find.next_document
new_object = @mongo.db('new').collection('copy-test').find.next_document
assert_equal old_object, new_object
@mongo.drop_database('old')
@mongo.drop_database('new')
end
def test_copy_database_with_auth
@mongo.db('old').collection('copy-test').insert('a' => 1)
@mongo.db('old').add_user('bob', 'secret')
assert_raise Mongo::OperationFailure do
@mongo.copy_database('old', 'new', 'localhost', 'bob', 'badpassword')
end
result = @mongo.copy_database('old', 'new', 'localhost', 'bob', 'secret')
assert result['ok'].to_i == 1
@mongo.drop_database('old')
@mongo.drop_database('new')
end
def test_database_names