Rationalizing startup.

This commit is contained in:
Austin Ziegler 2010-03-13 00:20:07 -05:00
parent 87a7cf1b6a
commit 31946c35c7
7 changed files with 66 additions and 139 deletions

View file

@ -1,38 +1 @@
require 'stringio' require 'net/ldap'
require 'openssl'
require 'socket'
require 'ostruct'
require 'base64'
require 'strscan'
if RUBY_VERSION =~ /^1.9/
begin
SHA1
rescue NameError
require 'digest/sha1'
SHA1 = Digest::SHA1
end
begin
MD5
rescue NameError
require 'digest/md5'
MD5 = Digest::MD5
end
end
if RUBY_VERSION =~ /^1.8/
require 'md5'
require 'sha1'
end
module Net
autoload :BER, 'net/ber'
autoload :LDAP, 'net/ldap'
autoload :LDIF, 'net/ldif'
autoload :SNMP, 'net/snmp'
module BER
autoload :BERParser, 'net/ber/ber_parser'
end
end
require 'net/ldap/core_ext/all'

View file

@ -1,3 +1,5 @@
require 'stringio'
module Net module Net
module BER module BER
module BERParser module BERParser
@ -106,3 +108,4 @@ module Net
end end
end end
end end

View file

@ -1,4 +1,5 @@
require 'openssl' require 'openssl'
require 'ostruct'
require 'net/ber' require 'net/ber'
require 'net/ldap/pdu' require 'net/ldap/pdu'
@ -9,7 +10,6 @@ require 'net/ldap/entry'
require 'net/ldap/core_ext/all' require 'net/ldap/core_ext/all'
module Net module Net
# == Net::LDAP # == Net::LDAP
# #
# This library provides a pure-Ruby implementation of the # This library provides a pure-Ruby implementation of the
@ -231,14 +231,10 @@ module Net
# and then disconnect from the server. The exception is Net::LDAP#open, which makes a connection # and then disconnect from the server. The exception is Net::LDAP#open, which makes a connection
# to the server and then keeps it open while it executes a user-supplied block. Net::LDAP#open # to the server and then keeps it open while it executes a user-supplied block. Net::LDAP#open
# closes the connection on completion of the block. # closes the connection on completion of the block.
#
class LDAP class LDAP
class LdapError < StandardError; end class LdapError < StandardError; end
VERSION = "0.0.5" VERSION = "0.5.0"
SearchScope_BaseObject = 0 SearchScope_BaseObject = 0
SearchScope_SingleLevel = 1 SearchScope_SingleLevel = 1
@ -322,23 +318,17 @@ module Net
68 => "Entry Already Exists" 68 => "Entry Already Exists"
} }
module LdapControls module LdapControls
PagedResults = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696 PagedResults = "1.2.840.113556.1.4.319" # Microsoft evil from RFC 2696
end end
#
# LDAP::result2string # LDAP::result2string
#
def LDAP::result2string code # :nodoc: def LDAP::result2string code # :nodoc:
ResultStrings[code] || "unknown result (#{code})" ResultStrings[code] || "unknown result (#{code})"
end end
attr_accessor :host, :port, :base attr_accessor :host, :port, :base
# Instantiate an object of type Net::LDAP to perform directory operations. # Instantiate an object of type Net::LDAP to perform directory operations.
# This constructor takes a Hash containing arguments, all of which are either optional or may be specified later with other methods as described below. The following arguments # This constructor takes a Hash containing arguments, all of which are either optional or may be specified later with other methods as described below. The following arguments
# are supported: # are supported:
@ -457,7 +447,6 @@ module Net
@encryption = args @encryption = args
end end
# #open takes the same parameters as #new. #open makes a network connection to the # #open takes the same parameters as #new. #open makes a network connection to the
# LDAP server and then passes a newly-created Net::LDAP object to the caller-supplied block. # LDAP server and then passes a newly-created Net::LDAP object to the caller-supplied block.
# Within the block, you can call any of the instance methods of Net::LDAP to # Within the block, you can call any of the instance methods of Net::LDAP to
@ -478,24 +467,25 @@ module Net
ldap1.open {|ldap| yield ldap } ldap1.open {|ldap| yield ldap }
end end
# Returns a meaningful result any time after # Returns a meaningful result any time after a protocol operation
# a protocol operation (#bind, #search, #add, #modify, #rename, #delete) # (#bind, #search, #add, #modify, #rename, #delete) has completed.
# has completed. # It returns an #OpenStruct containing an LDAP result code (0 means
# It returns an #OpenStruct containing an LDAP result code (0 means success), # success), and a human-readable string.
# and a human-readable string. #
# unless ldap.bind # unless ldap.bind
# puts "Result: #{ldap.get_operation_result.code}" # puts "Result: #{ldap.get_operation_result.code}"
# puts "Message: #{ldap.get_operation_result.message}" # puts "Message: #{ldap.get_operation_result.message}"
# end # end
# #
# Certain operations return additional information, accessible through members # Certain operations return additional information, accessible through
# of the object returned from #get_operation_result. Check #get_operation_result.error_message # members of the object returned from #get_operation_result. Check
# and #get_operation_result.matched_dn. # #get_operation_result.error_message and
# #get_operation_result.matched_dn.
# #
#-- #--
# Modified the implementation, 20Mar07. We might get a hash of LDAP response codes # Modified the implementation, 20Mar07. We might get a hash of LDAP
# instead of a simple numeric code. # response codes instead of a simple numeric code.
# #++
def get_operation_result def get_operation_result
os = OpenStruct.new os = OpenStruct.new
if @result.is_a?(Hash) if @result.is_a?(Hash)
@ -511,7 +501,6 @@ module Net
os os
end end
# Opens a network connection to the server and then # Opens a network connection to the server and then
# passes <tt>self</tt> to the caller-supplied block. The connection is # passes <tt>self</tt> to the caller-supplied block. The connection is
# closed when the block completes. Used for executing multiple # closed when the block completes. Used for executing multiple
@ -535,6 +524,7 @@ module Net
# We then pass self to the caller's block, where he will execute # We then pass self to the caller's block, where he will execute
# his LDAP operations. Of course they will all generate auth failures # his LDAP operations. Of course they will all generate auth failures
# if the bind was unsuccessful. # if the bind was unsuccessful.
#++
def open def open
raise LdapError.new( "open already in progress" ) if @open_connection raise LdapError.new( "open already in progress" ) if @open_connection
begin begin
@ -547,7 +537,6 @@ module Net
end end
end end
# Searches the LDAP directory for directory entries. # Searches the LDAP directory for directory entries.
# Takes a hash argument with parameters. Supported parameters include: # Takes a hash argument with parameters. Supported parameters include:
# * :base (a string specifying the tree-base for the search); # * :base (a string specifying the tree-base for the search);
@ -625,7 +614,7 @@ module Net
# handle DNs. Change it to a plain array. Eventually we may # handle DNs. Change it to a plain array. Eventually we may
# want to return a Dataset object that delegates to an internal # want to return a Dataset object that delegates to an internal
# array, so we can provide sort methods and what-not. # array, so we can provide sort methods and what-not.
# #++
def search args = {} def search args = {}
unless args[:ignore_server_caps] unless args[:ignore_server_caps]
args[:paged_searches_supported] = paged_searches_supported? args[:paged_searches_supported] = paged_searches_supported?
@ -718,7 +707,7 @@ module Net
# If there is an @open_connection, then perform the bind # If there is an @open_connection, then perform the bind
# on it. Otherwise, connect, bind, and disconnect. # on it. Otherwise, connect, bind, and disconnect.
# The latter operation is obviously useful only as an auth check. # The latter operation is obviously useful only as an auth check.
# #++
def bind(auth=@auth) def bind(auth=@auth)
if @open_connection if @open_connection
@result = @open_connection.bind auth @result = @open_connection.bind auth
@ -734,7 +723,6 @@ module Net
@result == 0 @result == 0
end end
# #
# #bind_as is for testing authentication credentials. # #bind_as is for testing authentication credentials.
# #
@ -794,7 +782,6 @@ module Net
result result
end end
# Adds a new entry to the remote LDAP server. # Adds a new entry to the remote LDAP server.
# Supported arguments: # Supported arguments:
# :dn :: Full DN of the new entry # :dn :: Full DN of the new entry
@ -821,7 +808,7 @@ module Net
#-- #--
# Provisional modification: Connection#add returns a full hash with LDAP status values, # Provisional modification: Connection#add returns a full hash with LDAP status values,
# instead of the simple result number we're used to getting. # instead of the simple result number we're used to getting.
# #++
def add args def add args
if @open_connection if @open_connection
@result = @open_connection.add( args ) @result = @open_connection.add( args )
@ -839,7 +826,6 @@ module Net
@result == 0 @result == 0
end end
# Modifies the attribute values of a particular entry on the LDAP directory. # Modifies the attribute values of a particular entry on the LDAP directory.
# Takes a hash with arguments. Supported arguments are: # Takes a hash with arguments. Supported arguments are:
# :dn :: (the full DN of the entry whose attributes are to be modified) # :dn :: (the full DN of the entry whose attributes are to be modified)
@ -937,7 +923,6 @@ module Net
@result == 0 @result == 0
end end
# Add a value to an attribute. # Add a value to an attribute.
# Takes the full DN of the entry to modify, # Takes the full DN of the entry to modify,
# the name (Symbol or String) of the attribute, and the value (String or # the name (Symbol or String) of the attribute, and the value (String or
@ -991,7 +976,6 @@ module Net
modify :dn => dn, :operations => [[:delete, attribute, nil]] modify :dn => dn, :operations => [[:delete, attribute, nil]]
end end
# Rename an entry on the remote DIS by changing the last RDN of its DN. # Rename an entry on the remote DIS by changing the last RDN of its DN.
# _Documentation_ _stub_ # _Documentation_ _stub_
# #
@ -1045,7 +1029,6 @@ module Net
@result == 0 @result == 0
end end
# (Experimental, subject to change). # (Experimental, subject to change).
# Return the rootDSE record from the LDAP server as a Net::LDAP::Entry, or an # Return the rootDSE record from the LDAP server as a Net::LDAP::Entry, or an
# empty Entry if the server doesn't return the record. # empty Entry if the server doesn't return the record.
@ -1058,7 +1041,7 @@ module Net
# We may be called by #search itself, which may need to determine things like paged # We may be called by #search itself, which may need to determine things like paged
# search capabilities. So to avoid an infinite regress, set :ignore_server_caps, # search capabilities. So to avoid an infinite regress, set :ignore_server_caps,
# which prevents us getting called recursively. # which prevents us getting called recursively.
# #++
def search_root_dse def search_root_dse
rs = search( rs = search(
:ignore_server_caps=>true, :ignore_server_caps=>true,
@ -1069,7 +1052,6 @@ module Net
(rs and rs.first) or Entry.new (rs and rs.first) or Entry.new
end end
# Return the root Subschema record from the LDAP server as a Net::LDAP::Entry, # Return the root Subschema record from the LDAP server as a Net::LDAP::Entry,
# or an empty Entry if the server doesn't return the record. On success, the # or an empty Entry if the server doesn't return the record. On success, the
# Net::LDAP::Entry returned from this call will have the attributes :dn, # Net::LDAP::Entry returned from this call will have the attributes :dn,
@ -1093,7 +1075,7 @@ module Net
# The :dn attribute in the returned Entry is the subschema name as returned from # The :dn attribute in the returned Entry is the subschema name as returned from
# the server. # the server.
# Set :ignore_server_caps, see the notes in search_root_dse. # Set :ignore_server_caps, see the notes in search_root_dse.
# #++
def search_subschema_entry def search_subschema_entry
rs = search( rs = search(
:ignore_server_caps=>true, :ignore_server_caps=>true,
@ -1116,30 +1098,25 @@ module Net
(rs and rs.first) or Entry.new (rs and rs.first) or Entry.new
end end
#-- #--
# Convenience method to query server capabilities. # Convenience method to query server capabilities.
# Only do this once per Net::LDAP object. # Only do this once per Net::LDAP object.
# Note, we call a search, and we might be called from inside a search! # Note, we call a search, and we might be called from inside a search!
# MUST refactor the root_dse call out. # MUST refactor the root_dse call out.
#++
def paged_searches_supported? def paged_searches_supported?
@server_caps ||= search_root_dse @server_caps ||= search_root_dse
@server_caps[:supportedcontrol].include?(LdapControls::PagedResults) @server_caps[:supportedcontrol].include?(LdapControls::PagedResults)
end end
end # class LDAP end # class LDAP
class LDAP class LDAP
# This is a private class used internally by the library. It should not be called by user code. # This is a private class used internally by the library. It should not
# be called by user code.
class Connection # :nodoc: class Connection # :nodoc:
LdapVersion = 3 LdapVersion = 3
MaxSaslChallenges = 10 MaxSaslChallenges = 10
#--
# initialize
#
def initialize server def initialize server
begin begin
@conn = TCPSocket.new( server[:host], server[:port] ) @conn = TCPSocket.new( server[:host], server[:port] )
@ -1159,6 +1136,7 @@ module Net
getc.ord getc.ord
end end
end end
def self.wrap_with_ssl(io) def self.wrap_with_ssl(io)
ctx = OpenSSL::SSL::SSLContext.new ctx = OpenSSL::SSL::SSLContext.new
conn = OpenSSL::SSL::SSLSocket.new(io, ctx) conn = OpenSSL::SSL::SSLSocket.new(io, ctx)
@ -1195,7 +1173,7 @@ module Net
# It does not require an alternative port for encrypted communications, as with # It does not require an alternative port for encrypted communications, as with
# simple_tls. # simple_tls.
# Thanks for Kouhei Sutou for generously contributing the :start_tls path. # Thanks for Kouhei Sutou for generously contributing the :start_tls path.
# #++
def setup_encryption args def setup_encryption args
case args[:method] case args[:method]
when :simple_tls when :simple_tls
@ -1226,6 +1204,7 @@ module Net
# sure a connection object gets closed without waiting # sure a connection object gets closed without waiting
# for a GC to happen. Clients shouldn't have to call it, # for a GC to happen. Clients shouldn't have to call it,
# but perhaps it will come in handy someday. # but perhaps it will come in handy someday.
#++
def close def close
@conn.close @conn.close
@conn = nil @conn = nil
@ -1233,16 +1212,15 @@ module Net
#-- #--
# next_msgid # next_msgid
# #++
def next_msgid def next_msgid
@msgid ||= 0 @msgid ||= 0
@msgid += 1 @msgid += 1
end end
#-- #--
# bind # bind
# #++
def bind auth def bind auth
meth = auth[:method] meth = auth[:method]
if [:simple, :anonymous, :anon].include?( meth ) if [:simple, :anonymous, :anon].include?( meth )
@ -1260,7 +1238,7 @@ module Net
# bind_simple # bind_simple
# Implements a simple user/psw authentication. # Implements a simple user/psw authentication.
# Accessed by calling #bind with a method of :simple or :anonymous. # Accessed by calling #bind with a method of :simple or :anonymous.
# #++
def bind_simple auth def bind_simple auth
user,psw = if auth[:method] == :simple user,psw = if auth[:method] == :simple
[auth[:username] || auth[:dn], auth[:password]] [auth[:username] || auth[:dn], auth[:password]]
@ -1292,7 +1270,7 @@ module Net
# field of the LDAP BindResponse packet. The challenge-response block may be called multiple # field of the LDAP BindResponse packet. The challenge-response block may be called multiple
# times during the course of a SASL authentication, and each time it must return a value # times during the course of a SASL authentication, and each time it must return a value
# that will be passed back to the server as the credential data in the next BindRequest packet. # that will be passed back to the server as the credential data in the next BindRequest packet.
# #++
def bind_sasl auth def bind_sasl auth
mech,cred,chall = auth[:mechanism],auth[:initial_credential],auth[:challenge_response] mech,cred,chall = auth[:mechanism],auth[:initial_credential],auth[:challenge_response]
raise LdapError.new( "invalid binding information" ) unless (mech && cred && chall) raise LdapError.new( "invalid binding information" ) unless (mech && cred && chall)
@ -1325,7 +1303,7 @@ module Net
# :gss_spnego. It requires :username and :password attributes, just like the :simple # :gss_spnego. It requires :username and :password attributes, just like the :simple
# authentication method. It performs a GSS-SPNEGO authentication with the server, which # authentication method. It performs a GSS-SPNEGO authentication with the server, which
# is presumed to be a Microsoft Active Directory. # is presumed to be a Microsoft Active Directory.
# #++
def bind_gss_spnego auth def bind_gss_spnego auth
require 'ntlm.rb' require 'ntlm.rb'
@ -1365,7 +1343,7 @@ module Net
# This implementation is kindof clunky and should probably be refactored. # This implementation is kindof clunky and should probably be refactored.
# Also, is it my imagination, or are A/Ds the slowest directory servers ever??? # Also, is it my imagination, or are A/Ds the slowest directory servers ever???
# OpenLDAP newer than version 2.2.0 supports paged searches. # OpenLDAP newer than version 2.2.0 supports paged searches.
# #++
def search args = {} def search args = {}
search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" ) search_filter = (args && args[:filter]) || Filter.eq( "objectclass", "*" )
search_filter = Filter.construct(search_filter) if search_filter.is_a?(String) search_filter = Filter.construct(search_filter) if search_filter.is_a?(String)
@ -1496,8 +1474,6 @@ module Net
result_code result_code
end end
#-- #--
# modify # modify
# TODO, need to support a time limit, in case the server fails to respond. # TODO, need to support a time limit, in case the server fails to respond.
@ -1505,7 +1481,7 @@ module Net
# Should return a proper error instead, probaby from farther up the chain. # Should return a proper error instead, probaby from farther up the chain.
# TODO!!! If the user specifies a bogus opcode, we'll throw a # TODO!!! If the user specifies a bogus opcode, we'll throw a
# confusing error here ("to_ber_enumerated is not defined on nil"). # confusing error here ("to_ber_enumerated is not defined on nil").
# #++
def modify args def modify args
modify_dn = args[:dn] or raise "Unable to modify empty DN" modify_dn = args[:dn] or raise "Unable to modify empty DN"
modify_ops = [] modify_ops = []
@ -1524,7 +1500,6 @@ module Net
pdu.result pdu.result
end end
#-- #--
# add # add
# TODO, need to support a time limit, in case the server fails to respond. # TODO, need to support a time limit, in case the server fails to respond.
@ -1532,7 +1507,7 @@ module Net
# than a simple result number. This is experimental, and eventually we'll want # than a simple result number. This is experimental, and eventually we'll want
# to do this with all the others. The point is to have access to the error message # to do this with all the others. The point is to have access to the error message
# and the matched-DN returned by the server. # and the matched-DN returned by the server.
# #++
def add args def add args
add_dn = args[:dn] or raise LdapError.new("Unable to add empty DN") add_dn = args[:dn] or raise LdapError.new("Unable to add empty DN")
add_attrs = [] add_attrs = []
@ -1548,11 +1523,10 @@ module Net
pdu.result pdu.result
end end
#-- #--
# rename # rename
# TODO, need to support a time limit, in case the server fails to respond. # TODO, need to support a time limit, in case the server fails to respond.
# #++
def rename args def rename args
old_dn = args[:olddn] or raise "Unable to rename empty DN" old_dn = args[:olddn] or raise "Unable to rename empty DN"
new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN" new_rdn = args[:newrdn] or raise "Unable to rename to empty RDN"
@ -1566,11 +1540,10 @@ module Net
pdu.result_code pdu.result_code
end end
#-- #--
# delete # delete
# TODO, need to support a time limit, in case the server fails to respond. # TODO, need to support a time limit, in case the server fails to respond.
# #++
def delete args def delete args
dn = args[:dn] or raise "Unable to delete empty DN" dn = args[:dn] or raise "Unable to delete empty DN"
@ -1581,10 +1554,6 @@ module Net
(be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 11) or raise LdapError.new( "response missing or invalid" ) (be = @conn.read_ber(AsnSyntax)) && (pdu = LdapPdu.new( be )) && (pdu.app_tag == 11) or raise LdapError.new( "response missing or invalid" )
pdu.result_code pdu.result_code
end end
end # class Connection end # class Connection
end # class LDAP end # class LDAP
end # module Net end # module Net

View file

@ -490,5 +490,3 @@ end # class Net::LDAP::FilterParser
end # class Net::LDAP end # class Net::LDAP
end # module Net end # module Net

View file

@ -1,4 +1,3 @@
# $Id$
# #
# LDAP PDU support classes # LDAP PDU support classes
# #
@ -24,12 +23,12 @@
# #
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
module Net require 'ostruct'
module Net
class LdapPduError < StandardError; end class LdapPduError < StandardError; end
class LdapPdu class LdapPdu
BindRequest = 0 BindRequest = 0
BindResult = 1 BindResult = 1
UnbindRequest = 2 UnbindRequest = 2

View file

@ -1,6 +1,3 @@
# $Id$
#
#
#---------------------------------------------------------------------------- #----------------------------------------------------------------------------
# #
# Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved. # Copyright (C) 2006 by Francis Cianfrocca. All Rights Reserved.
@ -22,41 +19,39 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# #
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
#
#
require 'digest/sha1'
require 'digest/md5'
module Net module Net
class LDAP class LDAP
class Password class Password
class << self class << self
# Generate a password-hash suitable for inclusion in an LDAP
# Generate a password-hash suitable for inclusion in an LDAP attribute. # attribute. Pass a hash type (currently supported: :md5 and :sha)
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext # and a plaintext password. This function will return a hashed
# password. This function will return a hashed representation. # representation.
# STUB: This is here to fulfill the requirements of an RFC, which one? #
# STUB: This is here to fulfill the requirements of an RFC, which
# one?
#
# TODO, gotta do salted-sha and (maybe) salted-md5. # TODO, gotta do salted-sha and (maybe) salted-md5.
# Should we provide sha1 as a synonym for sha1? I vote no because then # Should we provide sha1 as a synonym for sha1? I vote no because
# should you also provide ssha1 for symmetry? # then should you also provide ssha1 for symmetry?
def generate( type, str ) def generate(type, str)
digest, digest_name = case type digest, digest_name = case type
when :md5 when :md5
[Digest::MD5.new, 'MD5'] [Digest::MD5.new, 'MD5']
when :sha when :sha
[Digest::SHA1.new, 'SHA'] [Digest::SHA1.new, 'SHA']
# when ssha
else else
raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" ) raise Net::LDAP::LdapError.new("unsupported password-hash type (#{type})")
end end
digest << str.to_s digest << str.to_s
return "{#{digest_name}}#{[digest.digest].pack('m').chomp }" return "{#{digest_name}}#{[digest.digest].pack('m').chomp }"
end end
end end
end end
end end
end end

View file

@ -1,3 +1,3 @@
# Add 'lib' to load path. # Add 'lib' to load path.
require 'test/unit' require 'test/unit'
require 'net' require 'net/ldap'