net-ldap 0.14.0 → 0.16.3

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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +5 -2
  4. data/.rubocop_todo.yml +343 -219
  5. data/.travis.yml +27 -3
  6. data/CONTRIBUTING.md +1 -1
  7. data/History.rdoc +21 -0
  8. data/README.rdoc +10 -7
  9. data/Rakefile +1 -1
  10. data/lib/net-ldap.rb +1 -1
  11. data/lib/net/ber.rb +5 -6
  12. data/lib/net/ber/ber_parser.rb +3 -3
  13. data/lib/net/ber/core_ext.rb +6 -6
  14. data/lib/net/ldap.rb +65 -55
  15. data/lib/net/ldap/auth_adapter/gss_spnego.rb +2 -2
  16. data/lib/net/ldap/auth_adapter/sasl.rb +4 -2
  17. data/lib/net/ldap/auth_adapter/simple.rb +1 -1
  18. data/lib/net/ldap/connection.rb +58 -35
  19. data/lib/net/ldap/dataset.rb +2 -2
  20. data/lib/net/ldap/dn.rb +13 -14
  21. data/lib/net/ldap/entry.rb +5 -6
  22. data/lib/net/ldap/error.rb +1 -0
  23. data/lib/net/ldap/filter.rb +10 -3
  24. data/lib/net/ldap/instrumentation.rb +2 -2
  25. data/lib/net/ldap/password.rb +3 -5
  26. data/lib/net/ldap/pdu.rb +1 -1
  27. data/lib/net/ldap/version.rb +1 -1
  28. data/lib/net/snmp.rb +1 -1
  29. data/net-ldap.gemspec +4 -4
  30. data/script/ldap-docker +12 -0
  31. data/test/ber/test_ber.rb +1 -1
  32. data/test/fixtures/ca/docker-ca.pem +18 -0
  33. data/test/fixtures/{openldap/retcode.ldif → ldif/06-retcode.ldif} +7 -8
  34. data/test/fixtures/ldif/50-seed.ldif +374 -0
  35. data/test/integration/test_add.rb +1 -3
  36. data/test/integration/test_ber.rb +2 -2
  37. data/test/integration/test_bind.rb +193 -14
  38. data/test/integration/test_delete.rb +1 -3
  39. data/test/integration/test_open.rb +10 -11
  40. data/test/integration/test_password_modify.rb +29 -16
  41. data/test/integration/test_return_codes.rb +12 -4
  42. data/test/integration/test_search.rb +8 -8
  43. data/test/test_dn.rb +2 -3
  44. data/test/test_entry.rb +3 -2
  45. data/test/test_filter_parser.rb +5 -0
  46. data/test/test_helper.rb +12 -5
  47. data/test/test_ldap.rb +5 -5
  48. data/test/test_ldap_connection.rb +47 -35
  49. data/test/test_ldif.rb +13 -13
  50. data/test/test_password.rb +2 -2
  51. data/test/test_snmp.rb +4 -5
  52. data/test/test_ssl_ber.rb +7 -3
  53. data/testserver/ldapserver.rb +13 -22
  54. metadata +17 -26
  55. data/script/install-openldap +0 -115
  56. data/test/fixtures/cacert.pem +0 -20
  57. data/test/fixtures/openldap/memberof.ldif +0 -33
  58. data/test/fixtures/openldap/slapd.conf.ldif +0 -67
  59. data/test/fixtures/seed.ldif +0 -374
  60. data/test/support/vm/openldap/README.md +0 -32
  61. data/test/support/vm/openldap/Vagrantfile +0 -33
@@ -3,11 +3,24 @@ rvm:
3
3
  - 2.0.0
4
4
  - 2.1
5
5
  - 2.2
6
+ - 2.3
7
+ - 2.4
8
+ - 2.5
9
+ - 2.6
10
+ - 2.7
11
+ - jruby-9.2
6
12
  # optional
7
13
  - ruby-head
8
14
  - jruby-19mode
15
+ - jruby-9.2
9
16
  - jruby-head
10
- - rbx-2
17
+
18
+ addons:
19
+ hosts:
20
+ - ldap.example.org # needed for TLS verification
21
+
22
+ services:
23
+ - docker
11
24
 
12
25
  env:
13
26
  - INTEGRATION=openldap
@@ -16,7 +29,18 @@ before_install:
16
29
  - gem update bundler
17
30
 
18
31
  install:
