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.
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?