ldaptic 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +104 -0
- data/Rakefile +41 -0
- data/lib/ldaptic.rb +151 -0
- data/lib/ldaptic/active_model.rb +37 -0
- data/lib/ldaptic/adapters.rb +90 -0
- data/lib/ldaptic/adapters/abstract_adapter.rb +123 -0
- data/lib/ldaptic/adapters/active_directory_adapter.rb +78 -0
- data/lib/ldaptic/adapters/active_directory_ext.rb +12 -0
- data/lib/ldaptic/adapters/ldap_conn_adapter.rb +262 -0
- data/lib/ldaptic/adapters/net_ldap_adapter.rb +173 -0
- data/lib/ldaptic/adapters/net_ldap_ext.rb +24 -0
- data/lib/ldaptic/attribute_set.rb +283 -0
- data/lib/ldaptic/dn.rb +365 -0
- data/lib/ldaptic/entry.rb +646 -0
- data/lib/ldaptic/error_set.rb +34 -0
- data/lib/ldaptic/errors.rb +136 -0
- data/lib/ldaptic/escape.rb +110 -0
- data/lib/ldaptic/filter.rb +282 -0
- data/lib/ldaptic/methods.rb +387 -0
- data/lib/ldaptic/railtie.rb +9 -0
- data/lib/ldaptic/schema.rb +246 -0
- data/lib/ldaptic/syntaxes.rb +319 -0
- data/test/core.schema +582 -0
- data/test/ldaptic_active_model_test.rb +40 -0
- data/test/ldaptic_adapters_test.rb +35 -0
- data/test/ldaptic_attribute_set_test.rb +57 -0
- data/test/ldaptic_dn_test.rb +110 -0
- data/test/ldaptic_entry_test.rb +22 -0
- data/test/ldaptic_errors_test.rb +23 -0
- data/test/ldaptic_escape_test.rb +47 -0
- data/test/ldaptic_filter_test.rb +53 -0
- data/test/ldaptic_hierarchy_test.rb +90 -0
- data/test/ldaptic_schema_test.rb +44 -0
- data/test/ldaptic_syntaxes_test.rb +66 -0
- data/test/mock_adapter.rb +47 -0
- data/test/rbslapd1.rb +111 -0
- data/test/rbslapd4.rb +172 -0
- data/test/test_helper.rb +2 -0
- metadata +146 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
module Ldaptic
|
2
|
+
class ErrorSet < Hash
|
3
|
+
def initialize(base)
|
4
|
+
@base = base
|
5
|
+
super() { |h, k| h[k] = [] }
|
6
|
+
end
|
7
|
+
|
8
|
+
def add(attribute, message)
|
9
|
+
self[attribute] << message
|
10
|
+
end
|
11
|
+
|
12
|
+
def each
|
13
|
+
each_key do |attribute|
|
14
|
+
self[attribute].each do |message|
|
15
|
+
yield attribute, message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def full_messages
|
21
|
+
map do |attribute, message|
|
22
|
+
"#{@base.class.human_attribute_name(attribute)} #{message}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_a
|
27
|
+
full_messages
|
28
|
+
end
|
29
|
+
|
30
|
+
def size
|
31
|
+
full_messages.size
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Ldaptic
|
2
|
+
|
3
|
+
class Error < ::RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class EntryNotSaved < Error
|
7
|
+
end
|
8
|
+
|
9
|
+
# All server errors are instances of this class. The error message and error
|
10
|
+
# code can be accessed with <tt>exception.message</tt> and
|
11
|
+
# <tt>exception.code</tt> respectively.
|
12
|
+
class ServerError < Error
|
13
|
+
attr_accessor :code
|
14
|
+
end
|
15
|
+
|
16
|
+
# The module houses all subclasses of Ldaptic::ServerError. The methods
|
17
|
+
# contained within are for internal use only.
|
18
|
+
module Errors
|
19
|
+
|
20
|
+
#{
|
21
|
+
# 0=>"Success",
|
22
|
+
# 1=>"Operations error",
|
23
|
+
# 2=>"Protocol error",
|
24
|
+
# 3=>"Time limit exceeded",
|
25
|
+
# 4=>"Size limit exceeded",
|
26
|
+
# 5=>"Compare False",
|
27
|
+
# 6=>"Compare True",
|
28
|
+
# 7=>"Authentication method not supported"
|
29
|
+
# 8=>"Strong(er) authentication required",
|
30
|
+
# 9=>"Partial results and referral received",
|
31
|
+
# 10=>"Referral",
|
32
|
+
# 11=>"Administrative limit exceeded",
|
33
|
+
# 12=>"Critical extension is unavailable",
|
34
|
+
# 13=>"Confidentiality required",
|
35
|
+
# 14=>"SASL bind in progress",
|
36
|
+
# 16=>"No such attribute",
|
37
|
+
# 17=>"Undefined attribute type",
|
38
|
+
# 18=>"Inappropriate matching",
|
39
|
+
# 19=>"Constraint violation",
|
40
|
+
# 20=>"Type or value exists",
|
41
|
+
# 21=>"Invalid syntax",
|
42
|
+
# 32=>"No such object",
|
43
|
+
# 33=>"Alias problem",
|
44
|
+
# 34=>"Invalid DN syntax",
|
45
|
+
# 35=>"Entry is a leaf",
|
46
|
+
# 36=>"Alias dereferencing problem",
|
47
|
+
# 47=>"Proxy Authorization Failure",
|
48
|
+
# 48=>"Inappropriate authentication",
|
49
|
+
# 49=>"Invalid credentials",
|
50
|
+
# 50=>"Insufficient access",
|
51
|
+
# 51=>"Server is busy",
|
52
|
+
# 52=>"Server is unavailable",
|
53
|
+
# 53=>"Server is unwilling to perform",
|
54
|
+
# 54=>"Loop detected",
|
55
|
+
# 64=>"Naming violation",
|
56
|
+
# 65=>"Object class violation",
|
57
|
+
# 66=>"Operation not allowed on non-leaf",
|
58
|
+
# 67=>"Operation not allowed on RDN",
|
59
|
+
# 68=>"Already exists",
|
60
|
+
# 69=>"Cannot modify object class",
|
61
|
+
# 70=>"Results too large",
|
62
|
+
# 71=>"Operation affects multiple DSAs",
|
63
|
+
# 80=>"Internal (implementation specific) error",
|
64
|
+
# 81=>"Can't contact LDAP server",
|
65
|
+
# 82=>"Local error",
|
66
|
+
# 83=>"Encoding error",
|
67
|
+
# 84=>"Decoding error",
|
68
|
+
# 85=>"Timed out",
|
69
|
+
# 86=>"Unknown authentication method",
|
70
|
+
# 87=>"Bad search filter",
|
71
|
+
# 88=>"User cancelled operation",
|
72
|
+
# 89=>"Bad parameter to an ldap routine",
|
73
|
+
# 90=>"Out of memory",
|
74
|
+
# 91=>"Connect error",
|
75
|
+
# 92=>"Not Supported",
|
76
|
+
# 93=>"Control not found",
|
77
|
+
# 94=>"No results returned",
|
78
|
+
# 95=>"More results to return",
|
79
|
+
# 96=>"Client Loop",
|
80
|
+
# 97=>"Referral Limit Exceeded",
|
81
|
+
#}
|
82
|
+
|
83
|
+
# Error code 32.
|
84
|
+
class NoSuchObject < ServerError
|
85
|
+
end
|
86
|
+
|
87
|
+
# Error code 5.
|
88
|
+
class CompareFalse < ServerError
|
89
|
+
end
|
90
|
+
# Error code 6.
|
91
|
+
class CompareTrue < ServerError
|
92
|
+
end
|
93
|
+
|
94
|
+
EXCEPTIONS = {
|
95
|
+
32 => NoSuchObject,
|
96
|
+
5 => CompareFalse,
|
97
|
+
6 => CompareTrue
|
98
|
+
}
|
99
|
+
|
100
|
+
class << self
|
101
|
+
|
102
|
+
# Provides a backtrace minus all files shipped with Ldaptic.
|
103
|
+
def application_backtrace
|
104
|
+
dir = File.dirname(File.dirname(__FILE__))
|
105
|
+
c = caller
|
106
|
+
c.shift while c.first[0,dir.length] == dir
|
107
|
+
c
|
108
|
+
end
|
109
|
+
|
110
|
+
# Raise an exception (object only, no strings or classes) with the
|
111
|
+
# backtrace stripped of all Ldaptic files.
|
112
|
+
def raise(exception)
|
113
|
+
exception.set_backtrace(application_backtrace)
|
114
|
+
Kernel.raise exception
|
115
|
+
end
|
116
|
+
|
117
|
+
def for(code, message = nil) #:nodoc:
|
118
|
+
message ||= "Unknown error #{code}"
|
119
|
+
klass = EXCEPTIONS[code] || ServerError
|
120
|
+
exception = klass.new(message)
|
121
|
+
exception.code = code
|
122
|
+
exception
|
123
|
+
end
|
124
|
+
|
125
|
+
# Given an error code and a message, raise an Ldaptic::ServerError unless
|
126
|
+
# the code is zero. The right subclass is selected automatically if it
|
127
|
+
# is available.
|
128
|
+
def raise_unless_zero(code, message = nil)
|
129
|
+
raise self.for(code, message) unless code.zero?
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Ldaptic
|
2
|
+
|
3
|
+
# Encode an object with LDAP semantics. Generally this is just to_s, but
|
4
|
+
# dates and booleans get special treatment.
|
5
|
+
#
|
6
|
+
# If a symbol is passed in, underscores are replaced by dashes, aiding in
|
7
|
+
# bridging the gap between LDAP and Ruby conventions.
|
8
|
+
def self.encode(value)
|
9
|
+
if value.respond_to?(:utc)
|
10
|
+
value.dup.utc.strftime("%Y%m%d%H%M%S") + ".%06dZ" % value.usec
|
11
|
+
elsif [true, false].include?(value)
|
12
|
+
value.to_s.upcase
|
13
|
+
elsif value.respond_to?(:dn)
|
14
|
+
value.dn.dup
|
15
|
+
elsif value.kind_of?(Symbol)
|
16
|
+
value.to_s.gsub('_', '-')
|
17
|
+
else
|
18
|
+
value.to_s.dup
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Escape a string for use in an LDAP filter, or in a DN. If the second
|
23
|
+
# argument is +true+, asterisks are not escaped.
|
24
|
+
#
|
25
|
+
# If the first argument is not a string, it is handed off to LDAP::encode.
|
26
|
+
def self.escape(string, allow_asterisks = false)
|
27
|
+
string = Ldaptic.encode(string)
|
28
|
+
enc = lambda { |l| "\\%02X" % l.ord }
|
29
|
+
string.gsub!(/[()\\\0-\37"+,;<>]/, &enc)
|
30
|
+
string.gsub!(/\A[# ]| \Z/, &enc)
|
31
|
+
if allow_asterisks
|
32
|
+
string.gsub!('**', '\\\\2A')
|
33
|
+
else
|
34
|
+
string.gsub!('*', '\\\\2A')
|
35
|
+
end
|
36
|
+
string
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.unescape(string)
|
40
|
+
dest = ""
|
41
|
+
string = string.strip # Leading and trailing whitespace MUST be encoded
|
42
|
+
if string[0,1] == "#"
|
43
|
+
[string[1..-1]].pack("H*")
|
44
|
+
else
|
45
|
+
backslash = nil
|
46
|
+
string.each_byte do |byte|
|
47
|
+
case backslash
|
48
|
+
when true
|
49
|
+
char = byte.chr
|
50
|
+
if ('0'..'9').include?(char) || ('a'..'f').include?(char.downcase)
|
51
|
+
backslash = char
|
52
|
+
else
|
53
|
+
dest << byte
|
54
|
+
backslash = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
when String
|
58
|
+
dest << (backslash << byte).to_i(16)
|
59
|
+
backslash = nil
|
60
|
+
|
61
|
+
else
|
62
|
+
backslash = nil
|
63
|
+
if byte == 92 # ?\\
|
64
|
+
backslash = true
|
65
|
+
else
|
66
|
+
dest << byte
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
dest
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Split on a given character where it is not escaped. Either an integer or
|
75
|
+
# string represenation of the character may be used.
|
76
|
+
#
|
77
|
+
# Ldaptic.split("a*b", '*') # => ["a","b"]
|
78
|
+
# Ldaptic.split("a\\*b", '*') # => ["a\\*b"]
|
79
|
+
# Ldaptic.split("a\\\\*b", ?*) # => ["a\\\\","b"]
|
80
|
+
def self.split(string, character)
|
81
|
+
return [] if string.empty?
|
82
|
+
array = [""]
|
83
|
+
character = character.to_str.ord if character.respond_to?(:to_str)
|
84
|
+
backslash = false
|
85
|
+
|
86
|
+
string.each_byte do |byte|
|
87
|
+
if backslash
|
88
|
+
array.last << byte
|
89
|
+
backslash = false
|
90
|
+
elsif byte == 92 # ?\\
|
91
|
+
array.last << byte
|
92
|
+
backslash = true
|
93
|
+
elsif byte == character
|
94
|
+
array << ""
|
95
|
+
else
|
96
|
+
array.last << byte
|
97
|
+
end
|
98
|
+
end
|
99
|
+
array
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
class String
|
105
|
+
unless method_defined?(:ord)
|
106
|
+
def ord
|
107
|
+
self[0].to_i
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'ldaptic/escape'
|
2
|
+
|
3
|
+
module Ldaptic
|
4
|
+
|
5
|
+
# If the argument is already a valid Ldaptic::Filter object, return it
|
6
|
+
# untouched. Otherwise, pass it to the appropriate constructer of the
|
7
|
+
# appropriate subclass.
|
8
|
+
#
|
9
|
+
# Ldaptic::Filter("(cn=Wu*)").to_s #=> '(cn=Wu*)'
|
10
|
+
# Ldaptic::Filter({:cn=>"Wu*"}).to_s #=> '(cn=Wu\2A)'
|
11
|
+
# Ldaptic::Filter(["(cn=?*)","Wu*"]).to_s #=> '(cn=Wu\2A*)'
|
12
|
+
def self.Filter(argument)
|
13
|
+
case argument
|
14
|
+
when Filter::Abstract then argument
|
15
|
+
when [],nil then nil
|
16
|
+
when Array then Filter::Array .new(argument)
|
17
|
+
when Hash then Filter::Hash .new(argument)
|
18
|
+
when String then Filter::String .new(argument)
|
19
|
+
when Symbol then Filter::Attribute.new(argument)
|
20
|
+
when Proc, Method
|
21
|
+
Ldaptic::Filter(if argument.arity > 0
|
22
|
+
argument.call(Filter::Spawner)
|
23
|
+
elsif Filter::Spawner.respond_to?(:instance_exec)
|
24
|
+
Filter::Spawner.instance_exec(&argument)
|
25
|
+
else
|
26
|
+
Filter::Spawner.instance_eval(&argument)
|
27
|
+
end)
|
28
|
+
else raise TypeError, "Unknown LDAP Filter type", caller
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# See Ldaptic.Filter for the contructor and Ldaptic::Filter::Abstract for
|
33
|
+
# methods common to all filters.
|
34
|
+
#
|
35
|
+
# Useful subclasses include String, Array, and Hash.
|
36
|
+
module Filter
|
37
|
+
|
38
|
+
# The filter class from which all others derive.
|
39
|
+
class Abstract
|
40
|
+
|
41
|
+
# Combine two filters with a logical AND.
|
42
|
+
def &(other)
|
43
|
+
And.new(self, other)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Combine two filters with a logical OR.
|
47
|
+
def |(other)
|
48
|
+
Or.new(self, other)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Negate a filter.
|
52
|
+
#
|
53
|
+
# ~Ldaptic::Filter("(a=1)").to_s # => "(!(a=1))"
|
54
|
+
def ~
|
55
|
+
Not.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Generates the filter as a string.
|
59
|
+
def to_s
|
60
|
+
process || "(objectClass=*)"
|
61
|
+
end
|
62
|
+
|
63
|
+
alias to_str to_s
|
64
|
+
|
65
|
+
def inspect
|
66
|
+
if string = process
|
67
|
+
"#<#{Ldaptic::Filter.inspect} #{string}>"
|
68
|
+
else
|
69
|
+
"#<#{Ldaptic::Filter.inspect} invalid>"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_net_ldap_filter #:nodoc:
|
74
|
+
Net::LDAP::Filter.construct(process)
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_ber #:nodoc:
|
78
|
+
to_net_ldap_filter.to_ber
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
module Spawner # :nodoc:
|
84
|
+
def self.method_missing(method)
|
85
|
+
Attribute.new(method)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class Attribute < Abstract
|
90
|
+
def initialize(name)
|
91
|
+
if name.kind_of?(Symbol)
|
92
|
+
name = name.to_s.tr('_-', '-_')
|
93
|
+
end
|
94
|
+
@name = name
|
95
|
+
end
|
96
|
+
%w(== =~ >= <=).each do |method|
|
97
|
+
define_method(method) do |other|
|
98
|
+
Pair.new(@name, other, method)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
def process
|
102
|
+
"(#{@name}=*)"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# This class is used for raw LDAP queries. Note that the outermost set of
|
107
|
+
# parentheses *must* be used.
|
108
|
+
#
|
109
|
+
# Ldaptic::Filter("a=1") # Wrong
|
110
|
+
# Ldaptic::Filter("(a=1)") # Correct
|
111
|
+
class String < Abstract
|
112
|
+
|
113
|
+
def initialize(string) #:nodoc:
|
114
|
+
@string = string
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the original string
|
118
|
+
def process
|
119
|
+
@string
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
# Does ? parameter substitution.
|
125
|
+
#
|
126
|
+
# Ldaptic::Filter(["(cn=?*)", "Sm"]).to_s #=> "(cn=Sm*)"
|
127
|
+
class Array < Abstract
|
128
|
+
def initialize(array) #:nodoc:
|
129
|
+
@template = array.first
|
130
|
+
@parameters = array[1..-1]
|
131
|
+
end
|
132
|
+
def process
|
133
|
+
parameters = @parameters.dup
|
134
|
+
string = @template.gsub('?') { Ldaptic.escape(parameters.pop) }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Used in the implementation of Ldaptic::Filter::And and
|
139
|
+
# Ldaptic::Filter::Or. For internal use only.
|
140
|
+
class Join < Abstract
|
141
|
+
def initialize(operator, *args) #:nodoc:
|
142
|
+
@array = [operator] + args.map {|arg| Ldaptic::Filter(arg)}
|
143
|
+
end
|
144
|
+
def process
|
145
|
+
"(#{@array*''})" if @array.compact.size > 1
|
146
|
+
end
|
147
|
+
def to_net_ldap_filter #:nodoc
|
148
|
+
@array[1..-1].inject {|m, o| m.to_net_ldap_filter.send(@array.first, o.to_net_ldap_filter)}
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class And < Join
|
153
|
+
def initialize(*args)
|
154
|
+
super(:&, *args)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
class Or < Join
|
159
|
+
def initialize(*args)
|
160
|
+
super(:|, *args)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
class Not < Abstract
|
165
|
+
def initialize(object)
|
166
|
+
@object = Ldaptic::Filter(object)
|
167
|
+
end
|
168
|
+
def process
|
169
|
+
process = @object.process and "(!#{process})"
|
170
|
+
end
|
171
|
+
def to_net_ldap_filter #:nodoc:
|
172
|
+
~ @object.to_net_ldap_filter
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# A hash is the most general and most useful type of filter builder.
|
177
|
+
#
|
178
|
+
# Ldaptic::Filter(
|
179
|
+
# :givenName => "David",
|
180
|
+
# :sn! => "Thomas",
|
181
|
+
# :postalCode => (70000..80000)
|
182
|
+
# ).to_s # => "(&(givenName=David)(&(postalCode>=70000)(postalCode<=80000))(!(sn=Thomas)))"
|
183
|
+
#
|
184
|
+
# Including :* => true allows asterisks to pass through unaltered.
|
185
|
+
# Otherwise, they are escaped.
|
186
|
+
#
|
187
|
+
# Ldaptic::Filter(:givenName => "Dav*", :* => true).to_s # => "(givenName=Dav*)"
|
188
|
+
class Hash < Abstract
|
189
|
+
|
190
|
+
attr_accessor :escape_asterisks
|
191
|
+
attr_reader :hash
|
192
|
+
# Call Ldaptic::Filter(hash) instead of instantiating this class
|
193
|
+
# directly.
|
194
|
+
def initialize(hash)
|
195
|
+
@hash = hash.dup
|
196
|
+
@escape_asterisks = !@hash.delete(:*)
|
197
|
+
end
|
198
|
+
|
199
|
+
def process
|
200
|
+
string = @hash.map {|k, v| [k.to_s, v]}.sort.map do |(k, v)|
|
201
|
+
Pair.new(k, v, @escape_asterisks ? "==" : "=~").process
|
202
|
+
end.join
|
203
|
+
case @hash.size
|
204
|
+
when 0 then nil
|
205
|
+
when 1 then string
|
206
|
+
else "(&#{string})"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# Internal class used to process a single entry from a hash.
|
212
|
+
class Pair < Abstract
|
213
|
+
INVERSE_OPERATORS = {
|
214
|
+
"!=" => "==",
|
215
|
+
"!~" => "=~",
|
216
|
+
">" => "<=",
|
217
|
+
"<" => ">="
|
218
|
+
}
|
219
|
+
def initialize(key, value, operator)
|
220
|
+
@key, @value, @operator = key.to_s.dup, value, operator.to_s
|
221
|
+
@inverse = !!@key.sub!(/!$/, '')
|
222
|
+
if op = INVERSE_OPERATORS[@operator]
|
223
|
+
@inverse ^= true
|
224
|
+
@operator = op
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def process
|
229
|
+
k = @key
|
230
|
+
v = @value
|
231
|
+
if @operator == "=~"
|
232
|
+
operator = "=="
|
233
|
+
star = true
|
234
|
+
else
|
235
|
+
operator = @operator
|
236
|
+
star = false
|
237
|
+
end
|
238
|
+
inverse = @inverse
|
239
|
+
operator = "=" if operator == "=="
|
240
|
+
if v.respond_to?(:to_ary)
|
241
|
+
q = "(|" + v.map {|e| "(#{Ldaptic.encode(k)}=#{Ldaptic.escape(e, star)})"}.join + ")"
|
242
|
+
elsif v.kind_of?(Range)
|
243
|
+
q = []
|
244
|
+
if v.first != -1.0/0
|
245
|
+
q << "(#{Ldaptic.encode(k)}>=#{Ldaptic.escape(v.first, star)})"
|
246
|
+
end
|
247
|
+
if v.last != 1.0/0
|
248
|
+
if v.exclude_end?
|
249
|
+
q << "(!(#{Ldaptic.encode(k)}>=#{Ldaptic.escape(v.last, star)}))"
|
250
|
+
else
|
251
|
+
q << "(#{Ldaptic.encode(k)}<=#{Ldaptic.escape(v.last, star)})"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
q = "(&#{q*""})"
|
255
|
+
elsif v == true || v == :*
|
256
|
+
q = "(#{Ldaptic.encode(k)}=*)"
|
257
|
+
elsif !v
|
258
|
+
q = "(#{Ldaptic.encode(k)}=*)"
|
259
|
+
inverse ^= true
|
260
|
+
else
|
261
|
+
q = "(#{Ldaptic.encode(k)}#{operator}#{Ldaptic.escape(v, star)})"
|
262
|
+
end
|
263
|
+
inverse ? "(!#{q})" : q
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
module Conversions #:nodoc:
|
268
|
+
def to_ldap_filter
|
269
|
+
Ldaptic::Filter(self)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
class Hash
|
278
|
+
include Ldaptic::Filter::Conversions
|
279
|
+
end
|
280
|
+
class String
|
281
|
+
include Ldaptic::Filter::Conversions
|
282
|
+
end
|