net-ldap 0.12.0 → 0.19.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 +5 -5
- data/Contributors.rdoc +1 -0
- data/History.rdoc +112 -0
- data/README.rdoc +19 -9
- data/lib/net/ber/ber_parser.rb +4 -4
- data/lib/net/ber/core_ext/array.rb +1 -1
- data/lib/net/ber/core_ext/integer.rb +1 -1
- data/lib/net/ber/core_ext/string.rb +1 -1
- data/lib/net/ber/core_ext.rb +6 -6
- data/lib/net/ber.rb +39 -9
- data/lib/net/ldap/auth_adapter/gss_spnego.rb +9 -8
- data/lib/net/ldap/auth_adapter/sasl.rb +6 -4
- data/lib/net/ldap/auth_adapter/simple.rb +1 -1
- data/lib/net/ldap/connection.rb +173 -52
- data/lib/net/ldap/dataset.rb +3 -5
- data/lib/net/ldap/dn.rb +21 -30
- data/lib/net/ldap/entry.rb +15 -7
- data/lib/net/ldap/error.rb +2 -25
- data/lib/net/ldap/filter.rb +15 -8
- data/lib/net/ldap/instrumentation.rb +2 -2
- data/lib/net/ldap/password.rb +7 -5
- data/lib/net/ldap/pdu.rb +27 -3
- data/lib/net/ldap/version.rb +1 -1
- data/lib/net/ldap.rb +212 -91
- data/lib/net/snmp.rb +19 -19
- data/lib/net-ldap.rb +1 -1
- metadata +27 -96
- data/.gitignore +0 -9
- data/.rubocop.yml +0 -5
- data/.rubocop_todo.yml +0 -462
- data/.travis.yml +0 -31
- data/CONTRIBUTING.md +0 -54
- data/Gemfile +0 -2
- data/Rakefile +0 -23
- data/net-ldap.gemspec +0 -36
- data/script/changelog +0 -47
- data/script/install-openldap +0 -112
- data/script/package +0 -7
- data/script/release +0 -16
- data/test/ber/core_ext/test_array.rb +0 -22
- data/test/ber/core_ext/test_string.rb +0 -25
- data/test/ber/test_ber.rb +0 -145
- data/test/fixtures/cacert.pem +0 -20
- data/test/fixtures/openldap/memberof.ldif +0 -33
- data/test/fixtures/openldap/retcode.ldif +0 -76
- data/test/fixtures/openldap/slapd.conf.ldif +0 -67
- data/test/fixtures/seed.ldif +0 -374
- data/test/integration/test_add.rb +0 -28
- data/test/integration/test_ber.rb +0 -30
- data/test/integration/test_bind.rb +0 -34
- data/test/integration/test_delete.rb +0 -31
- data/test/integration/test_open.rb +0 -88
- data/test/integration/test_return_codes.rb +0 -38
- data/test/integration/test_search.rb +0 -77
- data/test/support/vm/openldap/.gitignore +0 -1
- data/test/support/vm/openldap/README.md +0 -32
- data/test/support/vm/openldap/Vagrantfile +0 -33
- data/test/test_auth_adapter.rb +0 -11
- data/test/test_dn.rb +0 -44
- data/test/test_entry.rb +0 -65
- data/test/test_filter.rb +0 -223
- data/test/test_filter_parser.rb +0 -24
- data/test/test_helper.rb +0 -66
- data/test/test_ldap.rb +0 -67
- data/test/test_ldap_connection.rb +0 -460
- data/test/test_ldif.rb +0 -104
- data/test/test_password.rb +0 -10
- data/test/test_rename.rb +0 -77
- data/test/test_search.rb +0 -39
- data/test/test_snmp.rb +0 -119
- data/test/test_ssl_ber.rb +0 -40
- data/test/testdata.ldif +0 -101
- data/testserver/ldapserver.rb +0 -210
- data/testserver/testdata.ldif +0 -101
data/lib/net/ldap/connection.rb
CHANGED
@@ -3,38 +3,63 @@
|
|
3
3
|
class Net::LDAP::Connection #:nodoc:
|
4
4
|
include Net::LDAP::Instrumentation
|
5
5
|
|
6
|
+
# Seconds before failing for socket connect timeout
|
7
|
+
DefaultConnectTimeout = 5
|
8
|
+
|
6
9
|
LdapVersion = 3
|
7
|
-
MaxSaslChallenges = 10
|
8
10
|
|
9
|
-
|
11
|
+
# Initialize a connection to an LDAP server
|
12
|
+
#
|
13
|
+
# :server
|
14
|
+
# :hosts Array of tuples specifying host, port
|
15
|
+
# :host host
|
16
|
+
# :port port
|
17
|
+
# :socket prepared socket
|
18
|
+
#
|
19
|
+
def initialize(server = {})
|
20
|
+
@server = server
|
10
21
|
@instrumentation_service = server[:instrumentation_service]
|
11
22
|
|
12
|
-
|
13
|
-
|
14
|
-
else
|
15
|
-
server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil?
|
16
|
-
open_connection(server)
|
17
|
-
end
|
23
|
+
# Allows tests to parameterize what socket class to use
|
24
|
+
@socket_class = server.fetch(:socket_class, DefaultSocket)
|
18
25
|
|
19
26
|
yield self if block_given?
|
20
27
|
end
|
21
28
|
|
22
|
-
def
|
29
|
+
def socket_class=(socket_class)
|
30
|
+
@socket_class = socket_class
|
31
|
+
end
|
32
|
+
|
33
|
+
def prepare_socket(server, timeout=nil, hostname='127.0.0.1')
|
23
34
|
socket = server[:socket]
|
24
35
|
encryption = server[:encryption]
|
25
36
|
|
26
37
|
@conn = socket
|
27
|
-
setup_encryption
|
38
|
+
setup_encryption(encryption, timeout, hostname) if encryption
|
28
39
|
end
|
29
40
|
|
30
41
|
def open_connection(server)
|
31
42
|
hosts = server[:hosts]
|
32
43
|
encryption = server[:encryption]
|
33
44
|
|
45
|
+
timeout = server[:connect_timeout] || DefaultConnectTimeout
|
46
|
+
socket_opts = {
|
47
|
+
connect_timeout: timeout,
|
48
|
+
}
|
49
|
+
|
34
50
|
errors = []
|
35
51
|
hosts.each do |host, port|
|
36
52
|
begin
|
37
|
-
prepare_socket(server.merge(socket:
|
53
|
+
prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)), timeout, host)
|
54
|
+
if encryption
|
55
|
+
if encryption[:tls_options] &&
|
56
|
+
encryption[:tls_options][:verify_mode] &&
|
57
|
+
encryption[:tls_options][:verify_mode] == OpenSSL::SSL::VERIFY_NONE
|
58
|
+
warn "not verifying SSL hostname of LDAPS server '#{host}:#{port}'"
|
59
|
+
else
|
60
|
+
@conn.post_connection_check(host)
|
61
|
+
end
|
62
|
+
end
|
38
63
|
return
|
39
64
|
rescue Net::LDAP::Error, SocketError, SystemCallError,
|
40
65
|
OpenSSL::SSL::SSLError => e
|
@@ -49,7 +74,8 @@ class Net::LDAP::Connection #:nodoc:
|
|
49
74
|
|
50
75
|
module GetbyteForSSLSocket
|
51
76
|
def getbyte
|
52
|
-
getc
|
77
|
+
c = getc
|
78
|
+
c && c.ord
|
53
79
|
end
|
54
80
|
end
|
55
81
|
|
@@ -60,7 +86,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
60
86
|
end
|
61
87
|
end
|
62
88
|
|
63
|
-
def self.wrap_with_ssl(io, tls_options = {})
|
89
|
+
def self.wrap_with_ssl(io, tls_options = {}, timeout=nil, hostname=nil)
|
64
90
|
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
|
65
91
|
|
66
92
|
ctx = OpenSSL::SSL::SSLContext.new
|
@@ -70,7 +96,23 @@ class Net::LDAP::Connection #:nodoc:
|
|
70
96
|
ctx.set_params(tls_options) unless tls_options.empty?
|
71
97
|
|
72
98
|
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
|
73
|
-
conn.
|
99
|
+
conn.hostname = hostname
|
100
|
+
|
101
|
+
begin
|
102
|
+
if timeout
|
103
|
+
conn.connect_nonblock
|
104
|
+
else
|
105
|
+
conn.connect
|
106
|
+
end
|
107
|
+
rescue IO::WaitReadable
|
108
|
+
raise Errno::ETIMEDOUT, "OpenSSL connection read timeout" unless
|
109
|
+
IO.select([conn], nil, nil, timeout)
|
110
|
+
retry
|
111
|
+
rescue IO::WaitWritable
|
112
|
+
raise Errno::ETIMEDOUT, "OpenSSL connection write timeout" unless
|
113
|
+
IO.select(nil, [conn], nil, timeout)
|
114
|
+
retry
|
115
|
+
end
|
74
116
|
|
75
117
|
# Doesn't work:
|
76
118
|
# conn.sync_close = true
|
@@ -107,17 +149,17 @@ class Net::LDAP::Connection #:nodoc:
|
|
107
149
|
# communications, as with simple_tls. Thanks for Kouhei Sutou for
|
108
150
|
# generously contributing the :start_tls path.
|
109
151
|
#++
|
110
|
-
def setup_encryption(args)
|
152
|
+
def setup_encryption(args, timeout=nil, hostname=nil)
|
111
153
|
args[:tls_options] ||= {}
|
112
154
|
case args[:method]
|
113
155
|
when :simple_tls
|
114
|
-
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
|
156
|
+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout, hostname)
|
115
157
|
# additional branches requiring server validation and peer certs, etc.
|
116
158
|
# go here.
|
117
159
|
when :start_tls
|
118
160
|
message_id = next_msgid
|
119
161
|
request = [
|
120
|
-
Net::LDAP::StartTlsOid.to_ber_contextspecific(0)
|
162
|
+
Net::LDAP::StartTlsOid.to_ber_contextspecific(0),
|
121
163
|
].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
122
164
|
|
123
165
|
write(request, nil, message_id)
|
@@ -127,11 +169,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
127
169
|
raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
|
128
170
|
end
|
129
171
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
raise Net::LDAP::StartTlSError, "start_tls failed: #{pdu.result_code}"
|
134
|
-
end
|
172
|
+
raise Net::LDAP::StartTLSError,
|
173
|
+
"start_tls failed: #{pdu.result_code}" unless pdu.result_code.zero?
|
174
|
+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout, hostname)
|
135
175
|
else
|
136
176
|
raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
|
137
177
|
end
|
@@ -143,7 +183,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
143
183
|
# have to call it, but perhaps it will come in handy someday.
|
144
184
|
#++
|
145
185
|
def close
|
146
|
-
return if @conn.nil?
|
186
|
+
return if !defined?(@conn) || @conn.nil?
|
147
187
|
@conn.close
|
148
188
|
@conn = nil
|
149
189
|
end
|
@@ -161,12 +201,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
161
201
|
|
162
202
|
# read messages until we have a match for the given message_id
|
163
203
|
while pdu = read
|
164
|
-
if pdu.message_id == message_id
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
next
|
169
|
-
end
|
204
|
+
return pdu if pdu.message_id == message_id
|
205
|
+
|
206
|
+
message_queue[pdu.message_id].push pdu
|
207
|
+
next
|
170
208
|
end
|
171
209
|
|
172
210
|
pdu
|
@@ -195,7 +233,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
195
233
|
def read(syntax = Net::LDAP::AsnSyntax)
|
196
234
|
ber_object =
|
197
235
|
instrument "read.net_ldap_connection", :syntax => syntax do |payload|
|
198
|
-
|
236
|
+
socket.read_ber(syntax) do |id, content_length|
|
199
237
|
payload[:object_type_id] = id
|
200
238
|
payload[:content_length] = content_length
|
201
239
|
end
|
@@ -225,7 +263,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
225
263
|
def write(request, controls = nil, message_id = next_msgid)
|
226
264
|
instrument "write.net_ldap_connection" do |payload|
|
227
265
|
packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
|
228
|
-
payload[:content_length] =
|
266
|
+
payload[:content_length] = socket.write(packet)
|
229
267
|
end
|
230
268
|
end
|
231
269
|
private :write
|
@@ -264,10 +302,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
264
302
|
control[2] = (control[2] == true).to_ber
|
265
303
|
control.to_ber_sequence
|
266
304
|
end
|
267
|
-
|
305
|
+
[
|
268
306
|
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
|
269
307
|
false.to_ber,
|
270
|
-
sort_control_values.to_ber_sequence.to_s.to_ber
|
308
|
+
sort_control_values.to_ber_sequence.to_s.to_ber,
|
271
309
|
].to_ber_sequence
|
272
310
|
end
|
273
311
|
|
@@ -364,12 +402,11 @@ class Net::LDAP::Connection #:nodoc:
|
|
364
402
|
# should collect this into a private helper to clarify the structure
|
365
403
|
query_limit = 0
|
366
404
|
if size > 0
|
367
|
-
if paged
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
end
|
405
|
+
query_limit = if paged
|
406
|
+
(((size - n_results) < 126) ? (size - n_results) : 0)
|
407
|
+
else
|
408
|
+
size
|
409
|
+
end
|
373
410
|
end
|
374
411
|
|
375
412
|
request = [
|
@@ -380,23 +417,29 @@ class Net::LDAP::Connection #:nodoc:
|
|
380
417
|
time.to_ber,
|
381
418
|
attrs_only.to_ber,
|
382
419
|
filter.to_ber,
|
383
|
-
ber_attrs.to_ber_sequence
|
420
|
+
ber_attrs.to_ber_sequence,
|
384
421
|
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
|
385
422
|
|
386
423
|
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
|
387
424
|
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
|
388
425
|
# we have to disable paging (even though server supports it) to get around this...
|
389
426
|
|
427
|
+
user_controls = args.fetch(:controls, [])
|
390
428
|
controls = []
|
391
429
|
controls <<
|
392
430
|
[
|
393
431
|
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
|
394
432
|
# Criticality MUST be false to interoperate with normal LDAPs.
|
395
433
|
false.to_ber,
|
396
|
-
rfc2696_cookie.map
|
434
|
+
rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
|
397
435
|
].to_ber_sequence if paged
|
398
436
|
controls << ber_sort if ber_sort
|
399
|
-
|
437
|
+
if controls.empty? && user_controls.empty?
|
438
|
+
controls = nil
|
439
|
+
else
|
440
|
+
controls += user_controls
|
441
|
+
controls = controls.to_ber_contextspecific(0)
|
442
|
+
end
|
400
443
|
|
401
444
|
write(request, controls, message_id)
|
402
445
|
|
@@ -432,6 +475,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
432
475
|
end
|
433
476
|
end
|
434
477
|
|
478
|
+
if result_pdu.nil?
|
479
|
+
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
|
480
|
+
end
|
481
|
+
|
435
482
|
# count number of pages of results
|
436
483
|
payload[:page_count] ||= 0
|
437
484
|
payload[:page_count] += 1
|
@@ -487,20 +534,20 @@ class Net::LDAP::Connection #:nodoc:
|
|
487
534
|
MODIFY_OPERATIONS = { #:nodoc:
|
488
535
|
:add => 0,
|
489
536
|
:delete => 1,
|
490
|
-
:replace => 2
|
537
|
+
:replace => 2,
|
491
538
|
}
|
492
539
|
|
493
540
|
def self.modify_ops(operations)
|
494
541
|
ops = []
|
495
542
|
if operations
|
496
|
-
operations.each
|
543
|
+
operations.each do |op, attrib, values|
|
497
544
|
# TODO, fix the following line, which gives a bogus error if the
|
498
545
|
# opcode is invalid.
|
499
546
|
op_ber = MODIFY_OPERATIONS[op.to_sym].to_ber_enumerated
|
500
|
-
values = [
|
501
|
-
values = [
|
502
|
-
ops << [
|
503
|
-
|
547
|
+
values = [values].flatten.map { |v| v.to_ber if v }.to_ber_set
|
548
|
+
values = [attrib.to_s.to_ber, values].to_ber_sequence
|
549
|
+
ops << [op_ber, values].to_ber
|
550
|
+
end
|
504
551
|
end
|
505
552
|
ops
|
506
553
|
end
|
@@ -519,7 +566,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
519
566
|
message_id = next_msgid
|
520
567
|
request = [
|
521
568
|
modify_dn.to_ber,
|
522
|
-
ops.to_ber_sequence
|
569
|
+
ops.to_ber_sequence,
|
523
570
|
].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest)
|
524
571
|
|
525
572
|
write(request, nil, message_id)
|
@@ -532,6 +579,51 @@ class Net::LDAP::Connection #:nodoc:
|
|
532
579
|
pdu
|
533
580
|
end
|
534
581
|
|
582
|
+
##
|
583
|
+
# Password Modify
|
584
|
+
#
|
585
|
+
# http://tools.ietf.org/html/rfc3062
|
586
|
+
#
|
587
|
+
# passwdModifyOID OBJECT IDENTIFIER ::= 1.3.6.1.4.1.4203.1.11.1
|
588
|
+
#
|
589
|
+
# PasswdModifyRequestValue ::= SEQUENCE {
|
590
|
+
# userIdentity [0] OCTET STRING OPTIONAL
|
591
|
+
# oldPasswd [1] OCTET STRING OPTIONAL
|
592
|
+
# newPasswd [2] OCTET STRING OPTIONAL }
|
593
|
+
#
|
594
|
+
# PasswdModifyResponseValue ::= SEQUENCE {
|
595
|
+
# genPasswd [0] OCTET STRING OPTIONAL }
|
596
|
+
#
|
597
|
+
# Encoded request:
|
598
|
+
#
|
599
|
+
# 00\x02\x01\x02w+\x80\x171.3.6.1.4.1.4203.1.11.1\x81\x100\x0E\x81\x05old\x82\x05new
|
600
|
+
#
|
601
|
+
def password_modify(args)
|
602
|
+
dn = args[:dn]
|
603
|
+
raise ArgumentError, 'DN is required' if !dn || dn.empty?
|
604
|
+
|
605
|
+
ext_seq = [Net::LDAP::PasswdModifyOid.to_ber_contextspecific(0)]
|
606
|
+
|
607
|
+
pwd_seq = []
|
608
|
+
pwd_seq << dn.to_ber(0x80)
|
609
|
+
pwd_seq << args[:old_password].to_ber(0x81) unless args[:old_password].nil?
|
610
|
+
pwd_seq << args[:new_password].to_ber(0x82) unless args[:new_password].nil?
|
611
|
+
ext_seq << pwd_seq.to_ber_sequence.to_ber(0x81)
|
612
|
+
|
613
|
+
request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
614
|
+
|
615
|
+
message_id = next_msgid
|
616
|
+
|
617
|
+
write(request, nil, message_id)
|
618
|
+
pdu = queued_read(message_id)
|
619
|
+
|
620
|
+
if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
|
621
|
+
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
|
622
|
+
end
|
623
|
+
|
624
|
+
pdu
|
625
|
+
end
|
626
|
+
|
535
627
|
#--
|
536
628
|
# TODO: need to support a time limit, in case the server fails to respond.
|
537
629
|
# Unlike other operation-methods in this class, we return a result hash
|
@@ -542,9 +634,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
542
634
|
def add(args)
|
543
635
|
add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN"
|
544
636
|
add_attrs = []
|
545
|
-
a = args[:attributes] and a.each
|
546
|
-
add_attrs << [
|
547
|
-
|
637
|
+
a = args[:attributes] and a.each do |k, v|
|
638
|
+
add_attrs << [k.to_s.to_ber, Array(v).map(&:to_ber).to_ber_set].to_ber_sequence
|
639
|
+
end
|
548
640
|
|
549
641
|
message_id = next_msgid
|
550
642
|
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(Net::LDAP::PDU::AddRequest)
|
@@ -600,4 +692,33 @@ class Net::LDAP::Connection #:nodoc:
|
|
600
692
|
|
601
693
|
pdu
|
602
694
|
end
|
695
|
+
|
696
|
+
# Internal: Returns a Socket like object used internally to communicate with
|
697
|
+
# LDAP server.
|
698
|
+
#
|
699
|
+
# Typically a TCPSocket, but can be a OpenSSL::SSL::SSLSocket
|
700
|
+
def socket
|
701
|
+
return @conn if defined?(@conn) && !@conn.nil?
|
702
|
+
|
703
|
+
# First refactoring uses the existing methods open_connection and
|
704
|
+
# prepare_socket to set @conn. Next cleanup would centralize connection
|
705
|
+
# handling here.
|
706
|
+
if @server[:socket]
|
707
|
+
prepare_socket(@server)
|
708
|
+
else
|
709
|
+
@server[:hosts] = [[@server[:host], @server[:port]]] if @server[:hosts].nil?
|
710
|
+
open_connection(@server)
|
711
|
+
end
|
712
|
+
|
713
|
+
@conn
|
714
|
+
end
|
715
|
+
|
716
|
+
private
|
717
|
+
|
718
|
+
# Wrap around Socket.tcp to normalize with other Socket initializers
|
719
|
+
class DefaultSocket
|
720
|
+
def self.new(host, port, socket_opts = {})
|
721
|
+
Socket.tcp(host, port, **socket_opts)
|
722
|
+
end
|
723
|
+
end
|
603
724
|
end # class Connection
|
data/lib/net/ldap/dataset.rb
CHANGED
@@ -29,7 +29,7 @@ class Net::LDAP::Dataset < Hash
|
|
29
29
|
keys.sort.each do |dn|
|
30
30
|
ary << "dn: #{dn}"
|
31
31
|
|
32
|
-
attributes = self[dn].keys.map
|
32
|
+
attributes = self[dn].keys.map(&:to_s).sort
|
33
33
|
attributes.each do |attr|
|
34
34
|
self[dn][attr.to_sym].each do |value|
|
35
35
|
if attr == "userpassword" or value_is_binary?(value)
|
@@ -103,7 +103,7 @@ class Net::LDAP::Dataset < Hash
|
|
103
103
|
# with the conversion of
|
104
104
|
def from_entry(entry)
|
105
105
|
dataset = Net::LDAP::Dataset.new
|
106
|
-
hash = {
|
106
|
+
hash = {}
|
107
107
|
entry.each_attribute do |attribute, value|
|
108
108
|
next if attribute == :dn
|
109
109
|
hash[attribute] = value
|
@@ -141,7 +141,7 @@ class Net::LDAP::Dataset < Hash
|
|
141
141
|
# $' is the dn-value
|
142
142
|
# Avoid the Base64 class because not all Ruby versions have it.
|
143
143
|
dn = ($1 == ":") ? $'.unpack('m').shift : $'
|
144
|
-
ds[dn] = Hash.new { |k,v| k[v] = [] }
|
144
|
+
ds[dn] = Hash.new { |k, v| k[v] = [] }
|
145
145
|
yield :dn, dn if block_given?
|
146
146
|
elsif line.empty?
|
147
147
|
dn = nil
|
@@ -164,5 +164,3 @@ class Net::LDAP::Dataset < Hash
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
end
|
167
|
-
|
168
|
-
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
|
@@ -81,7 +81,7 @@ class Net::LDAP::DN
|
|
81
81
|
value << char
|
82
82
|
when ',' then
|
83
83
|
state = :key
|
84
|
-
yield key.string.strip, value.string
|
84
|
+
yield key.string.strip, value.string
|
85
85
|
key = StringIO.new
|
86
86
|
value = StringIO.new;
|
87
87
|
else
|
@@ -93,7 +93,7 @@ class Net::LDAP::DN
|
|
93
93
|
when '\\' then state = :value_normal_escape
|
94
94
|
when ',' then
|
95
95
|
state = :key
|
96
|
-
yield key.string.strip, value.string
|
96
|
+
yield key.string.strip, value.string
|
97
97
|
key = StringIO.new
|
98
98
|
value = StringIO.new;
|
99
99
|
else value << 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
|
@@ -142,38 +142,37 @@ class Net::LDAP::DN
|
|
142
142
|
when ' ' then state = :value_end
|
143
143
|
when ',' then
|
144
144
|
state = :key
|
145
|
-
yield key.string.strip, value.string
|
145
|
+
yield key.string.strip, value.string
|
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
|
159
159
|
when ' ' then state = :value_end
|
160
160
|
when ',' then
|
161
161
|
state = :key
|
162
|
-
yield key.string.strip, value.string
|
162
|
+
yield key.string.strip, value.string
|
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
|
177
176
|
end
|
178
177
|
|
179
178
|
##
|
@@ -193,27 +192,19 @@ class Net::LDAP::DN
|
|
193
192
|
# http://tools.ietf.org/html/rfc2253 section 2.4 lists these exceptions
|
194
193
|
# for dn values. All of the following must be escaped in any normal string
|
195
194
|
# using a single backslash ('\') as escape.
|
196
|
-
ESCAPES =
|
197
|
-
',' => ',',
|
198
|
-
'+' => '+',
|
199
|
-
'"' => '"',
|
200
|
-
'\\' => '\\',
|
201
|
-
'<' => '<',
|
202
|
-
'>' => '>',
|
203
|
-
';' => ';',
|
204
|
-
}
|
195
|
+
ESCAPES = %w[, + " \\ < > ;]
|
205
196
|
|
206
|
-
# Compiled character class regexp using the
|
197
|
+
# Compiled character class regexp using the values from the above list, and
|
207
198
|
# checking for a space or # at the start, or space at the end, of the
|
208
199
|
# string.
|
209
200
|
ESCAPE_RE = Regexp.new("(^ |^#| $|[" +
|
210
|
-
ESCAPES.
|
201
|
+
ESCAPES.map { |e| Regexp.escape(e) }.join +
|
211
202
|
"])")
|
212
203
|
|
213
204
|
##
|
214
205
|
# Escape a string for use in a DN value
|
215
206
|
def self.escape(string)
|
216
|
-
string.gsub(ESCAPE_RE) { |char| "\\" +
|
207
|
+
string.gsub(ESCAPE_RE) { |char| "\\" + char }
|
217
208
|
end
|
218
209
|
|
219
210
|
##
|
data/lib/net/ldap/entry.rb
CHANGED
@@ -133,6 +133,13 @@ class Net::LDAP::Entry
|
|
133
133
|
@myhash.keys
|
134
134
|
end
|
135
135
|
|
136
|
+
##
|
137
|
+
# Creates a duplicate of the internal Hash containing the attributes
|
138
|
+
# of the entry.
|
139
|
+
def to_h
|
140
|
+
@myhash.dup
|
141
|
+
end
|
142
|
+
|
136
143
|
##
|
137
144
|
# Accesses each of the attributes present in the Entry.
|
138
145
|
#
|
@@ -140,11 +147,10 @@ class Net::LDAP::Entry
|
|
140
147
|
# arguments to the block: a Symbol giving the name of the attribute, and a
|
141
148
|
# (possibly empty) \Array of data values.
|
142
149
|
def each # :yields: attribute-name, data-values-array
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
}
|
150
|
+
return unless block_given?
|
151
|
+
attribute_names.each do|a|
|
152
|
+
attr_name, values = a, self[a]
|
153
|
+
yield attr_name, values
|
148
154
|
end
|
149
155
|
end
|
150
156
|
alias_method :each_attribute, :each
|
@@ -188,6 +194,8 @@ class Net::LDAP::Entry
|
|
188
194
|
sym.to_s[-1] == ?=
|
189
195
|
end
|
190
196
|
private :setter?
|
191
|
-
end # class Entry
|
192
197
|
|
193
|
-
|
198
|
+
def ==(other)
|
199
|
+
other.instance_of?(self.class) && @myhash == other.to_h
|
200
|
+
end
|
201
|
+
end # class Entry
|
data/lib/net/ldap/error.rb
CHANGED
@@ -1,37 +1,13 @@
|
|
1
1
|
class Net::LDAP
|
2
|
-
class LdapError < StandardError
|
3
|
-
def message
|
4
|
-
"Deprecation warning: Net::LDAP::LdapError is no longer used. Use Net::LDAP::Error or rescue one of it's subclasses. \n" + super
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
2
|
class Error < StandardError; end
|
9
3
|
|
10
4
|
class AlreadyOpenedError < Error; end
|
11
5
|
class SocketError < Error; end
|
12
|
-
class ConnectionRefusedError < Error;
|
13
|
-
def initialize(*args)
|
14
|
-
warn_deprecation_message
|
15
|
-
super
|
16
|
-
end
|
17
|
-
|
18
|
-
def message
|
19
|
-
warn_deprecation_message
|
20
|
-
super
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
def warn_deprecation_message
|
25
|
-
warn "Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead."
|
26
|
-
end
|
27
|
-
end
|
28
6
|
class ConnectionError < Error
|
29
7
|
def self.new(errors)
|
30
8
|
error = errors.first.first
|
31
9
|
if errors.size == 1
|
32
|
-
if error.
|
33
|
-
return Net::LDAP::ConnectionRefusedError.new(error.message)
|
34
|
-
end
|
10
|
+
return error if error.is_a? Errno::ECONNREFUSED
|
35
11
|
|
36
12
|
return Net::LDAP::Error.new(error.message)
|
37
13
|
end
|
@@ -59,6 +35,7 @@ class Net::LDAP
|
|
59
35
|
class ResponseTypeInvalidError < Error; end
|
60
36
|
class ResponseMissingOrInvalidError < Error; end
|
61
37
|
class EmptyDNError < Error; end
|
38
|
+
class InvalidDNError < Error; end
|
62
39
|
class HashTypeUnsupportedError < Error; end
|
63
40
|
class OperatorError < Error; end
|
64
41
|
class SubstringFilterError < Error; end
|