Import fixes from kschiess
This commit is contained in:
parent
d37c3b3ae6
commit
4c24cf239a
6 changed files with 249 additions and 248 deletions
16
Rakefile
16
Rakefile
|
@ -7,12 +7,18 @@ require 'hoe'
|
|||
$LOAD_PATH.unshift( "#{File.dirname(__FILE__)}/lib" )
|
||||
|
||||
# Pull in local 'net/ldap' as opposed to an installed version.
|
||||
require 'net/ldap'
|
||||
require 'net'
|
||||
|
||||
Hoe.new('net-ldap', Net::LDAP::VERSION) do |p|
|
||||
p.rubyforge_name = 'net-ldap'
|
||||
p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com')
|
||||
p.developer('Emiel van de Laar', 'gemiel@gmail.com')
|
||||
Hoe.spec "net-ldap" do
|
||||
developer 'Francis Cianfrocca', 'garbagecat10@gmail.com'
|
||||
developer 'Emiel van de Laar', 'gemiel@gmail.com'
|
||||
developer "Rory O'Connell", 'rory.ocon@gmail.com'
|
||||
end
|
||||
|
||||
# Hoe.new('net-ldap', Net::LDAP::VERSION) do |p|
|
||||
# p.rubyforge_name = 'net-ldap'
|
||||
# p.developer('Francis Cianfrocca', 'garbagecat10@gmail.com')
|
||||
# p.developer('Emiel van de Laar', 'gemiel@gmail.com')
|
||||
# end
|
||||
|
||||
# vim: syntax=Ruby
|
||||
|
|
|
@ -40,100 +40,97 @@ module Net
|
|||
# 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?
|
||||
# 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
|
||||
# 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 }
|
||||
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.
|
||||
fetch_byte = Proc.new { getc }
|
||||
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
|
||||
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
|
||||
|
||||
end
|
||||
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.
|
||||
|
@ -145,24 +142,24 @@ module Net
|
|||
#
|
||||
# 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)
|
||||
def read_ber_from_string str, syntax=nil
|
||||
id = str[0].ord or return nil
|
||||
n = str[1].ord 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
|
||||
|
||||
|
@ -170,52 +167,52 @@ module Net
|
|||
|
||||
# == 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
|
||||
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
|
||||
s = BerIdentifiedString.new( newobj || "" )
|
||||
s.ber_identifier = id
|
||||
s
|
||||
elsif objtype == :integer
|
||||
j = 0
|
||||
newobj.each_byte {|b| j = (j << 8) + b}
|
||||
j
|
||||
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
|
||||
# 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"
|
||||
newobj != "\000"
|
||||
elsif objtype == :null
|
||||
n = BerIdentifiedNull.new
|
||||
n.ber_identifier = id
|
||||
n
|
||||
n = BerIdentifiedNull.new
|
||||
n.ber_identifier = id
|
||||
n
|
||||
else
|
||||
raise BerError.new( "unsupported object type: id=#{id}" )
|
||||
raise BerError.new( "unsupported object type: id=#{id}" )
|
||||
end
|
||||
|
||||
[obj, n_consumed]
|
||||
|
|
|
@ -29,80 +29,78 @@
|
|||
|
||||
|
||||
module Net
|
||||
class LDAP
|
||||
|
||||
class Dataset < Hash
|
||||
|
||||
attr_reader :comments
|
||||
|
||||
|
||||
def Dataset::read_ldif io
|
||||
ds = Dataset.new
|
||||
|
||||
line = io.gets && chomp
|
||||
dn = nil
|
||||
|
||||
while line
|
||||
io.gets and chomp
|
||||
if $_ =~ /^[\s]+/
|
||||
line << " " << $'
|
||||
else
|
||||
nextline = $_
|
||||
|
||||
if line =~ /^\#/
|
||||
ds.comments << line
|
||||
elsif line =~ /^dn:[\s]*/i
|
||||
dn = $'
|
||||
ds[dn] = Hash.new {|k,v| k[v] = []}
|
||||
elsif line.length == 0
|
||||
dn = nil
|
||||
elsif line =~ /^([^:]+):([\:]?)[\s]*/
|
||||
# $1 is the attribute name
|
||||
# $2 is a colon iff the attr-value is base-64 encoded
|
||||
# $' is the attr-value
|
||||
# Avoid the Base64 class because not all Ruby versions have it.
|
||||
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
|
||||
ds[dn][$1.downcase.intern] << attrvalue
|
||||
class LDAP
|
||||
class Dataset < Hash
|
||||
attr_reader :comments
|
||||
class IOFilter
|
||||
def initialize(io)
|
||||
@io = io
|
||||
end
|
||||
|
||||
def gets
|
||||
s = @io.gets
|
||||
s.chomp if s
|
||||
end
|
||||
|
||||
line = nextline
|
||||
end
|
||||
end
|
||||
|
||||
def self.read_ldif io
|
||||
ds = Dataset.new
|
||||
|
||||
line = io.gets
|
||||
dn = nil
|
||||
|
||||
while line
|
||||
io.gets and chomp
|
||||
if new_line =~ /^[\s]+/
|
||||
line << " " << $'
|
||||
else
|
||||
nextline = new_line
|
||||
|
||||
if line =~ /^\#/
|
||||
ds.comments << line
|
||||
elsif line =~ /^dn:[\s]*/i
|
||||
dn = $'
|
||||
ds[dn] = Hash.new {|k,v| k[v] = []}
|
||||
elsif line.length == 0
|
||||
dn = nil
|
||||
elsif line =~ /^([^:]+):([\:]?)[\s]*/
|
||||
# $1 is the attribute name
|
||||
# $2 is a colon iff the attr-value is base-64 encoded
|
||||
# $' is the attr-value
|
||||
# Avoid the Base64 class because not all Ruby versions have it.
|
||||
attrvalue = ($2 == ":") ? $'.unpack('m').shift : $'
|
||||
ds[dn][$1.downcase.intern] << attrvalue
|
||||
end
|
||||
|
||||
line = nextline
|
||||
end
|
||||
end
|
||||
|
||||
ds
|
||||
end
|
||||
ds
|
||||
end
|
||||
|
||||
|
||||
def initialize
|
||||
@comments = []
|
||||
end
|
||||
def initialize
|
||||
@comments = []
|
||||
end
|
||||
|
||||
|
||||
def to_ldif
|
||||
ary = []
|
||||
ary += (@comments || [])
|
||||
def to_ldif
|
||||
ary = []
|
||||
ary += (@comments || [])
|
||||
keys.sort.each do |dn|
|
||||
ary << "dn: #{dn}"
|
||||
|
||||
keys.sort.each {|dn|
|
||||
ary << "dn: #{dn}"
|
||||
|
||||
self[dn].keys.map {|sym| sym.to_s}.sort.each {|attr|
|
||||
self[dn][attr.intern].each {|val|
|
||||
ary << "#{attr}: #{val}"
|
||||
}
|
||||
}
|
||||
|
||||
ary << ""
|
||||
}
|
||||
|
||||
block_given? and ary.each {|line| yield line}
|
||||
|
||||
ary
|
||||
end
|
||||
|
||||
|
||||
end # Dataset
|
||||
|
||||
end # LDAP
|
||||
end # Net
|
||||
self[dn].keys.map {|sym| sym.to_s}.sort.each do |attr|
|
||||
self[dn][attr.intern].each {|val| ary << "#{attr}: #{val}" }
|
||||
end
|
||||
|
||||
ary << ""
|
||||
end
|
||||
block_given? and ary.each {|line| yield line}
|
||||
ary
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,36 +27,36 @@
|
|||
|
||||
|
||||
module Net
|
||||
class LDAP
|
||||
class LDAP
|
||||
class Password
|
||||
class << self
|
||||
|
||||
|
||||
class Password
|
||||
class << self
|
||||
|
||||
# Generate a password-hash suitable for inclusion in an LDAP attribute.
|
||||
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
|
||||
# password. This function will return a hashed representation.
|
||||
# STUB: This is here to fulfill the requirements of an RFC, which one?
|
||||
# TODO, gotta do salted-sha and (maybe) salted-md5.
|
||||
# Should we provide sha1 as a synonym for sha1? I vote no because then
|
||||
# should you also provide ssha1 for symmetry?
|
||||
def generate( type, str )
|
||||
case type
|
||||
when :md5
|
||||
"{MD5}#{ [MD5.new( str.to_s ).digest].pack("m").chomp }"
|
||||
when :sha
|
||||
"{SHA}#{ [SHA1.new( str.to_s ).digest].pack("m").chomp }"
|
||||
# when ssha
|
||||
else
|
||||
raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" )
|
||||
# Generate a password-hash suitable for inclusion in an LDAP attribute.
|
||||
# Pass a hash type (currently supported: :md5 and :sha) and a plaintext
|
||||
# password. This function will return a hashed representation.
|
||||
# STUB: This is here to fulfill the requirements of an RFC, which one?
|
||||
# TODO, gotta do salted-sha and (maybe) salted-md5.
|
||||
# Should we provide sha1 as a synonym for sha1? I vote no because then
|
||||
# should you also provide ssha1 for symmetry?
|
||||
def generate( type, str )
|
||||
digest, digest_name = case type
|
||||
when :md5
|
||||
[Digest::MD5.new, 'MD5']
|
||||
when :sha
|
||||
[Digest::SHA1.new, 'sha']
|
||||
# when ssha
|
||||
else
|
||||
raise Net::LDAP::LdapError.new( "unsupported password-hash type (#{type})" )
|
||||
end
|
||||
|
||||
digest << str.to_s
|
||||
return "{#{digest_name}}#{[digest.digest].pack('m').chomp }"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end # class LDAP
|
||||
end # module Net
|
||||
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ class TestLdif < Test::Unit::TestCase
|
|||
TestLdifFilename = "#{File.dirname(__FILE__)}/testdata.ldif"
|
||||
|
||||
def test_empty_ldif
|
||||
ds = Net::LDAP::Dataset::read_ldif( StringIO.new )
|
||||
ds = Net::LDAP::Dataset.read_ldif( StringIO.new )
|
||||
assert_equal( true, ds.empty? )
|
||||
end
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class TestSnmp < Test::Unit::TestCase
|
|||
# partially-received data streams, such as from network connections.
|
||||
def test_consume_string
|
||||
data = "xxx"
|
||||
assert_equal( nil, data.read_ber! )
|
||||
assert_equal( data.read_ber!, nil )
|
||||
assert_equal( "xxx", data )
|
||||
|
||||
data = SnmpGetRequest + "!!!"
|
||||
|
|
Loading…
Reference in a new issue