net-ldap 0.12.0 → 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.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/Contributors.rdoc +1 -0
  3. data/History.rdoc +89 -0
  4. data/README.rdoc +19 -9
  5. data/lib/net/ber/ber_parser.rb +4 -4
  6. data/lib/net/ber/core_ext/array.rb +1 -1
  7. data/lib/net/ber/core_ext/integer.rb +1 -1
  8. data/lib/net/ber/core_ext/string.rb +1 -1
  9. data/lib/net/ber/core_ext.rb +6 -6
  10. data/lib/net/ber.rb +39 -9
  11. data/lib/net/ldap/auth_adapter/gss_spnego.rb +9 -8
  12. data/lib/net/ldap/auth_adapter/sasl.rb +6 -4
  13. data/lib/net/ldap/auth_adapter/simple.rb +1 -1
  14. data/lib/net/ldap/connection.rb +163 -50
  15. data/lib/net/ldap/dataset.rb +5 -5
  16. data/lib/net/ldap/dn.rb +13 -14
  17. data/lib/net/ldap/entry.rb +17 -7
  18. data/lib/net/ldap/error.rb +2 -25
  19. data/lib/net/ldap/filter.rb +15 -8
  20. data/lib/net/ldap/instrumentation.rb +2 -2
  21. data/lib/net/ldap/password.rb +7 -5
  22. data/lib/net/ldap/pdu.rb +27 -3
  23. data/lib/net/ldap/version.rb +1 -1
  24. data/lib/net/ldap.rb +209 -90
  25. data/lib/net/snmp.rb +19 -19
  26. data/lib/net-ldap.rb +1 -1
  27. metadata +30 -99
  28. data/.gitignore +0 -9
  29. data/.rubocop.yml +0 -5
  30. data/.rubocop_todo.yml +0 -462
  31. data/.travis.yml +0 -31
  32. data/CONTRIBUTING.md +0 -54
  33. data/Gemfile +0 -2
  34. data/Rakefile +0 -23
  35. data/net-ldap.gemspec +0 -36
  36. data/script/changelog +0 -47
  37. data/script/install-openldap +0 -112
  38. data/script/package +0 -7
  39. data/script/release +0 -16
  40. data/test/ber/core_ext/test_array.rb +0 -22
  41. data/test/ber/core_ext/test_string.rb +0 -25
  42. data/test/ber/test_ber.rb +0 -145
  43. data/test/fixtures/cacert.pem +0 -20
  44. data/test/fixtures/openldap/memberof.ldif +0 -33
  45. data/test/fixtures/openldap/retcode.ldif +0 -76
  46. data/test/fixtures/openldap/slapd.conf.ldif +0 -67
  47. data/test/fixtures/seed.ldif +0 -374
  48. data/test/integration/test_add.rb +0 -28
  49. data/test/integration/test_ber.rb +0 -30
  50. data/test/integration/test_bind.rb +0 -34
  51. data/test/integration/test_delete.rb +0 -31
  52. data/test/integration/test_open.rb +0 -88
  53. data/test/integration/test_return_codes.rb +0 -38
  54. data/test/integration/test_search.rb +0 -77
  55. data/test/support/vm/openldap/.gitignore +0 -1
  56. data/test/support/vm/openldap/README.md +0 -32
  57. data/test/support/vm/openldap/Vagrantfile +0 -33
  58. data/test/test_auth_adapter.rb +0 -11
  59. data/test/test_dn.rb +0 -44
  60. data/test/test_entry.rb +0 -65
  61. data/test/test_filter.rb +0 -223
  62. data/test/test_filter_parser.rb +0 -24
  63. data/test/test_helper.rb +0 -66
  64. data/test/test_ldap.rb +0 -67
  65. data/test/test_ldap_connection.rb +0 -460
  66. data/test/test_ldif.rb +0 -104
  67. data/test/test_password.rb +0 -10
  68. data/test/test_rename.rb +0 -77
  69. data/test/test_search.rb +0 -39
  70. data/test/test_snmp.rb +0 -119
  71. data/test/test_ssl_ber.rb +0 -40
  72. data/test/testdata.ldif +0 -101
  73. data/testserver/ldapserver.rb +0 -210
  74. data/testserver/testdata.ldif +0 -101
@@ -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
- def initialize(server)
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
- if server[:socket]
13
- prepare_socket(server)
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 prepare_socket(server)
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 encryption if 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: TCPSocket.new(host, port)))
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
- conn.connect
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
- if pdu.result_code.zero?
131
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
132
- else
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
- return pdu
166
- else
167
- message_queue[pdu.message_id].push pdu
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
- @conn.read_ber(syntax) do |id, content_length|
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] = @conn.write(packet)
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
- sort_control = [
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
- query_limit = (((size - n_results) < 126) ? (size -
369
- n_results) : 0)
370
- else
371
- query_limit = size
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{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
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 { |op, attrib, values|
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 = [ values ].flatten.map { |v| v.to_ber if v }.to_ber_set
501
- values = [ attrib.to_s.to_ber, values ].to_ber_sequence
502
- ops << [ op_ber, values ].to_ber
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 { |k, v|
546
- add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
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
@@ -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 { |attr| attr.to_s }.sort
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
- if [:value, :value_normal, :value_hexstring, :value_end].include? state
173
- yield key.string.strip, value.string.rstrip
174
- else
175
- raise "DN badly formed"
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
  ##
@@ -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
- if block_given?
144
- attribute_names.each {|a|
145
- attr_name,values = a,self[a]
146
- yield attr_name, values
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
- require 'net/ldap/dataset' unless defined? Net::LDAP::Dataset
200
+ def ==(other)
201
+ other.instance_of?(self.class) && @myhash == other.to_h
202
+ end
203
+ end # class Entry
@@ -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.kind_of? Errno::ECONNREFUSED
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
@@ -23,7 +23,7 @@
23
23
  class Net::LDAP::Filter
24
24
  ##
25
25
  # Known filter types.
26
- FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq ]
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 { |b|
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 {|a| a.to_ber}.to_ber_contextspecific(0)
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 {|a| a.to_ber}.to_ber_contextspecific(1)
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.to_s.gsub(/\\([a-fA-F\d]{2})/) { [$1.hex].pack("U") }
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:.]*[\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 |payload|
16
- payload[:result] = yield(payload) if block_given?
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?