ruby-ldapserver 0.5.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +3 -0
- data/.github/workflows/ci.yml +43 -0
- data/.gitignore +1 -0
- data/{ChangeLog → CHANGELOG.md} +28 -13
- data/README.md +141 -0
- data/examples/rbslapd1.rb +5 -4
- data/examples/rbslapd2.rb +1 -1
- data/examples/rbslapd3.rb +4 -4
- data/examples/rbslapd4.rb +90 -0
- data/examples/rbslapd5.rb +73 -0
- data/examples/rbslapd6.rb +75 -0
- data/lib/ldap/server/connection.rb +16 -10
- data/lib/ldap/server/dn.rb +220 -0
- data/lib/ldap/server/filter.rb +1 -1
- data/lib/ldap/server/operation.rb +47 -9
- data/lib/ldap/server/request.rb +166 -0
- data/lib/ldap/server/result.rb +1 -1
- data/lib/ldap/server/router.rb +220 -0
- data/lib/ldap/server/server.rb +25 -10
- data/lib/ldap/server/syntax.rb +1 -1
- data/lib/ldap/server/tcpserver.rb +16 -3
- data/lib/ldap/server/trie.rb +92 -0
- data/lib/ldap/server/version.rb +1 -1
- data/ruby-ldapserver.gemspec +9 -9
- data/test/dn_test.rb +149 -0
- data/test/encoding_test.rb +142 -179
- data/test/trie_test.rb +60 -0
- data.tar.gz.sig +2 -0
- metadata +84 -22
- metadata.gz.sig +0 -0
- data/README +0 -222
@@ -31,11 +31,11 @@ class Server
|
|
31
31
|
def log(msg, severity = Logger::INFO)
|
32
32
|
@logger.add(severity, msg, @io.peeraddr[3])
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def debug msg
|
36
36
|
log msg, Logger::DEBUG
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
def log_exception(e)
|
40
40
|
log "#{e}: #{e.backtrace.join("\n\tfrom ")}", Logger::ERROR
|
41
41
|
end
|
@@ -93,8 +93,6 @@ class Server
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def handle_requests
|
96
|
-
operationClass = @opt[:operation_class]
|
97
|
-
ocArgs = @opt[:operation_args] || []
|
98
96
|
catch(:close) do
|
99
97
|
while true
|
100
98
|
begin
|
@@ -120,9 +118,14 @@ class Server
|
|
120
118
|
case protocolOp.tag
|
121
119
|
when 0 # BindRequest
|
122
120
|
abandon_all
|
123
|
-
|
124
|
-
|
125
|
-
|
121
|
+
if @opt[:router]
|
122
|
+
@binddn, @version = @opt[:router].do_bind(self, messageId, protocolOp, controls)
|
123
|
+
else
|
124
|
+
operationClass = @opt[:operation_class]
|
125
|
+
ocArgs = @opt[:operation_args] || []
|
126
|
+
@binddn, @version = operationClass.new(self,messageId,*ocArgs).
|
127
|
+
do_bind(protocolOp, controls)
|
128
|
+
end
|
126
129
|
when 2 # UnbindRequest
|
127
130
|
throw(:close)
|
128
131
|
|
@@ -171,14 +174,17 @@ class Server
|
|
171
174
|
# client sends an overlapping request with same message ID,
|
172
175
|
# so we don't have to worry about the case where there is
|
173
176
|
# already a thread with this messageId in @threadgroup.
|
174
|
-
|
175
177
|
def start_op(messageId,protocolOp,controls,meth)
|
176
178
|
operationClass = @opt[:operation_class]
|
177
179
|
ocArgs = @opt[:operation_args] || []
|
178
180
|
thr = Thread.new do
|
179
181
|
begin
|
180
|
-
|
181
|
-
|
182
|
+
if @opt[:router]
|
183
|
+
@opt[:router].send meth, self, messageId, protocolOp, controls
|
184
|
+
else
|
185
|
+
operationClass.new(self,messageId,*ocArgs).
|
186
|
+
send(meth,protocolOp,controls)
|
187
|
+
end
|
182
188
|
rescue Exception => e
|
183
189
|
log_exception e
|
184
190
|
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'ldap/server/util'
|
2
|
+
|
3
|
+
module LDAP
|
4
|
+
class Server
|
5
|
+
|
6
|
+
class DN
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :dname
|
10
|
+
|
11
|
+
# Combines a set of elements to a syntactically correct DN
|
12
|
+
# elements is [elements, ...] where elements
|
13
|
+
# can be either { attr => val } or [attr, val]
|
14
|
+
def self.join(elements)
|
15
|
+
LDAP::Server::Operation.join_dn(elements)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(dn)
|
19
|
+
@dname = LDAP::Server::Operation.split_dn(dn)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the value of the first occurrence of attr (bottom-up)
|
23
|
+
def find_first(attr)
|
24
|
+
@dname.each do |pair|
|
25
|
+
return pair[attr.to_s] if pair[attr.to_s]
|
26
|
+
end
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the value of the last occurrence of attr (bottom-up)
|
31
|
+
def find_last(attr)
|
32
|
+
@dname.reverse_each do |pair|
|
33
|
+
return pair[attr.to_s] if pair[attr.to_s]
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns all values of all occurrences of attr (bottom-up)
|
39
|
+
def find(attr)
|
40
|
+
result = []
|
41
|
+
@dname.each do |pair|
|
42
|
+
result << pair[attr.to_s] if pair[attr.to_s]
|
43
|
+
end
|
44
|
+
result
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the value of the n-th occurrence of attr (top-down, 0 is first element)
|
48
|
+
def find_nth(attr, n)
|
49
|
+
i = 0
|
50
|
+
@dname.each do |pair|
|
51
|
+
if pair[attr.to_s]
|
52
|
+
return pair[attr.to_s] if i == n
|
53
|
+
i += 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# Whether or not the DN starts with dn (bottom-up)
|
60
|
+
# dn is a string
|
61
|
+
def start_with?(dn)
|
62
|
+
needle = LDAP::Server::Operation.split_dn(dn)
|
63
|
+
|
64
|
+
# Needle is longer than haystack
|
65
|
+
return false if needle.length > @dname.length
|
66
|
+
|
67
|
+
needle_index = 0
|
68
|
+
haystack_index = 0
|
69
|
+
|
70
|
+
while needle_index < needle.length
|
71
|
+
return false if @dname[haystack_index] != needle[needle_index]
|
72
|
+
needle_index += 1
|
73
|
+
haystack_index += 1
|
74
|
+
end
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
# Whether or not the DN starts with a format (bottom-up) (values are ignored)
|
79
|
+
# dn is a string
|
80
|
+
def start_with_format?(dn)
|
81
|
+
needle = LDAP::Server::Operation.split_dn(dn)
|
82
|
+
|
83
|
+
# Needle is longer than haystack
|
84
|
+
return false if needle.length > @dname.length
|
85
|
+
|
86
|
+
needle_index = 0
|
87
|
+
haystack_index = 0
|
88
|
+
|
89
|
+
while needle_index < needle.length
|
90
|
+
return false if @dname[haystack_index].keys != needle[needle_index].keys
|
91
|
+
needle_index += 1
|
92
|
+
haystack_index += 1
|
93
|
+
end
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Whether or not the DN ends with dn (top-down)
|
98
|
+
# dn is a string
|
99
|
+
def end_with?(dn)
|
100
|
+
needle = LDAP::Server::Operation.split_dn(dn)
|
101
|
+
|
102
|
+
# Needle is longer than haystack
|
103
|
+
return false if needle.length > @dname.length
|
104
|
+
|
105
|
+
needle_index = needle.length - 1
|
106
|
+
haystack_index = @dname.length - 1
|
107
|
+
|
108
|
+
while needle_index >= 0
|
109
|
+
return false if @dname[haystack_index] != needle[needle_index]
|
110
|
+
needle_index -= 1
|
111
|
+
haystack_index -= 1
|
112
|
+
end
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
# Whether or not the DN ends with format (top-down) (values are ignored)
|
117
|
+
# dn is a string
|
118
|
+
def end_with_format?(dn)
|
119
|
+
needle = LDAP::Server::Operation.split_dn(dn)
|
120
|
+
|
121
|
+
# Needle is longer than haystack
|
122
|
+
return false if needle.length > @dname.length
|
123
|
+
|
124
|
+
needle_index = needle.length - 1
|
125
|
+
haystack_index = @dname.length - 1
|
126
|
+
|
127
|
+
while needle_index >= 0
|
128
|
+
return false if @dname[haystack_index].keys != needle[needle_index].keys
|
129
|
+
needle_index -= 1
|
130
|
+
haystack_index -= 1
|
131
|
+
end
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Whether or not the DN equals dn (values are case sensitive)
|
136
|
+
# dn is a string
|
137
|
+
def equal?(dn)
|
138
|
+
split_dn = LDAP::Server::Operation.split_dn(dn)
|
139
|
+
|
140
|
+
return false if split_dn.length != @dname.length
|
141
|
+
|
142
|
+
@dname.each_with_index do |pair, index|
|
143
|
+
return false if pair != split_dn[index]
|
144
|
+
end
|
145
|
+
true
|
146
|
+
end
|
147
|
+
|
148
|
+
# Whether or not the DN equals dn's format (values are ignored) (case insensitive)
|
149
|
+
# dn is a string
|
150
|
+
def equal_format?(dn)
|
151
|
+
split_dn = LDAP::Server::Operation.split_dn(dn)
|
152
|
+
|
153
|
+
return false if split_dn.length != @dname.length
|
154
|
+
|
155
|
+
@dname.each_with_index do |pair, index|
|
156
|
+
return false if pair.keys != split_dn[index].keys
|
157
|
+
end
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
# Whether or not the DN constains a substring equal to dn (values are case sensitive)
|
162
|
+
# dn is a string
|
163
|
+
def include?(dn)
|
164
|
+
split_dn = LDAP::Server::Operation.split_dn(dn)
|
165
|
+
return false if split_dn.length > @dname.length
|
166
|
+
LDAP::Server::Operation.join_dn(@dname).include?(LDAP::Server::Operation.join_dn(split_dn))
|
167
|
+
end
|
168
|
+
|
169
|
+
# Whether or not the DN constains a substring format equal to dn (values are ignored) (case insensitive)
|
170
|
+
# dn is a string
|
171
|
+
def include_format?(dn)
|
172
|
+
split_dn = LDAP::Server::Operation.split_dn(dn)
|
173
|
+
|
174
|
+
return false if split_dn.length > @dname.length
|
175
|
+
|
176
|
+
haystack = []
|
177
|
+
@dname.each { |pair| haystack << pair.keys }
|
178
|
+
|
179
|
+
needle = []
|
180
|
+
split_dn.each { |pair| needle << pair.keys }
|
181
|
+
|
182
|
+
haystack.join.include?(needle.join)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Generates a mapping for variables
|
186
|
+
# For example:
|
187
|
+
# > dn = LDAP::Server.DN.new("uid=user,ou=Users,dc=mydomain,dc=com")
|
188
|
+
# > dn.parse("uid=:uid, ou=:category, dc=mydomain, dc=com")
|
189
|
+
# => { :uid => "user", :category => "Users" }
|
190
|
+
def parse(template_dn)
|
191
|
+
result = {}
|
192
|
+
template = LDAP::Server::Operation.split_dn(template_dn)
|
193
|
+
template.reverse.zip(@dname.reverse).each do |temp, const|
|
194
|
+
break if const and temp.keys.first != const.keys.first
|
195
|
+
if temp.values.first.start_with?(':')
|
196
|
+
sym = temp.values.first[1..-1].to_sym
|
197
|
+
if const
|
198
|
+
result[sym] = const.values.first unless result[sym]
|
199
|
+
else
|
200
|
+
result[sym] = nil
|
201
|
+
end
|
202
|
+
elsif temp.values.first != const.values.first
|
203
|
+
break
|
204
|
+
end
|
205
|
+
end
|
206
|
+
result
|
207
|
+
end
|
208
|
+
|
209
|
+
def each(&block)
|
210
|
+
@dname.each(&block)
|
211
|
+
end
|
212
|
+
|
213
|
+
def reverse_each(&block)
|
214
|
+
@dname.reverse_each(&block)
|
215
|
+
end
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
end
|
220
|
+
end
|
data/lib/ldap/server/filter.rb
CHANGED
@@ -202,7 +202,7 @@ class Server
|
|
202
202
|
when :eq, :approx, :le, :ge, :substrings
|
203
203
|
# the filter now includes a suitable matching object
|
204
204
|
return (filter[2] || LDAP::Server::MatchingRule::DefaultMatch).send(
|
205
|
-
filter.first, av[filter[1].to_s], *filter[3..-1])
|
205
|
+
filter.first, Array(av[filter[1].to_s]), *filter[3..-1])
|
206
206
|
|
207
207
|
when :true
|
208
208
|
return true
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'timeout'
|
2
|
+
require 'openssl'
|
2
3
|
require 'ldap/server/result'
|
3
4
|
require 'ldap/server/filter'
|
4
5
|
|
@@ -36,6 +37,7 @@ class Server
|
|
36
37
|
])
|
37
38
|
@schema = @connection.opt[:schema]
|
38
39
|
@server = @connection.opt[:server]
|
40
|
+
@attribute_range_limit = @connection.opt[:attribute_range_limit]
|
39
41
|
end
|
40
42
|
|
41
43
|
def log msg, severity = Logger::INFO
|
@@ -45,9 +47,9 @@ class Server
|
|
45
47
|
def debug msg
|
46
48
|
@connection.debug msg
|
47
49
|
end
|
48
|
-
|
50
|
+
|
49
51
|
# Send an exception report to the log
|
50
|
-
|
52
|
+
|
51
53
|
def log_exception msg
|
52
54
|
@connection.log_exception msg
|
53
55
|
end
|
@@ -84,7 +86,7 @@ class Server
|
|
84
86
|
seq << OpenSSL::ASN1::Sequence(rs, 3, :IMPLICIT, :APPLICATION)
|
85
87
|
end
|
86
88
|
yield seq if block_given? # opportunity to add more elements
|
87
|
-
|
89
|
+
|
88
90
|
send_LDAPMessage(OpenSSL::ASN1::Sequence(seq, tag, :IMPLICIT, :APPLICATION), opt)
|
89
91
|
end
|
90
92
|
|
@@ -96,6 +98,9 @@ class Server
|
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
101
|
+
|
102
|
+
AttributeRange = Struct.new :start, :end
|
103
|
+
|
99
104
|
# Send a found entry. Avs are {attr1=>val1, attr2=>[val2,val3]}
|
100
105
|
# If schema given, return operational attributes only if
|
101
106
|
# explicitly requested
|
@@ -114,23 +119,43 @@ class Server
|
|
114
119
|
sendall = @attributes == [] || @attributes.include?("*")
|
115
120
|
avseq = []
|
116
121
|
|
117
|
-
avs.
|
118
|
-
|
122
|
+
avs.each_with_index do |(attr, vals), aidx|
|
123
|
+
query_attr_idx = @attributes.index(attr)
|
124
|
+
if !query_attr_idx
|
119
125
|
next unless sendall
|
120
126
|
if @schema
|
121
127
|
a = @schema.find_attrtype(attr)
|
122
128
|
next unless a and (a.usage.nil? or a.usage == :userApplications)
|
123
129
|
end
|
124
130
|
end
|
131
|
+
query_attr = query_attr_idx && @attribute_ranges[query_attr_idx]
|
125
132
|
|
126
133
|
if @typesOnly
|
127
|
-
vals = []
|
134
|
+
vals = []
|
128
135
|
else
|
129
136
|
vals = [vals] unless vals.kind_of?(Array)
|
130
137
|
# FIXME: optionally do a value_to_s conversion here?
|
131
138
|
# FIXME: handle attribute;binary
|
132
139
|
end
|
133
140
|
|
141
|
+
if (@attribute_range_limit && vals.size > @attribute_range_limit) || query_attr&.start
|
142
|
+
if query_attr&.start
|
143
|
+
range_start = query_attr.start.to_i
|
144
|
+
range_end = query_attr.end == "*" ? -1 : query_attr.end.to_i
|
145
|
+
else
|
146
|
+
range_start = 0
|
147
|
+
range_end = @attribute_range_limit ? @attribute_range_limit - 1 : -1
|
148
|
+
end
|
149
|
+
range_end = range_start + @attribute_range_limit - 1 if @attribute_range_limit && (vals.size - range_start > @attribute_range_limit)
|
150
|
+
range_end = -1 if vals.size <= range_end
|
151
|
+
rvals = vals[range_start .. range_end]
|
152
|
+
vals = []
|
153
|
+
avseq << OpenSSL::ASN1::Sequence([
|
154
|
+
OpenSSL::ASN1::OctetString("#{attr};range=#{range_start}-#{range_end == -1 ? "*" : range_end}"),
|
155
|
+
OpenSSL::ASN1::Set(rvals.collect { |v| OpenSSL::ASN1::OctetString(v.to_s) })
|
156
|
+
])
|
157
|
+
end
|
158
|
+
|
134
159
|
avseq << OpenSSL::ASN1::Sequence([
|
135
160
|
OpenSSL::ASN1::OctetString(attr),
|
136
161
|
OpenSSL::ASN1::Set(vals.collect { |v| OpenSSL::ASN1::OctetString(v.to_s) })
|
@@ -200,8 +225,8 @@ class Server
|
|
200
225
|
when 0
|
201
226
|
simple_bind(version, dn, authentication.value)
|
202
227
|
when 3
|
203
|
-
mechanism = authentication.value[0].value
|
204
|
-
credentials = authentication.value[1].value
|
228
|
+
# mechanism = authentication.value[0].value
|
229
|
+
# credentials = authentication.value[1].value
|
205
230
|
# sasl_bind(version, dn, mechanism, credentials)
|
206
231
|
# FIXME: needs to exchange further BindRequests
|
207
232
|
raise LDAP::ResultError::AuthMethodNotSupported
|
@@ -246,7 +271,20 @@ class Server
|
|
246
271
|
client_timelimit = protocolOp.value[4].value.to_i
|
247
272
|
@typesOnly = protocolOp.value[5].value
|
248
273
|
filter = Filter::parse(protocolOp.value[6], @schema)
|
249
|
-
|
274
|
+
attributes = protocolOp.value[7].value.collect {|x| x.value}
|
275
|
+
attributes = attributes.map do |attr|
|
276
|
+
if attr =~ /(.*);range=(\d+)-(\d+|\*)\z/
|
277
|
+
[$1, $2, $3]
|
278
|
+
else
|
279
|
+
attr
|
280
|
+
end
|
281
|
+
end
|
282
|
+
@attributes = attributes.map do |name, |
|
283
|
+
name
|
284
|
+
end
|
285
|
+
@attribute_ranges = attributes.map do |_, range_start, range_end|
|
286
|
+
range_start && AttributeRange.new(range_start, range_end)
|
287
|
+
end
|
250
288
|
|
251
289
|
@rescount = 0
|
252
290
|
@sizelimit = server_sizelimit
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module LDAP
|
4
|
+
class Server
|
5
|
+
|
6
|
+
class Request
|
7
|
+
attr_accessor :connection, :typesOnly, :attributes, :rescount, :sizelimit
|
8
|
+
|
9
|
+
# Object to handle a single LDAP request. This object is created on
|
10
|
+
# every request by the router, and is passed as argument to the defined
|
11
|
+
# routes.
|
12
|
+
|
13
|
+
def initialize(connection, messageId)
|
14
|
+
@connection = connection
|
15
|
+
@respEnvelope = OpenSSL::ASN1::Sequence([
|
16
|
+
OpenSSL::ASN1::Integer(messageId),
|
17
|
+
# protocolOp,
|
18
|
+
# controls [0] OPTIONAL,
|
19
|
+
])
|
20
|
+
@schema = @connection.opt[:schema]
|
21
|
+
@server = @connection.opt[:server]
|
22
|
+
@rescount = 0
|
23
|
+
end
|
24
|
+
|
25
|
+
##################################################
|
26
|
+
### Utility methods to send protocol responses ###
|
27
|
+
##################################################
|
28
|
+
|
29
|
+
def send_LDAPMessage(protocolOp, opt={}) # :nodoc:
|
30
|
+
@respEnvelope.value[1] = protocolOp
|
31
|
+
if opt[:controls]
|
32
|
+
@respEnvelope.value[2] = OpenSSL::ASN1::Set(opt[:controls], 0, :IMPLICIT, APPLICATION)
|
33
|
+
else
|
34
|
+
@respEnvelope.value.delete_at(2)
|
35
|
+
end
|
36
|
+
|
37
|
+
@connection.write(@respEnvelope.to_der)
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_LDAPResult(tag, resultCode, opt={}) # :nodoc:
|
41
|
+
seq = [
|
42
|
+
OpenSSL::ASN1::Enumerated(resultCode),
|
43
|
+
OpenSSL::ASN1::OctetString(opt[:matchedDN] || ""),
|
44
|
+
OpenSSL::ASN1::OctetString(opt[:errorMessage] || ""),
|
45
|
+
]
|
46
|
+
if opt[:referral]
|
47
|
+
rs = opt[:referral].collect { |r| OpenSSL::ASN1::OctetString(r) }
|
48
|
+
seq << OpenSSL::ASN1::Sequence(rs, 3, :IMPLICIT, :APPLICATION)
|
49
|
+
end
|
50
|
+
yield seq if block_given? # opportunity to add more elements
|
51
|
+
|
52
|
+
send_LDAPMessage(OpenSSL::ASN1::Sequence(seq, tag, :IMPLICIT, :APPLICATION), opt)
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_BindResponse(resultCode, opt={})
|
56
|
+
send_LDAPResult(1, resultCode, opt) do |resp|
|
57
|
+
if opt[:serverSaslCreds]
|
58
|
+
resp << OpenSSL::ASN1::OctetString(opt[:serverSaslCreds], 7, :IMPLICIT, :APPLICATION)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Send a found entry. Avs are {attr1=>val1, attr2=>[val2,val3]}
|
64
|
+
# If schema given, return operational attributes only if
|
65
|
+
# explicitly requested
|
66
|
+
|
67
|
+
def send_SearchResultEntry(dn, avs, opt={})
|
68
|
+
@rescount += 1
|
69
|
+
if @sizelimit
|
70
|
+
raise LDAP::ResultError::SizeLimitExceeded if @rescount > @sizelimit
|
71
|
+
end
|
72
|
+
|
73
|
+
if @schema
|
74
|
+
# normalize the attribute names
|
75
|
+
@attributes = @attributes.map { |a| a == '*' ? a : @schema.find_attrtype(a).to_s }
|
76
|
+
end
|
77
|
+
|
78
|
+
sendall = @attributes == [] || @attributes.include?("*")
|
79
|
+
avseq = []
|
80
|
+
|
81
|
+
avs.each do |attr, vals|
|
82
|
+
if !@attributes.include?(attr)
|
83
|
+
next unless sendall
|
84
|
+
if @schema
|
85
|
+
a = @schema.find_attrtype(attr)
|
86
|
+
next unless a and (a.usage.nil? or a.usage == :userApplications)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if @typesOnly
|
91
|
+
vals = []
|
92
|
+
else
|
93
|
+
vals = [vals] unless vals.kind_of?(Array)
|
94
|
+
# FIXME: optionally do a value_to_s conversion here?
|
95
|
+
# FIXME: handle attribute;binary
|
96
|
+
end
|
97
|
+
|
98
|
+
avseq << OpenSSL::ASN1::Sequence([
|
99
|
+
OpenSSL::ASN1::OctetString(attr),
|
100
|
+
OpenSSL::ASN1::Set(vals.collect { |v| OpenSSL::ASN1::OctetString(v.to_s) })
|
101
|
+
])
|
102
|
+
end
|
103
|
+
|
104
|
+
send_LDAPMessage(OpenSSL::ASN1::Sequence([
|
105
|
+
OpenSSL::ASN1::OctetString(dn),
|
106
|
+
OpenSSL::ASN1::Sequence(avseq),
|
107
|
+
], 4, :IMPLICIT, :APPLICATION), opt)
|
108
|
+
end
|
109
|
+
|
110
|
+
def send_SearchResultDone(resultCode, opt={})
|
111
|
+
send_LDAPResult(5, resultCode, opt)
|
112
|
+
end
|
113
|
+
|
114
|
+
def send_ModifyResponse(resultCode, opt={})
|
115
|
+
send_LDAPResult(7, resultCode, opt)
|
116
|
+
end
|
117
|
+
|
118
|
+
def send_AddResponse(resultCode, opt={})
|
119
|
+
send_LDAPResult(9, resultCode, opt)
|
120
|
+
end
|
121
|
+
|
122
|
+
def send_DelResponse(resultCode, opt={})
|
123
|
+
send_LDAPResult(11, resultCode, opt)
|
124
|
+
end
|
125
|
+
|
126
|
+
def send_ModifyDNResponse(resultCode, opt={})
|
127
|
+
send_LDAPResult(13, resultCode, opt)
|
128
|
+
end
|
129
|
+
|
130
|
+
def send_CompareResponse(resultCode, opt={})
|
131
|
+
send_LDAPResult(15, resultCode, opt)
|
132
|
+
end
|
133
|
+
|
134
|
+
def send_ExtendedResponse(resultCode, opt={})
|
135
|
+
send_LDAPResult(24, resultCode, opt) do |resp|
|
136
|
+
if opt[:responseName]
|
137
|
+
resp << OpenSSL::ASN1::OctetString(opt[:responseName], 10, :IMPLICIT, :APPLICATION)
|
138
|
+
end
|
139
|
+
if opt[:response]
|
140
|
+
resp << OpenSSL::ASN1::OctetString(opt[:response], 11, :IMPLICIT, :APPLICATION)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
############################################################
|
146
|
+
### Methods to get parameters related to this connection ###
|
147
|
+
############################################################
|
148
|
+
|
149
|
+
# Server-set maximum time limit. Override for more complex behaviour
|
150
|
+
# (e.g. limit depends on @connection.binddn). Nil uses hardcoded default.
|
151
|
+
|
152
|
+
def server_timelimit
|
153
|
+
@connection.opt[:timelimit]
|
154
|
+
end
|
155
|
+
|
156
|
+
# Server-set maximum size limit. Override for more complex behaviour
|
157
|
+
# (e.g. limit depends on @connection.binddn). Return nil for unlimited.
|
158
|
+
|
159
|
+
def server_sizelimit
|
160
|
+
@connection.opt[:sizelimit]
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|