Skip to content

Commit 896eaa5

Browse files
author
Rory O'Connell
committed
Merge branch 'ldap-cleanup', remote branch 'halostatue/ldap-cleanup' into ldap-cleanup
2 parents 0c8a3c6 + 06ea324 commit 896eaa5

File tree

4 files changed

+344
-320
lines changed

4 files changed

+344
-320
lines changed

lib/net/ldap.rb

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ def search(args = {})
629629
if @open_connection
630630
@result = @open_connection.search(args) { |entry|
631631
result_set << entry if result_set
632-
yield(entry) if block_given?
632+
yield entry if block_given?
633633
}
634634
else
635635
@result = 0
@@ -639,7 +639,7 @@ def search(args = {})
639639
if (@result = conn.bind(args[:auth] || @auth)) == 0
640640
@result = conn.search(args) { |entry|
641641
result_set << entry if result_set
642-
yield(entry) if block_given?
642+
yield entry if block_given?
643643
}
644644
end
645645
ensure
@@ -1041,7 +1041,7 @@ def search_root_dse
10411041
:attributes => [ :namingContexts, :supportedLdapVersion,
10421042
:altServer, :supportedControl, :supportedExtension,
10431043
:supportedFeatures, :supportedSASLMechanisms])
1044-
(rs and rs.first) or Entry.new
1044+
(rs and rs.first) or Net::LDAP::Entry.new
10451045
end
10461046

10471047
# Return the root Subschema record from the LDAP server as a
@@ -1072,16 +1072,16 @@ def search_subschema_entry
10721072
rs = search(:ignore_server_caps => true, :base => "",
10731073
:scope => SearchScope_BaseObject,
10741074
:attributes => [:subschemaSubentry])
1075-
return Entry.new unless (rs and rs.first)
1075+
return Net::LDAP::Entry.new unless (rs and rs.first)
10761076

10771077
subschema_name = rs.first.subschemasubentry
1078-
return Entry.new unless (subschema_name and subschema_name.first)
1078+
return Net::LDAP::Entry.new unless (subschema_name and subschema_name.first)
10791079

10801080
rs = search(:ignore_server_caps => true, :base => subschema_name.first,
10811081
:scope => SearchScope_BaseObject,
10821082
:filter => "objectclass=subschema",
10831083
:attributes => [:objectclasses, :attributetypes])
1084-
(rs and rs.first) or Entry.new
1084+
(rs and rs.first) or Net::LDAP::Entry.new
10851085
end
10861086

10871087
#--
@@ -1405,7 +1405,7 @@ def search(args = {})
14051405
case pdu.app_tag
14061406
when 4 # search-data
14071407
n_results += 1
1408-
yield(pdu.search_entry) if block_given?
1408+
yield pdu.search_entry if block_given?
14091409
when 19 # search-referral
14101410
if return_referrals
14111411
if block_given?

lib/net/ldap/dataset.rb

Lines changed: 134 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,80 +20,155 @@
2020
#
2121
#---------------------------------------------------------------------------
2222

23-
module Net
24-
class LDAP
25-
class Dataset < Hash
26-
attr_reader :comments
27-
28-
class IOFilter
29-
def initialize(io)
30-
@io = io
31-
end
32-
def gets
33-
s = @io.gets
34-
s.chomp if s
35-
end
23+
##
24+
# An LDAP Dataset. Used primarily as an intermediate format for converting
25+
# to and from LDIF strings and Net::LDAP::Entry objects.
26+
class Net::LDAP::Dataset < Hash
27+
##
28+
# Dataset object comments.
29+
attr_reader :comments
30+
31+
class << self
32+
class ChompedIO #:nodoc:
33+
def initialize(io)
34+
@io = io
3635
end
36+
def gets
37+
s = @io.gets
38+
s.chomp if s
39+
end
40+
end
3741

38-
def self.read_ldif io
39-
ds = Dataset.new
40-
io = IOFilter.new(io)
42+
##
43+
# Reads an object that returns data line-wise (using #gets) and parses
44+
# LDIF data into a Dataset object.
45+
def read_ldif(io) #:yields: entry-type, value Used mostly for debugging.
46+
ds = Net::LDAP::Dataset.new
47+
io = ChompedIO.new(io)
4148

42-
line = io.gets
43-
dn = nil
49+
line = io.gets
50+
dn = nil
4451

