Navigation Menu

Skip to content
This repository has been archived by the owner on Sep 3, 2023. It is now read-only.

Commit

Permalink
Implement equality matching
Browse files Browse the repository at this point in the history
Different attributes have different matching semantics, (e.g.,
case-sensitive vs case-insensitive).  For known matching algorithms,
model our local comparison semantics after server side ones.
  • Loading branch information
tpope committed Jan 28, 2011
1 parent 970cce5 commit 99a8e7a
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 14 deletions.
16 changes: 8 additions & 8 deletions lib/ldaptic/attribute_set.rb
Expand Up @@ -104,9 +104,8 @@ def compare(target)
@entry.compare(@name, target)
end

# Adds the given attributes, discarding duplicates. Currently, a duplicate
# is determined by == (case sensitive) rather than by the server (typically
# case insensitive). All arrays are flattened.
# Adds the given attributes, discarding duplicates. All arrays are
# flattened.
def add(*attributes)
dest = @target.dup
safe_array(attributes).each do |attribute|
Expand Down Expand Up @@ -155,16 +154,13 @@ def clear

# Remove the given attributes given, functioning more or less like
# Array#delete, except accepting multiple arguments.
#
# Two passes are made to find each element, one case sensitive and one
# ignoring case, before giving up.
def delete(*attributes, &block)
return clear if attributes.flatten.empty?
dest = @target.dup
ret = []
safe_array(attributes).each do |attribute|
ret << dest.delete(attribute) do
match = dest.detect {|x| x.downcase == attribute.downcase}
match = dest.detect {|x| matchable(x) == matchable(attribute)}
if match
dest.delete(match)
else
Expand Down Expand Up @@ -297,7 +293,11 @@ def format(value)
end

def matchable(value)
format(value)
if @type
@type.matchable(value)
else
format(value)
end
end

def safe_array(attributes)
Expand Down
80 changes: 80 additions & 0 deletions lib/ldaptic/matching_rules.rb
@@ -0,0 +1,80 @@
require 'ldaptic/syntaxes'

module Ldaptic
# RFC4517 - Lightweight Directory Access Protocol (LDAP): Syntaxes and Matching Rules
# RFC4518 - Lightweight Directory Access Protocol (LDAP): Internationalized
# String Preparation
module MatchingRules
def self.for(name)
name = name.to_s
name = name[0..0].upcase + name[1..-1].to_s
if !name.empty? && const_defined?(name)
const_get(name)
else
CaseIgnoreMatch
end
end

class OctetStringMatch
def matchable(value)
value
end

def match(one, two)
matchable(one) == matchable(two)
end
end

class Boolean < OctetStringMatch
end

class CaseExactMatch < OctetStringMatch
def matchable(value)
super.gsub(/ +/, ' ').sub(/\A */, ' ').sub(/ *\z/, ' ')
end
end

class CaseExactIA5Match < CaseExactMatch
end

class CaseIgnoreMatch < CaseExactMatch
def matchable(value)
super.downcase
end
end

class CaseIgnoreIA5Match < CaseIgnoreMatch
end

class CaseIgnoreListMatch < CaseIgnoreMatch
end

class GeneralizedTimeMatch < OctetStringMatch
def matchable(value)
Ldaptic::Syntaxes::GeneralizedTime.parse(value)
end
end

class NumericStringMatch < OctetStringMatch
def matchable(value)
super.delete(' ')
end
end

class DistinguishedNameMatch < OctetStringMatch
def matchable(value)
Ldaptic::DN(value)
end
end

class TelephoneNumberMatch < CaseIgnoreMatch
# Doesn't remove unicode hyphen equivalents \u058A, \u2010, \u2011,
# \u2212, \ufe63, or \uff0d on account of unicode being so darn difficult
# to get right in both 1.8 and 1.9.
def matchable(value)
super.delete(' ').delete('-')
end
end

end
end
9 changes: 3 additions & 6 deletions lib/ldaptic/schema.rb
Expand Up @@ -201,12 +201,8 @@ def syntax_object(*args)
end
alias syntax syntax_object

def equality_testable(value)
Ldaptic.encode(value)
end

def match?(one, two)
equality_testable(one) == equality_testable(two)
def matchable(value)
Ldaptic::MatchingRules.for(equality).new.matchable(Ldaptic.encode(value))
end

end
Expand Down Expand Up @@ -253,3 +249,4 @@ class NameForm < NameDescObsoleteDefiniton
end

require 'ldaptic/syntaxes'
require 'ldaptic/matching_rules'
38 changes: 38 additions & 0 deletions test/ldapter_matching_rules_test.rb
@@ -0,0 +1,38 @@
require File.join(File.dirname(File.expand_path(__FILE__)),'test_helper')
require 'ldaptic/matching_rules'

class LdapticMatchingRulesTest < Test::Unit::TestCase
include Ldaptic::MatchingRules

def test_for
assert_equal GeneralizedTimeMatch, Ldaptic::MatchingRules.for("generalizedTimeMatch")
end

def test_case_exact_match
assert CaseExactMatch.new.match(' A bc', 'A bc')
assert !CaseExactMatch.new.match(' A bc', 'a bC')
end

def test_case_ignore_match
assert CaseIgnoreMatch.new.match(' A bc', 'a bC')
end

def test_generalized_time_match
assert_equal Time.utc(2000,1,1,12,34,56), GeneralizedTimeMatch.new.matchable("20000101123456.0Z")
end

def test_numeric_string
assert NumericStringMatch.new.match(' 123 4', '123 4')
assert !NumericStringMatch.new.match('1234', '1235')
end

def test_distinguished_name_match
assert DistinguishedNameMatch.new.match('a=1+b=2', 'B=2+A=1')
assert !DistinguishedNameMatch.new.match('a=1,b=2', 'b=2,a=1')
end

def test_telephone_number_match
assert TelephoneNumberMatch.new.match("911", "9 1-1-")
end

end

0 comments on commit 99a8e7a

Please sign in to comment.