Merge branch 'ldap-cleanup', remote branch 'halostatue/ldap-cleanup' into ldap-cleanup
This commit is contained in:
commit
896eaa533c
|
@ -629,7 +629,7 @@ class Net::LDAP
|
|||
if @open_connection
|
||||
@result = @open_connection.search(args) { |entry|
|
||||
result_set << entry if result_set
|
||||
yield(entry) if block_given?
|
||||
yield entry if block_given?
|
||||
}
|
||||
else
|
||||
@result = 0
|
||||
|
@ -639,7 +639,7 @@ class Net::LDAP
|
|||
if (@result = conn.bind(args[:auth] || @auth)) == 0
|
||||
@result = conn.search(args) { |entry|
|
||||
result_set << entry if result_set
|
||||
yield(entry) if block_given?
|
||||
yield entry if block_given?
|
||||
}
|
||||
end
|
||||
ensure
|
||||
|
@ -1041,7 +1041,7 @@ class Net::LDAP
|
|||
:attributes => [ :namingContexts, :supportedLdapVersion,
|
||||
:altServer, :supportedControl, :supportedExtension,
|
||||
:supportedFeatures, :supportedSASLMechanisms])
|
||||
(rs and rs.first) or Entry.new
|
||||
(rs and rs.first) or Net::LDAP::Entry.new
|
||||
end
|
||||
|
||||
# Return the root Subschema record from the LDAP server as a
|
||||
|
@ -1072,16 +1072,16 @@ class Net::LDAP
|
|||
rs = search(:ignore_server_caps => true, :base => "",
|
||||
:scope => SearchScope_BaseObject,
|
||||
:attributes => [:subschemaSubentry])
|
||||
return Entry.new unless (rs and rs.first)
|
||||
return Net::LDAP::Entry.new unless (rs and rs.first)
|
||||
|
||||
subschema_name = rs.first.subschemasubentry
|
||||
return Entry.new unless (subschema_name and subschema_name.first)
|
||||
return Net::LDAP::Entry.new unless (subschema_name and subschema_name.first)
|
||||
|
||||
rs = search(:ignore_server_caps => true, :base => subschema_name.first,
|
||||
:scope => SearchScope_BaseObject,
|
||||
:filter => "objectclass=subschema",
|
||||
:attributes => [:objectclasses, :attributetypes])
|
||||
(rs and rs.first) or Entry.new
|
||||
(rs and rs.first) or Net::LDAP::Entry.new
|
||||
end
|
||||
|
||||
#--
|
||||
|
@ -1405,7 +1405,7 @@ class Net::LDAP::Connection #:nodoc:
|
|||
case pdu.app_tag
|
||||
when 4 # search-data
|
||||
n_results += 1
|
||||
yield(pdu.search_entry) if block_given?
|
||||
yield pdu.search_entry if block_given?
|
||||
when 19 # search-referral
|
||||
if return_referrals
|
||||
if block_given?
|
||||
|
|
|
@ -20,80 +20,155 @@
|
|||
#
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
module Net
|
||||
class LDAP
|
||||
class Dataset < Hash
|
||||
attr_reader :comments
|
||||
##
|
||||
# An LDAP Dataset. Used primarily as an intermediate format for converting
|
||||
# to and from LDIF strings and Net::LDAP::Entry objects.
|
||||
class Net::LDAP::Dataset < Hash
|
||||
##
|
||||
# Dataset object comments.
|
||||
attr_reader :comments
|
||||
|
||||
class IOFilter
|
||||
def initialize(io)
|
||||
@io = io
|
||||
end
|
||||
def gets
|
||||
s = @io.gets
|
||||
s.chomp if s
|
||||
end
|
||||
class << self
|
||||
class ChompedIO #:nodoc:
|
||||
def initialize(io)
|
||||
@io = io
|
||||
end
|
||||
|
||||
def self.read_ldif io
|
||||
ds = Dataset.new
|
||||
io = IOFilter.new(io)
|
||||
|
||||
line = io.gets
|
||||
dn = nil
|
||||
|
||||
while line
|
||||
new_line = io.gets
|
||||
if new_line =~ /^[\s]+/
|
||||
line << " " << $'
|
||||
else
|
||||
nextline = new_line
|
||||
|
||||
if line =~ /^\#/
|
||||
ds.comments << line
|
||||
elsif line =~ /^dn:[\s]*/i
|
||||
dn = $'
|
||||
ds[dn] = Hash.new {|k,v| k[v] = []}
|
||||
elsif line.length == 0
|
||||
dn = nil
|
||||
elsif line =~ /^([^:]+):([\:]?)[\s]*/
|
||||
# $1 is the attribute name
|
||||
# $2 is a colon iff the attr-value is base-64 encoded
|
||||
# $' is the attr-value
|
||||
# Avoid the Base64 class because not all Ruby versions have it.
|
||||
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
|
||||
ds[dn][$1.downcase.intern] << attrvalue
|
||||
end
|
||||
|
||||
line = nextline
|
||||
end
|
||||
end
|
||||
|
||||
ds
|
||||
def gets
|
||||
s = @io.gets
|
||||
s.chomp if s
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads an object that returns data line-wise (using #gets) and parses
|
||||
# LDIF data into a Dataset object.
|
||||
def read_ldif(io) #:yields: entry-type, value Used mostly for debugging.
|
||||
ds = Net::LDAP::Dataset.new
|
||||
io = ChompedIO.new(io)
|
||||
|
||||
def initialize
|
||||
@comments = []
|
||||
end
|
||||
line = io.gets
|
||||
dn = nil
|
||||
|
||||
while line
|
||||
new_line = io.gets
|
||||
|
||||
def to_ldif
|
||||
ary = []
|
||||
ary += (@comments || [])
|
||||
keys.sort.each do |dn|
|
||||
ary << "dn: #{dn}"
|
||||
if new_line =~ /^[\s]+/
|
||||
line << " " << $'
|
||||
else
|
||||
nextline = new_line
|
||||
|
||||
self[dn].keys.map {|sym| sym.to_s}.sort.each do |attr|
|
||||
self[dn][attr.intern].each {|val| ary << "#{attr}: #{val}" }
|
||||
if line =~ /^#/
|
||||
ds.comments << line
|
||||
yield :comment, line if block_given?
|
||||
elsif line =~ /^dn:[\s]*/i
|
||||
dn = $'
|
||||
ds[dn] = Hash.new { |k,v| k[v] = [] }
|
||||
yield :dn, dn if block_given?
|
||||
elsif line.empty?
|
||||
dn = nil
|
||||
yield :end, nil if block_given?
|
||||
elsif line =~ /^([^:]+):([\:]?)[\s]*/
|
||||
# $1 is the attribute name
|
||||
# $2 is a colon iff the attr-value is base-64 encoded
|
||||
# $' is the attr-value
|
||||
# Avoid the Base64 class because not all Ruby versions have it.
|
||||
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
|
||||
ds[dn][$1.downcase.to_sym] << attrvalue
|
||||
yield :attr, [$1.downcase.to_sym, attrvalue] if block_given?
|
||||
end
|
||||
|
||||
ary << ""
|
||||
line = nextline
|
||||
end
|
||||
block_given? and ary.each {|line| yield line}
|
||||
ary
|
||||
end
|
||||
|
||||
ds
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a Dataset object from an Entry object. Used mostly to assist
|
||||
# with the conversion of
|
||||
def from_entry(entry)
|
||||
dataset = Net::LDAP::Dataset.new
|
||||
hash = { }
|
||||
entry.each_attribute do |attribute, value|
|
||||
next if attribute == :dn
|
||||
hash[attribute] = value
|
||||
end
|
||||
dataset[entry.dn] = hash
|
||||
dataset
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*args, &block) #:nodoc:
|
||||
super
|
||||
@comments = []
|
||||
end
|
||||
|
||||
##
|
||||
# Outputs an LDAP Dataset as an array of strings representing LDIF
|
||||
# entries.
|
||||
def to_ldif
|
||||
ary = []
|
||||
ary += @comments unless @comments.empty?
|
||||
keys.sort.each do |dn|
|
||||
ary << "dn: #{dn}"
|
||||
|
||||
attributes = self[dn].keys.map { |attr| attr.to_s }.sort
|
||||
attributes.each do |attr|
|
||||
self[dn][attr.to_sym].each do |value|
|
||||
if attr == "userpassword" or value_is_binary?(value)
|
||||
value = [value].pack("m").chomp.gsub(/\n/m, "\n ")
|
||||
ary << "#{attr}:: #{value}"
|
||||
else
|
||||
ary << "#{attr}: #{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ary << ""
|
||||
end
|
||||
block_given? and ary.each { |line| yield line}
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
##
|
||||
# Outputs an LDAP Dataset as an LDIF string.
|
||||
def to_ldif_string
|
||||
to_ldif.join("\n")
|
||||
end
|
||||
|
||||
##
|
||||
# Convert the parsed LDIF objects to Net::LDAP::Entry objects.
|
||||
def to_entries
|
||||
ary = []
|
||||
keys.each do |dn|
|
||||
entry = Net::LDAP::Entry.new(dn)
|
||||
self[dn].each do |attr, value|
|
||||
entry[attr] = value
|
||||
end
|
||||
ary << entry
|
||||
end
|
||||
ary
|
||||
end
|
||||
|
||||
# This is an internal convenience method to determine if a value requires
|
||||
# base64-encoding before conversion to LDIF output. The standard approach
|
||||
# in most LDAP tools is to check whether the value is a password, or if
|
||||
# the first or last bytes are non-printable. Microsoft Active Directory,
|
||||
# on the other hand, sometimes sends values that are binary in the middle.
|
||||
#
|
||||
# In the worst cases, this could be a nasty performance killer, which is
|
||||
# why we handle the simplest cases first. Ideally, we would also test the
|
||||
# first/last byte, but it's a bit harder to do this in a way that's
|
||||
# compatible with both 1.8.6 and 1.8.7.
|
||||
def value_is_binary?(value)
|
||||
value = value.to_s
|
||||
return true if value[0] == ?: or value[0] == ?<
|
||||
value.each_byte { |byte| return true if (byte < 32) || (byte > 126) }
|
||||
false
|
||||
end
|
||||
private :value_is_binary?
|
||||
end
|
||||
|
||||
require 'net/ldap/entry' unless defined? Net::LDAP::Entry
|
||||
|
|
|
@ -21,246 +21,188 @@
|
|||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
#
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
##
|
||||
# Objects of this class represent individual entries in an LDAP directory.
|
||||
# User code generally does not instantiate this class. Net::LDAP#search
|
||||
# provides objects of this class to user code, either as block parameters or
|
||||
# as return values.
|
||||
#
|
||||
# In LDAP-land, an "entry" is a collection of attributes that are uniquely
|
||||
# and globally identified by a DN ("Distinguished Name"). Attributes are
|
||||
# identified by short, descriptive words or phrases. Although a directory is
|
||||
# free to implement any attribute name, most of them follow rigorous
|
||||
# standards so that the range of commonly-encountered attribute names is not
|
||||
# large.
|
||||
#
|
||||
# An attribute name is case-insensitive. Most directories also restrict the
|
||||
# range of characters allowed in attribute names. To simplify handling
|
||||
# attribute names, Net::LDAP::Entry internally converts them to a standard
|
||||
# format. Therefore, the methods which take attribute names can take Strings
|
||||
# or Symbols, and work correctly regardless of case or capitalization.
|
||||
#
|
||||
# An attribute consists of zero or more data items called <i>values.</i> An
|
||||
# entry is the combination of a unique DN, a set of attribute names, and a
|
||||
# (possibly-empty) array of values for each attribute.
|
||||
#
|
||||
# Class Net::LDAP::Entry provides convenience methods for dealing with LDAP
|
||||
# entries. In addition to the methods documented below, you may access
|
||||
# individual attributes of an entry simply by giving the attribute name as
|
||||
# the name of a method call. For example:
|
||||
#
|
||||
# ldap.search( ... ) do |entry|
|
||||
# puts "Common name: #{entry.cn}"
|
||||
# puts "Email addresses:"
|
||||
# entry.mail.each {|ma| puts ma}
|
||||
# end
|
||||
#
|
||||
# If you use this technique to access an attribute that is not present in a
|
||||
# particular Entry object, a NoMethodError exception will be raised.
|
||||
#
|
||||
#--
|
||||
# Ugly problem to fix someday: We key off the internal hash with a canonical
|
||||
# form of the attribute name: convert to a string, downcase, then take the
|
||||
# symbol. Unfortunately we do this in at least three places. Should do it in
|
||||
# ONE place.
|
||||
class Net::LDAP::Entry
|
||||
##
|
||||
# This constructor is not generally called by user code.
|
||||
def initialize(dn = nil) #:nodoc:
|
||||
@myhash = {}
|
||||
@myhash[:dn] = [dn]
|
||||
end
|
||||
|
||||
module Net
|
||||
class LDAP
|
||||
##
|
||||
# Use the LDIF format for Marshal serialization.
|
||||
def _dump(depth) #:nodoc:
|
||||
to_ldif
|
||||
end
|
||||
|
||||
##
|
||||
# Use the LDIF format for Marshal serialization.
|
||||
def self._load(entry) #:nodoc:
|
||||
from_single_ldif_string(entry)
|
||||
end
|
||||
|
||||
# Objects of this class represent individual entries in an LDAP directory.
|
||||
# User code generally does not instantiate this class. Net::LDAP#search
|
||||
# provides objects of this class to user code, either as block parameters or
|
||||
# as return values.
|
||||
class << self
|
||||
##
|
||||
# Converts a single LDIF entry string into an Entry object. Useful for
|
||||
# Marshal serialization. If a string with multiple LDIF entries is
|
||||
# provided, an exception will be raised.
|
||||
def from_single_ldif_string(ldif)
|
||||
ds = Net::LDAP::Dataset.read_ldif(::StringIO.new(ldif))
|
||||
|
||||
return nil if ds.empty?
|
||||
|
||||
raise Net::LDAP::LdapError, "Too many LDIF entries" unless ds.size == 1
|
||||
|
||||
entry = ds.to_entries.first
|
||||
|
||||
return nil if entry.dn.nil?
|
||||
entry
|
||||
end
|
||||
|
||||
##
|
||||
# Canonicalizes an LDAP attribute name as a \Symbol. The name is
|
||||
# lowercased and, if present, a trailing equals sign is removed.
|
||||
def attribute_name(name)
|
||||
name = name.to_s.downcase
|
||||
name = name[0..-2] if name[-1] == ?=
|
||||
name.to_sym
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Sets or replaces the array of values for the provided attribute. The
|
||||
# attribute name is canonicalized prior to assignment.
|
||||
#
|
||||
# In LDAP-land, an "entry" is a collection of attributes that are uniquely
|
||||
# and globally identified by a DN ("Distinguished Name"). Attributes are
|
||||
# identified by short, descriptive words or phrases. Although a directory is
|
||||
# free to implement any attribute name, most of them follow rigorous
|
||||
# standards so that the range of commonly-encountered attribute names is not
|
||||
# large.
|
||||
# When an attribute is set using this, that attribute is now made
|
||||
# accessible through methods as well.
|
||||
#
|
||||
# An attribute name is case-insensitive. Most directories also restrict the
|
||||
# range of characters allowed in attribute names. To simplify handling
|
||||
# attribute names, Net::LDAP::Entry internally converts them to a standard
|
||||
# format. Therefore, the methods which take attribute names can take Strings
|
||||
# or Symbols, and work correctly regardless of case or capitalization.
|
||||
# entry = Net::LDAP::Entry.new("dc=com")
|
||||
# entry.foo # => NoMethodError
|
||||
# entry["foo"] = 12345 # => [12345]
|
||||
# entry.foo # => [12345]
|
||||
def []=(name, value)
|
||||
@myhash[self.class.attribute_name(name)] = Kernel::Array(value)
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the array of values for the provided attribute. The attribute name
|
||||
# is canonicalized prior to reading. Returns an empty array if the
|
||||
# attribute does not exist.
|
||||
def [](name)
|
||||
name = self.class.attribute_name(name)
|
||||
@myhash[name] || []
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the first distinguished name (dn) of the Entry as a \String.
|
||||
def dn
|
||||
self[:dn].first.to_s
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an array of the attribute names present in the Entry.
|
||||
def attribute_names
|
||||
@myhash.keys
|
||||
end
|
||||
|
||||
##
|
||||
# Accesses each of the attributes present in the Entry.
|
||||
#
|
||||
# An attribute consists of zero or more data items called <i>values.</i> An
|
||||
# entry is the combination of a unique DN, a set of attribute names, and a
|
||||
# (possibly-empty) array of values for each attribute.
|
||||
#
|
||||
# Class Net::LDAP::Entry provides convenience methods for dealing with LDAP
|
||||
# entries. In addition to the methods documented below, you may access
|
||||
# individual attributes of an entry simply by giving the attribute name as
|
||||
# the name of a method call. For example:
|
||||
#
|
||||
# ldap.search( ... ) do |entry|
|
||||
# puts "Common name: #{entry.cn}"
|
||||
# puts "Email addresses:"
|
||||
# entry.mail.each {|ma| puts ma}
|
||||
# end
|
||||
#
|
||||
# If you use this technique to access an attribute that is not present in a
|
||||
# particular Entry object, a NoMethodError exception will be raised.
|
||||
#
|
||||
#--
|
||||
# Ugly problem to fix someday: We key off the internal hash with a canonical
|
||||
# form of the attribute name: convert to a string, downcase, then take the
|
||||
# symbol. Unfortunately we do this in at least three places. Should do it in
|
||||
# ONE place.
|
||||
#
|
||||
class Entry
|
||||
# This constructor is not generally called by user code.
|
||||
#
|
||||
def initialize dn = nil # :nodoc:
|
||||
@myhash = {}
|
||||
@myhash[:dn] = [dn]
|
||||
end
|
||||
|
||||
def _dump depth
|
||||
to_ldif
|
||||
end
|
||||
|
||||
class << self
|
||||
def _load entry
|
||||
from_single_ldif_string entry
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# Discovered bug, 26Aug06: I noticed that we're not converting the
|
||||
# incoming value to an array if it isn't already one.
|
||||
def []=(name, value) # :nodoc:
|
||||
sym = attribute_name(name)
|
||||
value = [value] unless value.is_a?(Array)
|
||||
@myhash[sym] = value
|
||||
end
|
||||
|
||||
#--
|
||||
# We have to deal with this one as we do with []= because this one and not
|
||||
# the other one gets called in formulations like entry["CN"] << cn.
|
||||
#
|
||||
def [](name) # :nodoc:
|
||||
name = attribute_name(name) unless name.is_a?(Symbol)
|
||||
@myhash[name] || []
|
||||
end
|
||||
|
||||
# Returns the dn of the Entry as a String.
|
||||
def dn
|
||||
self[:dn][0].to_s
|
||||
end
|
||||
|
||||
# Returns an array of the attribute names present in the Entry.
|
||||
def attribute_names
|
||||
@myhash.keys
|
||||
end
|
||||
|
||||
# Accesses each of the attributes present in the Entry.
|
||||
# Calls a user-supplied block with each attribute in turn,
|
||||
# passing two arguments to the block: a Symbol giving
|
||||
# the name of the attribute, and a (possibly empty)
|
||||
# Array of data values.
|
||||
#
|
||||
def each
|
||||
if block_given?
|
||||
attribute_names.each {|a|
|
||||
attr_name,values = a,self[a]
|
||||
yield attr_name, values
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :each_attribute, :each
|
||||
|
||||
# Converts the Entry to a String, representing the
|
||||
# Entry's attributes in LDIF format.
|
||||
#--
|
||||
def to_ldif
|
||||
ary = []
|
||||
ary << "dn: #{dn}\n"
|
||||
v2 = "" # temp value, save on GC
|
||||
each_attribute do |k,v|
|
||||
unless k == :dn
|
||||
v.each {|v1|
|
||||
v2 = if (k == :userpassword) || is_attribute_value_binary?(v1)
|
||||
": #{Base64.encode64(v1).chomp.gsub(/\n/m,"\n ")}"
|
||||
else
|
||||
" #{v1}"
|
||||
end
|
||||
ary << "#{k}:#{v2}\n"
|
||||
}
|
||||
end
|
||||
end
|
||||
ary << "\n"
|
||||
ary.join
|
||||
end
|
||||
|
||||
#--
|
||||
# TODO, doesn't support broken lines.
|
||||
# It generates a SINGLE Entry object from an incoming LDIF stream which is
|
||||
# of course useless for big LDIF streams that encode many objects.
|
||||
#
|
||||
# DO NOT DOCUMENT THIS METHOD UNTIL THESE RESTRICTIONS ARE LIFTED.
|
||||
#
|
||||
# As it is, it's useful for unmarshalling objects that we create, but not
|
||||
# for reading arbitrary LDIF files. Eventually, we should have a class
|
||||
# method that parses large LDIF streams into individual LDIF blocks
|
||||
# (delimited by blank lines) and passes them here.
|
||||
#
|
||||
class << self
|
||||
def from_single_ldif_string ldif
|
||||
entry = Entry.new
|
||||
entry[:dn] = []
|
||||
ldif.split(/\r?\n/m).each {|line|
|
||||
break if line.length == 0
|
||||
if line =~ /\A([\w]+):(:?)[\s]*/
|
||||
entry[$1] <<= if $2 == ':'
|
||||
Base64.decode64($')
|
||||
else
|
||||
$'
|
||||
end
|
||||
end
|
||||
}
|
||||
entry.dn ? entry : nil
|
||||
end
|
||||
end
|
||||
|
||||
#--
|
||||
# Part of the support for getter and setter style access to attributes.
|
||||
#
|
||||
def respond_to?(sym)
|
||||
name = attribute_name(sym)
|
||||
return true if valid_attribute?(name)
|
||||
return super
|
||||
end
|
||||
|
||||
#--
|
||||
# Supports getter and setter style access for all the attributes that this
|
||||
# entry holds.
|
||||
#
|
||||
def method_missing sym, *args, &block # :nodoc:
|
||||
name = attribute_name(sym)
|
||||
|
||||
if valid_attribute? name
|
||||
if setter?(sym) && args.size == 1
|
||||
value = args.first
|
||||
value = [value] unless value.instance_of?(Array)
|
||||
self[name]= value
|
||||
|
||||
return value
|
||||
elsif args.empty?
|
||||
return self[name]
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def write
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
#--
|
||||
# Internal convenience method. It seems like the standard
|
||||
# approach in most LDAP tools to base64 encode an attribute
|
||||
# value if its first or last byte is nonprintable, or if
|
||||
# it's a password. But that turns out to be not nearly good
|
||||
# enough. There are plenty of A/D attributes that are binary
|
||||
# in the middle. This is probably a nasty performance killer.
|
||||
def is_attribute_value_binary? value
|
||||
v = value.to_s
|
||||
v.each_byte {|byt|
|
||||
return true if (byt < 32) || (byt > 126)
|
||||
# Calls a user-supplied block with each attribute in turn, passing two
|
||||
# arguments to the block: a Symbol giving the name of the attribute, and a
|
||||
# (possibly empty) \Array of data values.
|
||||
def each # :yields: attribute-name, data-values-array
|
||||
if block_given?
|
||||
attribute_names.each {|a|
|
||||
attr_name,values = a,self[a]
|
||||
yield attr_name, values
|
||||
}
|
||||
if v[0..0] == ':' or v[0..0] == '<'
|
||||
return true
|
||||
end
|
||||
end
|
||||
alias_method :each_attribute, :each
|
||||
|
||||
##
|
||||
# Converts the Entry to an LDIF-formatted String
|
||||
def to_ldif
|
||||
Net::LDAP::Dataset.from_entry(self).to_ldif_string
|
||||
end
|
||||
|
||||
def respond_to?(sym) #:nodoc:
|
||||
return true if valid_attribute?(self.class.attribute_name(sym))
|
||||
return super
|
||||
end
|
||||
|
||||
def method_missing(sym, *args, &block) #:nodoc:
|
||||
name = self.class.attribute_name(sym)
|
||||
|
||||
if valid_attribute?(name )
|
||||
if setter?(sym) && args.size == 1
|
||||
value = args.first
|
||||
value = Array(value)
|
||||
self[name]= value
|
||||
return value
|
||||
elsif args.empty?
|
||||
return self[name]
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# Returns the symbol that can be used to access the attribute that
|
||||
# sym_or_str designates.
|
||||
#
|
||||
def attribute_name(sym_or_str)
|
||||
str = sym_or_str.to_s.downcase
|
||||
super
|
||||
end
|
||||
|
||||
# Does str match 'something='? Still only returns :something
|
||||
return str[0...-1].to_sym if str.size>1 && str[-1] == ?=
|
||||
return str.to_sym
|
||||
end
|
||||
# Given a valid attribute symbol, returns true.
|
||||
def valid_attribute?(attr_name)
|
||||
attribute_names.include?(attr_name)
|
||||
end
|
||||
private :valid_attribute?
|
||||
|
||||
# Given a valid attribute symbol, returns true.
|
||||
#
|
||||
def valid_attribute?(attr_name)
|
||||
attribute_names.include?(attr_name)
|
||||
end
|
||||
# Returns true if the symbol ends with an equal sign.
|
||||
def setter?(sym)
|
||||
sym.to_s[-1] == ?=
|
||||
end
|
||||
private :setter?
|
||||
end # class Entry
|
||||
|
||||
def setter?(sym)
|
||||
sym.to_s[-1] == ?=
|
||||
end
|
||||
end # class Entry
|
||||
|
||||
|
||||
end # class LDAP
|
||||
end # module Net
|
||||
require 'net/ldap/dataset' unless defined? Net::LDAP::Dataset
|
||||
|
|
|
@ -7,53 +7,60 @@ require 'digest/sha1'
|
|||
require 'base64'
|
||||
|
||||
class TestLdif < Test::Unit::TestCase
|
||||
|
||||
TestLdifFilename = "#{File.dirname(__FILE__)}/testdata.ldif"
|
||||
|
||||
def test_empty_ldif
|
||||
ds = Net::LDAP::Dataset.read_ldif( StringIO.new )
|
||||
assert_equal( true, ds.empty? )
|
||||
ds = Net::LDAP::Dataset.read_ldif(StringIO.new)
|
||||
assert_equal(true, ds.empty?)
|
||||
end
|
||||
|
||||
def test_ldif_with_comments
|
||||
str = ["# Hello from LDIF-land", "# This is an unterminated comment"]
|
||||
io = StringIO.new( str[0] + "\r\n" + str[1] )
|
||||
ds = Net::LDAP::Dataset::read_ldif( io )
|
||||
assert_equal( str, ds.comments )
|
||||
io = StringIO.new(str[0] + "\r\n" + str[1])
|
||||
ds = Net::LDAP::Dataset::read_ldif(io)
|
||||
assert_equal(str, ds.comments)
|
||||
end
|
||||
|
||||
def test_ldif_with_password
|
||||
psw = "goldbricks"
|
||||
hashed_psw = "{SHA}" + Base64::encode64(Digest::SHA1.digest(psw)).chomp
|
||||
|
||||
ldif_encoded = Base64::encode64( hashed_psw ).chomp
|
||||
ds = Net::LDAP::Dataset::read_ldif( StringIO.new( "dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n" ))
|
||||
ldif_encoded = Base64::encode64(hashed_psw).chomp
|
||||
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: Goldbrick\r\nuserPassword:: #{ldif_encoded}\r\n\r\n"))
|
||||
recovered_psw = ds["Goldbrick"][:userpassword].shift
|
||||
assert_equal( hashed_psw, recovered_psw )
|
||||
assert_equal(hashed_psw, recovered_psw)
|
||||
end
|
||||
|
||||
def test_ldif_with_continuation_lines
|
||||
ds = Net::LDAP::Dataset::read_ldif( StringIO.new( "dn: abcdefg\r\n hijklmn\r\n\r\n" ))
|
||||
assert_equal( true, ds.has_key?( "abcdefg hijklmn" ))
|
||||
ds = Net::LDAP::Dataset::read_ldif(StringIO.new("dn: abcdefg\r\n hijklmn\r\n\r\n"))
|
||||
assert_equal(true, ds.has_key?("abcdefg hijklmn"))
|
||||
end
|
||||
|
||||
# TODO, INADEQUATE. We need some more tests
|
||||
# to verify the content.
|
||||
def test_ldif
|
||||
File.open( TestLdifFilename, "r" ) {|f|
|
||||
ds = Net::LDAP::Dataset::read_ldif( f )
|
||||
assert_equal( 13, ds.length )
|
||||
File.open(TestLdifFilename, "r") {|f|
|
||||
ds = Net::LDAP::Dataset::read_ldif(f)
|
||||
assert_equal(13, ds.length)
|
||||
}
|
||||
end
|
||||
|
||||
# TODO, need some tests.
|
||||
# Must test folded lines and base64-encoded lines as well as normal ones.
|
||||
#def test_to_ldif
|
||||
# File.open( TestLdifFilename, "r" ) {|f|
|
||||
# ds = Net::LDAP::Dataset::read_ldif( f )
|
||||
# ds.to_ldif
|
||||
# assert_equal( true, false ) # REMOVE WHEN WE HAVE SOME TESTS HERE.
|
||||
# }
|
||||
#end
|
||||
def test_to_ldif
|
||||
data = File.open(TestLdifFilename, "rb") { |f| f.read }
|
||||
io = StringIO.new(data)
|
||||
|
||||
entries = data.grep(/^dn:\s*/) { $'.chomp }
|
||||
dn_entries = entries.dup
|
||||
|
||||
ds = Net::LDAP::Dataset::read_ldif(io) { |type, value|
|
||||
case type
|
||||
when :dn
|
||||
assert_equal(dn_entries.first, value)
|
||||
dn_entries.shift
|
||||
end
|
||||
}
|
||||
assert_equal(entries.size, ds.size)
|
||||
assert_equal(entries.sort, ds.to_ldif.grep(/^dn:\s*/) { $'.chomp })
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue