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.
Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/Contributors.rdoc +1 -0
  3. data/History.rdoc +112 -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 +173 -52
  15. data/lib/net/ldap/dataset.rb +3 -5
  16. data/lib/net/ldap/dn.rb +21 -30
  17. data/lib/net/ldap/entry.rb +15 -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 +212 -91
  25. data/lib/net/snmp.rb +19 -19
  26. data/lib/net-ldap.rb +1 -1
  27. metadata +27 -96
  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, hostname='127.0.0.1')
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, 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: TCPSocket.new(host, port)))
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.ord
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.connect
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
- 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
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
- return pdu
166
- else
167
- message_queue[pdu.message_id].push pdu
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
- @conn.read_ber(syntax) do |id, content_length|
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] = @conn.write(packet)
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
- sort_control = [
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
- query_limit = (((size - n_results) < 126) ? (size -
369
- n_results) : 0)
370
- else
371
- query_limit = size
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{ |v| v.to_ber}.to_ber_sequence.to_s.to_ber
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
- controls = controls.empty? ? nil : controls.to_ber_contextspecific(0)
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 { |op, attrib, values|
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 = [ 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
- }
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 { |k, v|
546
- add_attrs << [ k.to_s.to_ber, Array(v).map { |m| m.to_ber}.to_ber_set ].to_ber_sequence
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
@@ -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 { |attr| attr.to_s }.sort
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.rstrip
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.rstrip
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.rstrip
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.rstrip
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
- 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
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 keys from the above hash, and
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.keys.map { |e| Regexp.escape(e) }.join +
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| "\\" + ESCAPES[char] }
207
+ string.gsub(ESCAPE_RE) { |char| "\\" + char }
217
208
  end
218
209
 
219
210
  ##
@@ -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
- if block_given?
144
- attribute_names.each {|a|
145
- attr_name,values = a,self[a]
146
- yield attr_name, values
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
- require 'net/ldap/dataset' unless defined? Net::LDAP::Dataset
198
+ def ==(other)
199
+ other.instance_of?(self.class) && @myhash == other.to_h
200
+ end
201
+ 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