net-ldap 0.3.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Contributors.rdoc +4 -0
- data/Hacking.rdoc +3 -8
- data/History.rdoc +181 -0
- data/README.rdoc +44 -12
- data/lib/net-ldap.rb +1 -1
- data/lib/net/ber.rb +41 -7
- data/lib/net/ber/ber_parser.rb +21 -7
- data/lib/net/ber/core_ext.rb +11 -18
- data/lib/net/ber/core_ext/array.rb +14 -0
- data/lib/net/ber/core_ext/integer.rb +74 -0
- data/lib/net/ber/core_ext/string.rb +24 -4
- data/lib/net/ber/core_ext/true_class.rb +2 -3
- data/lib/net/ldap.rb +441 -639
- data/lib/net/ldap/auth_adapter.rb +29 -0
- data/lib/net/ldap/auth_adapter/gss_spnego.rb +41 -0
- data/lib/net/ldap/auth_adapter/sasl.rb +62 -0
- data/lib/net/ldap/auth_adapter/simple.rb +34 -0
- data/lib/net/ldap/connection.rb +716 -0
- data/lib/net/ldap/dataset.rb +23 -9
- data/lib/net/ldap/dn.rb +13 -14
- data/lib/net/ldap/entry.rb +27 -9
- data/lib/net/ldap/error.rb +49 -0
- data/lib/net/ldap/filter.rb +58 -32
- data/lib/net/ldap/instrumentation.rb +23 -0
- data/lib/net/ldap/password.rb +23 -14
- data/lib/net/ldap/pdu.rb +70 -6
- data/lib/net/ldap/version.rb +5 -0
- data/lib/net/snmp.rb +237 -241
- metadata +71 -116
- data/.autotest +0 -11
- data/.gemtest +0 -0
- data/.rspec +0 -2
- data/Manifest.txt +0 -49
- data/Rakefile +0 -74
- data/autotest/discover.rb +0 -1
- data/lib/net/ber/core_ext/bignum.rb +0 -22
- data/lib/net/ber/core_ext/fixnum.rb +0 -66
- data/net-ldap.gemspec +0 -58
- data/spec/integration/ssl_ber_spec.rb +0 -36
- data/spec/spec.opts +0 -2
- data/spec/spec_helper.rb +0 -5
- data/spec/unit/ber/ber_spec.rb +0 -109
- data/spec/unit/ber/core_ext/string_spec.rb +0 -51
- data/spec/unit/ldap/dn_spec.rb +0 -80
- data/spec/unit/ldap/entry_spec.rb +0 -51
- data/spec/unit/ldap/filter_spec.rb +0 -84
- data/spec/unit/ldap_spec.rb +0 -48
- data/test/common.rb +0 -3
- data/test/test_entry.rb +0 -59
- data/test/test_filter.rb +0 -122
- data/test/test_ldap_connection.rb +0 -24
- data/test/test_ldif.rb +0 -79
- data/test/test_password.rb +0 -17
- data/test/test_rename.rb +0 -77
- data/test/test_snmp.rb +0 -114
- data/test/testdata.ldif +0 -101
- data/testserver/ldapserver.rb +0 -210
- data/testserver/testdata.ldif +0 -101
data/lib/net/ldap/dataset.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
|
+
require_relative 'entry'
|
2
|
+
|
1
3
|
# -*- ruby encoding: utf-8 -*-
|
2
4
|
##
|
3
5
|
# An LDAP Dataset. Used primarily as an intermediate format for converting
|
4
6
|
# to and from LDIF strings and Net::LDAP::Entry objects.
|
5
7
|
class Net::LDAP::Dataset < Hash
|
6
8
|
##
|
7
|
-
# Dataset object comments.
|
8
|
-
|
9
|
+
# Dataset object version, comments.
|
10
|
+
attr_accessor :version
|
11
|
+
attr_reader :comments
|
9
12
|
|
10
13
|
def initialize(*args, &block) # :nodoc:
|
11
14
|
super
|
15
|
+
@version = nil
|
12
16
|
@comments = []
|
13
17
|
end
|
14
18
|
|
@@ -17,11 +21,17 @@ class Net::LDAP::Dataset < Hash
|
|
17
21
|
# entries.
|
18
22
|
def to_ldif
|
19
23
|
ary = []
|
24
|
+
|
25
|
+
if version
|
26
|
+
ary << "version: #{version}"
|
27
|
+
ary << ""
|
28
|
+
end
|
29
|
+
|
20
30
|
ary += @comments unless @comments.empty?
|
21
31
|
keys.sort.each do |dn|
|
22
32
|
ary << "dn: #{dn}"
|
23
33
|
|
24
|
-
attributes = self[dn].keys.map
|
34
|
+
attributes = self[dn].keys.map(&:to_s).sort
|
25
35
|
attributes.each do |attr|
|
26
36
|
self[dn][attr.to_sym].each do |value|
|
27
37
|
if attr == "userpassword" or value_is_binary?(value)
|
@@ -95,7 +105,7 @@ class Net::LDAP::Dataset < Hash
|
|
95
105
|
# with the conversion of
|
96
106
|
def from_entry(entry)
|
97
107
|
dataset = Net::LDAP::Dataset.new
|
98
|
-
hash = {
|
108
|
+
hash = {}
|
99
109
|
entry.each_attribute do |attribute, value|
|
100
110
|
next if attribute == :dn
|
101
111
|
hash[attribute] = value
|
@@ -125,9 +135,15 @@ class Net::LDAP::Dataset < Hash
|
|
125
135
|
if line =~ /^#/
|
126
136
|
ds.comments << line
|
127
137
|
yield :comment, line if block_given?
|
128
|
-
elsif line =~ /^
|
129
|
-
|
130
|
-
|
138
|
+
elsif line =~ /^version:[\s]*([0-9]+)$/i
|
139
|
+
ds.version = $1
|
140
|
+
yield :version, line if block_given?
|
141
|
+
elsif line =~ /^dn:([\:]?)[\s]*/i
|
142
|
+
# $1 is a colon if the dn-value is base-64 encoded
|
143
|
+
# $' is the dn-value
|
144
|
+
# Avoid the Base64 class because not all Ruby versions have it.
|
145
|
+
dn = ($1 == ":") ? $'.unpack('m').shift : $'
|
146
|
+
ds[dn] = Hash.new { |k, v| k[v] = [] }
|
131
147
|
yield :dn, dn if block_given?
|
132
148
|
elsif line.empty?
|
133
149
|
dn = nil
|
@@ -150,5 +166,3 @@ class Net::LDAP::Dataset < Hash
|
|
150
166
|
end
|
151
167
|
end
|
152
168
|
end
|
153
|
-
|
154
|
-
require 'net/ldap/entry' unless defined? Net::LDAP::Entry
|
data/lib/net/ldap/dn.rb
CHANGED
@@ -57,19 +57,19 @@ class Net::LDAP::DN
|
|
57
57
|
state = :key_oid
|
58
58
|
key << char
|
59
59
|
when ' ' then state = :key
|
60
|
-
else raise "DN badly formed"
|
60
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
61
61
|
end
|
62
62
|
when :key_normal then
|
63
63
|
case char
|
64
64
|
when '=' then state = :value
|
65
65
|
when 'a'..'z', 'A'..'Z', '0'..'9', '-', ' ' then key << char
|
66
|
-
else raise "DN badly formed"
|
66
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
67
67
|
end
|
68
68
|
when :key_oid then
|
69
69
|
case char
|
70
70
|
when '=' then state = :value
|
71
71
|
when '0'..'9', '.', ' ' then key << char
|
72
|
-
else raise "DN badly formed"
|
72
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
73
73
|
end
|
74
74
|
when :value then
|
75
75
|
case char
|
@@ -110,7 +110,7 @@ class Net::LDAP::DN
|
|
110
110
|
when '0'..'9', 'a'..'f', 'A'..'F' then
|
111
111
|
state = :value_normal
|
112
112
|
value << "#{hex_buffer}#{char}".to_i(16).chr
|
113
|
-
else raise "DN badly formed"
|
113
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
114
114
|
end
|
115
115
|
when :value_quoted then
|
116
116
|
case char
|
@@ -132,7 +132,7 @@ class Net::LDAP::DN
|
|
132
132
|
when '0'..'9', 'a'..'f', 'A'..'F' then
|
133
133
|
state = :value_quoted
|
134
134
|
value << "#{hex_buffer}#{char}".to_i(16).chr
|
135
|
-
else raise "DN badly formed"
|
135
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
136
136
|
end
|
137
137
|
when :value_hexstring then
|
138
138
|
case char
|
@@ -145,14 +145,14 @@ class Net::LDAP::DN
|
|
145
145
|
yield key.string.strip, value.string.rstrip
|
146
146
|
key = StringIO.new
|
147
147
|
value = StringIO.new;
|
148
|
-
else raise "DN badly formed"
|
148
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
149
149
|
end
|
150
150
|
when :value_hexstring_hex then
|
151
151
|
case char
|
152
152
|
when '0'..'9', 'a'..'f', 'A'..'F' then
|
153
153
|
state = :value_hexstring
|
154
154
|
value << char
|
155
|
-
else raise "DN badly formed"
|
155
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
156
156
|
end
|
157
157
|
when :value_end then
|
158
158
|
case char
|
@@ -162,18 +162,17 @@ class Net::LDAP::DN
|
|
162
162
|
yield key.string.strip, value.string.rstrip
|
163
163
|
key = StringIO.new
|
164
164
|
value = StringIO.new;
|
165
|
-
else raise "DN badly formed"
|
165
|
+
else raise Net::LDAP::InvalidDNError, "DN badly formed"
|
166
166
|
end
|
167
|
-
else raise "Fell out of state machine"
|
167
|
+
else raise Net::LDAP::InvalidDNError, "Fell out of state machine"
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
171
171
|
# Last pair
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
end
|
172
|
+
raise Net::LDAP::InvalidDNError, "DN badly formed" unless
|
173
|
+
[:value, :value_normal, :value_hexstring, :value_end].include? state
|
174
|
+
|
175
|
+
yield key.string.strip, value.string.rstrip
|
177
176
|
end
|
178
177
|
|
179
178
|
##
|
data/lib/net/ldap/entry.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'dataset'
|
2
|
+
|
1
3
|
# -*- ruby encoding: utf-8 -*-
|
2
4
|
##
|
3
5
|
# Objects of this class represent individual entries in an LDAP directory.
|
@@ -71,7 +73,7 @@ class Net::LDAP::Entry
|
|
71
73
|
|
72
74
|
return nil if ds.empty?
|
73
75
|
|
74
|
-
raise Net::LDAP::
|
76
|
+
raise Net::LDAP::EntryOverflowError, "Too many LDIF entries" unless ds.size == 1
|
75
77
|
|
76
78
|
entry = ds.to_entries.first
|
77
79
|
|
@@ -113,6 +115,14 @@ class Net::LDAP::Entry
|
|
113
115
|
@myhash[name] || []
|
114
116
|
end
|
115
117
|
|
118
|
+
##
|
119
|
+
# Read the first value for the provided attribute. The attribute name
|
120
|
+
# is canonicalized prior to reading. Returns nil if the attribute does
|
121
|
+
# not exist.
|
122
|
+
def first(name)
|
123
|
+
self[name].first
|
124
|
+
end
|
125
|
+
|
116
126
|
##
|
117
127
|
# Returns the first distinguished name (dn) of the Entry as a \String.
|
118
128
|
def dn
|
@@ -125,6 +135,13 @@ class Net::LDAP::Entry
|
|
125
135
|
@myhash.keys
|
126
136
|
end
|
127
137
|
|
138
|
+
##
|
139
|
+
# Creates a duplicate of the internal Hash containing the attributes
|
140
|
+
# of the entry.
|
141
|
+
def to_h
|
142
|
+
@myhash.dup
|
143
|
+
end
|
144
|
+
|
128
145
|
##
|
129
146
|
# Accesses each of the attributes present in the Entry.
|
130
147
|
#
|
@@ -132,11 +149,10 @@ class Net::LDAP::Entry
|
|
132
149
|
# arguments to the block: a Symbol giving the name of the attribute, and a
|
133
150
|
# (possibly empty) \Array of data values.
|
134
151
|
def each # :yields: attribute-name, data-values-array
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
}
|
152
|
+
return unless block_given?
|
153
|
+
attribute_names.each do|a|
|
154
|
+
attr_name, values = a, self[a]
|
155
|
+
yield attr_name, values
|
140
156
|
end
|
141
157
|
end
|
142
158
|
alias_method :each_attribute, :each
|
@@ -147,7 +163,7 @@ class Net::LDAP::Entry
|
|
147
163
|
Net::LDAP::Dataset.from_entry(self).to_ldif_string
|
148
164
|
end
|
149
165
|
|
150
|
-
def respond_to?(sym) #:nodoc:
|
166
|
+
def respond_to?(sym, include_all = false) #:nodoc:
|
151
167
|
return true if valid_attribute?(self.class.attribute_name(sym))
|
152
168
|
return super
|
153
169
|
end
|
@@ -180,6 +196,8 @@ class Net::LDAP::Entry
|
|
180
196
|
sym.to_s[-1] == ?=
|
181
197
|
end
|
182
198
|
private :setter?
|
183
|
-
end # class Entry
|
184
199
|
|
185
|
-
|
200
|
+
def ==(other)
|
201
|
+
other.instance_of?(self.class) && @myhash == other.to_h
|
202
|
+
end
|
203
|
+
end # class Entry
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Net::LDAP
|
2
|
+
class Error < StandardError; end
|
3
|
+
|
4
|
+
class AlreadyOpenedError < Error; end
|
5
|
+
class SocketError < Error; end
|
6
|
+
class ConnectionError < Error
|
7
|
+
def self.new(errors)
|
8
|
+
error = errors.first.first
|
9
|
+
if errors.size == 1
|
10
|
+
return error if error.is_a? Errno::ECONNREFUSED
|
11
|
+
|
12
|
+
return Net::LDAP::Error.new(error.message)
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(errors)
|
19
|
+
message = "Unable to connect to any given server: \n #{errors.map { |e, h, p| "#{e.class}: #{e.message} (#{h}:#{p})" }.join("\n ")}"
|
20
|
+
super(message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
class NoOpenSSLError < Error; end
|
24
|
+
class NoStartTLSResultError < Error; end
|
25
|
+
class NoSearchBaseError < Error; end
|
26
|
+
class StartTLSError < Error; end
|
27
|
+
class EncryptionUnsupportedError < Error; end
|
28
|
+
class EncMethodUnsupportedError < Error; end
|
29
|
+
class AuthMethodUnsupportedError < Error; end
|
30
|
+
class BindingInformationInvalidError < Error; end
|
31
|
+
class NoBindResultError < Error; end
|
32
|
+
class SASLChallengeOverflowError < Error; end
|
33
|
+
class SearchSizeInvalidError < Error; end
|
34
|
+
class SearchScopeInvalidError < Error; end
|
35
|
+
class ResponseTypeInvalidError < Error; end
|
36
|
+
class ResponseMissingOrInvalidError < Error; end
|
37
|
+
class EmptyDNError < Error; end
|
38
|
+
class InvalidDNError < Error; end
|
39
|
+
class HashTypeUnsupportedError < Error; end
|
40
|
+
class OperatorError < Error; end
|
41
|
+
class SubstringFilterError < Error; end
|
42
|
+
class SearchFilterError < Error; end
|
43
|
+
class BERInvalidError < Error; end
|
44
|
+
class SearchFilterTypeUnknownError < Error; end
|
45
|
+
class BadAttributeError < Error; end
|
46
|
+
class FilterTypeUnknownError < Error; end
|
47
|
+
class FilterSyntaxInvalidError < Error; end
|
48
|
+
class EntryOverflowError < Error; end
|
49
|
+
end
|
data/lib/net/ldap/filter.rb
CHANGED
@@ -23,11 +23,11 @@
|
|
23
23
|
class Net::LDAP::Filter
|
24
24
|
##
|
25
25
|
# Known filter types.
|
26
|
-
FilterTypes = [
|
26
|
+
FilterTypes = [:ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq]
|
27
27
|
|
28
28
|
def initialize(op, left, right) #:nodoc:
|
29
29
|
unless FilterTypes.include?(op)
|
30
|
-
raise Net::LDAP::
|
30
|
+
raise Net::LDAP::OperatorError, "Invalid or unsupported operator #{op.inspect} in LDAP Filter."
|
31
31
|
end
|
32
32
|
@op = op
|
33
33
|
@left = left
|
@@ -65,6 +65,23 @@ class Net::LDAP::Filter
|
|
65
65
|
new(:eq, attribute, value)
|
66
66
|
end
|
67
67
|
|
68
|
+
##
|
69
|
+
# Creates a Filter object indicating a binary comparison.
|
70
|
+
# this prevents the search data from being forced into a UTF-8 string.
|
71
|
+
#
|
72
|
+
# This is primarily used for Microsoft Active Directory to compare
|
73
|
+
# GUID values.
|
74
|
+
#
|
75
|
+
# # for guid represented as hex charecters
|
76
|
+
# guid = "6a31b4a12aa27a41aca9603f27dd5116"
|
77
|
+
# guid_bin = [guid].pack("H*")
|
78
|
+
# f = Net::LDAP::Filter.bineq("objectGUID", guid_bin)
|
79
|
+
#
|
80
|
+
# This filter does not perform any escaping.
|
81
|
+
def bineq(attribute, value)
|
82
|
+
new(:bineq, attribute, value)
|
83
|
+
end
|
84
|
+
|
68
85
|
##
|
69
86
|
# Creates a Filter object indicating extensible comparison. This Filter
|
70
87
|
# object is currently considered EXPERIMENTAL.
|
@@ -225,7 +242,7 @@ class Net::LDAP::Filter
|
|
225
242
|
|
226
243
|
# http://tools.ietf.org/html/rfc4515 lists these exceptions from UTF1
|
227
244
|
# charset for filters. All of the following must be escaped in any normal
|
228
|
-
# string using a single backslash ('\') as escape.
|
245
|
+
# string using a single backslash ('\') as escape.
|
229
246
|
#
|
230
247
|
ESCAPES = {
|
231
248
|
"\0" => '00', # NUL = %x00 ; null character
|
@@ -234,10 +251,10 @@ class Net::LDAP::Filter
|
|
234
251
|
')' => '29', # RPARENS = %x29 ; right parenthesis (")")
|
235
252
|
'\\' => '5C', # ESC = %x5C ; esc (or backslash) ("\")
|
236
253
|
}
|
237
|
-
# Compiled character class regexp using the keys from the above hash.
|
254
|
+
# Compiled character class regexp using the keys from the above hash.
|
238
255
|
ESCAPE_RE = Regexp.new(
|
239
|
-
"[" +
|
240
|
-
ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
|
256
|
+
"[" +
|
257
|
+
ESCAPES.keys.map { |e| Regexp.escape(e) }.join +
|
241
258
|
"]")
|
242
259
|
|
243
260
|
##
|
@@ -270,18 +287,18 @@ class Net::LDAP::Filter
|
|
270
287
|
when 0xa4 # context-specific constructed 4, "substring"
|
271
288
|
str = ""
|
272
289
|
final = false
|
273
|
-
ber.last.each
|
290
|
+
ber.last.each do |b|
|
274
291
|
case b.ber_identifier
|
275
292
|
when 0x80 # context-specific primitive 0, SubstringFilter "initial"
|
276
|
-
raise Net::LDAP::
|
277
|
-
str += b
|
293
|
+
raise Net::LDAP::SubstringFilterError, "Unrecognized substring filter; bad initial value." if str.length > 0
|
294
|
+
str += escape(b)
|
278
295
|
when 0x81 # context-specific primitive 0, SubstringFilter "any"
|
279
|
-
str += "*#{b}"
|
296
|
+
str += "*#{escape(b)}"
|
280
297
|
when 0x82 # context-specific primitive 0, SubstringFilter "final"
|
281
|
-
str += "*#{b}"
|
298
|
+
str += "*#{escape(b)}"
|
282
299
|
final = true
|
283
300
|
end
|
284
|
-
|
301
|
+
end
|
285
302
|
str += "*" unless final
|
286
303
|
eq(ber.first.to_s, str)
|
287
304
|
when 0xa5 # context-specific constructed 5, "greaterOrEqual"
|
@@ -292,9 +309,9 @@ class Net::LDAP::Filter
|
|
292
309
|
# call to_s to get rid of the BER-identifiedness of the incoming string.
|
293
310
|
present?(ber.to_s)
|
294
311
|
when 0xa9 # context-specific constructed 9, "extensible comparison"
|
295
|
-
raise Net::LDAP::
|
296
|
-
|
297
|
-
# Reassembles the extensible filter parts
|
312
|
+
raise Net::LDAP::SearchFilterError, "Invalid extensible search filter, should be at least two elements" if ber.size < 2
|
313
|
+
|
314
|
+
# Reassembles the extensible filter parts
|
298
315
|
# (["sn", "2.4.6.8.10", "Barbara Jones", '1'])
|
299
316
|
type = value = dn = rule = nil
|
300
317
|
ber.each do |element|
|
@@ -310,10 +327,10 @@ class Net::LDAP::Filter
|
|
310
327
|
attribute << type if type
|
311
328
|
attribute << ":#{dn}" if dn
|
312
329
|
attribute << ":#{rule}" if rule
|
313
|
-
|
330
|
+
|
314
331
|
ex(attribute, value)
|
315
332
|
else
|
316
|
-
raise Net::LDAP::
|
333
|
+
raise Net::LDAP::BERInvalidError, "Invalid BER tag-value (#{ber.ber_identifier}) in search filter."
|
317
334
|
end
|
318
335
|
end
|
319
336
|
|
@@ -340,7 +357,7 @@ class Net::LDAP::Filter
|
|
340
357
|
when 0xa3 # equalityMatch. context-specific constructed 3.
|
341
358
|
eq(obj[0], obj[1])
|
342
359
|
else
|
343
|
-
raise Net::LDAP::
|
360
|
+
raise Net::LDAP::SearchFilterTypeUnknownError, "Unknown LDAP search-filter type: #{obj.ber_identifier}"
|
344
361
|
end
|
345
362
|
end
|
346
363
|
end
|
@@ -397,7 +414,7 @@ class Net::LDAP::Filter
|
|
397
414
|
case @op
|
398
415
|
when :ne
|
399
416
|
"!(#{@left}=#{@right})"
|
400
|
-
when :eq
|
417
|
+
when :eq, :bineq
|
401
418
|
"#{@left}=#{@right}"
|
402
419
|
when :ex
|
403
420
|
"#{@left}:=#{@right}"
|
@@ -473,7 +490,7 @@ class Net::LDAP::Filter
|
|
473
490
|
when :eq
|
474
491
|
if @right == "*" # presence test
|
475
492
|
@left.to_s.to_ber_contextspecific(7)
|
476
|
-
elsif @right =~ /[*]/ # substring
|
493
|
+
elsif @right.to_s =~ /[*]/ # substring
|
477
494
|
# Parsing substrings is a little tricky. We use String#split to
|
478
495
|
# break a string into substrings delimited by the * (star)
|
479
496
|
# character. But we also need to know whether there is a star at the
|
@@ -490,17 +507,17 @@ class Net::LDAP::Filter
|
|
490
507
|
first = nil
|
491
508
|
ary.shift
|
492
509
|
else
|
493
|
-
first = ary.shift.to_ber_contextspecific(0)
|
510
|
+
first = unescape(ary.shift).to_ber_contextspecific(0)
|
494
511
|
end
|
495
512
|
|
496
513
|
if ary.last.empty?
|
497
514
|
last = nil
|
498
515
|
ary.pop
|
499
516
|
else
|
500
|
-
last = ary.pop.to_ber_contextspecific(2)
|
517
|
+
last = unescape(ary.pop).to_ber_contextspecific(2)
|
501
518
|
end
|
502
519
|
|
503
|
-
seq = ary.map { |e| e.to_ber_contextspecific(1) }
|
520
|
+
seq = ary.map { |e| unescape(e).to_ber_contextspecific(1) }
|
504
521
|
seq.unshift first if first
|
505
522
|
seq.push last if last
|
506
523
|
|
@@ -508,11 +525,14 @@ class Net::LDAP::Filter
|
|
508
525
|
else # equality
|
509
526
|
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3)
|
510
527
|
end
|
528
|
+
when :bineq
|
529
|
+
# make sure data is not forced to UTF-8
|
530
|
+
[@left.to_s.to_ber, unescape(@right).to_ber_bin].to_ber_contextspecific(3)
|
511
531
|
when :ex
|
512
532
|
seq = []
|
513
533
|
|
514
534
|
unless @left =~ /^([-;\w]*)(:dn)?(:(\w+|[.\w]+))?$/
|
515
|
-
raise Net::LDAP::
|
535
|
+
raise Net::LDAP::BadAttributeError, "Bad attribute #{@left}"
|
516
536
|
end
|
517
537
|
type, dn, rule = $1, $2, $4
|
518
538
|
|
@@ -530,10 +550,10 @@ class Net::LDAP::Filter
|
|
530
550
|
[self.class.eq(@left, @right).to_ber].to_ber_contextspecific(2)
|
531
551
|
when :and
|
532
552
|
ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten
|
533
|
-
ary.map
|
553
|
+
ary.map(&:to_ber).to_ber_contextspecific(0)
|
534
554
|
when :or
|
535
555
|
ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten
|
536
|
-
ary.map
|
556
|
+
ary.map(&:to_ber).to_ber_contextspecific(1)
|
537
557
|
when :not
|
538
558
|
[@left.to_ber].to_ber_contextspecific(2)
|
539
559
|
end
|
@@ -619,15 +639,21 @@ class Net::LDAP::Filter
|
|
619
639
|
l = entry[@left] and l = Array(l) and l.index(@right)
|
620
640
|
end
|
621
641
|
else
|
622
|
-
raise Net::LDAP::
|
642
|
+
raise Net::LDAP::FilterTypeUnknownError, "Unknown filter type in match: #{@op}"
|
623
643
|
end
|
624
644
|
end
|
625
645
|
|
626
646
|
##
|
627
647
|
# Converts escaped characters (e.g., "\\28") to unescaped characters
|
628
|
-
#
|
648
|
+
# @note slawson20170317: Don't attempt to unescape 16 byte binary data which we assume are objectGUIDs
|
649
|
+
# The binary form of 5936AE79-664F-44EA-BCCB-5C39399514C6 triggers a BINARY -> UTF-8 conversion error
|
629
650
|
def unescape(right)
|
630
|
-
right
|
651
|
+
right = right.to_s
|
652
|
+
if right.length == 16 && right.encoding == Encoding::BINARY
|
653
|
+
right
|
654
|
+
else
|
655
|
+
right.to_s.gsub(/\\([a-fA-F\d]{2})/) { [$1.hex].pack("U") }
|
656
|
+
end
|
631
657
|
end
|
632
658
|
private :unescape
|
633
659
|
|
@@ -652,7 +678,7 @@ class Net::LDAP::Filter
|
|
652
678
|
def initialize(str)
|
653
679
|
require 'strscan' # Don't load strscan until we need it.
|
654
680
|
@filter = parse(StringScanner.new(str))
|
655
|
-
raise Net::LDAP::
|
681
|
+
raise Net::LDAP::FilterSyntaxInvalidError, "Invalid filter syntax." unless @filter
|
656
682
|
end
|
657
683
|
|
658
684
|
##
|
@@ -729,11 +755,11 @@ class Net::LDAP::Filter
|
|
729
755
|
# This parses a given expression inside of parentheses.
|
730
756
|
def parse_filter_branch(scanner)
|
731
757
|
scanner.scan(/\s*/)
|
732
|
-
if token = scanner.scan(/[-\w
|
758
|
+
if token = scanner.scan(/[-\w:.;]*[\w]/)
|
733
759
|
scanner.scan(/\s*/)
|
734
760
|
if op = scanner.scan(/<=|>=|!=|:=|=/)
|
735
761
|
scanner.scan(/\s*/)
|
736
|
-
if value = scanner.scan(/(?:[-\w
|
762
|
+
if value = scanner.scan(/(?:[-\[\]{}\w*.+\/:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\\[a-fA-F\d]{2})+/u)
|
737
763
|
# 20100313 AZ: Assumes that "(uid=george*)" is the same as
|
738
764
|
# "(uid=george* )". The standard doesn't specify, but I can find
|
739
765
|
# no examples that suggest otherwise.
|