Merge pull request #33 from DavidJLee/master

New filter type to allow searching of binary data
This commit is contained in:
Rory O'Connell 2012-08-28 15:51:53 -07:00
commit b6d9fbebfd
5 changed files with 50 additions and 2 deletions

View file

@ -19,3 +19,4 @@ Contributions since:
* Derek Harmel (derekharmel) * Derek Harmel (derekharmel)
* Erik Hetzner (egh) * Erik Hetzner (egh)
* nowhereman * nowhereman
* David J. Lee (DavidJLee)

View file

@ -16,6 +16,14 @@ module Net::BER::Extensions::String
[code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string [code].pack('C') + raw_string.length.to_ber_length_encoding + raw_string
end end
##
# Converts a string to a BER string but does *not* encode to UTF-8 first.
# This is required for proper representation of binary data for Microsoft
# Active Directory
def to_ber_bin(code = 0x04)
[code].pack('C') + length.to_ber_length_encoding + self
end
def raw_utf8_encoded def raw_utf8_encoded
if self.respond_to?(:encode) if self.respond_to?(:encode)
# Strings should be UTF-8 encoded according to LDAP. # Strings should be UTF-8 encoded according to LDAP.

View file

@ -308,6 +308,7 @@ class Net::LDAP
DefaultPort = 389 DefaultPort = 389
DefaultAuth = { :method => :anonymous } DefaultAuth = { :method => :anonymous }
DefaultTreebase = "dc=com" DefaultTreebase = "dc=com"
DefaultForceNoPage = false
StartTlsOid = "1.3.6.1.4.1.1466.20037" StartTlsOid = "1.3.6.1.4.1.1466.20037"
@ -373,6 +374,8 @@ class Net::LDAP
# specifying the Hash {:method => :simple_tls}. There is a fairly large # specifying the Hash {:method => :simple_tls}. There is a fairly large
# range of potential values that may be given for this parameter. See # range of potential values that may be given for this parameter. See
# #encryption for details. # #encryption for details.
# * :force_no_page => Set to true to prevent paged results even if your
# server says it supports them. This is a fix for MS Active Directory
# #
# Instantiating a Net::LDAP object does <i>not</i> result in network # Instantiating a Net::LDAP object does <i>not</i> result in network
# traffic to the LDAP server. It simply stores the connection and binding # traffic to the LDAP server. It simply stores the connection and binding
@ -383,6 +386,7 @@ class Net::LDAP
@verbose = false # Make this configurable with a switch on the class. @verbose = false # Make this configurable with a switch on the class.
@auth = args[:auth] || DefaultAuth @auth = args[:auth] || DefaultAuth
@base = args[:base] || DefaultTreebase @base = args[:base] || DefaultTreebase
@force_no_page = args[:force_no_page] || DefaultForceNoPage
encryption args[:encryption] # may be nil encryption args[:encryption] # may be nil
if pr = @auth[:password] and pr.respond_to?(:call) if pr = @auth[:password] and pr.respond_to?(:call)
@ -1108,6 +1112,10 @@ class Net::LDAP
# MUST refactor the root_dse call out. # MUST refactor the root_dse call out.
#++ #++
def paged_searches_supported? def paged_searches_supported?
# active directory returns that it supports paged results. However
# it returns binary data in the rfc2696_cookie which throws an
# encoding exception breaking searching.
return false if @force_no_page
@server_caps ||= search_root_dse @server_caps ||= search_root_dse
@server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS) @server_caps[:supportedcontrol].include?(Net::LDAP::LDAPControls::PAGED_RESULTS)
end end
@ -1433,6 +1441,10 @@ class Net::LDAP::Connection #:nodoc:
search_attributes.to_ber_sequence search_attributes.to_ber_sequence
].to_ber_appsequence(3) ].to_ber_appsequence(3)
# rfc2696_cookie sometimes contains binary data from Microsoft Active Directory
# this breaks when calling to_ber. (Can't force binary data to UTF-8)
# we have to disable paging (even though server supports it) to get around this...
controls = [] controls = []
controls << controls <<
[ [
@ -1582,7 +1594,7 @@ class Net::LDAP::Connection #:nodoc:
#-- #--
# 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"
delete_attrs = args[:delete_attributes] ? true : false delete_attrs = args[:delete_attributes] ? true : false

View file

@ -23,7 +23,7 @@
class Net::LDAP::Filter class Net::LDAP::Filter
## ##
# Known filter types. # Known filter types.
FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex ] FilterTypes = [ :ne, :eq, :ge, :le, :and, :or, :not, :ex, :bineq ]
def initialize(op, left, right) #:nodoc: def initialize(op, left, right) #:nodoc:
unless FilterTypes.include?(op) unless FilterTypes.include?(op)
@ -65,6 +65,23 @@ class Net::LDAP::Filter
new(:eq, attribute, value) new(:eq, attribute, value)
end end
##
# Creates a Filter object indicating a binary comparison.
# this prevents the search data from being forced into a UTF-8 string.
#
# This is primarily used for Microsoft Active Directory to compare
# GUID values.
#
# # for guid represented as hex charecters
# guid = "6a31b4a12aa27a41aca9603f27dd5116"
# guid_bin = [guid].pack("H*")
# f = Net::LDAP::Filter.bineq("objectGUID", guid_bin)
#
# This filter does not perform any escaping.
def bineq(attribute, value)
new(:bineq, attribute, value)
end
## ##
# Creates a Filter object indicating extensible comparison. This Filter # Creates a Filter object indicating extensible comparison. This Filter
# object is currently considered EXPERIMENTAL. # object is currently considered EXPERIMENTAL.
@ -399,6 +416,8 @@ class Net::LDAP::Filter
"!(#{@left}=#{@right})" "!(#{@left}=#{@right})"
when :eq when :eq
"#{@left}=#{@right}" "#{@left}=#{@right}"
when :bineq
"#{@left}=#{@right}"
when :ex when :ex
"#{@left}:=#{@right}" "#{@left}:=#{@right}"
when :ge when :ge
@ -508,6 +527,9 @@ class Net::LDAP::Filter
else # equality else # equality
[@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3) [@left.to_s.to_ber, unescape(@right).to_ber].to_ber_contextspecific(3)
end end
when :bineq
# make sure data is not forced to UTF-8
[@left.to_s.to_ber, unescape(@right).to_ber_bin].to_ber_contextspecific(3)
when :ex when :ex
seq = [] seq = []

View file

@ -84,6 +84,11 @@ describe "BER encoding of" do
it "should properly encode strings encodable as UTF-8" do it "should properly encode strings encodable as UTF-8" do
"teststring".encode("US-ASCII").to_ber.should == "\x04\nteststring" "teststring".encode("US-ASCII").to_ber.should == "\x04\nteststring"
end end
it "should properly encode binary data strings using to_ber_bin" do
# This is used for searching for GUIDs in Active Directory
["6a31b4a12aa27a41aca9603f27dd5116"].pack("H*").to_ber_bin.should ==
"\x04\x10" + "j1\xB4\xA1*\xA2zA\xAC\xA9`?'\xDDQ\x16"
end
it "should fail on strings that can not be converted to UTF-8" do it "should fail on strings that can not be converted to UTF-8" do
error = Encoding::UndefinedConversionError error = Encoding::UndefinedConversionError
lambda {"\x81".to_ber }.should raise_exception(error) lambda {"\x81".to_ber }.should raise_exception(error)