net-ldap 0.12.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/Contributors.rdoc +1 -0
- data/History.rdoc +89 -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 +163 -50
- data/lib/net/ldap/dataset.rb +5 -5
- data/lib/net/ldap/dn.rb +13 -14
- data/lib/net/ldap/entry.rb +17 -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 +209 -90
- data/lib/net/snmp.rb +19 -19
- data/lib/net-ldap.rb +1 -1
- metadata +30 -99
- 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)
|
23
34
|
socket = server[:socket]
|
24
35
|
encryption = server[:encryption]
|
25
36
|
|
26
37
|
@conn = socket
|
27
|
-
setup_encryption
|
38
|
+
setup_encryption(encryption, timeout) 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)
|
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
|
@@ -60,7 +85,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
60
85
|
end
|
61
86
|
end
|
62
87
|
|
63
|
-
def self.wrap_with_ssl(io, tls_options = {})
|
88
|
+
def self.wrap_with_ssl(io, tls_options = {}, timeout=nil)
|
64
89
|
raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
|
65
90
|
|
66
91
|
ctx = OpenSSL::SSL::SSLContext.new
|
@@ -70,7 +95,22 @@ class Net::LDAP::Connection #:nodoc:
|
|
70
95
|
ctx.set_params(tls_options) unless tls_options.empty?
|
71
96
|
|
72
97
|
conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
|
73
|
-
|
98
|
+
|
99
|
+
begin
|
100
|
+
if timeout
|
101
|
+
conn.connect_nonblock
|
102
|
+
else
|
103
|
+
conn.connect
|
104
|
+
end
|
105
|
+
rescue IO::WaitReadable
|
106
|
+
raise Errno::ETIMEDOUT, "OpenSSL connection read timeout" unless
|
107
|
+
IO.select([conn], nil, nil, timeout)
|
108
|
+
retry
|
109
|
+
rescue IO::WaitWritable
|
110
|
+
raise Errno::ETIMEDOUT, "OpenSSL connection write timeout" unless
|
111
|
+
IO.select(nil, [conn], nil, timeout)
|
112
|
+
retry
|
113
|
+
end
|
74
114
|
|
75
115
|
# Doesn't work:
|
76
116
|
# conn.sync_close = true
|
@@ -107,17 +147,17 @@ class Net::LDAP::Connection #:nodoc:
|
|
107
147
|
# communications, as with simple_tls. Thanks for Kouhei Sutou for
|
108
148
|
# generously contributing the :start_tls path.
|
109
149
|
#++
|
110
|
-
def setup_encryption(args)
|
150
|
+
def setup_encryption(args, timeout=nil)
|
111
151
|
args[:tls_options] ||= {}
|
112
152
|
case args[:method]
|
113
153
|
when :simple_tls
|
114
|
-
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
|
154
|
+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
|
115
155
|
# additional branches requiring server validation and peer certs, etc.
|
116
156
|
# go here.
|
117
157
|
when :start_tls
|
118
158
|
message_id = next_msgid
|
119
159
|
request = [
|
120
|
-
Net::LDAP::StartTlsOid.to_ber_contextspecific(0)
|
160
|
+
Net::LDAP::StartTlsOid.to_ber_contextspecific(0),
|
121
161
|
].to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
122
162
|
|
123
163
|
write(request, nil, message_id)
|
@@ -127,11 +167,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
127
167
|
raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
|
128
168
|
end
|
129
169
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
raise Net::LDAP::StartTlSError, "start_tls failed: #{pdu.result_code}"
|
134
|
-
end
|
170
|
+
raise Net::LDAP::StartTLSError,
|
171
|
+
"start_tls failed: #{pdu.result_code}" unless pdu.result_code.zero?
|
172
|
+
@conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
|
135
173
|
else
|
136
174
|
raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
|
137
175
|
end
|
@@ -143,7 +181,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
143
181
|
# have to call it, but perhaps it will come in handy someday.
|
144
182
|
#++
|
145
183
|
def close
|
146
|
-
return if @conn.nil?
|
184
|
+
return if !defined?(@conn) || @conn.nil?
|
147
185
|
@conn.close
|
148
186
|
@conn = nil
|
149
187
|
end
|
@@ -161,12 +199,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
161
199
|
|
162
200
|
# read messages until we have a match for the given message_id
|
163
201
|
while pdu = read
|
164
|
-
if pdu.message_id == message_id
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
next
|
169
|
-
end
|
202
|
+
return pdu if pdu.message_id == message_id
|
203
|
+
|
204
|
+
message_queue[pdu.message_id].push pdu
|
205
|
+
next
|
170
206
|
end
|
171
207
|
|
172
208
|
pdu
|
@@ -195,7 +231,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
195
231
|
def read(syntax = Net::LDAP::AsnSyntax)
|
196
232
|
ber_object =
|
197
233
|
instrument "read.net_ldap_connection", :syntax => syntax do |payload|
|
198
|
-
|
234
|
+
socket.read_ber(syntax) do |id, content_length|
|
199
235
|
payload[:object_type_id] = id
|
200
236
|
payload[:content_length] = content_length
|
201
237
|
end
|
@@ -225,7 +261,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
225
261
|
def write(request, controls = nil, message_id = next_msgid)
|
226
262
|
instrument "write.net_ldap_connection" do |payload|
|
227
263
|
packet = [message_id.to_ber, request, controls].compact.to_ber_sequence
|
228
|
-
payload[:content_length] =
|
264
|
+
payload[:content_length] = socket.write(packet)
|
229
265
|
end
|
230
266
|
end
|
231
267
|
private :write
|
@@ -264,10 +300,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
264
300
|
control[2] = (control[2] == true).to_ber
|
265
301
|
control.to_ber_sequence
|
266
302
|
end
|
267
|
-
|
303
|
+
[
|
268
304
|
Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
|
269
305
|
false.to_ber,
|
270
|
-
sort_control_values.to_ber_sequence.to_s.to_ber
|
306
|
+
sort_control_values.to_ber_sequence.to_s.to_ber,
|
271
307
|
].to_ber_sequence
|
272
308
|
end
|
273
309
|
|
@@ -364,12 +400,11 @@ class Net::LDAP::Connection #:nodoc:
|
|
364
400
|
# should collect this into a private helper to clarify the structure
|
365
401
|
query_limit = 0
|
366
402
|
if size > 0
|
367
|
-
if paged
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
end
|
403
|
+
query_limit = if paged
|
404
|
+
(((size - n_results) < 126) ? (size - n_results) : 0)
|
405
|
+
else
|
406
|
+
size
|
407
|
+
end
|
373
408
|
end
|
374
409
|
|
375
410
|
request = [
|
@@ -380,7 +415,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
380
415
|
time.to_ber,
|
381
416
|
attrs_only.to_ber,
|
382
417
|
filter.to_ber,
|
383
|
-
ber_attrs.to_ber_sequence
|
418
|
+
ber_attrs.to_ber_sequence,
|
384
419
|
].to_ber_appsequence(Net::LDAP::PDU::SearchRequest)
|
385
420
|
|
386
421
|
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
|
@@ -393,7 +428,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
393
428
|
Net::LDAP::LDAPControls::PAGED_RESULTS.to_ber,
|
394
429
|
# Criticality MUST be false to interoperate with normal LDAPs.
|
395
430
|
false.to_ber,
|
396
|
-
rfc2696_cookie.map
|
431
|
+
rfc2696_cookie.map(&:to_ber).to_ber_sequence.to_s.to_ber,
|
397
432
|
].to_ber_sequence if paged
|
398
433
|
controls << ber_sort if ber_sort
|
399
434
|
controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
|
@@ -432,6 +467,10 @@ class Net::LDAP::Connection #:nodoc:
|
|
432
467
|
end
|
433
468
|
end
|
434
469
|
|
470
|
+
if result_pdu.nil?
|
471
|
+
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
|
472
|
+
end
|
473
|
+
|
435
474
|
# count number of pages of results
|
436
475
|
payload[:page_count] ||= 0
|
437
476
|
payload[:page_count] += 1
|
@@ -487,20 +526,20 @@ class Net::LDAP::Connection #:nodoc:
|
|
487
526
|
MODIFY_OPERATIONS = { #:nodoc:
|
488
527
|
:add => 0,
|
489
528
|
:delete => 1,
|
490
|
-
:replace => 2
|
529
|
+
:replace => 2,
|
491
530
|
}
|
492
531
|
|
493
532
|
def self.modify_ops(operations)
|
494
533
|
ops = []
|
495
534
|
if operations
|
496
|
-
operations.each
|
535
|
+
operations.each do |op, attrib, values|
|
497
536
|
# TODO, fix the following line, which gives a bogus error if the
|
498
537
|
# opcode is invalid.
|
499
538
|
op_ber = MODIFY_OPERATIONS[op.to_sym].to_ber_enumerated
|
500
|
-
values = [
|
501
|
-
values = [
|
502
|
-
ops << [
|
503
|
-
|
539
|
+
values = [values].flatten.map { |v| v.to_ber if v }.to_ber_set
|
540
|
+
values = [attrib.to_s.to_ber, values].to_ber_sequence
|
541
|
+
ops << [op_ber, values].to_ber
|
542
|
+
end
|
504
543
|
end
|
505
544
|
ops
|
506
545
|
end
|
@@ -519,7 +558,7 @@ class Net::LDAP::Connection #:nodoc:
|
|
519
558
|
message_id = next_msgid
|
520
559
|
request = [
|
521
560
|
modify_dn.to_ber,
|
522
|
-
ops.to_ber_sequence
|
561
|
+
ops.to_ber_sequence,
|
523
562
|
].to_ber_appsequence(Net::LDAP::PDU::ModifyRequest)
|
524
563
|
|
525
564
|
write(request, nil, message_id)
|
@@ -532,6 +571,51 @@ class Net::LDAP::Connection #:nodoc:
|
|
532
571
|
pdu
|
533
572
|
end
|
534
573
|
|
574
|
+
##
|
575
|
+
# Password Modify
|
576
|
+
#
|
577
|
+
# http://tools.ietf.org/html/rfc3062
|
578
|
+
#
|
579
|
+
# passwdModifyOID OBJECT IDENTIFIER ::= 1.3.6.1.4.1.4203.1.11.1
|
580
|
+
#
|
581
|
+
# PasswdModifyRequestValue ::= SEQUENCE {
|
582
|
+
# userIdentity [0] OCTET STRING OPTIONAL
|
583
|
+
# oldPasswd [1] OCTET STRING OPTIONAL
|
584
|
+
# newPasswd [2] OCTET STRING OPTIONAL }
|
585
|
+
#
|
586
|
+
# PasswdModifyResponseValue ::= SEQUENCE {
|
587
|
+
# genPasswd [0] OCTET STRING OPTIONAL }
|
588
|
+
#
|
589
|
+
# Encoded request:
|
590
|
+
#
|
591
|
+
# 00\x02\x01\x02w+\x80\x171.3.6.1.4.1.4203.1.11.1\x81\x100\x0E\x81\x05old\x82\x05new
|
592
|
+
#
|
593
|
+
def password_modify(args)
|
594
|
+
dn = args[:dn]
|
595
|
+
raise ArgumentError, 'DN is required' if !dn || dn.empty?
|
596
|
+
|
597
|
+
ext_seq = [Net::LDAP::PasswdModifyOid.to_ber_contextspecific(0)]
|
598
|
+
|
599
|
+
pwd_seq = []
|
600
|
+
pwd_seq << dn.to_ber(0x80)
|
601
|
+
pwd_seq << args[:old_password].to_ber(0x81) unless args[:old_password].nil?
|
602
|
+
pwd_seq << args[:new_password].to_ber(0x82) unless args[:new_password].nil?
|
603
|
+
ext_seq << pwd_seq.to_ber_sequence.to_ber(0x81)
|
604
|
+
|
605
|
+
request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
|
606
|
+
|
607
|
+
message_id = next_msgid
|
608
|
+
|
609
|
+
write(request, nil, message_id)
|
610
|
+
pdu = queued_read(message_id)
|
611
|
+
|
612
|
+
if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
|
613
|
+
raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
|
614
|
+
end
|
615
|
+
|
616
|
+
pdu
|
617
|
+
end
|
618
|
+
|
535
619
|
#--
|
536
620
|
# TODO: need to support a time limit, in case the server fails to respond.
|
537
621
|
# Unlike other operation-methods in this class, we return a result hash
|
@@ -542,9 +626,9 @@ class Net::LDAP::Connection #:nodoc:
|
|
542
626
|
def add(args)
|
543
627
|
add_dn = args[:dn] or raise Net::LDAP::EmptyDNError, "Unable to add empty DN"
|
544
628
|
add_attrs = []
|
545
|
-
a = args[:attributes] and a.each
|
546
|
-
add_attrs << [
|
547
|
-
|
629
|
+
a = args[:attributes] and a.each do |k, v|
|
630
|
+
add_attrs << [k.to_s.to_ber, Array(v).map(&:to_ber).to_ber_set].to_ber_sequence
|
631
|
+
end
|
548
632
|
|
549
633
|
message_id = next_msgid
|
550
634
|
request = [add_dn.to_ber, add_attrs.to_ber_sequence].to_ber_appsequence(Net::LDAP::PDU::AddRequest)
|
@@ -600,4 +684,33 @@ class Net::LDAP::Connection #:nodoc:
|
|
600
684
|
|
601
685
|
pdu
|
602
686
|
end
|
687
|
+
|
688
|
+
# Internal: Returns a Socket like object used internally to communicate with
|
689
|
+
# LDAP server.
|
690
|
+
#
|
691
|
+
# Typically a TCPSocket, but can be a OpenSSL::SSL::SSLSocket
|
692
|
+
def socket
|
693
|
+
return @conn if defined?(@conn) && !@conn.nil?
|
694
|
+
|
695
|
+
# First refactoring uses the existing methods open_connection and
|
696
|
+
# prepare_socket to set @conn. Next cleanup would centralize connection
|
697
|
+
# handling here.
|
698
|
+
if @server[:socket]
|
699
|
+
prepare_socket(@server)
|
700
|
+
else
|
701
|
+
@server[:hosts] = [[@server[:host], @server[:port]]] if @server[:hosts].nil?
|
702
|
+
open_connection(@server)
|
703
|
+
end
|
704
|
+
|
705
|
+
@conn
|
706
|
+
end
|
707
|
+
|
708
|
+
private
|
709
|
+
|
710
|
+
# Wrap around Socket.tcp to normalize with other Socket initializers
|
711
|
+
class DefaultSocket
|
712
|
+
def self.new(host, port, socket_opts = {})
|
713
|
+
Socket.tcp(host, port, **socket_opts)
|
714
|
+
end
|
715
|
+
end
|
603
716
|
end # class Connection
|
data/lib/net/ldap/dataset.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
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
|
@@ -29,7 +31,7 @@ class Net::LDAP::Dataset < Hash
|
|
29
31
|
keys.sort.each do |dn|
|
30
32
|
ary << "dn: #{dn}"
|
31
33
|
|
32
|
-
attributes = self[dn].keys.map
|
34
|
+
attributes = self[dn].keys.map(&:to_s).sort
|
33
35
|
attributes.each do |attr|
|
34
36
|
self[dn][attr.to_sym].each do |value|
|
35
37
|
if attr == "userpassword" or value_is_binary?(value)
|
@@ -103,7 +105,7 @@ class Net::LDAP::Dataset < Hash
|
|
103
105
|
# with the conversion of
|
104
106
|
def from_entry(entry)
|
105
107
|
dataset = Net::LDAP::Dataset.new
|
106
|
-
hash = {
|
108
|
+
hash = {}
|
107
109
|
entry.each_attribute do |attribute, value|
|
108
110
|
next if attribute == :dn
|
109
111
|
hash[attribute] = value
|
@@ -141,7 +143,7 @@ class Net::LDAP::Dataset < Hash
|
|
141
143
|
# $' is the dn-value
|
142
144
|
# Avoid the Base64 class because not all Ruby versions have it.
|
143
145
|
dn = ($1 == ":") ? $'.unpack('m').shift : $'
|
144
|
-
ds[dn] = Hash.new { |k,v| k[v] = [] }
|
146
|
+
ds[dn] = Hash.new { |k, v| k[v] = [] }
|
145
147
|
yield :dn, dn if block_given?
|
146
148
|
elsif line.empty?
|
147
149
|
dn = nil
|
@@ -164,5 +166,3 @@ class Net::LDAP::Dataset < Hash
|
|
164
166
|
end
|
165
167
|
end
|
166
168
|
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
|
@@ -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.
|
@@ -133,6 +135,13 @@ class Net::LDAP::Entry
|
|
133
135
|
@myhash.keys
|
134
136
|
end
|
135
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
|
+
|
136
145
|
##
|
137
146
|
# Accesses each of the attributes present in the Entry.
|
138
147
|
#
|
@@ -140,11 +149,10 @@ class Net::LDAP::Entry
|
|
140
149
|
# arguments to the block: a Symbol giving the name of the attribute, and a
|
141
150
|
# (possibly empty) \Array of data values.
|
142
151
|
def each # :yields: attribute-name, data-values-array
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
}
|
152
|
+
return unless block_given?
|
153
|
+
attribute_names.each do|a|
|
154
|
+
attr_name, values = a, self[a]
|
155
|
+
yield attr_name, values
|
148
156
|
end
|
149
157
|
end
|
150
158
|
alias_method :each_attribute, :each
|
@@ -188,6 +196,8 @@ class Net::LDAP::Entry
|
|
188
196
|
sym.to_s[-1] == ?=
|
189
197
|
end
|
190
198
|
private :setter?
|
191
|
-
end # class Entry
|
192
199
|
|
193
|
-
|
200
|
+
def ==(other)
|
201
|
+
other.instance_of?(self.class) && @myhash == other.to_h
|
202
|
+
end
|
203
|
+
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
|
data/lib/net/ldap/filter.rb
CHANGED
@@ -23,7 +23,7 @@
|
|
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)
|
@@ -287,7 +287,7 @@ class Net::LDAP::Filter
|
|
287
287
|
when 0xa4 # context-specific constructed 4, "substring"
|
288
288
|
str = ""
|
289
289
|
final = false
|
290
|
-
ber.last.each
|
290
|
+
ber.last.each do |b|
|
291
291
|
case b.ber_identifier
|
292
292
|
when 0x80 # context-specific primitive 0, SubstringFilter "initial"
|
293
293
|
raise Net::LDAP::SubstringFilterError, "Unrecognized substring filter; bad initial value." if str.length > 0
|
@@ -298,7 +298,7 @@ class Net::LDAP::Filter
|
|
298
298
|
str += "*#{escape(b)}"
|
299
299
|
final = true
|
300
300
|
end
|
301
|
-
|
301
|
+
end
|
302
302
|
str += "*" unless final
|
303
303
|
eq(ber.first.to_s, str)
|
304
304
|
when 0xa5 # context-specific constructed 5, "greaterOrEqual"
|
@@ -490,7 +490,7 @@ class Net::LDAP::Filter
|
|
490
490
|
when :eq
|
491
491
|
if @right == "*" # presence test
|
492
492
|
@left.to_s.to_ber_contextspecific(7)
|
493
|
-
elsif @right =~ /[*]/ # substring
|
493
|
+
elsif @right.to_s =~ /[*]/ # substring
|
494
494
|
# Parsing substrings is a little tricky. We use String#split to
|
495
495
|
# break a string into substrings delimited by the * (star)
|
496
496
|
# character. But we also need to know whether there is a star at the
|
@@ -550,10 +550,10 @@ class Net::LDAP::Filter
|
|
550
550
|
[self.class.eq(@left, @right).to_ber].to_ber_contextspecific(2)
|
551
551
|
when :and
|
552
552
|
ary = [@left.coalesce(:and), @right.coalesce(:and)].flatten
|
553
|
-
ary.map
|
553
|
+
ary.map(&:to_ber).to_ber_contextspecific(0)
|
554
554
|
when :or
|
555
555
|
ary = [@left.coalesce(:or), @right.coalesce(:or)].flatten
|
556
|
-
ary.map
|
556
|
+
ary.map(&:to_ber).to_ber_contextspecific(1)
|
557
557
|
when :not
|
558
558
|
[@left.to_ber].to_ber_contextspecific(2)
|
559
559
|
end
|
@@ -645,8 +645,15 @@ class Net::LDAP::Filter
|
|
645
645
|
|
646
646
|
##
|
647
647
|
# Converts escaped characters (e.g., "\\28") to unescaped characters
|
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
|
648
650
|
def unescape(right)
|
649
|
-
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
|
650
657
|
end
|
651
658
|
private :unescape
|
652
659
|
|
@@ -748,7 +755,7 @@ class Net::LDAP::Filter
|
|
748
755
|
# This parses a given expression inside of parentheses.
|
749
756
|
def parse_filter_branch(scanner)
|
750
757
|
scanner.scan(/\s*/)
|
751
|
-
if token = scanner.scan(/[-\w
|
758
|
+
if token = scanner.scan(/[-\w:.;]*[\w]/)
|
752
759
|
scanner.scan(/\s*/)
|
753
760
|
if op = scanner.scan(/<=|>=|!=|:=|=/)
|
754
761
|
scanner.scan(/\s*/)
|
@@ -12,8 +12,8 @@ module Net::LDAP::Instrumentation
|
|
12
12
|
def instrument(event, payload = {})
|
13
13
|
payload = (payload || {}).dup
|
14
14
|
if instrumentation_service
|
15
|
-
instrumentation_service.instrument(event, payload) do |
|
16
|
-
|
15
|
+
instrumentation_service.instrument(event, payload) do |instr_payload|
|
16
|
+
instr_payload[:result] = yield(instr_payload) if block_given?
|
17
17
|
end
|
18
18
|
else
|
19
19
|
yield(payload) if block_given?
|