19
- - if [ "$INTEGRATION" = "openldap" ]; then sudo script/install-openldap; fi
32
+ - >
33
+ docker run \
34
+ --hostname ldap.example.org \
35
+ --env LDAP_TLS_VERIFY_CLIENT=try \
36
+ -p 389:389 \
37
+ -p 636:636 \
38
+ -v "$(pwd)"/test/fixtures/ldif:/container/service/slapd/assets/config/bootstrap/ldif/custom \
39
+ --name openldap \
40
+ --detach \
41
+ osixia/openldap:1.3.0 \
42
+ --copy-service \
43
+ --loglevel debug \
20
44
  - bundle install
21
45
 
22
46
  script: bundle exec rake ci
@@ -25,8 +49,8 @@ matrix:
25
49
  allow_failures:
26
50
  - rvm: ruby-head
27
51
  - rvm: jruby-19mode
52
+ - rvm: jruby-9.2
28
53
  - rvm: jruby-head
29
- - rvm: rbx-2
30
54
  fast_finish: true
31
55
 
32
56
  notifications:
@@ -49,6 +49,6 @@ MyClass.new \
49
49
  baz: 'garply'
50
50
  ```
51
51
 
52
- [issues]: https://github.com/ruby-net-ldap/ruby-net-ldap/issues
52
+ [issues]: https://github.com/ruby-ldap/ruby-net-ldap/issues
53
53
  [pr]: https://help.github.com/articles/using-pull-requests
54
54
  [travis]: https://travis-ci.org/ruby-ldap/ruby-net-ldap
@@ -1,3 +1,24 @@
1
+ === Net::LDAP 0.16.2
2
+
3
+ * Net::LDAP#open does not cache bind result {#334}[https://github.com/ruby-ldap/ruby-net-ldap/pull/334]
4
+ * Fix CI build {#333}[https://github.com/ruby-ldap/ruby-net-ldap/pull/333]
5
+ * Fix to "undefined method 'result_code'" {#308}[https://github.com/ruby-ldap/ruby-net-ldap/pull/308]
6
+ * Fixed Exception: incompatible character encodings: ASCII-8BIT and UTF-8 in filter.rb {#285}[https://github.com/ruby-ldap/ruby-net-ldap/pull/285]
7
+
8
+ === Net::LDAP 0.16.1
9
+
10
+ * Send DN and newPassword with password_modify request {#271}[https://github.com/ruby-ldap/ruby-net-ldap/pull/271]
11
+
12
+ === Net::LDAP 0.16.0
13
+
14
+ * Sasl fix {#281}[https://github.com/ruby-ldap/ruby-net-ldap/pull/281]
15
+ * enable TLS hostname validation {#279}[https://github.com/ruby-ldap/ruby-net-ldap/pull/279]
16
+ * update rubocop to 0.42.0 {#278}[https://github.com/ruby-ldap/ruby-net-ldap/pull/278]
17
+
18
+ === Net::LDAP 0.15.0
19
+
20
+ * Respect connect_timeout when establishing SSL connections {#273}[https://github.com/ruby-ldap/ruby-net-ldap/pull/273]
21
+
1
22
  === Net::LDAP 0.14.0
2
23
 
3
24
  * Normalize the encryption parameter passed to the LDAP constructor {#264}[https://github.com/ruby-ldap/ruby-net-ldap/pull/264]
@@ -1,4 +1,4 @@
1
- = Net::LDAP for Ruby {<img src="https://travis-ci.org/ruby-ldap/ruby-net-ldap.png" />}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
1
+ = Net::LDAP for Ruby {<img src="https://travis-ci.org/ruby-ldap/ruby-net-ldap.svg" />}[https://travis-ci.org/ruby-ldap/ruby-net-ldap]
2
2
 
3
3
  == Description
4
4
 
@@ -21,7 +21,7 @@ the most recent LDAP RFCs (4510–4519, plus portions of 4520–4532).
21
21
 
22
22
  == Synopsis
23
23
 
24
- See Net::LDAP for documentation and usage samples.
24
+ See {Net::LDAP on rubydoc.info}[https://www.rubydoc.info/gems/net-ldap/Net/LDAP] for documentation and usage samples.
25
25
 
26
26
  == Requirements
27
27
 
@@ -52,12 +52,15 @@ This task will run the test suite and the
52
52
 
53
53
  rake rubotest
54
54
 
55
- To run the integration tests against an LDAP server:
55
+ CI takes too long? If your local box supports
56
+ {Docker}[https://www.docker.com/], you can also run integration tests locally.
57
+ Simply run:
56
58
 
57
- cd test/support/vm/openldap
58
- vagrant up
59
- cd ../../../..
60
- INTEGRATION=openldap bundle exec rake rubotest
59
+ script/ldap-docker
60
+ INTEGRATION=openldap rake test
61
+
62
+ CAVEAT: you need to add the following line to /etc/hosts
63
+ 127.0.0.1 ldap.example.org
61
64
 
62
65
  == Release
63
66
 
data/Rakefile CHANGED
@@ -15,7 +15,7 @@ Rake::TestTask.new do |t|
15
15
  end
16
16
 
17
17
  desc 'Run tests and RuboCop (RuboCop runs on mri only)'
18
- task ci: [:test]
18
+ task ci: Bundler.current_ruby.mri? ? [:test, :rubocop] : [:test]
19
19
 
20
20
  desc 'Run tests and RuboCop'
21
21
  task rubotest: [:test, :rubocop]
@@ -1,2 +1,2 @@
1
1
  # -*- ruby encoding: utf-8 -*-
2
- require 'net/ldap'
2
+ require_relative 'net/ldap'
@@ -1,5 +1,5 @@
1
1
  # -*- ruby encoding: utf-8 -*-
2
- require 'net/ldap/version'
2
+ require_relative 'ldap/version'
3
3
 
4
4
  module Net # :nodoc:
5
5
  ##
@@ -327,11 +327,10 @@ class Net::BER::BerIdentifiedString < String
327
327
  # Check the encoding of the newly created String and set the encoding
328
328
  # to 'UTF-8' (NOTE: we do NOT change the bytes, but only set the
329
329
  # encoding to 'UTF-8').
330
+ return unless encoding == Encoding::BINARY
330
331
  current_encoding = encoding
331
- if current_encoding == Encoding::BINARY
332
- force_encoding('UTF-8')
333
- force_encoding(current_encoding) unless valid_encoding?
334
- end
332
+ force_encoding('UTF-8')
333
+ force_encoding(current_encoding) unless valid_encoding?
335
334
  end
336
335
  end
337
336
 
@@ -350,4 +349,4 @@ module Net::BER
350
349
  Null = Net::BER::BerIdentifiedNull.new
351
350
  end
352
351
 
353
- require 'net/ber/core_ext'
352
+ require_relative 'ber/core_ext'
@@ -172,10 +172,10 @@ module Net::BER::BERParser
172
172
  yield id, content_length if block_given?
173
173
 
174
174
  if -1 == content_length
175
- raise Net::BER::BerError, "Indeterminite BER content length not implemented."
176
- else
177
- data = read(content_length)
175
+ raise Net::BER::BerError,
176
+ "Indeterminite BER content length not implemented."
178
177
  end
178
+ data = read(content_length)
179
179
 
180
180
  parse_ber_object(syntax, id, data)
181
181
  end
@@ -1,5 +1,5 @@
1
1
  # -*- ruby encoding: utf-8 -*-
2
- require 'net/ber/ber_parser'
2
+ require_relative 'ber_parser'
3
3
  # :stopdoc:
4
4
  class IO
5
5
  include Net::BER::BERParser
@@ -19,35 +19,35 @@ end
19
19
  module Net::BER::Extensions # :nodoc:
20
20
  end
21
21
 
22
- require 'net/ber/core_ext/string'
22
+ require_relative 'core_ext/string'
23
23
  # :stopdoc:
24
24
  class String
25
25
  include Net::BER::BERParser
26
26
  include Net::BER::Extensions::String
27
27
  end
28
28
 
29
- require 'net/ber/core_ext/array'
29
+ require_relative 'core_ext/array'
30
30
  # :stopdoc:
31
31
  class Array
32
32
  include Net::BER::Extensions::Array
33
33
  end
34
34
  # :startdoc:
35
35
 
36
- require 'net/ber/core_ext/integer'
36
+ require_relative 'core_ext/integer'
37
37
  # :stopdoc:
38
38
  class Integer
39
39
  include Net::BER::Extensions::Integer
40
40
  end
41
41
  # :startdoc:
42
42
 
43
- require 'net/ber/core_ext/true_class'
43
+ require_relative 'core_ext/true_class'
44
44
  # :stopdoc:
45
45
  class TrueClass
46
46
  include Net::BER::Extensions::TrueClass
47
47
  end
48
48
  # :startdoc:
49
49
 
50
- require 'net/ber/core_ext/false_class'
50
+ require_relative 'core_ext/false_class'
51
51
  # :stopdoc:
52
52
  class FalseClass
53
53
  include Net::BER::Extensions::FalseClass
@@ -17,19 +17,19 @@ module Net # :nodoc:
17
17
  end
18
18
  require 'socket'
19
19
 
20
- require 'net/ber'
21
- require 'net/ldap/pdu'
22
- require 'net/ldap/filter'
23
- require 'net/ldap/dataset'
24
- require 'net/ldap/password'
25
- require 'net/ldap/entry'
26
- require 'net/ldap/instrumentation'
27
- require 'net/ldap/connection'
28
- require 'net/ldap/version'
29
- require 'net/ldap/error'
30
- require 'net/ldap/auth_adapter'
31
- require 'net/ldap/auth_adapter/simple'
32
- require 'net/ldap/auth_adapter/sasl'
20
+ require_relative 'ber'
21
+ require_relative 'ldap/pdu'
22
+ require_relative 'ldap/filter'
23
+ require_relative 'ldap/dataset'
24
+ require_relative 'ldap/password'
25
+ require_relative 'ldap/entry'
26
+ require_relative 'ldap/instrumentation'
27
+ require_relative 'ldap/connection'
28
+ require_relative 'ldap/version'
29
+ require_relative 'ldap/error'
30
+ require_relative 'ldap/auth_adapter'
31
+ require_relative 'ldap/auth_adapter/simple'
32
+ require_relative 'ldap/auth_adapter/sasl'
33
33
 
34
34
  Net::LDAP::AuthAdapter.register([:simple, :anon, :anonymous], Net::LDAP::AuthAdapter::Simple)
35
35
  Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
@@ -476,61 +476,73 @@ class Net::LDAP
476
476
  # specify a treebase. If you give a treebase value in any particular
477
477
  # call to #search, that value will override any treebase value you give
478
478
  # here.
479
+ # * :force_no_page => Set to true to prevent paged results even if your
480
+ # server says it supports them. This is a fix for MS Active Directory
481
+ # * :instrumentation_service => An object responsible for instrumenting
482
+ # operations, compatible with ActiveSupport::Notifications' public API.
479
483
  # * :encryption => specifies the encryption to be used in communicating
480
484
  # with the LDAP server. The value must be a Hash containing additional
481
485
  # parameters, which consists of two keys:
482
486
  # method: - :simple_tls or :start_tls
483
- # options: - Hash of options for that method
487
+ # tls_options: - Hash of options for that method
484
488
  # The :simple_tls encryption method encrypts <i>all</i> communications
485
489
  # with the LDAP server. It completely establishes SSL/TLS encryption with
486
490
  # the LDAP server before any LDAP-protocol data is exchanged. There is no
487
491
  # plaintext negotiation and no special encryption-request controls are
488
492
  # sent to the server. <i>The :simple_tls option is the simplest, easiest
489
493
  # way to encrypt communications between Net::LDAP and LDAP servers.</i>
490
- # It's intended for cases where you have an implicit level of trust in the
491
- # authenticity of the LDAP server. No validation of the LDAP server's SSL
492
- # certificate is performed. This means that :simple_tls will not produce
493
- # errors if the LDAP server's encryption certificate is not signed by a
494
- # well-known Certification Authority. If you get communications or
495
- # protocol errors when using this option, check with your LDAP server
496
- # administrator. Pay particular attention to the TCP port you are
497
- # connecting to. It's impossible for an LDAP server to support plaintext
498
- # LDAP communications and <i>simple TLS</i> connections on the same port.
499
- # The standard TCP port for unencrypted LDAP connections is 389, but the
500
- # standard port for simple-TLS encrypted connections is 636. Be sure you
501
- # are using the correct port.
502
- #
494
+ # If you get communications or protocol errors when using this option,
495
+ # check with your LDAP server administrator. Pay particular attention
496
+ # to the TCP port you are connecting to. It's impossible for an LDAP
497
+ # server to support plaintext LDAP communications and <i>simple TLS</i>
498
+ # connections on the same port. The standard TCP port for unencrypted
499
+ # LDAP connections is 389, but the standard port for simple-TLS
500
+ # encrypted connections is 636. Be sure you are using the correct port.
503
501
  # The :start_tls like the :simple_tls encryption method also encrypts all
504
502
  # communcations with the LDAP server. With the exception that it operates
505
503
  # over the standard TCP port.
506
504
  #
507
- # In order to verify certificates and enable other TLS options, the
508
- # :tls_options hash can be passed alongside :simple_tls or :start_tls.
509
- # This hash contains any options that can be passed to
510
- # OpenSSL::SSL::SSLContext#set_params(). The most common options passed
511
- # should be OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, or the :ca_file option,
512
- # which contains a path to a Certificate Authority file (PEM-encoded).
513
- #
514
- # Example for a default setup without custom settings:
515
- # {
516
- # :method => :simple_tls,
517
- # :tls_options => OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
518
- # }
519
- #
520
- # Example for specifying a CA-File and only allowing TLSv1.1 connections:
505
+ # To validate the LDAP server's certificate (a security must if you're
506
+ # talking over the public internet), you need to set :tls_options
507
+ # something like this...
521
508
  #
522
- # {
523
- # :method => :start_tls,
524
- # :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
509
+ # Net::LDAP.new(
510
+ # # ... set host, bind dn, etc ...
511
+ # encryption: {
512
+ # method: :simple_tls,
513
+ # tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS,
525
514
  # }
526
- # * :force_no_page => Set to true to prevent paged results even if your
527
- # server says it supports them. This is a fix for MS Active Directory
528
- # * :instrumentation_service => An object responsible for instrumenting
529
- # operations, compatible with ActiveSupport::Notifications' public API.
515
+ # )
516
+ #
517
+ # The above will use the operating system-provided store of CA
518
+ # certificates to validate your LDAP server's cert.
519
+ # If cert validation fails, it'll happen during the #bind
520
+ # whenever you first try to open a connection to the server.
521
+ # Those methods will throw Net::LDAP::ConnectionError with
522
+ # a message about certificate verify failing. If your
523
+ # LDAP server's certificate is signed by DigiCert, Comodo, etc.,
524
+ # you're probably good. If you've got a self-signed cert but it's
525
+ # been added to the host's OS-maintained CA store (e.g. on Debian
526
+ # add foobar.crt to /usr/local/share/ca-certificates/ and run
527
+ # `update-ca-certificates`), then the cert should pass validation.
528
+ # To ignore the OS's CA store, put your CA in a PEM-encoded file and...
529
+ #
530
+ # encryption: {
531
+ # method: :simple_tls,
532
+ # tls_options: { ca_file: '/path/to/my-little-ca.pem',
533
+ # ssl_version: 'TLSv1_1' },
534
+ # }
535
+ #
536
+ # As you might guess, the above example also fails the connection
537
+ # if the client can't negotiate TLS v1.1.
538
+ # tls_options is ultimately passed to OpenSSL::SSL::SSLContext#set_params
539
+ # For more details, see
540
+ # http://ruby-doc.org/stdlib-2.0.0/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html
530
541
  #
531
542
  # Instantiating a Net::LDAP object does <i>not</i> result in network
532
543
  # traffic to the LDAP server. It simply stores the connection and binding
533
- # parameters in the object.
544
+ # parameters in the object. That's why Net::LDAP.new doesn't throw
545
+ # cert validation errors itself; #bind does instead.
534
546
  def initialize(args = {})
535
547
  @host = args[:host] || DefaultHost
536
548
  @port = args[:port] || DefaultPort
@@ -700,7 +712,7 @@ class Net::LDAP
700
712
  begin
701
713
  @open_connection = new_connection
702
714
  payload[:connection] = @open_connection
703
- payload[:bind] = @open_connection.bind(@auth)
715
+ payload[:bind] = @result = @open_connection.bind(@auth)
704
716
  yield self
705
717
  ensure
706
718
  @open_connection.close if @open_connection
@@ -1286,11 +1298,9 @@ class Net::LDAP
1286
1298
  else
1287
1299
  begin
1288
1300
  conn = new_connection
1289
- if (result = conn.bind(args[:auth] || @auth)).result_code == Net::LDAP::ResultCodeSuccess
1290
- yield conn
1291
- else
1292
- return result
1293
- end
1301
+ result = conn.bind(args[:auth] || @auth)
1302
+ return result unless result.result_code == Net::LDAP::ResultCodeSuccess
1303
+ yield conn
1294
1304
  ensure
1295
1305
  conn.close if conn
1296
1306
  end
@@ -1,5 +1,5 @@
1
- require 'net/ldap/auth_adapter'
2
- require 'net/ldap/auth_adapter/sasl'
1
+ require_relative '../auth_adapter'
2
+ require_relative 'sasl'
3
3
 
4
4
  module Net
5
5
  class LDAP
@@ -1,9 +1,11 @@
1
- require 'net/ldap/auth_adapter'
1
+ require_relative '../auth_adapter'
2
2
 
3
3
  module Net
4
4
  class LDAP
5
5
  class AuthAdapter
6
6
  class Sasl < Net::LDAP::AuthAdapter
7
+ MAX_SASL_CHALLENGES = 10
8
+
7
9
  #--
8
10
  # Required parameters: :mechanism, :initial_credential and
9
11
  # :challenge_response
@@ -47,7 +49,7 @@ module Net
47
49
  end
48
50
 
49
51
  return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
50
- raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
52
+ raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MAX_SASL_CHALLENGES)
51
53
 
52
54
  cred = chall.call(pdu.result_server_sasl_creds)
53
55
  end
@@ -1,4 +1,4 @@
1
- require 'net/ldap/auth_adapter'
1
+ require_relative '../auth_adapter'
2
2
 
3
3
  module Net
4
4
  class LDAP
@@ -7,7 +7,6 @@ class Net::LDAP::Connection #:nodoc:
7
7
  DefaultConnectTimeout = 5
8
8
 
9
9
  LdapVersion = 3
10
- MaxSaslChallenges = 10
11
10
 
12
11
  # Initialize a connection to an LDAP server
13
12
  #
@@ -31,26 +30,36 @@ class Net::LDAP::Connection #:nodoc:
31
30
  @socket_class = socket_class
32
31
  end
33
32
 
34
- def prepare_socket(server)
33
+ def prepare_socket(server, timeout=nil)
35
34
  socket = server[:socket]
36
35
  encryption = server[:encryption]
37
36
 
38
37
  @conn = socket
39
- setup_encryption encryption if encryption
38
+ setup_encryption(encryption, timeout) if encryption
40
39
  end
41
40
 
42
41
  def open_connection(server)
43
42
  hosts = server[:hosts]
44
43
  encryption = server[:encryption]
45
44
 
45
+ timeout = server[:connect_timeout] || DefaultConnectTimeout
46
46
  socket_opts = {
47
- connect_timeout: server[:connect_timeout] || DefaultConnectTimeout,
47
+ connect_timeout: timeout,
48
48
  }
49
49
 
50
50
  errors = []
51
51
  hosts.each do |host, port|
52
52
  begin
53
- prepare_socket(server.merge(socket: @socket_class.new(host, port, socket_opts)))
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
54
63
  return
55
64
  rescue Net::LDAP::Error, SocketError, SystemCallError,
56
65
  OpenSSL::SSL::SSLError => e
@@ -76,7 +85,7 @@ class Net::LDAP::Connection #:nodoc:
76
85
  end
77
86
  end
78
87
 
79
- def self.wrap_with_ssl(io, tls_options = {})
88
+ def self.wrap_with_ssl(io, tls_options = {}, timeout=nil)
80
89
  raise Net::LDAP::NoOpenSSLError, "OpenSSL is unavailable" unless Net::LDAP::HasOpenSSL
81
90
 
82
91
  ctx = OpenSSL::SSL::SSLContext.new
@@ -86,7 +95,22 @@ class Net::LDAP::Connection #:nodoc:
86
95
  ctx.set_params(tls_options) unless tls_options.empty?
87
96
 
88
97
  conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
89
- 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
90
114
 
91
115
  # Doesn't work:
92
116
  # conn.sync_close = true
@@ -123,11 +147,11 @@ class Net::LDAP::Connection #:nodoc:
123
147
  # communications, as with simple_tls. Thanks for Kouhei Sutou for
124
148
  # generously contributing the :start_tls path.
125
149
  #++
126
- def setup_encryption(args)
150
+ def setup_encryption(args, timeout=nil)
127
151
  args[:tls_options] ||= {}
128
152
  case args[:method]
129
153
  when :simple_tls
130
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
154
+ @conn = self.class.wrap_with_ssl(@conn, args[:tls_options], timeout)
131
155
  # additional branches requiring server validation and peer certs, etc.
132
156
  # go here.
133
157
  when :start_tls
@@ -143,11 +167,9 @@ class Net::LDAP::Connection #:nodoc:
143
167
  raise Net::LDAP::NoStartTLSResultError, "no start_tls result"
144
168
  end
145
169
 
146
- if pdu.result_code.zero?
147
- @conn = self.class.wrap_with_ssl(@conn, args[:tls_options])
148
- else
149
- raise Net::LDAP::StartTLSError, "start_tls failed: #{pdu.result_code}"
150
- 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)
151
173
  else
152
174
  raise Net::LDAP::EncMethodUnsupportedError, "unsupported encryption method #{args[:method]}"
153
175
  end
@@ -159,7 +181,7 @@ class Net::LDAP::Connection #:nodoc:
159
181
  # have to call it, but perhaps it will come in handy someday.
160
182
  #++
161
183
  def close
162
- return if @conn.nil?
184
+ return if !defined?(@conn) || @conn.nil?
163
185
  @conn.close
164
186
  @conn = nil
165
187
  end
@@ -177,12 +199,10 @@ class Net::LDAP::Connection #:nodoc:
177
199
 
178
200
  # read messages until we have a match for the given message_id
179
201
  while pdu = read
180
- if pdu.message_id == message_id
181
- return pdu
182
- else
183
- message_queue[pdu.message_id].push pdu
184
- next
185
- end
202
+ return pdu if pdu.message_id == message_id
203
+
204
+ message_queue[pdu.message_id].push pdu
205
+ next
186
206
  end
187
207
 
188
208
  pdu
@@ -280,7 +300,7 @@ class Net::LDAP::Connection #:nodoc:
280
300
  control[2] = (control[2] == true).to_ber
281
301
  control.to_ber_sequence
282
302
  end
283
- sort_control = [
303
+ [
284
304
  Net::LDAP::LDAPControls::SORT_REQUEST.to_ber,
285
305
  false.to_ber,
286
306
  sort_control_values.to_ber_sequence.to_s.to_ber,
@@ -380,12 +400,11 @@ class Net::LDAP::Connection #:nodoc:
380
400
  # should collect this into a private helper to clarify the structure
381
401
  query_limit = 0
382
402
  if size > 0
383
- if paged
384
- query_limit = (((size - n_results) < 126) ? (size -
385
- n_results) : 0)
386
- else
387
- query_limit = size
388
- end
403
+ query_limit = if paged
404
+ (((size - n_results) < 126) ? (size - n_results) : 0)
405
+ else
406
+ size
407
+ end
389
408
  end
390
409
 
391
410
  request = [
@@ -448,6 +467,10 @@ class Net::LDAP::Connection #:nodoc:
448
467
  end
449
468
  end
450
469
 
470
+ if result_pdu.nil?
471
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing"
472
+ end
473
+
451
474
  # count number of pages of results
452
475
  payload[:page_count] ||= 0
453
476
  payload[:page_count] += 1
@@ -573,11 +596,11 @@ class Net::LDAP::Connection #:nodoc:
573
596
 
574
597
  ext_seq = [Net::LDAP::PasswdModifyOid.to_ber_contextspecific(0)]
575
598
 
576
- unless args[:old_password].nil?
577
- pwd_seq = [args[:old_password].to_ber(0x81)]
578
- pwd_seq << args[:new_password].to_ber(0x82) unless args[:new_password].nil?
579
- ext_seq << pwd_seq.to_ber_sequence.to_ber(0x81)
580
- end
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)
581
604
 
582
605
  request = ext_seq.to_ber_appsequence(Net::LDAP::PDU::ExtendedRequest)
583
606
 
@@ -587,7 +610,7 @@ class Net::LDAP::Connection #:nodoc:
587
610
  pdu = queued_read(message_id)
588
611
 
589
612
  if !pdu || pdu.app_tag != Net::LDAP::PDU::ExtendedResponse
590
- raise Net::LDAP::ResponseMissingError, "response missing or invalid"
613
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
591
614
  end
592
615
 
593
616
  pdu
@@ -687,7 +710,7 @@ class Net::LDAP::Connection #:nodoc:
687
710
  # Wrap around Socket.tcp to normalize with other Socket initializers
688
711
  class DefaultSocket
689
712
  def self.new(host, port, socket_opts = {})
690
- Socket.tcp(host, port, socket_opts)
713
+ Socket.tcp(host, port, **socket_opts)
691
714
  end
692
715
  end
693
716
  end # class Connection