net-ldap 0.11 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of net-ldap might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 093221df6c10572671e2d05af258d473d1c17e78
4
- data.tar.gz: e27ca24638d8d46f2a8a39900fb90d8ca6a7f1fe
3
+ metadata.gz: 8c96669822fdcf032465b410615500b91a2f31ec
4
+ data.tar.gz: 643738d3f05ae5469ef1eb773326d5943eacf923
5
5
  SHA512:
6
- metadata.gz: 7f1bdba62c083cfc5b128226107f94815fb6d8b2d4d04fb47e30271289d6ffa193dc5e9363a2f44a04b7d5a30af08a97bba3d5afeda4ce035d7581f533db639a
7
- data.tar.gz: 9ff4126e94e791948777a1ed4396c21cdf62fb9a446a4eb240364302367c08a27566243af69bdf907fd2f8bfbe92117513eccc033db97f3c47acefab348971a1
6
+ metadata.gz: 6a7a455c387b73745d6da20d890181c20776562751f0c4bd43b9eb9a7ad9d2637771e1dfdaecbeb7e951ba25bca9859219e32e1154a88eed12e162152f47df0e
7
+ data.tar.gz: e92116ed732575ba7f0f05fb52f1148bd9c432de28cd3d0391b72835a091294dd7fe260c3b5aa1c96266b31edc9dd861cedd5d6066f57520dfb5c19e05a7b727
@@ -2,25 +2,28 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
- - 2.1.2
5
+ - 2.1
6
+ - 2.2
6
7
  # optional
8
+ - ruby-head
7
9
  - jruby-19mode
8
- - rbx-19mode
10
+ - jruby-head
9
11
  - rbx-2
10
12
 
11
13
  env:
12
14
  - INTEGRATION=openldap
13
15
 
14
16
  install:
15
- - if [ "$INTEGRATION" = "openldap" ]; then ./script/install-openldap; fi
17
+ - if [ "$INTEGRATION" = "openldap" ]; then sudo script/install-openldap; fi
16
18
  - bundle install
17
19
 
18
20
  script: bundle exec rake ci
19
21
 
20
22
  matrix:
21
23
  allow_failures:
24
+ - rvm: ruby-head
22
25
  - rvm: jruby-19mode
23
- - rvm: rbx-19mode
26
+ - rvm: jruby-head
24
27
  - rvm: rbx-2
25
28
  fast_finish: true
26
29
 
