net-ldap 0.14.0 → 0.16.3

Sign up to get free protection for your applications and to get access to all the features.
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