diff --git a/lib/net/ldap/filter.rb b/lib/net/ldap/filter.rb index 23a88f2..4e8aec8 100644 --- a/lib/net/ldap/filter.rb +++ b/lib/net/ldap/filter.rb @@ -1,3 +1,4 @@ +# Encoding: UTF-8 # Copyright (C) 2006 by Francis Cianfrocca and other contributors. All # Rights Reserved. # @@ -243,10 +244,28 @@ class Net::LDAP::Filter alias_method :present, :present? alias_method :pres, :present? + # http://tools.ietf.org/html/rfc4515 lists these exceptions from UTF1 + # charset for filters. All of the following must be escaped in any normal + # string using a single backslash ('\') as escape. + # + ESCAPES = { + '!' => '21', # EXCLAMATION = %x21 ; exclamation mark ("!") + '&' => '26', # AMPERSAND = %x26 ; ampersand (or AND symbol) ("&") + '*' => '2A', # ASTERISK = %x2A ; asterisk ("*") + ':' => '3A', # COLON = %x3A ; colon (":") + '|' => '7C', # VERTBAR = %x7C ; vertical bar (or pipe) ("|") + '~' => '7E', # TILDE = %x7E ; tilde ("~") + } + # Compiled character class regexp using the keys from the above hash. + ESCAPE_RE = Regexp.new( + "[" + + ESCAPES.keys.map { |e| Regexp.escape(e) }.join + + "]") + ## # Escape a string for use in an LDAP filter def escape(string) - string.gsub(/[\*\(\)\\\0]/) {|s| sprintf("\\%02x", s[0]) } + string.gsub(ESCAPE_RE) { |char| "\\" + ESCAPES[char] } end ## diff --git a/spec/unit/ldap/filter_spec.rb b/spec/unit/ldap/filter_spec.rb index fac0c28..623f032 100644 --- a/spec/unit/ldap/filter_spec.rb +++ b/spec/unit/ldap/filter_spec.rb @@ -49,5 +49,35 @@ describe Net::LDAP::Filter do Net::LDAP::Filter.construct("uid=O'Keefe").to_rfc2254.should == "(uid=O'Keefe)" end end - + + describe "convenience filter constructors" do + def eq(attribute, value) + described_class.eq(attribute, value) + end + describe "<- .equals(attr, val)" do + it "should delegate to .eq with escaping" do + described_class.equals('dn', 'f*oo').should == eq('dn', 'f\2Aoo') + end + end + describe "<- .begins(attr, val)" do + it "should delegate to .eq with escaping" do + described_class.begins('dn', 'f*oo').should == eq('dn', 'f\2Aoo*') + end + end + describe "<- .ends(attr, val)" do + it "should delegate to .eq with escaping" do + described_class.ends('dn', 'f*oo').should == eq('dn', '*f\2Aoo') + end + end + describe "<- .contains(attr, val)" do + it "should delegate to .eq with escaping" do + described_class.contains('dn', 'f*oo').should == eq('dn', '*f\2Aoo*') + end + end + end + describe "<- .escape(str)" do + it "should escape !, &, *, :, | and ~" do + Net::LDAP::Filter.escape('!&*:|~').should == "\\21\\26\\2A\\3A\\7C\\7E" + end + end end \ No newline at end of file diff --git a/test/test_filter.rb b/test/test_filter.rb index c7214a7..88495f1 100644 --- a/test/test_filter.rb +++ b/test/test_filter.rb @@ -24,13 +24,6 @@ class TestFilter < Test::Unit::TestCase assert_equal("(uid=george *)", Filter.eq("uid", "george *").to_s) end - def test_convenience_filters - assert_equal("(uid=\\2a)", Filter.equals("uid", "*").to_s) - assert_equal("(uid=\\28*)", Filter.begins("uid", "(").to_s) - assert_equal("(uid=*\\29)", Filter.ends("uid", ")").to_s) - assert_equal("(uid=*\\5c*)", Filter.contains("uid", "\\").to_s) - end - def test_c2 assert_equal("(uid=george *)", Filter.from_rfc2254("uid=george *").to_rfc2254)