Significant mods to LDAP#search
This commit is contained in:
parent
193f76e530
commit
7be5474f06
117
lib/net/ldap.rb
117
lib/net/ldap.rb
|
@ -23,6 +23,7 @@ require 'net/ldap/pdu'
|
||||||
require 'net/ldap/filter'
|
require 'net/ldap/filter'
|
||||||
require 'net/ldap/dataset'
|
require 'net/ldap/dataset'
|
||||||
require 'net/ldap/psw'
|
require 'net/ldap/psw'
|
||||||
|
require 'net/ldap/entry'
|
||||||
|
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
@ -372,17 +373,17 @@ module Net
|
||||||
# Note that in the standalone case, we're permitting the caller
|
# Note that in the standalone case, we're permitting the caller
|
||||||
# to modify the auth parms.
|
# to modify the auth parms.
|
||||||
#
|
#
|
||||||
def search args
|
def searchx args
|
||||||
if @open_connection
|
if @open_connection
|
||||||
@result = @open_connection.search( args ) {|values|
|
@result = @open_connection.searchx( args ) {|values|
|
||||||
block_given? and yield( values )
|
yield( values ) if block_given?
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@result = 0
|
@result = 0
|
||||||
conn = Connection.new( :host => @host, :port => @port )
|
conn = Connection.new( :host => @host, :port => @port )
|
||||||
if (@result = conn.bind( args[:auth] || @auth )) == 0
|
if (@result = conn.bind( args[:auth] || @auth )) == 0
|
||||||
@result = conn.search( args ) {|values|
|
@result = conn.searchx( args ) {|values|
|
||||||
block_given? and yield( values )
|
yield( values ) if block_given?
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
conn.close
|
conn.close
|
||||||
|
@ -391,6 +392,49 @@ module Net
|
||||||
@result == 0
|
@result == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# This is a re-implementation of search that replaces the
|
||||||
|
# original one (now renamed searchx and possibly destined to go away).
|
||||||
|
# The difference is that we return a dataset (or nil) from the
|
||||||
|
# call, and pass _each entry_ as it is received from the server
|
||||||
|
# to the caler-supplied block. This will probably make things
|
||||||
|
# far faster as we can do useful work during the network latency
|
||||||
|
# of the search. The downside is that we have no access to the
|
||||||
|
# whole set while processing the blocks, so we can't do stuff
|
||||||
|
# like sort the DNs until after the call completes.
|
||||||
|
# It's also possible that this interacts badly with server timeouts.
|
||||||
|
# We'll have to ensure that something reasonable happens if
|
||||||
|
# the caller has processed half a result set when we throw a timeout
|
||||||
|
# error.
|
||||||
|
# Another important difference is that we return a result set from
|
||||||
|
# this method rather than a T/F indication.
|
||||||
|
# Since this can be very heavy-weight, we define an argument flag
|
||||||
|
# that the caller can set to suppress the return of a result set,
|
||||||
|
# if he's planning to process every entry as it comes from the server.
|
||||||
|
#
|
||||||
|
def search args
|
||||||
|
result_set = (args and args[:return_result] == false) ? nil : {}
|
||||||
|
|
||||||
|
if @open_connection
|
||||||
|
@result = @open_connection.search( args ) {|entry|
|
||||||
|
result_set[entry.dn] = entry if result_set
|
||||||
|
yield( entry ) if block_given?
|
||||||
|
}
|
||||||
|
else
|
||||||
|
@result = 0
|
||||||
|
conn = Connection.new( :host => @host, :port => @port )
|
||||||
|
if (@result = conn.bind( args[:auth] || @auth )) == 0
|
||||||
|
@result = conn.search( args ) {|entry|
|
||||||
|
result_set[entry.dn] = entry if result_set
|
||||||
|
yield( entry ) if block_given?
|
||||||
|
}
|
||||||
|
end
|
||||||
|
conn.close
|
||||||
|
end
|
||||||
|
|
||||||
|
@result == 0 and result_set
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# bind
|
# bind
|
||||||
# Bind and unbind.
|
# Bind and unbind.
|
||||||
|
@ -505,7 +549,7 @@ module Net
|
||||||
raise LdapError.new( "no connection to server" )
|
raise LdapError.new( "no connection to server" )
|
||||||
end
|
end
|
||||||
|
|
||||||
block_given? and yield self
|
yield self if block_given?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -552,15 +596,66 @@ module Net
|
||||||
|
|
||||||
#
|
#
|
||||||
# search
|
# search
|
||||||
|
# Alternate implementation, this yields each search entry to the caller
|
||||||
|
# as it are received.
|
||||||
# TODO, certain search parameters are hardcoded.
|
# TODO, certain search parameters are hardcoded.
|
||||||
# TODO, if we mis-parse the server results or the results are wrong, we can block
|
# TODO, if we mis-parse the server results or the results are wrong, we can block
|
||||||
# forever. That's because we keep reading results until we get a type-5 packet,
|
# forever. That's because we keep reading results until we get a type-5 packet,
|
||||||
# which might never come. We need to support the time-limit in the protocol.
|
# which might never come. We need to support the time-limit in the protocol.
|
||||||
|
#--
|
||||||
|
# WARNING: this code substantially recapitulates the searchx method.
|
||||||
#
|
#
|
||||||
def search args
|
def search args
|
||||||
search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" )
|
search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" )
|
||||||
search_base = (args && args[:base]) || "dc=example,dc=com"
|
search_base = (args && args[:base]) || "dc=example,dc=com"
|
||||||
search_attributes = ((args && args[:attributes]) || []).map {|attr| attr.to_s.to_ber}
|
search_attributes = ((args && args[:attributes]) || []).map {|attr| attr.to_s.to_ber}
|
||||||
|
|
||||||
|
request = [
|
||||||
|
search_base.to_ber,
|
||||||
|
2.to_ber_enumerated,
|
||||||
|
0.to_ber_enumerated,
|
||||||
|
0.to_ber,
|
||||||
|
0.to_ber,
|
||||||
|
false.to_ber,
|
||||||
|
search_filter.to_ber,
|
||||||
|
search_attributes.to_ber_sequence
|
||||||
|
].to_ber_appsequence(3)
|
||||||
|
pkt = [next_msgid.to_ber, request].to_ber_sequence
|
||||||
|
@conn.write pkt
|
||||||
|
|
||||||
|
result_code = 0
|
||||||
|
|
||||||
|
while (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be ))
|
||||||
|
case pdu.app_tag
|
||||||
|
when 4 # search-data
|
||||||
|
yield( pdu.search_entry ) if block_given?
|
||||||
|
when 5 # search-result
|
||||||
|
result_code = pdu.result_code
|
||||||
|
break
|
||||||
|
else
|
||||||
|
raise LdapError.new( "invalid response-type in search: #{pdu.app_tag}" )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result_code
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# searchx
|
||||||
|
# Original implementation, this doesn't return until all data have been
|
||||||
|
# received from the server.
|
||||||
|
# TODO, certain search parameters are hardcoded.
|
||||||
|
# TODO, if we mis-parse the server results or the results are wrong, we can block
|
||||||
|
# forever. That's because we keep reading results until we get a type-5 packet,
|
||||||
|
# which might never come. We need to support the time-limit in the protocol.
|
||||||
|
#--
|
||||||
|
# WARNING: this code substantially recapitulates the search method.
|
||||||
|
#
|
||||||
|
def searchx args
|
||||||
|
search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" )
|
||||||
|
search_base = (args && args[:base]) || "dc=example,dc=com"
|
||||||
|
search_attributes = ((args && args[:attributes]) || []).map {|attr| attr.to_s.to_ber}
|
||||||
request = [
|
request = [
|
||||||
search_base.to_ber,
|
search_base.to_ber,
|
||||||
2.to_ber_enumerated,
|
2.to_ber_enumerated,
|
||||||
|
@ -665,13 +760,3 @@ module Net
|
||||||
end # module Net
|
end # module Net
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
if __FILE__ == $0
|
|
||||||
puts "No default action"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
71
lib/net/ldap/entry.rb
Normal file
71
lib/net/ldap/entry.rb
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# $Id$
|
||||||
|
#
|
||||||
|
# LDAP Entry (search-result) support classes
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Gmail: garbagecat10
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
#
|
||||||
|
#---------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
|
||||||
|
|
||||||
|
class Entry
|
||||||
|
|
||||||
|
def initialize dn = nil
|
||||||
|
@myhash = Hash.new {|k,v| k[v] = [] }
|
||||||
|
self[:dn] = [dn]
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def []= name, value
|
||||||
|
sym = name.to_s.downcase.intern
|
||||||
|
@myhash[sym] = value
|
||||||
|
end
|
||||||
|
|
||||||
|
def [] name
|
||||||
|
unless name.is_a?(Symbol)
|
||||||
|
name = name.to_s.downcase.intern
|
||||||
|
end
|
||||||
|
@myhash[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def dn
|
||||||
|
self[:dn].shift
|
||||||
|
end
|
||||||
|
|
||||||
|
def attribute_names
|
||||||
|
@myhash.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end # class Entry
|
||||||
|
|
||||||
|
|
||||||
|
end # class LDAP
|
||||||
|
end # module Net
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ class LdapPdu
|
||||||
ModifyRDNResponse = 13
|
ModifyRDNResponse = 13
|
||||||
|
|
||||||
attr_reader :msg_id, :app_tag
|
attr_reader :msg_id, :app_tag
|
||||||
attr_reader :search_dn, :search_attributes
|
attr_reader :search_dn, :search_attributes, :search_entry
|
||||||
|
|
||||||
#
|
#
|
||||||
# initialize
|
# initialize
|
||||||
|
@ -129,12 +129,17 @@ class LdapPdu
|
||||||
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
# NOW OBSERVE CAREFULLY: WE ARE DOWNCASING THE RETURNED ATTRIBUTE NAMES.
|
||||||
# This is to make them more predictable for user programs, but it
|
# This is to make them more predictable for user programs, but it
|
||||||
# may not be a good idea. Maybe this should be configurable.
|
# may not be a good idea. Maybe this should be configurable.
|
||||||
|
# ALTERNATE IMPLEMENTATION: In addition to @search_dn and @search_attributes,
|
||||||
|
# we also return @search_entry, which is an LDAP::Entry object.
|
||||||
|
# If that works out well, then we'll remove the first two.
|
||||||
#
|
#
|
||||||
def parse_search_return sequence
|
def parse_search_return sequence
|
||||||
sequence.length >= 2 or raise LdapPduError
|
sequence.length >= 2 or raise LdapPduError
|
||||||
|
@search_entry = LDAP::Entry.new( sequence[0] )
|
||||||
@search_dn = sequence[0]
|
@search_dn = sequence[0]
|
||||||
@search_attributes = {}
|
@search_attributes = {}
|
||||||
sequence[1].each {|seq|
|
sequence[1].each {|seq|
|
||||||
|
@search_entry[seq[0]] = seq[1]
|
||||||
@search_attributes[seq[0].downcase.intern] = seq[1]
|
@search_attributes[seq[0].downcase.intern] = seq[1]
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue