Cleaned up the layout quite a bit to make Gemifying and including into Rails 3 less painful. Some steps to 1.9 compatibility
This commit is contained in:
parent
ba08042d75
commit
d37c3b3ae6
37
Manifest.txt
37
Manifest.txt
|
@ -1,37 +0,0 @@
|
||||||
COPYING
|
|
||||||
History.txt
|
|
||||||
LICENSE
|
|
||||||
Manifest.txt
|
|
||||||
README.txt
|
|
||||||
Rakefile
|
|
||||||
Release-Announcement
|
|
||||||
lib/net/ber.rb
|
|
||||||
lib/net/ldap.rb
|
|
||||||
lib/net/ldap/dataset.rb
|
|
||||||
lib/net/ldap/entry.rb
|
|
||||||
lib/net/ldap/filter.rb
|
|
||||||
lib/net/ldap/pdu.rb
|
|
||||||
lib/net/ldap/psw.rb
|
|
||||||
lib/net/ldif.rb
|
|
||||||
lib/net/snmp.rb
|
|
||||||
pre-setup.rb
|
|
||||||
setup.rb
|
|
||||||
test/common.rb
|
|
||||||
test/test_ber.rb
|
|
||||||
test/test_entry.rb
|
|
||||||
test/test_filter.rb
|
|
||||||
test/test_ldif.rb
|
|
||||||
test/test_password.rb
|
|
||||||
test/test_snmp.rb
|
|
||||||
test/testdata.ldif
|
|
||||||
tests/NOTICE.txt
|
|
||||||
tests/testber.rb
|
|
||||||
tests/testdata.ldif
|
|
||||||
tests/testem.rb
|
|
||||||
tests/testfilter.rb
|
|
||||||
tests/testldap.rb
|
|
||||||
tests/testldif.rb
|
|
||||||
tests/testpsw.rb
|
|
||||||
tests/testsnmp.rb
|
|
||||||
testserver/ldapserver.rb
|
|
||||||
testserver/testdata.ldif
|
|
|
@ -1,95 +0,0 @@
|
||||||
We're pleased to announce version 0.0.4 of Net::LDAP, the pure-Ruby LDAP library.
|
|
||||||
|
|
||||||
This version adds an implementation of Net::LDAP#bind_as, which allows
|
|
||||||
you to authenticate users who log into your applications using simple
|
|
||||||
identifiers like email addresses and simple usernames. Thanks to Simon Claret
|
|
||||||
for suggesting the original implementation in his page on the Rails-Wiki,
|
|
||||||
and for valuable comments and help with testing.
|
|
||||||
|
|
||||||
We have un-deprecated Net::LDAP#modify, which can be useful with
|
|
||||||
LDAP servers that observe non-standard transactional and concurrency
|
|
||||||
semantics in LDAP Modify operations. Note: this is a documentation change,
|
|
||||||
not a code change. Thanks to Justin Forder for providing the rationale
|
|
||||||
for this change.
|
|
||||||
|
|
||||||
We added a larger set of special characters which may appear in RFC-2254
|
|
||||||
standard search filters. Thanks to Andre Nathan for this patch.
|
|
||||||
|
|
||||||
We fixed a bug that was preventing Net::LDAP#open from being called more
|
|
||||||
than once on the same object.
|
|
||||||
|
|
||||||
|
|
||||||
Net::LDAP is a feature-complete LDAP client which can access as much as
|
|
||||||
possible of the functionality of the most-used LDAP server implementations.
|
|
||||||
This library does not wrap any existing native-code LDAP libraries, creates no
|
|
||||||
Ruby extensions, and has no dependencies external to Ruby.
|
|
||||||
|
|
||||||
If anyone wants to contribute suggestions, insights or (especially)
|
|
||||||
code, please email me at garbagecat10 .. .. gmail.com.
|
|
||||||
|
|
||||||
= What is Net::LDAP for Ruby?
|
|
||||||
This library provides a pure-Ruby implementation of an LDAP client.
|
|
||||||
It can be used to access any server which implements the LDAP protocol.
|
|
||||||
|
|
||||||
Net::LDAP is intended to provide full LDAP functionality while hiding
|
|
||||||
the more arcane aspects of the LDAP protocol itself, so as to make the
|
|
||||||
programming interface as Ruby-like as possible.
|
|
||||||
|
|
||||||
In particular, this means that there is no direct dependence on the
|
|
||||||
structure of the various "traditional" LDAP clients. This is a ground-up
|
|
||||||
rethinking of the LDAP API.
|
|
||||||
|
|
||||||
Net::LDAP is based on RFC-2251, which specifies the Lightweight Directory
|
|
||||||
Access Protocol, as amended and extended by subsequent RFCs and by the more
|
|
||||||
widely-used directory implementations.
|
|
||||||
|
|
||||||
Homepage:: http://rubyforge.org/projects/net-ldap/
|
|
||||||
Download:: http://rubyforge.org/frs/?group_id=143
|
|
||||||
Copyright:: 2006 by Francis Cianfrocca
|
|
||||||
|
|
||||||
== LICENCE NOTES
|
|
||||||
Please read the file LICENCE for licensing restrictions on this library. In
|
|
||||||
the simplest terms, this library is available under the same terms as Ruby
|
|
||||||
itself.
|
|
||||||
|
|
||||||
== Requirements and Installation
|
|
||||||
Net::LDAP requires Ruby 1.8.2 or better.
|
|
||||||
|
|
||||||
Net::LDAP can be installed with:
|
|
||||||
|
|
||||||
% ruby setup.rb
|
|
||||||
|
|
||||||
Alternatively, you can use the RubyGems version of Net::LDAP available
|
|
||||||
as ruby-net-ldap-0.0.2.gem from the usual sources.
|
|
||||||
|
|
||||||
== Whet your appetite:
|
|
||||||
require 'net/ldap'
|
|
||||||
|
|
||||||
ldap = Net::LDAP.new :host => server_ip_address,
|
|
||||||
:port => 389,
|
|
||||||
:auth => {
|
|
||||||
:method => :simple,
|
|
||||||
:username => "cn=manager,dc=example,dc=com",
|
|
||||||
:password => "opensesame"
|
|
||||||
}
|
|
||||||
|
|
||||||
filter = Net::LDAP::Filter.eq( "cn", "George*" )
|
|
||||||
treebase = "dc=example,dc=com"
|
|
||||||
|
|
||||||
ldap.search( :base => treebase, :filter => filter ) do |entry|
|
|
||||||
puts "DN: #{entry.dn}"
|
|
||||||
entry.each do |attribute, values|
|
|
||||||
puts " #{attribute}:"
|
|
||||||
values.each do |value|
|
|
||||||
puts " --->#{value}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
p ldap.get_operation_result
|
|
||||||
|
|
||||||
== Net::LDAP 0.0.2: May 3, 2006
|
|
||||||
* Fixed malformation in distro tarball and gem.
|
|
||||||
* Improved documentation.
|
|
||||||
* Supported "paged search control."
|
|
||||||
|
|
38
lib/net.rb
Normal file
38
lib/net.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
require 'stringio'
|
||||||
|
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'
|
583
lib/net/ber.rb
583
lib/net/ber.rb
|
@ -28,529 +28,80 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
|
module Net
|
||||||
|
module BER
|
||||||
|
|
||||||
|
#--
|
||||||
|
# This condenses our nicely self-documenting ASN hashes down
|
||||||
|
# to an array for fast lookups.
|
||||||
|
# Scoped to be called as a module method, but not intended for
|
||||||
|
# user code to call.
|
||||||
|
#
|
||||||
|
def self.compile_syntax(syn)
|
||||||
|
out = [nil] * 256
|
||||||
|
syn.each do |tclass, tclasses|
|
||||||
|
tagclass = {:universal=>0, :application=>64, :context_specific=>128, :private=>192} [tclass]
|
||||||
|
tclasses.each do |codingtype,codings|
|
||||||
|
encoding = {:primitive=>0, :constructed=>32} [codingtype]
|
||||||
|
codings.each {|tag, objtype| out[tagclass + encoding + tag] = objtype }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ber
|
||||||
|
# Provisional implementation.
|
||||||
|
# We ASSUME that our incoming value is an array, and we
|
||||||
|
# use the Array#to_ber_oid method defined below.
|
||||||
|
# We probably should obsolete that method, actually, in
|
||||||
|
# and move the code here.
|
||||||
|
# WE ARE NOT CURRENTLY ENCODING THE BER-IDENTIFIER.
|
||||||
|
# This implementation currently hardcodes 6, the universal OID tag.
|
||||||
|
ary = @value.dup
|
||||||
|
first = ary.shift
|
||||||
|
raise Net::BER::BerError.new(" invalid OID" ) unless [0,1,2].include?(first)
|
||||||
|
first = first * 40 + ary.shift
|
||||||
|
ary.unshift first
|
||||||
|
oid = ary.pack("w*")
|
||||||
|
[6, oid.length].pack("CC") + oid
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
module BER
|
module BER
|
||||||
|
class BerError < StandardError; end
|
||||||
class BerError < StandardError; end
|
|
||||||
|
class BerIdentifiedString < String
|
||||||
|
attr_accessor :ber_identifier
|
||||||
class BerIdentifiedString < String
|
def initialize args
|
||||||
attr_accessor :ber_identifier
|
super args
|
||||||
def initialize args
|
|
||||||
super args
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class BerIdentifiedArray < Array
|
|
||||||
attr_accessor :ber_identifier
|
|
||||||
def initialize
|
|
||||||
super
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class BerIdentifiedNull
|
|
||||||
attr_accessor :ber_identifier
|
|
||||||
def to_ber
|
|
||||||
"\005\000"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class BerIdentifiedOid
|
|
||||||
attr_accessor :ber_identifier
|
|
||||||
def initialize oid
|
|
||||||
if oid.is_a?(String)
|
|
||||||
oid = oid.split(/\./).map {|s| s.to_i }
|
|
||||||
end
|
|
||||||
@value = oid
|
|
||||||
end
|
|
||||||
def to_ber
|
|
||||||
# Provisional implementation.
|
|
||||||
# We ASSUME that our incoming value is an array, and we
|
|
||||||
# use the Array#to_ber_oid method defined below.
|
|
||||||
# We probably should obsolete that method, actually, in
|
|
||||||
# and move the code here.
|
|
||||||
# WE ARE NOT CURRENTLY ENCODING THE BER-IDENTIFIER.
|
|
||||||
# This implementation currently hardcodes 6, the universal OID tag.
|
|
||||||
ary = @value.dup
|
|
||||||
first = ary.shift
|
|
||||||
raise Net::BER::BerError.new(" invalid OID" ) unless [0,1,2].include?(first)
|
|
||||||
first = first * 40 + ary.shift
|
|
||||||
ary.unshift first
|
|
||||||
oid = ary.pack("w*")
|
|
||||||
[6, oid.length].pack("CC") + oid
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#--
|
|
||||||
# This condenses our nicely self-documenting ASN hashes down
|
|
||||||
# to an array for fast lookups.
|
|
||||||
# Scoped to be called as a module method, but not intended for
|
|
||||||
# user code to call.
|
|
||||||
#
|
|
||||||
def self.compile_syntax syn
|
|
||||||
out = [nil] * 256
|
|
||||||
syn.each {|tclass,tclasses|
|
|
||||||
tagclass = {:universal=>0, :application=>64, :context_specific=>128, :private=>192} [tclass]
|
|
||||||
tclasses.each {|codingtype,codings|
|
|
||||||
encoding = {:primitive=>0, :constructed=>32} [codingtype]
|
|
||||||
codings.each {|tag,objtype|
|
|
||||||
out[tagclass + encoding + tag] = objtype
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out
|
|
||||||
end
|
|
||||||
|
|
||||||
# This module is for mixing into IO and IO-like objects.
|
|
||||||
module BERParser
|
|
||||||
|
|
||||||
# The order of these follows the class-codes in BER.
|
|
||||||
# Maybe this should have been a hash.
|
|
||||||
TagClasses = [:universal, :application, :context_specific, :private]
|
|
||||||
|
|
||||||
BuiltinSyntax = BER.compile_syntax( {
|
|
||||||
:universal => {
|
|
||||||
:primitive => {
|
|
||||||
1 => :boolean,
|
|
||||||
2 => :integer,
|
|
||||||
4 => :string,
|
|
||||||
5 => :null,
|
|
||||||
6 => :oid,
|
|
||||||
10 => :integer,
|
|
||||||
13 => :string # (relative OID)
|
|
||||||
},
|
|
||||||
:constructed => {
|
|
||||||
16 => :array,
|
|
||||||
17 => :array
|
|
||||||
}
|
|
||||||
},
|
|
||||||
:context_specific => {
|
|
||||||
:primitive => {
|
|
||||||
10 => :integer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
#
|
|
||||||
# read_ber
|
|
||||||
# TODO: clean this up so it works properly with partial
|
|
||||||
# packets coming from streams that don't block when
|
|
||||||
# we ask for more data (like StringIOs). At it is,
|
|
||||||
# this can throw TypeErrors and other nasties.
|
|
||||||
#--
|
|
||||||
# BEWARE, this violates DRY and is largely equal in functionality to
|
|
||||||
# read_ber_from_string. Eventually that method may subsume the functionality
|
|
||||||
# of this one.
|
|
||||||
#
|
|
||||||
def read_ber syntax=nil
|
|
||||||
# don't bother with this line, since IO#getc by definition returns nil on eof.
|
|
||||||
#return nil if eof?
|
|
||||||
|
|
||||||
id = getc or return nil # don't trash this value, we'll use it later
|
|
||||||
#tag = id & 31
|
|
||||||
#tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
|
|
||||||
#tagclass = TagClasses[ id >> 6 ]
|
|
||||||
#encoding = (id & 0x20 != 0) ? :constructed : :primitive
|
|
||||||
|
|
||||||
n = getc
|
|
||||||
lengthlength,contentlength = if n <= 127
|
|
||||||
[1,n]
|
|
||||||
else
|
|
||||||
# Replaced the inject because it profiles hot.
|
|
||||||
#j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
|
|
||||||
j = 0
|
|
||||||
read( n & 127 ).each_byte {|n1| j = (j << 8) + n1}
|
|
||||||
[1 + (n & 127), j]
|
|
||||||
end
|
end
|
||||||
|
end
|
||||||
newobj = read contentlength
|
|
||||||
|
class BerIdentifiedArray < Array
|
||||||
# This exceptionally clever and clear bit of code is verrrry slow.
|
attr_accessor :ber_identifier
|
||||||
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
def initialize
|
||||||
|
super
|
||||||
|
end
|
||||||
# == is expensive so sort this if/else so the common cases are at the top.
|
end
|
||||||
obj = if objtype == :string
|
|
||||||
#(newobj || "").dup
|
class BerIdentifiedNull
|
||||||
s = BerIdentifiedString.new( newobj || "" )
|
attr_accessor :ber_identifier
|
||||||
s.ber_identifier = id
|
def to_ber
|
||||||
s
|
"\005\000"
|
||||||
elsif objtype == :integer
|
end
|
||||||
j = 0
|
end
|
||||||
newobj.each_byte {|b| j = (j << 8) + b}
|
|
||||||
j
|
class BerIdentifiedOid
|
||||||
elsif objtype == :oid
|
attr_accessor :ber_identifier
|
||||||
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
def initialize oid
|
||||||
# Potentially not good enough. We may need a BerIdentifiedOid
|
if oid.is_a?(String)
|
||||||
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
oid = oid.split(/\./).map {|s| s.to_i }
|
||||||
# and also a to_s method that produces the familiar dotted notation.
|
|
||||||
oid = newobj.unpack("w*")
|
|
||||||
f = oid.shift
|
|
||||||
g = if f < 40
|
|
||||||
[0, f]
|
|
||||||
elsif f < 80
|
|
||||||
[1, f-40]
|
|
||||||
else
|
|
||||||
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
|
||||||
end
|
|
||||||
oid.unshift g.last
|
|
||||||
oid.unshift g.first
|
|
||||||
oid
|
|
||||||
elsif objtype == :array
|
|
||||||
#seq = []
|
|
||||||
seq = BerIdentifiedArray.new
|
|
||||||
seq.ber_identifier = id
|
|
||||||
sio = StringIO.new( newobj || "" )
|
|
||||||
# Interpret the subobject, but note how the loop
|
|
||||||
# is built: nil ends the loop, but false (a valid
|
|
||||||
# BER value) does not!
|
|
||||||
while (e = sio.read_ber(syntax)) != nil
|
|
||||||
seq << e
|
|
||||||
end
|
end
|
||||||
seq
|
@value = oid
|
||||||
elsif objtype == :boolean
|
|
||||||
newobj != "\000"
|
|
||||||
elsif objtype == :null
|
|
||||||
n = BerIdentifiedNull.new
|
|
||||||
n.ber_identifier = id
|
|
||||||
n
|
|
||||||
else
|
|
||||||
#raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
|
|
||||||
raise BerError.new( "unsupported object type: id=#{id}" )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add the identifier bits into the object if it's a String or an Array.
|
|
||||||
# We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
|
|
||||||
# Replaced this mechanism with subclasses because the instance_eval profiled too hot.
|
|
||||||
#obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
|
|
||||||
#obj.ber_identifier = id if obj.respond_to?(:ber_identifier)
|
|
||||||
obj
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
#--
|
|
||||||
# Violates DRY! This replicates the functionality of #read_ber.
|
|
||||||
# Eventually this method may replace that one.
|
|
||||||
# This version of #read_ber behaves properly in the face of incomplete
|
|
||||||
# data packets. If a full BER object is detected, we return an array containing
|
|
||||||
# the detected object and the number of bytes consumed from the string.
|
|
||||||
# If we don't detect a complete packet, return nil.
|
|
||||||
#
|
|
||||||
# Observe that weirdly we recursively call the original #read_ber in here.
|
|
||||||
# That needs to be fixed if we ever obsolete the original method in favor of this one.
|
|
||||||
def read_ber_from_string str, syntax=nil
|
|
||||||
id = str[0] or return nil
|
|
||||||
n = str[1] or return nil
|
|
||||||
n_consumed = 2
|
|
||||||
lengthlength,contentlength = if n <= 127
|
|
||||||
[1,n]
|
|
||||||
else
|
|
||||||
n1 = n & 127
|
|
||||||
return nil unless str.length >= (n_consumed + n1)
|
|
||||||
j = 0
|
|
||||||
n1.times {
|
|
||||||
j = (j << 8) + str[n_consumed]
|
|
||||||
n_consumed += 1
|
|
||||||
}
|
|
||||||
[1 + (n1), j]
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil unless str.length >= (n_consumed + contentlength)
|
|
||||||
newobj = str[n_consumed...(n_consumed + contentlength)]
|
|
||||||
n_consumed += contentlength
|
|
||||||
|
|
||||||
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
|
||||||
|
|
||||||
# == is expensive so sort this if/else so the common cases are at the top.
|
|
||||||
obj = if objtype == :array
|
|
||||||
seq = BerIdentifiedArray.new
|
|
||||||
seq.ber_identifier = id
|
|
||||||
sio = StringIO.new( newobj || "" )
|
|
||||||
# Interpret the subobject, but note how the loop
|
|
||||||
# is built: nil ends the loop, but false (a valid
|
|
||||||
# BER value) does not!
|
|
||||||
# Also, we can use the standard read_ber method because
|
|
||||||
# we know for sure we have enough data. (Although this
|
|
||||||
# might be faster than the standard method.)
|
|
||||||
while (e = sio.read_ber(syntax)) != nil
|
|
||||||
seq << e
|
|
||||||
end
|
|
||||||
seq
|
|
||||||
elsif objtype == :string
|
|
||||||
s = BerIdentifiedString.new( newobj || "" )
|
|
||||||
s.ber_identifier = id
|
|
||||||
s
|
|
||||||
elsif objtype == :integer
|
|
||||||
j = 0
|
|
||||||
newobj.each_byte {|b| j = (j << 8) + b}
|
|
||||||
j
|
|
||||||
elsif objtype == :oid
|
|
||||||
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
|
||||||
# Potentially not good enough. We may need a BerIdentifiedOid
|
|
||||||
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
|
||||||
# and also a to_s method that produces the familiar dotted notation.
|
|
||||||
oid = newobj.unpack("w*")
|
|
||||||
f = oid.shift
|
|
||||||
g = if f < 40
|
|
||||||
[0,f]
|
|
||||||
elsif f < 80
|
|
||||||
[1, f-40]
|
|
||||||
else
|
|
||||||
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
|
||||||
end
|
|
||||||
oid.unshift g.last
|
|
||||||
oid.unshift g.first
|
|
||||||
oid
|
|
||||||
elsif objtype == :boolean
|
|
||||||
newobj != "\000"
|
|
||||||
elsif objtype == :null
|
|
||||||
n = BerIdentifiedNull.new
|
|
||||||
n.ber_identifier = id
|
|
||||||
n
|
|
||||||
else
|
|
||||||
raise BerError.new( "unsupported object type: id=#{id}" )
|
|
||||||
end
|
|
||||||
|
|
||||||
[obj, n_consumed]
|
|
||||||
end
|
|
||||||
|
|
||||||
end # module BERParser
|
|
||||||
end # module BER
|
|
||||||
|
|
||||||
end # module Net
|
|
||||||
|
|
||||||
|
|
||||||
class IO
|
|
||||||
include Net::BER::BERParser
|
|
||||||
end
|
|
||||||
|
|
||||||
require "stringio"
|
|
||||||
class StringIO
|
|
||||||
include Net::BER::BERParser
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
require 'openssl'
|
|
||||||
class OpenSSL::SSL::SSLSocket
|
|
||||||
include Net::BER::BERParser
|
|
||||||
end
|
|
||||||
rescue LoadError
|
|
||||||
# Ignore LoadError.
|
|
||||||
# DON'T ignore NameError, which means the SSLSocket class
|
|
||||||
# is somehow unavailable on this implementation of Ruby's openssl.
|
|
||||||
# This may be WRONG, however, because we don't yet know how Ruby's
|
|
||||||
# openssl behaves on machines with no OpenSSL library. I suppose
|
|
||||||
# it's possible they do not fail to require 'openssl' but do not
|
|
||||||
# create the classes. So this code is provisional.
|
|
||||||
# Also, you might think that OpenSSL::SSL::SSLSocket inherits from
|
|
||||||
# IO so we'd pick it up above. But you'd be wrong.
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class String
|
|
||||||
include Net::BER::BERParser
|
|
||||||
def read_ber syntax=nil
|
|
||||||
StringIO.new(self).read_ber(syntax)
|
|
||||||
end
|
|
||||||
def read_ber! syntax=nil
|
|
||||||
obj,n_consumed = read_ber_from_string(self, syntax)
|
|
||||||
if n_consumed
|
|
||||||
self.slice!(0...n_consumed)
|
|
||||||
obj
|
|
||||||
else
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
#----------------------------------------------
|
|
||||||
|
|
||||||
|
|
||||||
class FalseClass
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
"\001\001\000"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
class TrueClass
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
"\001\001\001"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Fixnum
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
#
|
|
||||||
def to_ber
|
|
||||||
"\002" + to_ber_internal
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_enumerated
|
|
||||||
#
|
|
||||||
def to_ber_enumerated
|
|
||||||
"\012" + to_ber_internal
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_length_encoding
|
|
||||||
#
|
|
||||||
def to_ber_length_encoding
|
|
||||||
if self <= 127
|
|
||||||
[self].pack('C')
|
|
||||||
else
|
|
||||||
i = [self].pack('N').sub(/^[\0]+/,"")
|
|
||||||
[0x80 + i.length].pack('C') + i
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generate a BER-encoding for an application-defined INTEGER.
|
|
||||||
# Example: SNMP's Counter, Gauge, and TimeTick types.
|
|
||||||
#
|
|
||||||
def to_ber_application tag
|
|
||||||
[0x40 + tag].pack("C") + to_ber_internal
|
|
||||||
end
|
|
||||||
|
|
||||||
#--
|
|
||||||
# Called internally to BER-encode the length and content bytes of a Fixnum.
|
|
||||||
# The caller will prepend the tag byte.
|
|
||||||
def to_ber_internal
|
|
||||||
# PLEASE optimize this code path. It's awfully ugly and probably slow.
|
|
||||||
# It also doesn't understand negative numbers yet.
|
|
||||||
raise Net::BER::BerError.new( "range error in fixnum" ) unless self >= 0
|
|
||||||
z = [self].pack("N")
|
|
||||||
zlen = if self < 0x80
|
|
||||||
1
|
|
||||||
elsif self < 0x8000
|
|
||||||
2
|
|
||||||
elsif self < 0x800000
|
|
||||||
3
|
|
||||||
else
|
|
||||||
4
|
|
||||||
end
|
|
||||||
[zlen].pack("C") + z[0-zlen,zlen]
|
|
||||||
end
|
|
||||||
private :to_ber_internal
|
|
||||||
|
|
||||||
end # class Fixnum
|
|
||||||
|
|
||||||
|
|
||||||
class Bignum
|
|
||||||
|
|
||||||
def to_ber
|
|
||||||
#i = [self].pack('w')
|
|
||||||
#i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
|
|
||||||
#[2, i.length].pack("CC") + i
|
|
||||||
|
|
||||||
# Ruby represents Bignums as two's-complement numbers so we may actually be
|
|
||||||
# good as far as representing negatives goes.
|
|
||||||
# I'm sure this implementation can be improved performance-wise if necessary.
|
|
||||||
# Ruby's Bignum#size returns the number of bytes in the internal representation
|
|
||||||
# of the number, but it can and will include leading zero bytes on at least
|
|
||||||
# some implementations. Evidently Ruby stores these as sets of quadbytes.
|
|
||||||
# It's not illegal in BER to encode all of the leading zeroes but let's strip
|
|
||||||
# them out anyway.
|
|
||||||
#
|
|
||||||
sz = self.size
|
|
||||||
out = "\000" * sz
|
|
||||||
(sz*8).times {|bit|
|
|
||||||
if self[bit] == 1
|
|
||||||
out[bit/8] += (1 << (bit % 8))
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
while out.length > 1 and out[-1] == 0
|
|
||||||
out.slice!(-1,1)
|
|
||||||
end
|
|
||||||
|
|
||||||
[2, out.length].pack("CC") + out.reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class String
|
|
||||||
#
|
|
||||||
# to_ber
|
|
||||||
# A universal octet-string is tag number 4,
|
|
||||||
# but others are possible depending on the context, so we
|
|
||||||
# let the caller give us one.
|
|
||||||
# The preferred way to do this in user code is via to_ber_application_sring
|
|
||||||
# and to_ber_contextspecific.
|
|
||||||
#
|
|
||||||
def to_ber code = 4
|
|
||||||
[code].pack('C') + length.to_ber_length_encoding + self
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_application_string
|
|
||||||
#
|
|
||||||
def to_ber_application_string code
|
|
||||||
to_ber( 0x40 + code )
|
|
||||||
end
|
|
||||||
|
|
||||||
#
|
|
||||||
# to_ber_contextspecific
|
|
||||||
#
|
|
||||||
def to_ber_contextspecific code
|
|
||||||
to_ber( 0x80 + code )
|
|
||||||
end
|
|
||||||
|
|
||||||
end # class String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Array
|
|
||||||
#
|
|
||||||
# to_ber_appsequence
|
|
||||||
# An application-specific sequence usually gets assigned
|
|
||||||
# a tag that is meaningful to the particular protocol being used.
|
|
||||||
# This is different from the universal sequence, which usually
|
|
||||||
# gets a tag value of 16.
|
|
||||||
# Now here's an interesting thing: We're adding the X.690
|
|
||||||
# "application constructed" code at the top of the tag byte (0x60),
|
|
||||||
# but some clients, notably ldapsearch, send "context-specific
|
|
||||||
# constructed" (0xA0). The latter would appear to violate RFC-1777,
|
|
||||||
# but what do I know? We may need to change this.
|
|
||||||
#
|
|
||||||
|
|
||||||
def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
|
|
||||||
def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
|
|
||||||
def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
|
|
||||||
def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
|
|
||||||
def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
|
|
||||||
|
|
||||||
def to_ber_oid
|
|
||||||
ary = self.dup
|
|
||||||
first = ary.shift
|
|
||||||
raise Net::BER::BerError.new( "invalid OID" ) unless [0,1,2].include?(first)
|
|
||||||
first = first * 40 + ary.shift
|
|
||||||
ary.unshift first
|
|
||||||
oid = ary.pack("w*")
|
|
||||||
[6, oid.length].pack("CC") + oid
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def to_ber_seq_internal code
|
|
||||||
s = self.to_s
|
|
||||||
[code].pack('C') + s.length.to_ber_length_encoding + s
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
end # class Array
|
|
||||||
|
|
||||||
|
|
||||||
|
|
225
lib/net/ber/ber_parser.rb
Normal file
225
lib/net/ber/ber_parser.rb
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
module Net
|
||||||
|
module BER
|
||||||
|
module BERParser
|
||||||
|
# The order of these follows the class-codes in BER.
|
||||||
|
# Maybe this should have been a hash.
|
||||||
|
TagClasses = [:universal, :application, :context_specific, :private]
|
||||||
|
|
||||||
|
BuiltinSyntax = Net::BER.compile_syntax( {
|
||||||
|
:universal => {
|
||||||
|
:primitive => {
|
||||||
|
1 => :boolean,
|
||||||
|
2 => :integer,
|
||||||
|
4 => :string,
|
||||||
|
5 => :null,
|
||||||
|
6 => :oid,
|
||||||
|
10 => :integer,
|
||||||
|
13 => :string # (relative OID)
|
||||||
|
},
|
||||||
|
:constructed => {
|
||||||
|
16 => :array,
|
||||||
|
17 => :array
|
||||||
|
}
|
||||||
|
},
|
||||||
|
:context_specific => {
|
||||||
|
:primitive => {
|
||||||
|
10 => :integer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
#
|
||||||
|
# read_ber
|
||||||
|
# TODO: clean this up so it works properly with partial
|
||||||
|
# packets coming from streams that don't block when
|
||||||
|
# we ask for more data (like StringIOs). At it is,
|
||||||
|
# this can throw TypeErrors and other nasties.
|
||||||
|
#--
|
||||||
|
# BEWARE, this violates DRY and is largely equal in functionality to
|
||||||
|
# read_ber_from_string. Eventually that method may subsume the functionality
|
||||||
|
# of this one.
|
||||||
|
#
|
||||||
|
def read_ber syntax=nil
|
||||||
|
# don't bother with this line, since IO#getc by definition returns nil on eof.
|
||||||
|
#return nil if eof?
|
||||||
|
|
||||||
|
# here we'll create two different procs, one for 1.8 and one for 1.9
|
||||||
|
# the reason being getc doesn't return a byte value in 1.9, so we need to
|
||||||
|
# get the byte code out of the 1.9 encoded string
|
||||||
|
|
||||||
|
if RUBY_VERSION =~ /^1\.9/
|
||||||
|
fetch_byte = Proc.new { getc.bytes.first }
|
||||||
|
elsif RUBY_VERSION =~ /^1\.8/
|
||||||
|
fetch_byte = Proc.new { getc }
|
||||||
|
end
|
||||||
|
|
||||||
|
id = fetch_byte.call or return nil # don't trash this value, we'll use it later
|
||||||
|
#tag = id & 31
|
||||||
|
#tag < 31 or raise BerError.new( "unsupported tag encoding: #{id}" )
|
||||||
|
#tagclass = TagClasses[ id >> 6 ]
|
||||||
|
#encoding = (id & 0x20 != 0) ? :constructed : :primitive
|
||||||
|
|
||||||
|
n = fetch_byte.call
|
||||||
|
lengthlength,contentlength = if n <= 127
|
||||||
|
[1,n]
|
||||||
|
else
|
||||||
|
# Replaced the inject because it profiles hot.
|
||||||
|
#j = (0...(n & 127)).inject(0) {|mem,x| mem = (mem << 8) + getc}
|
||||||
|
j = 0
|
||||||
|
read( n & 127 ).each_byte {|n1| j = (j << 8) + n1}
|
||||||
|
[1 + (n & 127), j]
|
||||||
|
end
|
||||||
|
|
||||||
|
newobj = read contentlength
|
||||||
|
|
||||||
|
# This exceptionally clever and clear bit of code is verrrry slow.
|
||||||
|
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
||||||
|
|
||||||
|
|
||||||
|
# == is expensive so sort this if/else so the common cases are at the top.
|
||||||
|
obj = if objtype == :string
|
||||||
|
#(newobj || "").dup
|
||||||
|
s = BerIdentifiedString.new( newobj || "" )
|
||||||
|
s.ber_identifier = id
|
||||||
|
s
|
||||||
|
elsif objtype == :integer
|
||||||
|
j = 0
|
||||||
|
newobj.each_byte {|b| j = (j << 8) + b}
|
||||||
|
j
|
||||||
|
elsif objtype == :oid
|
||||||
|
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
||||||
|
# Potentially not good enough. We may need a BerIdentifiedOid
|
||||||
|
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
||||||
|
# and also a to_s method that produces the familiar dotted notation.
|
||||||
|
oid = newobj.unpack("w*")
|
||||||
|
f = oid.shift
|
||||||
|
g = if f < 40
|
||||||
|
[0, f]
|
||||||
|
elsif f < 80
|
||||||
|
[1, f-40]
|
||||||
|
else
|
||||||
|
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
||||||
|
end
|
||||||
|
oid.unshift g.last
|
||||||
|
oid.unshift g.first
|
||||||
|
oid
|
||||||
|
elsif objtype == :array
|
||||||
|
#seq = []
|
||||||
|
seq = BerIdentifiedArray.new
|
||||||
|
seq.ber_identifier = id
|
||||||
|
sio = StringIO.new( newobj || "" )
|
||||||
|
# Interpret the subobject, but note how the loop
|
||||||
|
# is built: nil ends the loop, but false (a valid
|
||||||
|
# BER value) does not!
|
||||||
|
while (e = sio.read_ber(syntax)) != nil
|
||||||
|
seq << e
|
||||||
|
end
|
||||||
|
seq
|
||||||
|
elsif objtype == :boolean
|
||||||
|
newobj != "\000"
|
||||||
|
elsif objtype == :null
|
||||||
|
n = BerIdentifiedNull.new
|
||||||
|
n.ber_identifier = id
|
||||||
|
n
|
||||||
|
else
|
||||||
|
#raise BerError.new( "unsupported object type: class=#{tagclass}, encoding=#{encoding}, tag=#{tag}" )
|
||||||
|
raise BerError.new( "unsupported object type: id=#{id}" )
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add the identifier bits into the object if it's a String or an Array.
|
||||||
|
# We can't add extra stuff to Fixnums and booleans, not that it makes much sense anyway.
|
||||||
|
# Replaced this mechanism with subclasses because the instance_eval profiled too hot.
|
||||||
|
#obj and ([String,Array].include? obj.class) and obj.instance_eval "def ber_identifier; #{id}; end"
|
||||||
|
#obj.ber_identifier = id if obj.respond_to?(:ber_identifier)
|
||||||
|
obj
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# Violates DRY! This replicates the functionality of #read_ber.
|
||||||
|
# Eventually this method may replace that one.
|
||||||
|
# This version of #read_ber behaves properly in the face of incomplete
|
||||||
|
# data packets. If a full BER object is detected, we return an array containing
|
||||||
|
# the detected object and the number of bytes consumed from the string.
|
||||||
|
# If we don't detect a complete packet, return nil.
|
||||||
|
#
|
||||||
|
# Observe that weirdly we recursively call the original #read_ber in here.
|
||||||
|
# That needs to be fixed if we ever obsolete the original method in favor of this one.
|
||||||
|
def read_ber_from_string str, syntax=nil
|
||||||
|
id = str[0] or return nil
|
||||||
|
n = str[1] or return nil
|
||||||
|
n_consumed = 2
|
||||||
|
lengthlength,contentlength = if n <= 127
|
||||||
|
[1,n]
|
||||||
|
else
|
||||||
|
n1 = n & 127
|
||||||
|
return nil unless str.length >= (n_consumed + n1)
|
||||||
|
j = 0
|
||||||
|
n1.times {
|
||||||
|
j = (j << 8) + str[n_consumed]
|
||||||
|
n_consumed += 1
|
||||||
|
}
|
||||||
|
[1 + (n1), j]
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil unless str.length >= (n_consumed + contentlength)
|
||||||
|
newobj = str[n_consumed...(n_consumed + contentlength)]
|
||||||
|
n_consumed += contentlength
|
||||||
|
|
||||||
|
objtype = (syntax && syntax[id]) || BuiltinSyntax[id]
|
||||||
|
|
||||||
|
# == is expensive so sort this if/else so the common cases are at the top.
|
||||||
|
obj = if objtype == :array
|
||||||
|
seq = BerIdentifiedArray.new
|
||||||
|
seq.ber_identifier = id
|
||||||
|
sio = StringIO.new( newobj || "" )
|
||||||
|
# Interpret the subobject, but note how the loop
|
||||||
|
# is built: nil ends the loop, but false (a valid
|
||||||
|
# BER value) does not!
|
||||||
|
# Also, we can use the standard read_ber method because
|
||||||
|
# we know for sure we have enough data. (Although this
|
||||||
|
# might be faster than the standard method.)
|
||||||
|
while (e = sio.read_ber(syntax)) != nil
|
||||||
|
seq << e
|
||||||
|
end
|
||||||
|
seq
|
||||||
|
elsif objtype == :string
|
||||||
|
s = BerIdentifiedString.new( newobj || "" )
|
||||||
|
s.ber_identifier = id
|
||||||
|
s
|
||||||
|
elsif objtype == :integer
|
||||||
|
j = 0
|
||||||
|
newobj.each_byte {|b| j = (j << 8) + b}
|
||||||
|
j
|
||||||
|
elsif objtype == :oid
|
||||||
|
# cf X.690 pgh 8.19 for an explanation of this algorithm.
|
||||||
|
# Potentially not good enough. We may need a BerIdentifiedOid
|
||||||
|
# as a subclass of BerIdentifiedArray, to get the ber identifier
|
||||||
|
# and also a to_s method that produces the familiar dotted notation.
|
||||||
|
oid = newobj.unpack("w*")
|
||||||
|
f = oid.shift
|
||||||
|
g = if f < 40
|
||||||
|
[0,f]
|
||||||
|
elsif f < 80
|
||||||
|
[1, f-40]
|
||||||
|
else
|
||||||
|
[2, f-80] # f-80 can easily be > 80. What a weird optimization.
|
||||||
|
end
|
||||||
|
oid.unshift g.last
|
||||||
|
oid.unshift g.first
|
||||||
|
oid
|
||||||
|
elsif objtype == :boolean
|
||||||
|
newobj != "\000"
|
||||||
|
elsif objtype == :null
|
||||||
|
n = BerIdentifiedNull.new
|
||||||
|
n.ber_identifier = id
|
||||||
|
n
|
||||||
|
else
|
||||||
|
raise BerError.new( "unsupported object type: id=#{id}" )
|
||||||
|
end
|
||||||
|
|
||||||
|
[obj, n_consumed]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,33 +9,20 @@
|
||||||
#
|
#
|
||||||
# This program is free software.
|
# This program is free software.
|
||||||
# You may re-distribute and/or modify this program under the same terms
|
# You may re-distribute and/or modify this program under the same terms
|
||||||
# as Ruby itself: Ruby Distribution License or GNU General Public License.
|
# as Ruby itself: Ruby Distribution License or GNU General Public License.x
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# See Net::LDAP for documentation and usage samples.
|
# See Net::LDAP for documentation and usage samples.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
require 'socket'
|
|
||||||
require 'ostruct'
|
|
||||||
|
|
||||||
begin
|
|
||||||
require 'openssl'
|
|
||||||
$net_ldap_openssl_available = true
|
|
||||||
rescue LoadError
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'net/ber'
|
|
||||||
require 'net/ldap/pdu'
|
require 'net/ldap/pdu'
|
||||||
require 'net/ldap/filter'
|
require 'net/ldap/filter'
|
||||||
require 'net/ldap/dataset'
|
require 'net/ldap/dataset'
|
||||||
require 'net/ldap/psw'
|
require 'net/ldap/psw'
|
||||||
require 'net/ldap/entry'
|
require 'net/ldap/entry'
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
|
@ -273,30 +260,30 @@ module Net
|
||||||
|
|
||||||
AsnSyntax = BER.compile_syntax({
|
AsnSyntax = BER.compile_syntax({
|
||||||
:application => {
|
:application => {
|
||||||
:primitive => {
|
:primitive => {
|
||||||
2 => :null # UnbindRequest body
|
2 => :null # UnbindRequest body
|
||||||
},
|
},
|
||||||
:constructed => {
|
:constructed => {
|
||||||
0 => :array, # BindRequest
|
0 => :array, # BindRequest
|
||||||
1 => :array, # BindResponse
|
1 => :array, # BindResponse
|
||||||
2 => :array, # UnbindRequest
|
2 => :array, # UnbindRequest
|
||||||
3 => :array, # SearchRequest
|
3 => :array, # SearchRequest
|
||||||
4 => :array, # SearchData
|
4 => :array, # SearchData
|
||||||
5 => :array, # SearchResult
|
5 => :array, # SearchResult
|
||||||
6 => :array, # ModifyRequest
|
6 => :array, # ModifyRequest
|
||||||
7 => :array, # ModifyResponse
|
7 => :array, # ModifyResponse
|
||||||
8 => :array, # AddRequest
|
8 => :array, # AddRequest
|
||||||
9 => :array, # AddResponse
|
9 => :array, # AddResponse
|
||||||
10 => :array, # DelRequest
|
10 => :array, # DelRequest
|
||||||
11 => :array, # DelResponse
|
11 => :array, # DelResponse
|
||||||
12 => :array, # ModifyRdnRequest
|
12 => :array, # ModifyRdnRequest
|
||||||
13 => :array, # ModifyRdnResponse
|
13 => :array, # ModifyRdnResponse
|
||||||
14 => :array, # CompareRequest
|
14 => :array, # CompareRequest
|
||||||
15 => :array, # CompareResponse
|
15 => :array, # CompareResponse
|
||||||
16 => :array, # AbandonRequest
|
16 => :array, # AbandonRequest
|
||||||
19 => :array, # SearchResultReferral
|
19 => :array, # SearchResultReferral
|
||||||
24 => :array, # Unsolicited Notification
|
24 => :array, # Unsolicited Notification
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
:context_specific => {
|
:context_specific => {
|
||||||
:primitive => {
|
:primitive => {
|
||||||
|
@ -745,7 +732,7 @@ module Net
|
||||||
# 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
|
||||||
else
|
else
|
||||||
|
@ -1212,14 +1199,12 @@ module Net
|
||||||
def setup_encryption args
|
def setup_encryption args
|
||||||
case args[:method]
|
case args[:method]
|
||||||
when :simple_tls
|
when :simple_tls
|
||||||
raise LdapError.new("openssl unavailable") unless $net_ldap_openssl_available
|
|
||||||
ctx = OpenSSL::SSL::SSLContext.new
|
ctx = OpenSSL::SSL::SSLContext.new
|
||||||
@conn = OpenSSL::SSL::SSLSocket.new(@conn, ctx)
|
@conn = OpenSSL::SSL::SSLSocket.new(@conn, ctx)
|
||||||
@conn.connect
|
@conn.connect
|
||||||
@conn.sync_close = true
|
@conn.sync_close = true
|
||||||
# additional branches requiring server validation and peer certs, etc. go here.
|
# additional branches requiring server validation and peer certs, etc. go here.
|
||||||
when :start_tls
|
when :start_tls
|
||||||
raise LdapError.new("openssl unavailable") unless $net_ldap_openssl_available
|
|
||||||
msgid = next_msgid.to_ber
|
msgid = next_msgid.to_ber
|
||||||
request = [StartTlsOid.to_ber].to_ber_appsequence( Net::LdapPdu::ExtendedRequest )
|
request = [StartTlsOid.to_ber].to_ber_appsequence( Net::LdapPdu::ExtendedRequest )
|
||||||
request_pkt = [msgid, request].to_ber_sequence
|
request_pkt = [msgid, request].to_ber_sequence
|
||||||
|
|
43
lib/net/ldap/core_ext/all.rb
Normal file
43
lib/net/ldap/core_ext/all.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'net/ldap/core_ext/array'
|
||||||
|
require 'net/ldap/core_ext/string'
|
||||||
|
require 'net/ldap/core_ext/bignum'
|
||||||
|
require 'net/ldap/core_ext/fixnum'
|
||||||
|
require 'net/ldap/core_ext/false_class'
|
||||||
|
require 'net/ldap/core_ext/true_class'
|
||||||
|
|
||||||
|
class Array
|
||||||
|
include Net::LDAP::Extensions::Array
|
||||||
|
end
|
||||||
|
|
||||||
|
class String
|
||||||
|
include Net::LDAP::Extensions::String
|
||||||
|
include Net::BER::BERParser
|
||||||
|
end
|
||||||
|
|
||||||
|
class Bignum
|
||||||
|
include Net::LDAP::Extensions::Bignum
|
||||||
|
end
|
||||||
|
|
||||||
|
class Fixnum
|
||||||
|
include Net::LDAP::Extensions::Fixnum
|
||||||
|
end
|
||||||
|
|
||||||
|
class FalseClass
|
||||||
|
include Net::LDAP::Extensions::FalseClass
|
||||||
|
end
|
||||||
|
|
||||||
|
class TrueClass
|
||||||
|
include Net::LDAP::Extensions::TrueClass
|
||||||
|
end
|
||||||
|
|
||||||
|
class IO
|
||||||
|
include Net::BER::BERParser
|
||||||
|
end
|
||||||
|
|
||||||
|
class StringIO
|
||||||
|
include Net::BER::BERParser
|
||||||
|
end
|
||||||
|
|
||||||
|
class OpenSSL::SSL::SSLSocket
|
||||||
|
include Net::BER::BERParser
|
||||||
|
end
|
42
lib/net/ldap/core_ext/array.rb
Normal file
42
lib/net/ldap/core_ext/array.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module Array
|
||||||
|
#
|
||||||
|
# to_ber_appsequence
|
||||||
|
# An application-specific sequence usually gets assigned
|
||||||
|
# a tag that is meaningful to the particular protocol being used.
|
||||||
|
# This is different from the universal sequence, which usually
|
||||||
|
# gets a tag value of 16.
|
||||||
|
# Now here's an interesting thing: We're adding the X.690
|
||||||
|
# "application constructed" code at the top of the tag byte (0x60),
|
||||||
|
# but some clients, notably ldapsearch, send "context-specific
|
||||||
|
# constructed" (0xA0). The latter would appear to violate RFC-1777,
|
||||||
|
# but what do I know? We may need to change this.
|
||||||
|
#
|
||||||
|
|
||||||
|
def to_ber id = 0; to_ber_seq_internal( 0x30 + id ); end
|
||||||
|
def to_ber_set id = 0; to_ber_seq_internal( 0x31 + id ); end
|
||||||
|
def to_ber_sequence id = 0; to_ber_seq_internal( 0x30 + id ); end
|
||||||
|
def to_ber_appsequence id = 0; to_ber_seq_internal( 0x60 + id ); end
|
||||||
|
def to_ber_contextspecific id = 0; to_ber_seq_internal( 0xA0 + id ); end
|
||||||
|
|
||||||
|
def to_ber_oid
|
||||||
|
ary = self.dup
|
||||||
|
first = ary.shift
|
||||||
|
raise Net::BER::BerError.new( "invalid OID" ) unless [0,1,2].include?(first)
|
||||||
|
first = first * 40 + ary.shift
|
||||||
|
ary.unshift first
|
||||||
|
oid = ary.pack("w*")
|
||||||
|
[6, oid.length].pack("CC") + oid
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def to_ber_seq_internal code
|
||||||
|
s = self.to_s
|
||||||
|
[code].pack('C') + s.length.to_ber_length_encoding + s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end # class Array
|
38
lib/net/ldap/core_ext/bignum.rb
Normal file
38
lib/net/ldap/core_ext/bignum.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module Bignum
|
||||||
|
|
||||||
|
def to_ber
|
||||||
|
#i = [self].pack('w')
|
||||||
|
#i.length > 126 and raise Net::BER::BerError.new( "range error in bignum" )
|
||||||
|
#[2, i.length].pack("CC") + i
|
||||||
|
|
||||||
|
# Ruby represents Bignums as two's-complement numbers so we may actually be
|
||||||
|
# good as far as representing negatives goes.
|
||||||
|
# I'm sure this implementation can be improved performance-wise if necessary.
|
||||||
|
# Ruby's Bignum#size returns the number of bytes in the internal representation
|
||||||
|
# of the number, but it can and will include leading zero bytes on at least
|
||||||
|
# some implementations. Evidently Ruby stores these as sets of quadbytes.
|
||||||
|
# It's not illegal in BER to encode all of the leading zeroes but let's strip
|
||||||
|
# them out anyway.
|
||||||
|
#
|
||||||
|
sz = self.size
|
||||||
|
out = "\000" * sz
|
||||||
|
(sz*8).times {|bit|
|
||||||
|
if self[bit] == 1
|
||||||
|
out[bit/8] += (1 << (bit % 8))
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
while out.length > 1 and out[-1] == 0
|
||||||
|
out.slice!(-1,1)
|
||||||
|
end
|
||||||
|
|
||||||
|
[2, out.length].pack("CC") + out.reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
lib/net/ldap/core_ext/false_class.rb
Normal file
11
lib/net/ldap/core_ext/false_class.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module FalseClass
|
||||||
|
def to_ber
|
||||||
|
"\001\001\000"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
52
lib/net/ldap/core_ext/fixnum.rb
Normal file
52
lib/net/ldap/core_ext/fixnum.rb
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module Fixnum
|
||||||
|
def to_ber
|
||||||
|
"\002" + to_ber_internal
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ber_enumerated
|
||||||
|
"\012" + to_ber_internal
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_ber_length_encoding
|
||||||
|
if self <= 127
|
||||||
|
[self].pack('C')
|
||||||
|
else
|
||||||
|
i = [self].pack('N').sub(/^[\0]+/,"")
|
||||||
|
[0x80 + i.length].pack('C') + i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a BER-encoding for an application-defined INTEGER.
|
||||||
|
# Example: SNMP's Counter, Gauge, and TimeTick types.
|
||||||
|
#
|
||||||
|
def to_ber_application tag
|
||||||
|
[0x40 + tag].pack("C") + to_ber_internal
|
||||||
|
end
|
||||||
|
|
||||||
|
#--
|
||||||
|
# Called internally to BER-encode the length and content bytes of a Fixnum.
|
||||||
|
# The caller will prepend the tag byte.
|
||||||
|
def to_ber_internal
|
||||||
|
# PLEASE optimize this code path. It's awfully ugly and probably slow.
|
||||||
|
# It also doesn't understand negative numbers yet.
|
||||||
|
raise Net::BER::BerError.new( "range error in fixnum" ) unless self >= 0
|
||||||
|
z = [self].pack("N")
|
||||||
|
zlen = if self < 0x80
|
||||||
|
1
|
||||||
|
elsif self < 0x8000
|
||||||
|
2
|
||||||
|
elsif self < 0x800000
|
||||||
|
3
|
||||||
|
else
|
||||||
|
4
|
||||||
|
end
|
||||||
|
[zlen].pack("C") + z[0-zlen,zlen]
|
||||||
|
end
|
||||||
|
private :to_ber_internal
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
48
lib/net/ldap/core_ext/string.rb
Normal file
48
lib/net/ldap/core_ext/string.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module String
|
||||||
|
#
|
||||||
|
# to_ber
|
||||||
|
# A universal octet-string is tag number 4,
|
||||||
|
# but others are possible depending on the context, so we
|
||||||
|
# let the caller give us one.
|
||||||
|
# The preferred way to do this in user code is via to_ber_application_sring
|
||||||
|
# and to_ber_contextspecific.
|
||||||
|
#
|
||||||
|
def to_ber code = 4
|
||||||
|
[code].pack('C') + length.to_ber_length_encoding + self
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# to_ber_application_string
|
||||||
|
#
|
||||||
|
def to_ber_application_string code
|
||||||
|
to_ber( 0x40 + code )
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# to_ber_contextspecific
|
||||||
|
#
|
||||||
|
def to_ber_contextspecific code
|
||||||
|
to_ber( 0x80 + code )
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ber syntax=nil
|
||||||
|
StringIO.new(self).read_ber(syntax)
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_ber! syntax=nil
|
||||||
|
obj,n_consumed = read_ber_from_string(self, syntax)
|
||||||
|
if n_consumed
|
||||||
|
self.slice!(0...n_consumed)
|
||||||
|
obj
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
lib/net/ldap/core_ext/true_class.rb
Normal file
11
lib/net/ldap/core_ext/true_class.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module Net
|
||||||
|
class LDAP
|
||||||
|
module Extensions
|
||||||
|
module TrueClass
|
||||||
|
def to_ber
|
||||||
|
"\001\001\001"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -26,10 +26,6 @@
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
require 'base64'
|
|
||||||
|
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
class LDAP
|
class LDAP
|
||||||
|
|
||||||
|
|
|
@ -408,7 +408,6 @@ class FilterParser #:nodoc:
|
||||||
attr_reader :filter
|
attr_reader :filter
|
||||||
|
|
||||||
def initialize str
|
def initialize str
|
||||||
require 'strscan'
|
|
||||||
@filter = parse( StringScanner.new( str )) or raise Net::LDAP::LdapError.new( "invalid filter syntax" )
|
@filter = parse( StringScanner.new( str )) or raise Net::LDAP::LdapError.new( "invalid filter syntax" )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,8 @@ class Password
|
||||||
def generate( type, str )
|
def generate( type, str )
|
||||||
case type
|
case type
|
||||||
when :md5
|
when :md5
|
||||||
require 'md5'
|
|
||||||
"{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
|
"{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
|
||||||
when :sha
|
when :sha
|
||||||
require 'sha1'
|
|
||||||
"{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
|
"{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
|
||||||
# when ssha
|
# when ssha
|
||||||
else
|
else
|
||||||
|
|
|
@ -27,13 +27,8 @@
|
||||||
# THIS FILE IS A STUB.
|
# THIS FILE IS A STUB.
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
class LDIF
|
class LDIF
|
||||||
|
end
|
||||||
|
end
|
||||||
end # class LDIF
|
|
||||||
|
|
||||||
|
|
||||||
end # module Net
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,6 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
require 'net/ber'
|
|
||||||
|
|
||||||
|
|
||||||
module Net
|
module Net
|
||||||
|
|
||||||
class SNMP
|
class SNMP
|
||||||
|
|
45
pre-setup.rb
45
pre-setup.rb
|
@ -1,45 +0,0 @@
|
||||||
require 'rdoc/rdoc'
|
|
||||||
##
|
|
||||||
# Build the rdoc documentation. Also, try to build the RI documentation.
|
|
||||||
#
|
|
||||||
def build_rdoc(options)
|
|
||||||
RDoc::RDoc.new.document(options)
|
|
||||||
rescue RDoc::RDocError => e
|
|
||||||
$stderr.puts e.message
|
|
||||||
rescue Exception => e
|
|
||||||
$stderr.puts "Couldn't build RDoc documentation\n#{e.message}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_ri(files)
|
|
||||||
RDoc::RDoc.new.document(["--ri-site", "--merge"] + files)
|
|
||||||
rescue RDoc::RDocError => e
|
|
||||||
$stderr.puts e.message
|
|
||||||
rescue Exception => e
|
|
||||||
$stderr.puts "Couldn't build Ri documentation\n#{e.message}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def run_tests(test_list)
|
|
||||||
return if test_list.empty?
|
|
||||||
|
|
||||||
require 'test/unit/ui/console/testrunner'
|
|
||||||
$:.unshift "lib"
|
|
||||||
test_list.each do |test|
|
|
||||||
next if File.directory?(test)
|
|
||||||
require test
|
|
||||||
end
|
|
||||||
|
|
||||||
tests = []
|
|
||||||
ObjectSpace.each_object { |o| tests << o if o.kind_of?(Class) }
|
|
||||||
tests.delete_if { |o| !o.ancestors.include?(Test::Unit::TestCase) }
|
|
||||||
tests.delete_if { |o| o == Test::Unit::TestCase }
|
|
||||||
|
|
||||||
tests.each { |test| Test::Unit::UI::Console::TestRunner.run(test) }
|
|
||||||
$:.shift
|
|
||||||
end
|
|
||||||
|
|
||||||
rdoc = %w(--main README.txt --line-numbers)
|
|
||||||
ri = %w(--ri-site --merge)
|
|
||||||
dox = %w(README.txt History.txt lib)
|
|
||||||
build_rdoc rdoc + dox
|
|
||||||
build_ri ri + dox
|
|
||||||
#run_tests Dir["test/**/*"]
|
|
|
@ -1,6 +0,0 @@
|
||||||
Most of the tests here have been migrated to ../test
|
|
||||||
where all new tests will be created. These have been
|
|
||||||
left here for now just for reference and will me
|
|
||||||
migrated as well.
|
|
||||||
|
|
||||||
These will not be run automatically by rake.
|
|
|
@ -1,190 +0,0 @@
|
||||||
# $Id$
|
|
||||||
#
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
$:.unshift "lib"
|
|
||||||
|
|
||||||
require 'test/unit'
|
|
||||||
|
|
||||||
require 'net/ldap'
|
|
||||||
require 'stringio'
|
|
||||||
|
|
||||||
|
|
||||||
class TestLdapClient < Test::Unit::TestCase
|
|
||||||
|
|
||||||
# TODO: these tests crash and burn if the associated
|
|
||||||
# LDAP testserver isn't up and running.
|
|
||||||
# We rely on being able to read a file with test data
|
|
||||||
# in LDIF format.
|
|
||||||
# TODO, WARNING: for the moment, this data is in a file
|
|
||||||
# whose name and location are HARDCODED into the
|
|
||||||
# instance method load_test_data.
|
|
||||||
|
|
||||||
def setup
|
|
||||||
@host = "127.0.0.1"
|
|
||||||
@port = 3890
|
|
||||||
@auth = {
|
|
||||||
:method => :simple,
|
|
||||||
:username => "cn=bigshot,dc=bayshorenetworks,dc=com",
|
|
||||||
:password => "opensesame"
|
|
||||||
}
|
|
||||||
|
|
||||||
@ldif = load_test_data
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Get some test data which will be used to validate
|
|
||||||
# the responses from the test LDAP server we will
|
|
||||||
# connect to.
|
|
||||||
# TODO, Bogus: we are HARDCODING the location of the file for now.
|
|
||||||
#
|
|
||||||
def load_test_data
|
|
||||||
ary = File.readlines( "tests/testdata.ldif" )
|
|
||||||
hash = {}
|
|
||||||
while line = ary.shift and line.chomp!
|
|
||||||
if line =~ /^dn:[\s]*/i
|
|
||||||
dn = $'
|
|
||||||
hash[dn] = {}
|
|
||||||
while attr = ary.shift and attr.chomp! and attr =~ /^([\w]+)[\s]*:[\s]*/
|
|
||||||
hash[dn][$1.downcase.intern] ||= []
|
|
||||||
hash[dn][$1.downcase.intern] << $'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
hash
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Binding tests.
|
|
||||||
# Need tests for all kinds of network failures and incorrect auth.
|
|
||||||
# TODO: Implement a class-level timeout for operations like bind.
|
|
||||||
# Search has a timeout defined at the protocol level, other ops do not.
|
|
||||||
# TODO, use constants for the LDAP result codes, rather than hardcoding them.
|
|
||||||
def test_bind
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
|
|
||||||
assert_equal( true, ldap.bind )
|
|
||||||
assert_equal( 0, ldap.get_operation_result.code )
|
|
||||||
assert_equal( "Success", ldap.get_operation_result.message )
|
|
||||||
|
|
||||||
bad_username = @auth.merge( {:username => "cn=badguy,dc=imposters,dc=com"} )
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => bad_username
|
|
||||||
assert_equal( false, ldap.bind )
|
|
||||||
assert_equal( 48, ldap.get_operation_result.code )
|
|
||||||
assert_equal( "Inappropriate Authentication", ldap.get_operation_result.message )
|
|
||||||
|
|
||||||
bad_password = @auth.merge( {:password => "cornhusk"} )
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => bad_password
|
|
||||||
assert_equal( false, ldap.bind )
|
|
||||||
assert_equal( 49, ldap.get_operation_result.code )
|
|
||||||
assert_equal( "Invalid Credentials", ldap.get_operation_result.message )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_search
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
|
|
||||||
|
|
||||||
search = {:base => "dc=smalldomain,dc=com"}
|
|
||||||
assert_equal( false, ldap.search( search ))
|
|
||||||
assert_equal( 32, ldap.get_operation_result.code )
|
|
||||||
|
|
||||||
search = {:base => "dc=bayshorenetworks,dc=com"}
|
|
||||||
assert_equal( true, ldap.search( search ))
|
|
||||||
assert_equal( 0, ldap.get_operation_result.code )
|
|
||||||
|
|
||||||
ldap.search( search ) {|res|
|
|
||||||
assert_equal( res, @ldif )
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# This is a helper routine for test_search_attributes.
|
|
||||||
def internal_test_search_attributes attrs_to_search
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
|
|
||||||
assert( ldap.bind )
|
|
||||||
|
|
||||||
search = {
|
|
||||||
:base => "dc=bayshorenetworks,dc=com",
|
|
||||||
:attributes => attrs_to_search
|
|
||||||
}
|
|
||||||
|
|
||||||
ldif = @ldif
|
|
||||||
ldif.each {|dn,entry|
|
|
||||||
entry.delete_if {|attr,value|
|
|
||||||
! attrs_to_search.include?(attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_equal( true, ldap.search( search ))
|
|
||||||
ldap.search( search ) {|res|
|
|
||||||
res_keys = res.keys.sort
|
|
||||||
ldif_keys = ldif.keys.sort
|
|
||||||
assert( res_keys, ldif_keys )
|
|
||||||
res.keys.each {|rk|
|
|
||||||
assert( res[rk], ldif[rk] )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def test_search_attributes
|
|
||||||
internal_test_search_attributes [:mail]
|
|
||||||
internal_test_search_attributes [:cn]
|
|
||||||
internal_test_search_attributes [:ou]
|
|
||||||
internal_test_search_attributes [:hasaccessprivilege]
|
|
||||||
internal_test_search_attributes ["mail"]
|
|
||||||
internal_test_search_attributes ["cn"]
|
|
||||||
internal_test_search_attributes ["ou"]
|
|
||||||
internal_test_search_attributes ["hasaccessrole"]
|
|
||||||
|
|
||||||
internal_test_search_attributes [:mail, :cn, :ou, :hasaccessrole]
|
|
||||||
internal_test_search_attributes [:mail, "cn", :ou, "hasaccessrole"]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def test_search_filters
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
|
|
||||||
search = {
|
|
||||||
:base => "dc=bayshorenetworks,dc=com",
|
|
||||||
:filter => Net::LDAP::Filter.eq( "sn", "Fosse" )
|
|
||||||
}
|
|
||||||
|
|
||||||
ldap.search( search ) {|res|
|
|
||||||
p res
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_open
|
|
||||||
ldap = Net::LDAP.new :host => @host, :port => @port, :auth => @auth
|
|
||||||
ldap.open {|ldap|
|
|
||||||
10.times {
|
|
||||||
rc = ldap.search( :base => "dc=bayshorenetworks,dc=com" )
|
|
||||||
assert_equal( true, rc )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
def test_ldap_open
|
|
||||||
Net::LDAP.open( :host => @host, :port => @port, :auth => @auth ) {|ldap|
|
|
||||||
10.times {
|
|
||||||
rc = ldap.search( :base => "dc=bayshorenetworks,dc=com" )
|
|
||||||
assert_equal( true, rc )
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue