Skip to content

Commit d35d3bf

Browse files
committed
Merge remote-tracking branch 'origin/master' into lazy-socket-initialization
2 parents e7cc5ae + 987c522 commit d35d3bf

18 files changed

+389
-75
lines changed

Diff for: .travis.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
language: ruby
22
rvm:
3-
- 1.9.3
43
- 2.0.0
54
- 2.1
65
- 2.2
@@ -13,6 +12,9 @@ rvm:
1312
env:
1413
- INTEGRATION=openldap
1514

15+
before_install:
16+
- gem update bundler
17+
1618
install:
1719
- if [ "$INTEGRATION" = "openldap" ]; then sudo script/install-openldap; fi
1820
- bundle install

Diff for: Contributors.rdoc

+1
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ Contributions since:
2222
* David J. Lee (DavidJLee)
2323
* Cody Cutrer (ccutrer)
2424
* WoodsBagotAndreMarquesLee
25+
* Rufus Post (mynameisrufus)

Diff for: History.rdoc

+15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
=== Net::LDAP 0.13.0
2+
3+
* Set a connect_timeout for the creation of a socket {#243}[https://github.com/ruby-ldap/ruby-net-ldap/pull/243]
4+
* Update bundler before installing gems with bundler {#245}[https://github.com/ruby-ldap/ruby-net-ldap/pull/245]
5+
* Net::LDAP#encryption accepts string {#239}[https://github.com/ruby-ldap/ruby-net-ldap/pull/239]
6+
* Adds correct UTF-8 encoding to Net::BER::BerIdentifiedString {#242}[https://github.com/ruby-ldap/ruby-net-ldap/pull/242]
7+
* Remove 2.3.0-preview since ruby-head already is included {#241}[https://github.com/ruby-ldap/ruby-net-ldap/pull/241]
8+
* Drop support for ruby 1.9.3 {#240}[https://github.com/ruby-ldap/ruby-net-ldap/pull/240]
9+
* Fixed capitalization of StartTLSError {#234}[https://github.com/ruby-ldap/ruby-net-ldap/pull/234]
10+
11+
=== Net::LDAP 0.12.1
12+
13+
* Whitespace formatting cleanup {#236}[https://github.com/ruby-ldap/ruby-net-ldap/pull/236]
14+
* Set operation result if LDAP server is not accessible {#232}[https://github.com/ruby-ldap/ruby-net-ldap/pull/232]
15+
116
=== Net::LDAP 0.12.0
217

318
* DRY up connection handling logic {#224}[https://github.com/ruby-ldap/ruby-net-ldap/pull/224]

Diff for: lib/net/ber.rb

+35-4
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ module Net # :nodoc:
106106
# <tr><th>CHARACTER STRING</th><th>C</th><td>29: 61 (0x3d, 0b00111101)</td></tr>
107107
# <tr><th>BMPString</th><th>P</th><td>30: 30 (0x1e, 0b00011110)</td></tr>
108108
# <tr><th>BMPString</th><th>C</th><td>30: 62 (0x3e, 0b00111110)</td></tr>
109+
# <tr><th>ExtendedResponse</th><th>C</th><td>107: 139 (0x8b, 0b010001011)</td></tr>
109110
# </table>
110111
module BER
111112
VERSION = Net::LDAP::VERSION
@@ -293,13 +294,43 @@ def to_arr
293294

294295
##
295296
# A String object with a BER identifier attached.
297+
#
296298
class Net::BER::BerIdentifiedString < String
297299
attr_accessor :ber_identifier
300+
301+
# The binary data provided when parsing the result of the LDAP search
302+
# has the encoding 'ASCII-8BIT' (which is basically 'BINARY', or 'unknown').
303+
#
304+
# This is the kind of a backtrace showing how the binary `data` comes to
305+
# BerIdentifiedString.new(data):
306+
#
307+
# @conn.read_ber(syntax)
308+
# -> StringIO.new(self).read_ber(syntax), i.e. included from module
309+
# -> Net::BER::BERParser.read_ber(syntax)
310+
# -> (private)Net::BER::BERParser.parse_ber_object(syntax, id, data)
311+
#
312+
# In the `#parse_ber_object` method `data`, according to its OID, is being
313+
# 'casted' to one of the Net::BER:BerIdentifiedXXX classes.
314+
#
315+
# As we are using LDAP v3 we can safely assume that the data is encoded
316+
# in UTF-8 and therefore the only thing to be done when instantiating is to
317+
# switch the encoding from 'ASCII-8BIT' to 'UTF-8'.
318+
#
319+
# Unfortunately, there are some ActiveDirectory specific attributes
320+
# (like `objectguid`) that should remain binary (do they really?).
321+
# Using the `#valid_encoding?` we can trap this cases. Special cases like
322+
# Japanese, Korean, etc. encodings might also profit from this. However
323+
# I have no clue how this encodings function.
298324
def initialize args
299-
super begin
300-
args.respond_to?(:encode) ? args.encode('UTF-8') : args
301-
rescue
302-
args
325+
super
326+
#
327+
# Check the encoding of the newly created String and set the encoding
328+
# to 'UTF-8' (NOTE: we do NOT change the bytes, but only set the
329+
# encoding to 'UTF-8').
330+
current_encoding = encoding
331+
if current_encoding == Encoding::BINARY
332+
force_encoding('UTF-8')
333+
force_encoding(current_encoding) unless valid_encoding?
303334
end
304335
end
305336
end

Diff for: lib/net/ldap.rb

+118-54
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ class LDAP
7979
#
8080
# p ldap.get_operation_result
8181
#
82+
# === Setting connect timeout
83+
#
84+
# By default, Net::LDAP uses TCP sockets with a connection timeout of 5 seconds.
85+
#
86+
# This value can be tweaked passing the :connect_timeout parameter.
87+
# i.e.
88+
# ldap = Net::LDAP.new ...,
89+
# :connect_timeout => 3
8290
#
8391
# == A Brief Introduction to LDAP
8492
#
@@ -315,7 +323,14 @@ class Net::LDAP
315323
:constructed => constructed,
316324
}
317325

326+
universal = {
327+
constructed: {
328+
107 => :array #ExtendedResponse (PasswdModifyResponseValue)
329+
}
330+
}
331+
318332
AsnSyntax = Net::BER.compile_syntax(:application => application,
333+
:universal => universal,
319334
:context_specific => context_specific)
320335

321336
DefaultHost = "127.0.0.1"
@@ -324,7 +339,8 @@ class Net::LDAP
324339
DefaultTreebase = "dc=com"
325340
DefaultForceNoPage = false
326341

327-
StartTlsOid = "1.3.6.1.4.1.1466.20037"
342+
StartTlsOid = '1.3.6.1.4.1.1466.20037'
343+
PasswdModifyOid = '1.3.6.1.4.1.4203.1.11.1'
328344

329345
# https://tools.ietf.org/html/rfc4511#section-4.1.9
330346
# https://tools.ietf.org/html/rfc4511#appendix-A
@@ -461,11 +477,52 @@ def self.result2string(code) #:nodoc:
461477
# call to #search, that value will override any treebase value you give
462478
# here.
463479
# * :encryption => specifies the encryption to be used in communicating
464-
# with the LDAP server. The value is either a Hash containing additional
465-
# parameters, or the Symbol :simple_tls, which is equivalent to
466-
# specifying the Hash {:method => :simple_tls}. There is a fairly large
467-
# range of potential values that may be given for this parameter. See
468-
# #encryption for details.
480+
# with the LDAP server. The value must be a Hash containing additional
481+
# parameters, which consists of two keys:
482+
# method: - :simple_tls or :start_tls
483+
# options: - Hash of options for that method
484+
# The :simple_tls encryption method encrypts <i>all</i> communications
485+
# with the LDAP server. It completely establishes SSL/TLS encryption with
486+
# the LDAP server before any LDAP-protocol data is exchanged. There is no
487+
# plaintext negotiation and no special encryption-request controls are
488+
# sent to the server. <i>The :simple_tls option is the simplest, easiest
489+
# 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+
#
503+
# The :start_tls like the :simple_tls encryption method also encrypts all
504+
# communcations with the LDAP server. With the exception that it operates
505+
# over the standard TCP port.
506+
#
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:
521+
#
522+
# {
523+
# :method => :start_tls,
524+
# :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
525+
# }
469526
# * :force_no_page => Set to true to prevent paged results even if your
470527
# server says it supports them. This is a fix for MS Active Directory
471528
# * :instrumentation_service => An object responsible for instrumenting
@@ -482,7 +539,8 @@ def initialize(args = {})
482539
@auth = args[:auth] || DefaultAuth
483540
@base = args[:base] || DefaultTreebase
484541
@force_no_page = args[:force_no_page] || DefaultForceNoPage
485-
encryption args[:encryption] # may be nil
542+
@encryption = args[:encryption] # may be nil
543+
@connect_timeout = args[:connect_timeout]
486544

487545
if pr = @auth[:password] and pr.respond_to?(:call)
488546
@auth[:password] = pr.call
@@ -546,52 +604,16 @@ def authenticate(username, password)
546604
# additional capabilities are added, more configuration values will be
547605
# added here.
548606
#
549-
# The :simple_tls encryption method encrypts <i>all</i> communications
550-
# with the LDAP server. It completely establishes SSL/TLS encryption with
551-
# the LDAP server before any LDAP-protocol data is exchanged. There is no
552-
# plaintext negotiation and no special encryption-request controls are
553-
# sent to the server. <i>The :simple_tls option is the simplest, easiest
554-
# way to encrypt communications between Net::LDAP and LDAP servers.</i>
555-
# It's intended for cases where you have an implicit level of trust in the
556-
# authenticity of the LDAP server. No validation of the LDAP server's SSL
557-
# certificate is performed. This means that :simple_tls will not produce
558-
# errors if the LDAP server's encryption certificate is not signed by a
559-
# well-known Certification Authority. If you get communications or
560-
# protocol errors when using this option, check with your LDAP server
561-
# administrator. Pay particular attention to the TCP port you are
562-
# connecting to. It's impossible for an LDAP server to support plaintext
563-
# LDAP communications and <i>simple TLS</i> connections on the same port.
564-
# The standard TCP port for unencrypted LDAP connections is 389, but the
565-
# standard port for simple-TLS encrypted connections is 636. Be sure you
566-
# are using the correct port.
567-
#
568-
# The :start_tls like the :simple_tls encryption method also encrypts all
569-
# communcations with the LDAP server. With the exception that it operates
570-
# over the standard TCP port.
571-
#
572-
# In order to verify certificates and enable other TLS options, the
573-
# :tls_options hash can be passed alongside :simple_tls or :start_tls.
574-
# This hash contains any options that can be passed to
575-
# OpenSSL::SSL::SSLContext#set_params(). The most common options passed
576-
# should be OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, or the :ca_file option,
577-
# which contains a path to a Certificate Authority file (PEM-encoded).
578-
#
579-
# Example for a default setup without custom settings:
580-
# {
581-
# :method => :simple_tls,
582-
# :tls_options => OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
583-
# }
584-
#
585-
# Example for specifying a CA-File and only allowing TLSv1.1 connections:
586-
#
587-
# {
588-
# :method => :start_tls,
589-
# :tls_options => { :ca_file => "/etc/cafile.pem", :ssl_version => "TLSv1_1" }
590-
# }
607+
# This method is deprecated.
608+
#
591609
def encryption(args)
592-
case args
610+
warn "Deprecation warning: please give :encryption option as a Hash to Net::LDAP.new"
611+
return if args.nil?
612+
return @encryption = args if args.is_a? Hash
613+
614+
case method = args.to_sym
593615
when :simple_tls, :start_tls
594-
args = { :method => args, :tls_options => {} }
616+
args = { :method => method, :tls_options => {} }
595617
end
596618
@encryption = args
597619
end
@@ -637,8 +659,11 @@ def self.open(args)
637659
#++
638660
def get_operation_result
639661
result = @result
640-
result = result.result if result.is_a?(Net::LDAP::PDU)
641662
os = OpenStruct.new
663+
if result.is_a?(Net::LDAP::PDU)
664+
os.extended_response = result.extended_response
665+
result = result.result
666+
end
642667
if result.is_a?(Hash)
643668
# We might get a hash of LDAP response codes instead of a simple
644669
# numeric code.
@@ -1027,6 +1052,44 @@ def modify(args)
10271052
end
10281053
end
10291054

1055+
# Password Modify
1056+
#
1057+
# Change existing password:
1058+
#
1059+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
1060+
# auth = {
1061+
# method: :simple,
1062+
# username: dn,
1063+
# password: 'passworD1'
1064+
# }
1065+
# ldap.password_modify(dn: dn,
1066+
# auth: auth,
1067+
# old_password: 'passworD1',
1068+
# new_password: 'passworD2')
1069+
#
1070+
# Or get the LDAP server to generate a password for you:
1071+
#
1072+
# dn = 'uid=modify-password-user1,ou=People,dc=rubyldap,dc=com'
1073+
# auth = {
1074+
# method: :simple,
1075+
# username: dn,
1076+
# password: 'passworD1'
1077+
# }
1078+
# ldap.password_modify(dn: dn,
1079+
# auth: auth,
1080+
# old_password: 'passworD1')
1081+
#
1082+
# ldap.get_operation_result.extended_response[0][0] #=> 'VtcgGf/G'
1083+
#
1084+
def password_modify(args)
1085+
instrument "modify_password.net_ldap", args do |payload|
1086+
@result = use_connection(args) do |conn|
1087+
conn.password_modify(args)
1088+
end
1089+
@result.success?
1090+
end
1091+
end
1092+
10301093
# Add a value to an attribute. Takes the full DN of the entry to modify,
10311094
# the name (Symbol or String) of the attribute, and the value (String or
10321095
# Array). If the attribute does not exist (and there are no schema
@@ -1247,12 +1310,13 @@ def new_connection
12471310
:port => @port,
12481311
:hosts => @hosts,
12491312
:encryption => @encryption,
1250-
:instrumentation_service => @instrumentation_service
1313+
:instrumentation_service => @instrumentation_service,
1314+
:connect_timeout => @connect_timeout
12511315

12521316
# Force connect to see if there's a connection error
12531317
connection.socket
12541318
connection
1255-
rescue Errno::ECONNREFUSED, Net::LDAP::ConnectionRefusedError => e
1319+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::LDAP::ConnectionRefusedError => e
12561320
@result = {
12571321
:resultCode => 52,
12581322
:errorMessage => ResultStrings[ResultCodeUnavailable]

Diff for: lib/net/ldap/auth_adapter/gss_spnego.rb

+5-4
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ def bind(auth)
2929
t3_msg.serialize
3030
}
3131

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)
32+
Net::LDAP::AuthAdapter::Sasl.new(@connection).bind \
33+
:method => :sasl,
34+
:mechanism => "GSS-SPNEGO",
35+
:initial_credential => NTLM::Message::Type1.new.serialize,
36+
:challenge_response => nego
3637
end
3738
end
3839
end

0 commit comments

Comments
 (0)