Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added Net::LDAP::Filter.parse_ber and associated helper methods and t…

…ests.
  • Loading branch information...
commit da882af0748ff575b33f525aaf32720366636731 1 parent bc1129f
blackhedd authored
View
5 Rakefile
@@ -75,6 +75,11 @@ task :test_snmp do |t|
run_test_set t, ['tests/testsnmp.rb', 'tests/testber.rb']
end
+desc "(Provisional) Run tests for filters"
+task :test_filters do |t|
+ run_test_set t, ['tests/testfilter.rb']
+end
+
spec = eval(File.read("net-ldap.gemspec"))
spec.version = $version
desc "Build the RubyGem for #$name."
View
2  lib/net/ldap.rb
@@ -311,6 +311,8 @@ class LdapError < Exception; end
2 => :array, # SearchFilter-NOT
3 => :array, # Seach referral
4 => :array, # unknown use in Microsoft Outlook
+ 5 => :array, # SearchFilter-GE
+ 6 => :array, # SearchFilter-LE
7 => :array, # serverSaslCreds
}
}
View
59 lib/net/ldap/filter.rb
@@ -115,6 +115,11 @@ def | filter; Filter.new :or, self, filter; end
# Removed GT and LT. They're not in the RFC.
def ~@; Filter.new :not, self, nil; end
+ # Equality operator for filters, useful primarily for constructing unit tests.
+ def == filter
+ str = "[@op,@left,@right]"
+ self.instance_eval(str) == filter.instance_eval(str)
+ end
def to_s
case @op
@@ -224,6 +229,58 @@ def to_ber
end
end
+
+ # Converts an LDAP search filter in BER format to an Net::LDAP::Filter
+ # object. The incoming BER object most likely came to us by parsing an
+ # LDAP searchRequest PDU.
+ # Cf the comments under #to_ber, including the grammar snippet from the RFC.
+ #--
+ # We're hardcoding the BER constants from the RFC. Ought to break them out
+ # into constants.
+ #
+ def Filter::parse_ber ber
+ case ber.ber_identifier
+ when 0xa0 # context-specific constructed 0, "and"
+ ber.map {|b| Filter::parse_ber(b)}.inject {|memo,obj| memo & obj}
+ when 0xa1 # context-specific constructed 1, "or"
+ ber.map {|b| Filter::parse_ber(b)}.inject {|memo,obj| memo | obj}
+ when 0xa2 # context-specific constructed 2, "not"
+ ~ Filter::parse_ber( ber.first )
+ when 0xa3 # context-specific constructed 3, "equalityMatch"
+ if ber.last == "*"
+ else
+ Filter.eq( ber.first, ber.last )
+ end
+ when 0xa4 # context-specific constructed 4, "substring"
+ str = ""
+ final = false
+ ber.last.each {|b|
+ case b.ber_identifier
+ when 0x80 # context-specific primitive 0, SubstringFilter "initial"
+ raise "unrecognized substring filter, bad initial" if str.length > 0
+ str += b
+ when 0x81 # context-specific primitive 0, SubstringFilter "any"
+ str += "*#{b}"
+ when 0x82 # context-specific primitive 0, SubstringFilter "final"
+ str += "*#{b}"
+ final = true
+ end
+ }
+ str += "*" unless final
+ Filter.eq( ber.first.to_s, str )
+ when 0xa5 # context-specific constructed 5, "greaterOrEqual"
+ Filter.ge( ber.first.to_s, ber.last.to_s )
+ when 0xa6 # context-specific constructed 5, "lessOrEqual"
+ Filter.le( ber.first.to_s, ber.last.to_s )
+ when 0x87 # context-specific primitive 7, "present"
+ # call to_s to get rid of the BER-identifiedness of the incoming string.
+ Filter.pres( ber.to_s )
+ else
+ raise "invalid BER tag-value (#{ber.ber_identifier}) in search filter"
+ end
+ end
+
+
#--
# coalesce
# This is a private helper method for dealing with chains of ANDs and ORs
@@ -260,6 +317,8 @@ def Filter::parse_ldap_filter obj
end
+
+
#--
# We got a hash of attribute values.
# Do we match the attributes?
View
93 tests/testfilter.rb
@@ -11,27 +11,88 @@
class TestFilter < Test::Unit::TestCase
- def setup
- end
+ def setup
+ end
- def teardown
- end
+ def teardown
+ end
- def test_rfc_2254
- p Net::LDAP::Filter.from_rfc2254( " ( uid=george* ) " )
- p Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
- p Net::LDAP::Filter.from_rfc2254( "uid<george*" )
- p Net::LDAP::Filter.from_rfc2254( "uid <= george*" )
- p Net::LDAP::Filter.from_rfc2254( "uid>george*" )
- p Net::LDAP::Filter.from_rfc2254( "uid>=george*" )
- p Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
+ # Note that the RFC doesn't define either less-than or greater-than.
+ def test_rfc_2254
+ Net::LDAP::Filter.from_rfc2254( " ( uid=george* ) " )
+ Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
+ Net::LDAP::Filter.from_rfc2254( "uid <= george*" )
+ Net::LDAP::Filter.from_rfc2254( "uid>=george*" )
+ Net::LDAP::Filter.from_rfc2254( "uid!=george*" )
- p Net::LDAP::Filter.from_rfc2254( "(& (uid!=george* ) (mail=*))" )
- p Net::LDAP::Filter.from_rfc2254( "(| (uid!=george* ) (mail=*))" )
- p Net::LDAP::Filter.from_rfc2254( "(! (mail=*))" )
- end
+ Net::LDAP::Filter.from_rfc2254( "(& (uid!=george* ) (mail=*))" )
+ Net::LDAP::Filter.from_rfc2254( "(| (uid!=george* ) (mail=*))" )
+ Net::LDAP::Filter.from_rfc2254( "(! (mail=*))" )
+ end
+ def test_filters_from_ber
+ [
+ Net::LDAP::Filter.eq( "objectclass", "*" ),
+ Net::LDAP::Filter.pres( "objectclass" ),
+ Net::LDAP::Filter.eq( "objectclass", "ou" ),
+ Net::LDAP::Filter.ge( "uid", "500" ),
+ Net::LDAP::Filter.le( "uid", "500" ),
+ (~ Net::LDAP::Filter.pres( "objectclass" )),
+ (Net::LDAP::Filter.pres( "objectclass" ) & Net::LDAP::Filter.pres( "ou" )),
+ (Net::LDAP::Filter.pres( "objectclass" ) & Net::LDAP::Filter.pres( "ou" ) & Net::LDAP::Filter.pres("sn")),
+ (Net::LDAP::Filter.pres( "objectclass" ) | Net::LDAP::Filter.pres( "ou" ) | Net::LDAP::Filter.pres("sn")),
+
+ Net::LDAP::Filter.eq( "objectclass", "*aaa" ),
+ Net::LDAP::Filter.eq( "objectclass", "*aaa*bbb" ),
+ Net::LDAP::Filter.eq( "objectclass", "*aaa*bbb*ccc" ),
+ Net::LDAP::Filter.eq( "objectclass", "aaa*bbb" ),
+ Net::LDAP::Filter.eq( "objectclass", "aaa*bbb*ccc" ),
+ Net::LDAP::Filter.eq( "objectclass", "abc*def*1111*22*g" ),
+ Net::LDAP::Filter.eq( "objectclass", "*aaa*" ),
+ Net::LDAP::Filter.eq( "objectclass", "*aaa*bbb*" ),
+ Net::LDAP::Filter.eq( "objectclass", "*aaa*bbb*ccc*" ),
+ Net::LDAP::Filter.eq( "objectclass", "aaa*" ),
+ Net::LDAP::Filter.eq( "objectclass", "aaa*bbb*" ),
+ Net::LDAP::Filter.eq( "objectclass", "aaa*bbb*ccc*" ),
+ ].each {|ber|
+ f = Net::LDAP::Filter.parse_ber( ber.to_ber.read_ber( Net::LDAP::AsnSyntax) )
+ assert( f == ber )
+ assert_equal( f.to_ber, ber.to_ber )
+ }
+
+ end
+
+ def test_ber_from_rfc2254_filter
+ [
+ Net::LDAP::Filter.construct( "objectclass=*" ),
+ Net::LDAP::Filter.construct("objectclass=ou" ),
+ Net::LDAP::Filter.construct("uid >= 500" ),
+ Net::LDAP::Filter.construct("uid <= 500" ),
+ Net::LDAP::Filter.construct("(!(uid=*))" ),
+ Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*))" ),
+ Net::LDAP::Filter.construct("(&(uid=*)(objectclass=*)(sn=*))" ),
+ Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*))" ),
+ Net::LDAP::Filter.construct("(|(uid=*)(objectclass=*)(sn=*))" ),
+
+ Net::LDAP::Filter.construct("objectclass=*aaa"),
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb"),
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc"),
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb"),
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc"),
+ Net::LDAP::Filter.construct("objectclass=abc*def*1111*22*g"),
+ Net::LDAP::Filter.construct("objectclass=*aaa*"),
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*"),
+ Net::LDAP::Filter.construct("objectclass=*aaa*bbb*ccc*"),
+ Net::LDAP::Filter.construct("objectclass=aaa*"),
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*"),
+ Net::LDAP::Filter.construct("objectclass=aaa*bbb*ccc*"),
+ ].each {|ber|
+ f = Net::LDAP::Filter.parse_ber( ber.to_ber.read_ber( Net::LDAP::AsnSyntax) )
+ assert( f == ber )
+ assert_equal( f.to_ber, ber.to_ber )
+ }
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.