@@ -1,3 +1,21 @@
1
+ === Net::LDAP 0.12.0
2
+
3
+ * DRY up connection handling logic {#224}[https://github.com/ruby-ldap/ruby-net-ldap/pull/224]
4
+ * Define auth adapters {#226}[https://github.com/ruby-ldap/ruby-net-ldap/pull/226]
5
+ * add slash to attribute value filter {#225}[https://github.com/ruby-ldap/ruby-net-ldap/pull/225]
6
+ * Add the ability to provide a list of hosts for a connection {#223}[https://github.com/ruby-ldap/ruby-net-ldap/pull/223]
7
+ * Specify the port of LDAP server by giving INTEGRATION_PORT {#221}[https://github.com/ruby-ldap/ruby-net-ldap/pull/221]
8
+ * Correctly set BerIdentifiedString values to UTF-8 {#212}[https://github.com/ruby-ldap/ruby-net-ldap/pull/212]
9
+ * Raise Net::LDAP::ConnectionRefusedError when new connection is refused. {#213}[https://github.com/ruby-ldap/ruby-net-ldap/pull/213]
10
+ * obscure auth password upon #inspect, added test, closes #216 {#217}[https://github.com/ruby-ldap/ruby-net-ldap/pull/217]
11
+ * Fixing incorrect error class name {#207}[https://github.com/ruby-ldap/ruby-net-ldap/pull/207]
12
+ * Travis update {#205}[https://github.com/ruby-ldap/ruby-net-ldap/pull/205]
13
+ * Remove obsolete rbx-19mode from Travis {#204}[https://github.com/ruby-ldap/ruby-net-ldap/pull/204]
14
+ * mv "sudo" from script/install-openldap to .travis.yml {#199}[https://github.com/ruby-ldap/ruby-net-ldap/pull/199]
15
+ * Remove meaningless shebang {#200}[https://github.com/ruby-ldap/ruby-net-ldap/pull/200]
16
+ * Fix Travis CI build {#202}[https://github.com/ruby-ldap/ruby-net-ldap/pull/202]
17
+ * README.rdoc: fix travis link {#195}[https://github.com/ruby-ldap/ruby-net-ldap/pull/195]
18
+
1
19
  === Net::LDAP 0.11
2
20
  * Major enhancements:
3
21
  * #183 Specific errors subclassing Net::LDAP::Error
@@ -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/github/ruby-net-ldap]
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]
2
2
 
3
3
  == Description
4
4
 
@@ -37,6 +37,14 @@ sources.
37
37
 
38
38
  Simply require either 'net-ldap' or 'net/ldap'.
39
39
 
40
+ == Extensions
41
+
42
+ This library focuses on the core LDAP RFCs referenced in the description.
43
+ However, we recognize there are commonly used extensions to the spec that are
44
+ useful. If there is another library which handles it, we list it here.
45
+
46
+ * {resolv-srv}[https://rubygems.org/gems/resolv-srv]: Support RFC2782 SRV record lookup and failover
47
+
40
48
  == Develop
41
49
 
42
50
  This task will run the test suite and the
@@ -55,10 +63,11 @@ To run the integration tests against an LDAP server:
55
63
 
56
64
  This section is for gem maintainers to cut a new version of the gem.
57
65
 
66
+ * Check out a new branch `release-VERSION`
58
67
  * Update lib/net/ldap/version.rb to next version number X.X.X following {semver}(http://semver.org/).
59
- * Update `History.rdoc`. Get latest changes with `git log --oneline vLAST_RELEASE..HEAD | grep Merge`
60
-
61
- * On the master branch, run `script/release`
68
+ * Update `History.rdoc`. Get latest changes with `script/changelog`
69
+ * Open a pull request with these changes for review
70
+ * After merging, on the master branch, run `script/release`
62
71
 
63
72
  :include: Contributors.rdoc
64
73
 
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env rake
2
1
  # -*- ruby encoding: utf-8 -*-
3
2
  # vim: syntax=ruby
4
3
 
@@ -296,9 +296,11 @@ end
296
296
  class Net::BER::BerIdentifiedString < String
297
297
  attr_accessor :ber_identifier
298
298
  def initialize args
299
- super args
300
- # LDAP uses UTF-8 encoded strings
301
- self.encode('UTF-8') if self.respond_to?(:encoding) rescue self
299
+ super begin
300
+ args.respond_to?(:encode) ? args.encode('UTF-8') : args
301
+ rescue
302
+ args
303
+ end
302
304
  end
303
305
  end
304
306
 
@@ -27,6 +27,12 @@ require 'net/ldap/instrumentation'
27
27
  require 'net/ldap/connection'
28
28
  require 'net/ldap/version'
29
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'
33
+
34
+ Net::LDAP::AuthAdapter.register([:simple, :anon, :anonymous], Net::LDAP::AuthAdapter::Simple)
35
+ Net::LDAP::AuthAdapter.register(:sasl, Net::LDAP::AuthAdapter::Sasl)
30
36
 
31
37
  # == Quick-start for the Impatient
32
38
  # === Quick Example of a user-authentication against an LDAP directory:
@@ -432,6 +438,7 @@ class Net::LDAP
432
438
 
433
439
  attr_accessor :host
434
440
  attr_accessor :port
441
+ attr_accessor :hosts
435
442
  attr_accessor :base
436
443
 
437
444
  # Instantiate an object of type Net::LDAP to perform directory operations.
@@ -440,6 +447,8 @@ class Net::LDAP
440
447
  # described below. The following arguments are supported:
441
448
  # * :host => the LDAP server's IP-address (default 127.0.0.1)
442
449
  # * :port => the LDAP server's TCP port (default 389)
450
+ # * :hosts => an enumerable of pairs of hosts and corresponding ports with
451
+ # which to attempt opening connections (default [[host, port]])
443
452
  # * :auth => a Hash containing authorization parameters. Currently
444
453
  # supported values include: {:method => :anonymous} and {:method =>
445
454
  # :simple, :username => your_user_name, :password => your_password }
@@ -468,6 +477,7 @@ class Net::LDAP
468
477
  def initialize(args = {})
469
478
  @host = args[:host] || DefaultHost
470
479
  @port = args[:port] || DefaultPort
480
+ @hosts = args[:hosts]
471
481
  @verbose = false # Make this configurable with a switch on the class.
472
482
  @auth = args[:auth] || DefaultAuth
473
483
  @base = args[:base] || DefaultTreebase
@@ -1195,6 +1205,13 @@ class Net::LDAP
1195
1205
  @server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
1196
1206
  end
1197
1207
 
1208
+ # Mask auth password
1209
+ def inspect
1210
+ inspected = super
1211
+ inspected.gsub! @auth[:password], "*******" if @auth[:password]
1212
+ inspected
1213
+ end
1214
+
1198
1215
  private
1199
1216
 
1200
1217
  # Yields an open connection if there is one, otherwise establishes a new
@@ -1223,6 +1240,7 @@ class Net::LDAP
1223
1240
  Net::LDAP::Connection.new \
1224
1241
  :host => @host,
1225
1242
  :port => @port,
1243
+ :hosts => @hosts,
1226
1244
  :encryption => @encryption,
1227
1245
  :instrumentation_service => @instrumentation_service
1228
1246
  end
@@ -0,0 +1,29 @@
1
+ module Net
2
+ class LDAP
3
+ class AuthAdapter
4
+ def self.register(names, adapter)
5
+ names = Array(names)
6
+ @adapters ||= {}
7
+ names.each do |name|
8
+ @adapters[name] = adapter
9
+ end
10
+ end
11
+
12
+ def self.[](name)
13
+ a = @adapters[name]
14
+ if a.nil?
15
+ raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{name})"
16
+ end
17
+ return a
18
+ end
19
+
20
+ def initialize(conn)
21
+ @connection = conn
22
+ end
23
+
24
+ def bind
25
+ raise "bind method must be overwritten"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ require 'net/ldap/auth_adapter'
2
+ require 'net/ldap/auth_adapter/sasl'
3
+
4
+ module Net
5
+ class LDAP
6
+ module AuthAdapers
7
+ #--
8
+ # PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
9
+ # Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
10
+ # integrate it without introducing an external dependency.
11
+ #
12
+ # This authentication method is accessed by calling #bind with a :method
13
+ # parameter of :gss_spnego. It requires :username and :password
14
+ # attributes, just like the :simple authentication method. It performs a
15
+ # GSS-SPNEGO authentication with the server, which is presumed to be a
16
+ # Microsoft Active Directory.
17
+ #++
18
+ class GSS_SPNEGO < Net::LDAP::AuthAdapter
19
+ def bind(auth)
20
+ require 'ntlm'
21
+
22
+ user, psw = [auth[:username] || auth[:dn], auth[:password]]
23
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
24
+
25
+ nego = proc { |challenge|
26
+ t2_msg = NTLM::Message.parse(challenge)
27
+ t3_msg = t2_msg.response({ :user => user, :password => psw },
28
+ { :ntlmv2 => true })
29
+ t3_msg.serialize
30
+ }
31
+
32
+ Net::LDAP::AuthAdapter::Sasl.new(@connection).
33
+ bind(:method => :sasl, :mechanism => "GSS-SPNEGO",
34
+ :initial_credential => NTLM::Message::Type1.new.serialize,
35
+ :challenge_response => nego)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,60 @@
1
+ require 'net/ldap/auth_adapter'
2
+
3
+ module Net
4
+ class LDAP
5
+ class AuthAdapter
6
+ class Sasl < Net::LDAP::AuthAdapter
7
+ #--
8
+ # Required parameters: :mechanism, :initial_credential and
9
+ # :challenge_response
10
+ #
11
+ # Mechanism is a string value that will be passed in the SASL-packet's
12
+ # "mechanism" field.
13
+ #
14
+ # Initial credential is most likely a string. It's passed in the initial
15
+ # BindRequest that goes to the server. In some protocols, it may be empty.
16
+ #
17
+ # Challenge-response is a Ruby proc that takes a single parameter and
18
+ # returns an object that will typically be a string. The
19
+ # challenge-response block is called when the server returns a
20
+ # BindResponse with a result code of 14 (saslBindInProgress). The
21
+ # challenge-response block receives a parameter containing the data
22
+ # returned by the server in the saslServerCreds field of the LDAP
23
+ # BindResponse packet. The challenge-response block may be called multiple
24
+ # times during the course of a SASL authentication, and each time it must
25
+ # return a value that will be passed back to the server as the credential
26
+ # data in the next BindRequest packet.
27
+ #++
28
+ def bind(auth)
29
+ mech, cred, chall = auth[:mechanism], auth[:initial_credential],
30
+ auth[:challenge_response]
31
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
32
+
33
+ message_id = @connection.next_msgid
34
+
35
+ n = 0
36
+ loop {
37
+ sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
38
+ request = [
39
+ Net::LDAP::Connection::LdapVersion.to_ber, "".to_ber, sasl
40
+ ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
41
+
42
+ @connection.send(:write, request, nil, message_id)
43
+ pdu = @connection.queued_read(message_id)
44
+
45
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
46
+ raise Net::LDAP::NoBindResultError, "no bind result"
47
+ end
48
+
49
+ return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
50
+ raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
51
+
52
+ cred = chall.call(pdu.result_server_sasl_creds)
53
+ }
54
+
55
+ raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ require 'net/ldap/auth_adapter'
2
+
3
+ module Net
4
+ class LDAP
5
+ class AuthAdapter
6
+ class Simple < AuthAdapter
7
+ def bind(auth)
8
+ user, psw = if auth[:method] == :simple
9
+ [auth[:username] || auth[:dn], auth[:password]]
10
+ else
11
+ ["", ""]
12
+ end
13
+
14
+ raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
15
+
16
+ message_id = @connection.next_msgid
17
+ request = [
18
+ Net::LDAP::Connection::LdapVersion.to_ber, user.to_ber,
19
+ psw.to_ber_contextspecific(0)
20
+ ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
21
+
22
+ @connection.send(:write, request, nil, message_id)
23
+ pdu = @connection.queued_read(message_id)
24
+
25
+ if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
26
+ raise Net::LDAP::NoBindResultError, "no bind result"
27
+ end
28
+
29
+ pdu
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -9,23 +9,42 @@ class Net::LDAP::Connection #:nodoc:
9
9
  def initialize(server)
10
10
  @instrumentation_service = server[:instrumentation_service]
11
11
 
12
- begin
13
- @conn = server[:socket] || TCPSocket.new(server[:host], server[:port])
14
- rescue SocketError
15
- raise Net::LDAP::Error, "No such address or other socket error."
16
- rescue Errno::ECONNREFUSED
17
- raise Net::LDAP::Error, "Server #{server[:host]} refused connection on port #{server[:port]}."
18
- rescue Errno::EHOSTUNREACH => error
19
- raise Net::LDAP::Error, "Host #{server[:host]} was unreachable (#{error.message})"
20
- rescue Errno::ETIMEDOUT
21
- raise Net::LDAP::Error, "Connection to #{server[:host]} timed out."
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)
22
17
  end
23
18
 
24
- if server[:encryption]
25
- setup_encryption server[:encryption]
19
+ yield self if block_given?
20
+ end
21
+
22
+ def prepare_socket(server)
23
+ socket = server[:socket]
24
+ encryption = server[:encryption]
25
+
26
+ @conn = socket
27
+ setup_encryption encryption if encryption
28
+ end
29
+
30
+ def open_connection(server)
31
+ hosts = server[:hosts]
32
+ encryption = server[:encryption]
33
+
34
+ errors = []
35
+ hosts.each do |host, port|
36
+ begin
37
+ prepare_socket(server.merge(socket: TCPSocket.new(host, port)))
38
+ return
39
+ rescue Net::LDAP::Error, SocketError, SystemCallError,
40
+ OpenSSL::SSL::SSLError => e
41
+ # Ensure the connection is closed in the event a setup failure.
42
+ close
43
+ errors << [e, host, port]
44
+ end
26
45
  end
27
46
 
28
- yield self if block_given?
47
+ raise Net::LDAP::ConnectionError.new(errors)
29
48
  end
30
49
 
31
50
  module GetbyteForSSLSocket
@@ -63,18 +82,18 @@ class Net::LDAP::Connection #:nodoc:
63
82
  end
64
83
 
65
84
  #--
66
- # Helper method called only from new, and only after we have a
67
- # successfully-opened @conn instance variable, which is a TCP connection.
68
- # Depending on the received arguments, we establish SSL, potentially
69
- # replacing the value of @conn accordingly. Don't generate any errors here
70
- # if no encryption is requested. DO raise Net::LDAP::Error objects if encryption
71
- # is requested and we have trouble setting it up. That includes if OpenSSL
72
- # is not set up on the machine. (Question: how does the Ruby OpenSSL
73
- # wrapper react in that case?) DO NOT filter exceptions raised by the
74
- # OpenSSL library. Let them pass back to the user. That should make it
75
- # easier for us to debug the problem reports. Presumably (hopefully?) that
76
- # will also produce recognizable errors if someone tries to use this on a
77
- # machine without OpenSSL.
85
+ # Helper method called only from prepare_socket or open_connection, and only
86
+ # after we have a successfully-opened @conn instance variable, which is a TCP
87
+ # connection. Depending on the received arguments, we establish SSL,
88
+ # potentially replacing the value of @conn accordingly. Don't generate any
89
+ # errors here if no encryption is requested. DO raise Net::LDAP::Error objects
90
+ # if encryption is requested and we have trouble setting it up. That includes
91
+ # if OpenSSL is not set up on the machine. (Question: how does the Ruby
92
+ # OpenSSL wrapper react in that case?) DO NOT filter exceptions raised by the
93
+ # OpenSSL library. Let them pass back to the user. That should make it easier
94
+ # for us to debug the problem reports. Presumably (hopefully?) that will also
95
+ # produce recognizable errors if someone tries to use this on a machine
96
+ # without OpenSSL.
78
97
  #
79
98
  # The simple_tls method is intended as the simplest, stupidest, easiest
80
99
  # solution for people who want nothing more than encrypted comms with the
@@ -124,6 +143,7 @@ class Net::LDAP::Connection #:nodoc:
124
143
  # have to call it, but perhaps it will come in handy someday.
125
144
  #++
126
145
  def close
146
+ return if @conn.nil?
127
147
  @conn.close
128
148
  @conn = nil
129
149
  end
@@ -218,130 +238,11 @@ class Net::LDAP::Connection #:nodoc:
218
238
  def bind(auth)
219
239
  instrument "bind.net_ldap_connection" do |payload|
220
240
  payload[:method] = meth = auth[:method]
221
- if [:simple, :anonymous, :anon].include?(meth)
222
- bind_simple auth
223
- elsif meth == :sasl
224
- bind_sasl(auth)
225
- elsif meth == :gss_spnego
226
- bind_gss_spnego(auth)
227
- else
228
- raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (#{meth})"
229
- end
241
+ adapter = Net::LDAP::AuthAdapter[meth]
242
+ adapter.new(self).bind(auth)
230
243
  end
231
244
  end
232
245
 
233
- #--
234
- # Implements a simple user/psw authentication. Accessed by calling #bind
235
- # with a method of :simple or :anonymous.
236
- #++
237
- def bind_simple(auth)
238
- user, psw = if auth[:method] == :simple
239
- [auth[:username] || auth[:dn], auth[:password]]
240
- else
241
- ["", ""]
242
- end
243
-
244
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
245
-
246
- message_id = next_msgid
247
- request = [
248
- LdapVersion.to_ber, user.to_ber,
249
- psw.to_ber_contextspecific(0)
250
- ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
251
-
252
- write(request, nil, message_id)
253
- pdu = queued_read(message_id)
254
-
255
- if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
256
- raise Net::LDAP::NoBindResultError, "no bind result"
257
- end
258
-
259
- pdu
260
- end
261
-
262
- #--
263
- # Required parameters: :mechanism, :initial_credential and
264
- # :challenge_response
265
- #
266
- # Mechanism is a string value that will be passed in the SASL-packet's
267
- # "mechanism" field.
268
- #
269
- # Initial credential is most likely a string. It's passed in the initial
270
- # BindRequest that goes to the server. In some protocols, it may be empty.
271
- #
272
- # Challenge-response is a Ruby proc that takes a single parameter and
273
- # returns an object that will typically be a string. The
274
- # challenge-response block is called when the server returns a
275
- # BindResponse with a result code of 14 (saslBindInProgress). The
276
- # challenge-response block receives a parameter containing the data
277
- # returned by the server in the saslServerCreds field of the LDAP
278
- # BindResponse packet. The challenge-response block may be called multiple
279
- # times during the course of a SASL authentication, and each time it must
280
- # return a value that will be passed back to the server as the credential
281
- # data in the next BindRequest packet.
282
- #++
283
- def bind_sasl(auth)
284
- mech, cred, chall = auth[:mechanism], auth[:initial_credential],
285
- auth[:challenge_response]
286
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (mech && cred && chall)
287
-
288
- message_id = next_msgid
289
-
290
- n = 0
291
- loop {
292
- sasl = [mech.to_ber, cred.to_ber].to_ber_contextspecific(3)
293
- request = [
294
- LdapVersion.to_ber, "".to_ber, sasl
295
- ].to_ber_appsequence(Net::LDAP::PDU::BindRequest)
296
-
297
- write(request, nil, message_id)
298
- pdu = queued_read(message_id)
299
-
300
- if !pdu || pdu.app_tag != Net::LDAP::PDU::BindResult
301
- raise Net::LDAP::NoBindResultError, "no bind result"
302
- end
303
-
304
- return pdu unless pdu.result_code == Net::LDAP::ResultCodeSaslBindInProgress
305
- raise Net::LDAP::SASLChallengeOverflowError, "sasl-challenge overflow" if ((n += 1) > MaxSaslChallenges)
306
-
307
- cred = chall.call(pdu.result_server_sasl_creds)
308
- }
309
-
310
- raise Net::LDAP::SASLChallengeOverflowError, "why are we here?"
311
- end
312
- private :bind_sasl
313
-
314
- #--
315
- # PROVISIONAL, only for testing SASL implementations. DON'T USE THIS YET.
316
- # Uses Kohei Kajimoto's Ruby/NTLM. We have to find a clean way to
317
- # integrate it without introducing an external dependency.
318
- #
319
- # This authentication method is accessed by calling #bind with a :method
320
- # parameter of :gss_spnego. It requires :username and :password
321
- # attributes, just like the :simple authentication method. It performs a
322
- # GSS-SPNEGO authentication with the server, which is presumed to be a
323
- # Microsoft Active Directory.
324
- #++
325
- def bind_gss_spnego(auth)
326
- require 'ntlm'
327
-
328
- user, psw = [auth[:username] || auth[:dn], auth[:password]]
329
- raise Net::LDAP::BindingInformationInvalidError, "Invalid binding information" unless (user && psw)
330
-
331
- nego = proc { |challenge|
332
- t2_msg = NTLM::Message.parse(challenge)
333
- t3_msg = t2_msg.response({ :user => user, :password => psw },
334
- { :ntlmv2 => true })
335
- t3_msg.serialize
336
- }
337
-
338
- bind_sasl(:method => :sasl, :mechanism => "GSS-SPNEGO",
339
- :initial_credential => NTLM::Message::Type1.new.serialize,
340
- :challenge_response => nego)
341
- end
342
- private :bind_gss_spnego
343
-
344
-
345
246
  #--
346
247
  # Allow the caller to specify a sort control
347
248
  #
@@ -652,7 +553,7 @@ class Net::LDAP::Connection #:nodoc:
652
553
  pdu = queued_read(message_id)
653
554
 
654
555
  if !pdu || pdu.app_tag != Net::LDAP::PDU::AddResponse
655
- raise Net::LDAP::ResponseMissingError, "response missing or invalid"
556
+ raise Net::LDAP::ResponseMissingOrInvalidError, "response missing or invalid"
656
557
  end
657
558
 
658
559
  pdu
@@ -9,7 +9,41 @@ class Net::LDAP
9
9
 
10
10
  class AlreadyOpenedError < Error; end
11
11
  class SocketError < Error; end
12
- class ConnectionRefusedError < 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
+ class ConnectionError < Error
29
+ def self.new(errors)
30
+ error = errors.first.first
31
+ if errors.size == 1
32
+ if error.kind_of? Errno::ECONNREFUSED
33
+ return Net::LDAP::ConnectionRefusedError.new(error.message)
34
+ end
35
+
36
+ return Net::LDAP::Error.new(error.message)
37
+ end
38
+
39
+ super
40
+ end
41
+
42
+ def initialize(errors)
43
+ message = "Unable to connect to any given server: \n #{errors.map { |e, h, p| "#{e.class}: #{e.message} (#{h}:#{p})" }.join("\n ")}"
44
+ super(message)
45
+ end
46
+ end
13
47
  class NoOpenSSLError < Error; end
14
48
  class NoStartTLSResultError < Error; end
15
49
  class NoSearchBaseError < Error; end
@@ -752,7 +752,7 @@ class Net::LDAP::Filter
752
752
  scanner.scan(/\s*/)
753
753
  if op = scanner.scan(/<=|>=|!=|:=|=/)
754
754
  scanner.scan(/\s*/)
755
- if value = scanner.scan(/(?:[-\[\]{}\w*.+:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\\[a-fA-F\d]{2})+/u)
755
+ if value = scanner.scan(/(?:[-\[\]{}\w*.+\/:@=,#\$%&!'^~\s\xC3\x80-\xCA\xAF]|[^\x00-\x7F]|\\[a-fA-F\d]{2})+/u)
756
756
  # 20100313 AZ: Assumes that "(uid=george*)" is the same as
757
757
  # "(uid=george* )". The standard doesn't specify, but I can find
758
758
  # no examples that suggest otherwise.
@@ -1,5 +1,5 @@
1
1
  module Net
2
2
  class LDAP
3
- VERSION = "0.11"
3
+ VERSION = "0.12.0"
4
4
  end
5
5
  end
@@ -32,4 +32,5 @@ the most recent LDAP RFCs (4510-4519, plutions of 4520-4532).}
32
32
  s.add_development_dependency("flexmock", "~> 1.3")
33
33
  s.add_development_dependency("rake", "~> 10.0")
34
34
  s.add_development_dependency("rubocop", "~> 0.28.0")
35
+ s.add_development_dependency("test-unit")
35
36
  end
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ # Usage: script/changelog [-r <repo>] [-b <base>] [-h <head>]
3
+ #
4
+ # repo: BASE string of GitHub REPOsitory url. e.g. "user_or_org/REPOsitory". Defaults to git remote url.
5
+ # base: git ref to compare from. e.g. "v1.3.1". Defaults to latest git tag.
6
+ # head: git ref to compare to. Defaults to "HEAD".
7
+ #
8
+ # Generate a changelog preview from pull requests merged between `base` and
9
+ # `head`.
10
+ #
11
+ # https://github.com/jch/release-scripts/blob/master/changelog
12
+ set -e
13
+
14
+ [ $# -eq 0 ] && set -- --help
15
+ while [[ $# > 1 ]]
16
+ do
17
+ key="$1"
18
+ case $key in
19
+ -r|--repo)
20
+ repo="$2"
21
+ shift
22
+ ;;
23
+ -b|--base)
24
+ base="$2"
25
+ shift
26
+ ;;
27
+ -h|--head)
28
+ head="$2"
29
+ shift
30
+ ;;
31
+ *)
32
+ ;;
33
+ esac
34
+ shift
35
+ done
36
+
37
+ repo="${repo:-$(git remote -v | grep push | awk '{print $2}' | cut -d'/' -f4- | sed 's/\.git//')}"
38
+ base="${base:-$(git tag -l | sort -t. -k 1,1n -k 2,2n -k 3,3n | tail -n 1)}"
39
+ head="${head:-HEAD}"
40
+ api_url="https://api.github.com"
41
+
42
+ # get merged PR's. Better way is to query the API for these, but this is easier
43
+ for pr in $(git log --oneline $base..$head | grep "Merge pull request" | awk '{gsub("#",""); print $5}')
44
+ do
45
+ # frustrated with trying to pull out the right values, fell back to ruby
46
+ curl -s "$api_url/repos/$repo/pulls/$pr" | ruby -rjson -e 'pr=JSON.parse(STDIN.read); puts "* #{pr[%q(title)]} {##{pr[%q(number)]}}[#{pr[%q(html_url)]}]"'
47
+ done
@@ -6,68 +6,69 @@ BASE_PATH="$( cd `dirname $0`/../test/fixtures/openldap && pwd )"
6
6
  SEED_PATH="$( cd `dirname $0`/../test/fixtures && pwd )"
7
7
 
8
8
  dpkg -s slapd time ldap-utils gnutls-bin ssl-cert > /dev/null ||\
9
- DEBIAN_FRONTEND=noninteractive sudo -E apt-get install -y --force-yes slapd time ldap-utils gnutls-bin ssl-cert
9
+ DEBIAN_FRONTEND=noninteractive apt-get update -y --force-yes && \
10
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes slapd time ldap-utils gnutls-bin ssl-cert
10
11
 
11
- sudo /etc/init.d/slapd stop
12
+ /etc/init.d/slapd stop
12
13
 
13
14
  TMPDIR=$(mktemp -d)
14
15
  cd $TMPDIR
15
16
 
16
17
  # Delete data and reconfigure.
17
- sudo cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG
18
- sudo rm -rf /etc/ldap/slapd.d/*
19
- sudo rm -rf /var/lib/ldap/*
20
- sudo cp -v ./DB_CONFIG /var/lib/ldap/DB_CONFIG
21
- sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif
18
+ cp -v /var/lib/ldap/DB_CONFIG ./DB_CONFIG
19
+ rm -rf /etc/ldap/slapd.d/*
20
+ rm -rf /var/lib/ldap/*
21
+ cp -v ./DB_CONFIG /var/lib/ldap/DB_CONFIG
22
+ slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/slapd.conf.ldif
22
23
  # Load memberof and ref-int overlays and configure them.
23
- sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif
24
+ slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/memberof.ldif
24
25
  # Load retcode overlay and configure
25
- sudo slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/retcode.ldif
26
+ slapadd -F /etc/ldap/slapd.d -b "cn=config" -l $BASE_PATH/retcode.ldif
26
27
 
27
28
  # Add base domain.
28
- sudo slapadd -F /etc/ldap/slapd.d <<EOM
29
+ slapadd -F /etc/ldap/slapd.d <<EOM
29
30
  dn: dc=rubyldap,dc=com
30
31
  objectClass: top
31
32
  objectClass: domain
32
33
  dc: rubyldap
33
34
  EOM
34
35
 
35
- sudo chown -R openldap.openldap /etc/ldap/slapd.d
36
- sudo chown -R openldap.openldap /var/lib/ldap
36
+ chown -R openldap.openldap /etc/ldap/slapd.d
37
+ chown -R openldap.openldap /var/lib/ldap
37
38
 
38
- sudo /etc/init.d/slapd start
39
+ /etc/init.d/slapd start
39
40
 
40
41
  # Import seed data.
41
42
  # NOTE: use ldapadd in order for memberOf and refint to apply, instead of:
42
- # cat $SEED_PATH/seed.ldif | sudo slapadd -F /etc/ldap/slapd.d
43
- /usr/bin/time sudo ldapadd -x -D "cn=admin,dc=rubyldap,dc=com" -w passworD1 \
43
+ # cat $SEED_PATH/seed.ldif | slapadd -F /etc/ldap/slapd.d
44
+ /usr/bin/time ldapadd -x -D "cn=admin,dc=rubyldap,dc=com" -w passworD1 \
44
45
  -h localhost -p 389 \
45
46
  -f $SEED_PATH/seed.ldif
46
47
 
47
- sudo rm -rf $TMPDIR
48
+ rm -rf $TMPDIR
48
49
 
49
50
  # SSL
50
51
 
51
- sudo sh -c "certtool --generate-privkey > /etc/ssl/private/cakey.pem"
52
+ sh -c "certtool --generate-privkey > /etc/ssl/private/cakey.pem"
52
53
 
53
- sudo sh -c "cat > /etc/ssl/ca.info <<EOF
54
+ sh -c "cat > /etc/ssl/ca.info <<EOF
54
55
  cn = rubyldap
55
56
  ca
56
57
  cert_signing_key
57
58
  EOF"
58
59
 
59
60
  # Create the self-signed CA certificate:
60
- sudo certtool --generate-self-signed \
61
+ certtool --generate-self-signed \
61
62
  --load-privkey /etc/ssl/private/cakey.pem \
62
63
  --template /etc/ssl/ca.info \
63
64
  --outfile /etc/ssl/certs/cacert.pem
64
65
 
65
66
  # Make a private key for the server:
66
- sudo certtool --generate-privkey \
67
+ certtool --generate-privkey \
67
68
  --bits 1024 \
68
69
  --outfile /etc/ssl/private/ldap01_slapd_key.pem
69
70
 
70
- sudo sh -c "cat > /etc/ssl/ldap01.info <<EOF
71
+ sh -c "cat > /etc/ssl/ldap01.info <<EOF
71
72
  organization = Example Company
72
73
  cn = ldap01.example.com
73
74
  tls_www_server
@@ -77,14 +78,14 @@ expiration_days = 3650
77
78
  EOF"
78
79
 
79
80
  # Create the server certificate
80
- sudo certtool --generate-certificate \
81
+ certtool --generate-certificate \
81
82
  --load-privkey /etc/ssl/private/ldap01_slapd_key.pem \
82
83
  --load-ca-certificate /etc/ssl/certs/cacert.pem \
83
84
  --load-ca-privkey /etc/ssl/private/cakey.pem \
84
85
  --template /etc/ssl/ldap01.info \
85
86
  --outfile /etc/ssl/certs/ldap01_slapd_cert.pem
86
87
 
87
- sudo ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF | true
88
+ ldapmodify -Y EXTERNAL -H ldapi:/// <<EOF | true
88
89
  dn: cn=config
89
90
  add: olcTLSCACertificateFile
90
91
  olcTLSCACertificateFile: /etc/ssl/certs/cacert.pem
@@ -101,11 +102,11 @@ EOF
101
102
  # protected by TLS/SSL whereas LDAPS, like HTTPS, is a distinct
102
103
  # encrypted-from-the-start protocol that operates over TCP port 636. But we
103
104
  # enable it for testing here.
104
- sudo sed -i -e 's|^SLAPD_SERVICES="\(.*\)"|SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"|' /etc/default/slapd
105
+ sed -i -e 's|^SLAPD_SERVICES="\(.*\)"|SLAPD_SERVICES="ldap:/// ldapi:/// ldaps:///"|' /etc/default/slapd
105
106
 
106
- sudo adduser openldap ssl-cert
107
- sudo chgrp ssl-cert /etc/ssl/private/ldap01_slapd_key.pem
108
- sudo chmod g+r /etc/ssl/private/ldap01_slapd_key.pem
109
- sudo chmod o-r /etc/ssl/private/ldap01_slapd_key.pem
107
+ adduser openldap ssl-cert
108
+ chgrp ssl-cert /etc/ssl/private/ldap01_slapd_key.pem
109
+ chmod g+r /etc/ssl/private/ldap01_slapd_key.pem
110
+ chmod o-r /etc/ssl/private/ldap01_slapd_key.pem
110
111
 
111
- sudo service slapd restart
112
+ service slapd restart
@@ -0,0 +1,11 @@
1
+ require 'test_helper'
2
+
3
+ class TestAuthAdapter < Test::Unit::TestCase
4
+ def test_undefined_auth_adapter
5
+ flexmock(TCPSocket).should_receive(:new).ordered.with('ldap.example.com', 379).once.and_return(nil)
6
+ conn = Net::LDAP::Connection.new(host: 'ldap.example.com', port: 379)
7
+ assert_raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (foo)" do
8
+ conn.bind(method: :foo)
9
+ end
10
+ end
11
+ end
@@ -14,6 +14,10 @@ class TestFilterParser < Test::Unit::TestCase
14
14
  assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(cn=[{something}])")
15
15
  end
16
16
 
17
+ def test_slash
18
+ assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(departmentNumber=FOO//BAR/FOO)")
19
+ end
20
+
17
21
  def test_colons
18
22
  assert_kind_of Net::LDAP::Filter, Net::LDAP::Filter::FilterParser.parse("(ismemberof=cn=edu:berkeley:app:calmessages:deans,ou=campus groups,dc=berkeley,dc=edu)")
19
23
  end
@@ -56,7 +56,7 @@ class LDAPIntegrationTestCase < Test::Unit::TestCase
56
56
  @service = MockInstrumentationService.new
57
57
  @ldap = Net::LDAP.new \
58
58
  host: ENV.fetch('INTEGRATION_HOST', 'localhost'),
59
- port: 389,
59
+ port: ENV.fetch('INTEGRATION_PORT', 389),
60
60
  admin_user: 'uid=admin,dc=rubyldap,dc=com',
61
61
  admin_password: 'passworD1',
62
62
  search_domains: %w(dc=rubyldap,dc=com),
@@ -57,4 +57,11 @@ class TestLDAPInstrumentation < Test::Unit::TestCase
57
57
  assert_equal "(uid=user1)", payload[:filter]
58
58
  assert_equal result.size, payload[:size]
59
59
  end
60
+
61
+ def test_obscure_auth
62
+ password = "opensesame"
63
+ assert_include(@subject.inspect, "anonymous")
64
+ @subject.auth "joe_user", password
65
+ assert_not_include(@subject.inspect, password)
66
+ end
60
67
  end
@@ -1,6 +1,52 @@
1
1
  require_relative 'test_helper'
2
2
 
3
3
  class TestLDAPConnection < Test::Unit::TestCase
4
+ def capture_stderr
5
+ stderr, $stderr = $stderr, StringIO.new
6
+ yield
7
+ $stderr.string
8
+ ensure
9
+ $stderr = stderr
10
+ end
11
+
12
+ def test_list_of_hosts_with_first_host_successful
13
+ hosts = [
14
+ ['test.mocked.com', 636],
15
+ ['test2.mocked.com', 636],
16
+ ['test3.mocked.com', 636],
17
+ ]
18
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_return(nil)
19
+ flexmock(TCPSocket).should_receive(:new).ordered.never
20
+ Net::LDAP::Connection.new(:hosts => hosts)
21
+ end
22
+
23
+ def test_list_of_hosts_with_first_host_failure
24
+ hosts = [
25
+ ['test.mocked.com', 636],
26
+ ['test2.mocked.com', 636],
27
+ ['test3.mocked.com', 636],
28
+ ]
29
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
30
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_return(nil)
31
+ flexmock(TCPSocket).should_receive(:new).ordered.never
32
+ Net::LDAP::Connection.new(:hosts => hosts)
33
+ end
34
+
35
+ def test_list_of_hosts_with_all_hosts_failure
36
+ hosts = [
37
+ ['test.mocked.com', 636],
38
+ ['test2.mocked.com', 636],
39
+ ['test3.mocked.com', 636],
40
+ ]
41
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
42
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_raise(SocketError)
43
+ flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[2]).once.and_raise(SocketError)
44
+ flexmock(TCPSocket).should_receive(:new).ordered.never
45
+ assert_raise Net::LDAP::ConnectionError do
46
+ Net::LDAP::Connection.new(:hosts => hosts)
47
+ end
48
+ end
49
+
4
50
  def test_unresponsive_host
5
51
  assert_raise Net::LDAP::Error do
6
52
  Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
@@ -14,6 +60,16 @@ class TestLDAPConnection < Test::Unit::TestCase
14
60
  end
15
61
  end
16
62
 
63
+ def test_connection_refused
64
+ flexmock(TCPSocket).should_receive(:new).and_raise(Errno::ECONNREFUSED)
65
+ stderr = capture_stderr do
66
+ assert_raise Net::LDAP::ConnectionRefusedError do
67
+ Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
68
+ end
69
+ end
70
+ assert_equal("Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead.\n", stderr)
71
+ end
72
+
17
73
  def test_raises_unknown_exceptions
18
74
  error = Class.new(StandardError)
19
75
  flexmock(TCPSocket).should_receive(:new).and_raise(error)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ldap
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.11'
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francis Cianfrocca
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2015-01-21 00:00:00.000000000 Z
16
+ date: 2015-10-27 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: flexmock
@@ -57,6 +57,20 @@ dependencies:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
59
  version: 0.28.0
60
+ - !ruby/object:Gem::Dependency
61
+ name: test-unit
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
60
74
  description: |-
61
75
  Net::LDAP for Ruby (also called net-ldap) implements client access for the
62
76
  Lightweight Directory Access Protocol (LDAP), an IETF standard protocol for
@@ -106,6 +120,10 @@ files:
106
120
  - lib/net/ber/core_ext/string.rb
107
121
  - lib/net/ber/core_ext/true_class.rb
108
122
  - lib/net/ldap.rb
123
+ - lib/net/ldap/auth_adapter.rb
124
+ - lib/net/ldap/auth_adapter/gss_spnego.rb
125
+ - lib/net/ldap/auth_adapter/sasl.rb
126
+ - lib/net/ldap/auth_adapter/simple.rb
109
127
  - lib/net/ldap/connection.rb
110
128
  - lib/net/ldap/dataset.rb
111
129
  - lib/net/ldap/dn.rb
@@ -118,6 +136,7 @@ files:
118
136
  - lib/net/ldap/version.rb
119
137
  - lib/net/snmp.rb
120
138
  - net-ldap.gemspec
139
+ - script/changelog
121
140
  - script/install-openldap
122
141
  - script/package
123
142
  - script/release
@@ -139,6 +158,7 @@ files:
139
158
  - test/support/vm/openldap/.gitignore
140
159
  - test/support/vm/openldap/README.md
141
160
  - test/support/vm/openldap/Vagrantfile
161
+ - test/test_auth_adapter.rb
142
162
  - test/test_dn.rb
143
163
  - test/test_entry.rb
144
164
  - test/test_filter.rb
@@ -177,7 +197,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
197
  version: '0'
178
198
  requirements: []
179
199
  rubyforge_project:
180
- rubygems_version: 2.2.2
200
+ rubygems_version: 2.2.3
181
201
  signing_key:
182
202
  specification_version: 4
183
203
  summary: Net::LDAP for Ruby (also called net-ldap) implements client access for the
@@ -202,6 +222,7 @@ test_files:
202
222
  - test/support/vm/openldap/.gitignore
203
223
  - test/support/vm/openldap/README.md
204
224
  - test/support/vm/openldap/Vagrantfile
225
+ - test/test_auth_adapter.rb
205
226
  - test/test_dn.rb
206
227
  - test/test_entry.rb
207
228
  - test/test_filter.rb