45-
while line
46-
new_line = io.gets
47-
if new_line =~ /^[\s]+/
48-
line << " " << $'
49-
else
50-
nextline = new_line
51-
52-
if line =~ /^\#/
53-
ds.comments << line
54-
elsif line =~ /^dn:[\s]*/i
55-
dn = $'
56-
ds[dn] = Hash.new {|k,v| k[v] = []}
57-
elsif line.length == 0
58-
dn = nil
59-
elsif line =~ /^([^:]+):([\:]?)[\s]*/
60-
# $1 is the attribute name
61-
# $2 is a colon iff the attr-value is base-64 encoded
62-
# $' is the attr-value
63-
# Avoid the Base64 class because not all Ruby versions have it.
64-
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
65-
ds[dn][$1.downcase.intern] << attrvalue
66-
end
67-
68-
line = nextline
52+
while line
53+
new_line = io.gets
54+
55+
if new_line =~ /^[\s]+/
56+
line << " " << $'
57+
else
58+
nextline = new_line
59+
60+
if line =~ /^#/
61+
ds.comments << line
62+
yield :comment, line if block_given?
63+
elsif line =~ /^dn:[\s]*/i
64+
dn = $'
65+
ds[dn] = Hash.new { |k,v| k[v] = [] }
66+
yield :dn, dn if block_given?
67+
elsif line.empty?
68+
dn = nil
69+
yield :end, nil if block_given?
70+
elsif line =~ /^([^:]+):([\:]?)[\s]*/
71+
# $1 is the attribute name
72+
# $2 is a colon iff the attr-value is base-64 encoded
73+
# $' is the attr-value
74+
# Avoid the Base64 class because not all Ruby versions have it.
75+
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
76+
ds[dn][$1.downcase.to_sym] << attrvalue
77+
yield :attr, [$1.downcase.to_sym, attrvalue] if block_given?
6978
end
70-
end
7179

72-
ds
80+
line = nextline
81+
end
7382
end
7483

84+
ds
85+
end
7586

76-
def initialize
77-
@comments = []
87+
##
88+
# Creates a Dataset object from an Entry object. Used mostly to assist
89+
# with the conversion of
90+
def from_entry(entry)
91+
dataset = Net::LDAP::Dataset.new
92+
hash = { }
93+
entry.each_attribute do |attribute, value|
94+
next if attribute == :dn
95+
hash[attribute] = value
7896
end
97+
dataset[entry.dn] = hash
98+
dataset
99+
end
100+
end
79101

102+
def initialize(*args, &block) #:nodoc:
103+
super
104+
@comments = []
105+
end
80106

81-
def to_ldif
82-
ary = []
83-
ary += (@comments || [])
84-
keys.sort.each do |dn|
85-
ary << "dn: #{dn}"
107+
##
108+
# Outputs an LDAP Dataset as an array of strings representing LDIF
109+
# entries.
110+
def to_ldif
111+
ary = []
112+
ary += @comments unless @comments.empty?
113+
keys.sort.each do |dn|
114+
ary << "dn: #{dn}"
86115

87-
self[dn].keys.map {|sym| sym.to_s}.sort.each do |attr|
88-
self[dn][attr.intern].each {|val| ary << "#{attr}: #{val}" }
116+
attributes = self[dn].keys.map { |attr| attr.to_s }.sort
117+
attributes.each do |attr|
118+
self[dn][attr.to_sym].each do |value|
119+
if attr == "userpassword" or value_is_binary?(value)
120+
value = [value].pack("m").chomp.gsub(/\n/m, "\n ")
121+
ary << "#{attr}:: #{value}"
122+
else
123+
ary << "#{attr}: #{value}"
89124
end
90-
91-
ary << ""
92125
end
93-
block_given? and ary.each {|line| yield line}
94-
ary
95126
end
96127

97-
end
98-
end
128+
ary << ""
129+
end
130+
block_given? and ary.each { |line| yield line}
131+
132+
ary
133+
end
134+
135+
##
136+
# Outputs an LDAP Dataset as an LDIF string.
137+
def to_ldif_string
138+
to_ldif.join("\n")
139+
end
140+
141+
##
142+
# Convert the parsed LDIF objects to Net::LDAP::Entry objects.
143+
def to_entries
144+
ary = []
145+
keys.each do |dn|
146+
entry = Net::LDAP::Entry.new(dn)
147+
self[dn].each do |attr, value|
148+
entry[attr] = value
149+
end
150+
ary << entry
151+
end
152+
ary
153+
end
154+
155+
# This is an internal convenience method to determine if a value requires
156+
# base64-encoding before conversion to LDIF output. The standard approach
157+
# in most LDAP tools is to check whether the value is a password, or if
158+
# the first or last bytes are non-printable. Microsoft Active Directory,
159+
# on the other hand, sometimes sends values that are binary in the middle.
160+
#
161+
# In the worst cases, this could be a nasty performance killer, which is
162+
# why we handle the simplest cases first. Ideally, we would also test the
163+
# first/last byte, but it's a bit harder to do this in a way that's
164+
# compatible with both 1.8.6 and 1.8.7.
165+
def value_is_binary?(value)
166+
value = value.to_s
167+
return true if value[0] == ?: or value[0] == ?<
168+
value.each_byte { |byte| return true if (byte < 32) || (byte > 126) }
169+
false
170+
end
171+
private :value_is_binary?
99172
end
173+
174+
require 'net/ldap/entry' unless defined? Net::LDAP::Entry

0 commit comments

Comments
 (0)