1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
# -*- ruby encoding: utf-8 -*-
##
# 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
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) # :nodoc:
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?
class << self
class ChompedIO # :nodoc:
def initialize(io)
@io = io
end
def gets
s = @io.gets
s.chomp if s
end
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
##
# Reads an object that returns data line-wise (using #gets) and parses
# LDIF data into a Dataset object.
def read_ldif(io)
ds = Net::LDAP::Dataset.new
io = ChompedIO.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
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
line = nextline
end
end
ds
end
end
end
require 'net/ldap/entry' unless defined? Net::LDAP::Entry
|