fakeldap 0.0.1 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +7 -1
- data/lib/fakeldap.rb +133 -10
- data/lib/fakeldap/version.rb +1 -1
- metadata +50 -191
- data/vendor/ruby-ldapserver/COPYING +0 -27
- data/vendor/ruby-ldapserver/ChangeLog +0 -83
- data/vendor/ruby-ldapserver/Manifest.txt +0 -32
- data/vendor/ruby-ldapserver/README +0 -222
- data/vendor/ruby-ldapserver/Rakefile +0 -22
- data/vendor/ruby-ldapserver/doc/LDAP.html +0 -104
- data/vendor/ruby-ldapserver/doc/LDAP/Abandon.html +0 -112
- data/vendor/ruby-ldapserver/doc/LDAP/Error.html +0 -115
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError.html +0 -241
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AdminLimitExceeded.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AffectsMultipleDSAs.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AliasDereferencingProblem.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AliasProblem.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AttributeOrValueExists.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/AuthMethodNotSupported.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/Busy.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/CompareFalse.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/CompareTrue.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/ConfidentialityRequired.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/ConstraintViolation.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/EntryAlreadyExists.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InappropriateAuthentication.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InappropriateMatching.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InsufficientAccessRights.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InvalidAttributeSyntax.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InvalidCredentials.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/InvalidDNSyntax.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/IsLeaf.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/LoopDetect.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/NamingViolation.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/NoSuchAttribute.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/NoSuchObject.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/NotAllowedOnNonLeaf.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/NotAllowedOnRDN.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/ObjectClassModsProhibited.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/ObjectClassViolation.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/OperationsError.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/Other.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/ProtocolError.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/Referral.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/SaslBindInProgress.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/SizeLimitExceeded.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/StrongAuthRequired.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/Success.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/TimeLimitExceeded.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/Unavailable.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/UnavailableCriticalExtension.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/UndefinedAttributeType.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/ResultError/UnwillingToPerform.html +0 -158
- data/vendor/ruby-ldapserver/doc/LDAP/Server.html +0 -1056
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Connection.html +0 -1353
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Filter.html +0 -634
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule.html +0 -1132
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/DefaultMatchingClass.html +0 -219
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/Equality.html +0 -170
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/IA5Downcase.html +0 -143
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/IA5Trim.html +0 -155
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/Integer.html +0 -143
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/Ordering.html +0 -212
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/StringDowncase.html +0 -143
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/StringTrim.html +0 -154
- data/vendor/ruby-ldapserver/doc/LDAP/Server/MatchingRule/Substrings.html +0 -177
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Operation.html +0 -2994
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Schema.html +0 -2024
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Schema/AttributeType.html +0 -1462
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Schema/ObjectClass.html +0 -1097
- data/vendor/ruby-ldapserver/doc/LDAP/Server/Syntax.html +0 -1254
- data/vendor/ruby-ldapserver/doc/LDAP/Server/VERSION.html +0 -134
- data/vendor/ruby-ldapserver/doc/_index.html +0 -662
- data/vendor/ruby-ldapserver/doc/class_list.html +0 -36
- data/vendor/ruby-ldapserver/doc/css/common.css +0 -1
- data/vendor/ruby-ldapserver/doc/css/full_list.css +0 -50
- data/vendor/ruby-ldapserver/doc/css/style.css +0 -303
- data/vendor/ruby-ldapserver/doc/file.README.html +0 -399
- data/vendor/ruby-ldapserver/doc/file_list.html +0 -38
- data/vendor/ruby-ldapserver/doc/frames.html +0 -13
- data/vendor/ruby-ldapserver/doc/index.html +0 -399
- data/vendor/ruby-ldapserver/doc/js/app.js +0 -204
- data/vendor/ruby-ldapserver/doc/js/full_list.js +0 -112
- data/vendor/ruby-ldapserver/doc/js/jquery.js +0 -154
- data/vendor/ruby-ldapserver/doc/method_list.html +0 -1571
- data/vendor/ruby-ldapserver/doc/top-level-namespace.html +0 -88
- data/vendor/ruby-ldapserver/examples/README +0 -89
- data/vendor/ruby-ldapserver/examples/mkcert.rb +0 -31
- data/vendor/ruby-ldapserver/examples/rbslapd1.rb +0 -111
- data/vendor/ruby-ldapserver/examples/rbslapd2.rb +0 -161
- data/vendor/ruby-ldapserver/examples/rbslapd3.rb +0 -172
- data/vendor/ruby-ldapserver/examples/speedtest.rb +0 -37
- data/vendor/ruby-ldapserver/lib/ldap/server.rb +0 -4
- data/vendor/ruby-ldapserver/lib/ldap/server/connection.rb +0 -276
- data/vendor/ruby-ldapserver/lib/ldap/server/filter.rb +0 -223
- data/vendor/ruby-ldapserver/lib/ldap/server/match.rb +0 -283
- data/vendor/ruby-ldapserver/lib/ldap/server/operation.rb +0 -487
- data/vendor/ruby-ldapserver/lib/ldap/server/preforkserver.rb +0 -93
- data/vendor/ruby-ldapserver/lib/ldap/server/result.rb +0 -71
- data/vendor/ruby-ldapserver/lib/ldap/server/schema.rb +0 -592
- data/vendor/ruby-ldapserver/lib/ldap/server/server.rb +0 -89
- data/vendor/ruby-ldapserver/lib/ldap/server/syntax.rb +0 -235
- data/vendor/ruby-ldapserver/lib/ldap/server/tcpserver.rb +0 -91
- data/vendor/ruby-ldapserver/lib/ldap/server/util.rb +0 -88
- data/vendor/ruby-ldapserver/lib/ldap/server/version.rb +0 -11
- data/vendor/ruby-ldapserver/test/core.schema +0 -582
- data/vendor/ruby-ldapserver/test/encoding_test.rb +0 -279
- data/vendor/ruby-ldapserver/test/filter_test.rb +0 -107
- data/vendor/ruby-ldapserver/test/match_test.rb +0 -59
- data/vendor/ruby-ldapserver/test/schema_test.rb +0 -113
- data/vendor/ruby-ldapserver/test/syntax_test.rb +0 -40
- data/vendor/ruby-ldapserver/test/test_helper.rb +0 -2
- data/vendor/ruby-ldapserver/test/util_test.rb +0 -51
@@ -1,172 +0,0 @@
|
|
1
|
-
#!/usr/local/bin/ruby -w
|
2
|
-
|
3
|
-
# This is similar to rbslapd1.rb but here we use TOMITA Masahiro's prefork
|
4
|
-
# library: <http://raa.ruby-lang.org/project/prefork/>
|
5
|
-
# Advantages over Ruby threading:
|
6
|
-
# - each client connection is handled in its own process; don't need
|
7
|
-
# to worry about Ruby thread blocking (except if one client issues
|
8
|
-
# overlapping LDAP operations down the same connection, which is uncommon)
|
9
|
-
# - better scalability on multi-processor systems
|
10
|
-
# - better scalability on single-processor systems (e.g. shouldn't hit
|
11
|
-
# max FDs per process limit)
|
12
|
-
# Disadvantages:
|
13
|
-
# - client connections can't share state in RAM. So our shared directory
|
14
|
-
# now has to be read from disk, and flushed to disk after every update.
|
15
|
-
#
|
16
|
-
# Additionally, I have added schema support. An LDAP v3 client can
|
17
|
-
# query the schema remotely, and adds/modifies have data validated.
|
18
|
-
|
19
|
-
$:.unshift('../lib')
|
20
|
-
|
21
|
-
require 'ldap/server'
|
22
|
-
require 'ldap/server/schema'
|
23
|
-
require 'yaml'
|
24
|
-
|
25
|
-
$debug = nil # $stderr
|
26
|
-
|
27
|
-
# An object to keep our in-RAM database and synchronise it to disk
|
28
|
-
# when necessary
|
29
|
-
|
30
|
-
class Directory
|
31
|
-
attr_reader :data
|
32
|
-
|
33
|
-
def initialize(filename)
|
34
|
-
@filename = filename
|
35
|
-
@stat = nil
|
36
|
-
update
|
37
|
-
end
|
38
|
-
|
39
|
-
# synchronise with directory on disk (re-read if it has changed)
|
40
|
-
|
41
|
-
def update
|
42
|
-
begin
|
43
|
-
tmp = {}
|
44
|
-
sb = File.stat(@filename)
|
45
|
-
return if @stat and @stat.ino == sb.ino and @stat.mtime == sb.mtime
|
46
|
-
File.open(@filename) do |f|
|
47
|
-
tmp = YAML::load(f.read)
|
48
|
-
@stat = f.stat
|
49
|
-
end
|
50
|
-
rescue Errno::ENOENT
|
51
|
-
end
|
52
|
-
@data = tmp
|
53
|
-
end
|
54
|
-
|
55
|
-
# write back to disk
|
56
|
-
|
57
|
-
def write
|
58
|
-
File.open(@filename+".new","w") { |f| f.write(YAML::dump(@data)) }
|
59
|
-
File.rename(@filename+".new",@filename)
|
60
|
-
@stat = File.stat(@filename)
|
61
|
-
end
|
62
|
-
|
63
|
-
# run a block while holding a lock on the database
|
64
|
-
|
65
|
-
def lock
|
66
|
-
File.open(@filename+".lock","w") do |f|
|
67
|
-
f.flock(File::LOCK_EX) # will block here until lock available
|
68
|
-
yield
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# We subclass the Operation class, overriding the methods to do what we need
|
74
|
-
|
75
|
-
class DirOperation < LDAP::Server::Operation
|
76
|
-
def initialize(connection, messageID, dir)
|
77
|
-
super(connection, messageID)
|
78
|
-
@dir = dir
|
79
|
-
end
|
80
|
-
|
81
|
-
def search(basedn, scope, deref, filter)
|
82
|
-
$debug << "Search: basedn=#{basedn.inspect}, scope=#{scope.inspect}, deref=#{deref.inspect}, filter=#{filter.inspect}\n" if $debug
|
83
|
-
basedn.downcase!
|
84
|
-
|
85
|
-
case scope
|
86
|
-
when LDAP::Server::BaseObject
|
87
|
-
# client asked for single object by DN
|
88
|
-
@dir.update
|
89
|
-
obj = @dir.data[basedn]
|
90
|
-
raise LDAP::ResultError::NoSuchObject unless obj
|
91
|
-
ok = LDAP::Server::Filter.run(filter, obj)
|
92
|
-
$debug << "Match=#{ok.inspect}: #{obj.inspect}\n" if $debug
|
93
|
-
send_SearchResultEntry(basedn, obj) if ok
|
94
|
-
|
95
|
-
when LDAP::Server::WholeSubtree
|
96
|
-
@dir.update
|
97
|
-
@dir.data.each do |dn, av|
|
98
|
-
$debug << "Considering #{dn}\n" if $debug
|
99
|
-
next unless dn.index(basedn, -basedn.length) # under basedn?
|
100
|
-
next unless LDAP::Server::Filter.run(filter, av) # attribute filter?
|
101
|
-
$debug << "Sending: #{av.inspect}\n" if $debug
|
102
|
-
send_SearchResultEntry(dn, av)
|
103
|
-
end
|
104
|
-
|
105
|
-
else
|
106
|
-
raise LDAP::ResultError::UnwillingToPerform, "OneLevel not implemented"
|
107
|
-
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def add(dn, entry)
|
112
|
-
entry = @schema.validate(entry)
|
113
|
-
entry['createTimestamp'] = [Time.now.gmtime.strftime("%Y%m%d%H%MZ")]
|
114
|
-
entry['creatorsName'] = [@connection.binddn.to_s]
|
115
|
-
# FIXME: normalize the DN and check it's below our root DN
|
116
|
-
# FIXME: validate that a superior object exists
|
117
|
-
# FIXME: validate that entry contains the RDN attribute (yuk)
|
118
|
-
dn.downcase!
|
119
|
-
@dir.lock do
|
120
|
-
@dir.update
|
121
|
-
raise LDAP::ResultError::EntryAlreadyExists if @dir.data[dn]
|
122
|
-
@dir.data[dn] = entry
|
123
|
-
@dir.write
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def del(dn)
|
128
|
-
dn.downcase!
|
129
|
-
@dir.lock do
|
130
|
-
@dir.update
|
131
|
-
raise LDAP::ResultError::NoSuchObject unless @dir.data.has_key?(dn)
|
132
|
-
@dir.data.delete(dn)
|
133
|
-
@dir.write
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def modify(dn, ops)
|
138
|
-
dn.downcase!
|
139
|
-
@dir.lock do
|
140
|
-
@dir.update
|
141
|
-
entry = @dir.data[dn]
|
142
|
-
raise LDAP::ResultError::NoSuchObject unless entry
|
143
|
-
entry = @schema.validate(ops, entry) # also does the update
|
144
|
-
entry['modifyTimestamp'] = [Time.now.gmtime.strftime("%Y%m%d%H%MZ")]
|
145
|
-
entry['modifiersName'] = [@connection.binddn.to_s]
|
146
|
-
@dir.data[dn] = entry
|
147
|
-
@dir.write
|
148
|
-
end
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
directory = Directory.new("ldapdb.yaml")
|
153
|
-
|
154
|
-
schema = LDAP::Server::Schema.new
|
155
|
-
schema.load_system
|
156
|
-
schema.load_file("../test/core.schema")
|
157
|
-
schema.resolve_oids
|
158
|
-
|
159
|
-
s = LDAP::Server.new(
|
160
|
-
:port => 1389,
|
161
|
-
:nodelay => true,
|
162
|
-
:listen => 10,
|
163
|
-
# :ssl_key_file => "key.pem",
|
164
|
-
# :ssl_cert_file => "cert.pem",
|
165
|
-
# :ssl_on_connect => true,
|
166
|
-
:operation_class => DirOperation,
|
167
|
-
:operation_args => [directory],
|
168
|
-
:schema => schema,
|
169
|
-
:namingContexts => ['dc=example,dc=com']
|
170
|
-
)
|
171
|
-
s.run_prefork
|
172
|
-
s.join
|
@@ -1,37 +0,0 @@
|
|
1
|
-
#!/usr/local/bin/ruby
|
2
|
-
|
3
|
-
require 'ldap'
|
4
|
-
|
5
|
-
CHILDREN = 10
|
6
|
-
CONNECTS = 1 # per child
|
7
|
-
SEARCHES = 100 # per connection
|
8
|
-
|
9
|
-
pids = []
|
10
|
-
CHILDREN.times do
|
11
|
-
pids << fork do
|
12
|
-
CONNECTS.times do
|
13
|
-
conn = LDAP::Conn.new("localhost",1389)
|
14
|
-
conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
|
15
|
-
conn.bind
|
16
|
-
SEARCHES.times do
|
17
|
-
res = conn.search("cn=Fred Flintstone,dc=example,dc=com", LDAP::LDAP_SCOPE_BASE,
|
18
|
-
"(objectclass=*)") do |e|
|
19
|
-
#puts "#{$$} #{e.dn.inspect}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
conn.unbind
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
okcount = 0
|
27
|
-
badcount = 0
|
28
|
-
pids.each do |p|
|
29
|
-
Process.wait(p)
|
30
|
-
if $?.exitstatus == 0
|
31
|
-
okcount += 1
|
32
|
-
else
|
33
|
-
badcount += 1
|
34
|
-
end
|
35
|
-
end
|
36
|
-
puts "Children finished: #{okcount} ok, #{badcount} failed"
|
37
|
-
exit badcount > 0 ? 1 : 0
|
@@ -1,276 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
require 'openssl'
|
3
|
-
require 'ldap/server/result'
|
4
|
-
|
5
|
-
module LDAP
|
6
|
-
class Server
|
7
|
-
|
8
|
-
# An object which handles an LDAP connection. Note that LDAP allows
|
9
|
-
# requests and responses to be exchanged asynchronously: e.g. a client
|
10
|
-
# can send three requests, and the three responses can come back in
|
11
|
-
# any order. For that reason, we start a new thread for each request,
|
12
|
-
# and we need a mutex on the io object so that multiple responses don't
|
13
|
-
# interfere with each other.
|
14
|
-
|
15
|
-
class Connection
|
16
|
-
attr_reader :binddn, :version, :opt
|
17
|
-
|
18
|
-
def initialize(io, opt={})
|
19
|
-
@io = io
|
20
|
-
@opt = opt
|
21
|
-
@mutex = Mutex.new
|
22
|
-
@active_reqs = {} # map message ID to thread object
|
23
|
-
@binddn = nil
|
24
|
-
@version = 3
|
25
|
-
@logger = @opt[:logger] || $stderr
|
26
|
-
@ssl = false
|
27
|
-
|
28
|
-
startssl if @opt[:ssl_on_connect]
|
29
|
-
end
|
30
|
-
|
31
|
-
def log(msg)
|
32
|
-
@logger << "[#{@io.peeraddr[3]}]: #{msg}\n"
|
33
|
-
end
|
34
|
-
|
35
|
-
def startssl # :yields:
|
36
|
-
@mutex.synchronize do
|
37
|
-
raise LDAP::ResultError::OperationsError if @ssl or @active_reqs.size > 0
|
38
|
-
yield if block_given?
|
39
|
-
@io = OpenSSL::SSL::SSLSocket.new(@io, @opt[:ssl_ctx])
|
40
|
-
@io.sync_close = true
|
41
|
-
@io.accept
|
42
|
-
@ssl = true
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Read one ASN1 element from the given stream.
|
47
|
-
# Return String containing the raw element.
|
48
|
-
|
49
|
-
def ber_read(io)
|
50
|
-
blk = io.read(2) # minimum: short tag, short length
|
51
|
-
throw(:close) if blk.nil?
|
52
|
-
|
53
|
-
codepoints = blk.respond_to?(:codepoints) ? blk.codepoints.to_a : blk
|
54
|
-
|
55
|
-
tag = codepoints[0] & 0x1f
|
56
|
-
len = codepoints[1]
|
57
|
-
|
58
|
-
if tag == 0x1f # long form
|
59
|
-
tag = 0
|
60
|
-
while true
|
61
|
-
ch = io.getc
|
62
|
-
blk << ch
|
63
|
-
tag = (tag << 7) | (ch & 0x7f)
|
64
|
-
break if (ch & 0x80) == 0
|
65
|
-
end
|
66
|
-
len = io.getc
|
67
|
-
blk << len
|
68
|
-
end
|
69
|
-
|
70
|
-
if (len & 0x80) != 0 # long form
|
71
|
-
len = len & 0x7f
|
72
|
-
raise LDAP::ResultError::ProtocolError, "Indefinite length encoding not supported" if len == 0
|
73
|
-
offset = blk.length
|
74
|
-
blk << io.read(len)
|
75
|
-
# is there a more efficient way of doing this?
|
76
|
-
len = 0
|
77
|
-
blk[offset..-1].each_byte { |b| len = (len << 8) | b }
|
78
|
-
end
|
79
|
-
|
80
|
-
offset = blk.length
|
81
|
-
blk << io.read(len)
|
82
|
-
return blk
|
83
|
-
# or if we wanted to keep the partial decoding we've done:
|
84
|
-
# return blk, [blk[0] >> 6, tag], offset
|
85
|
-
end
|
86
|
-
|
87
|
-
def handle_requests
|
88
|
-
operationClass = @opt[:operation_class]
|
89
|
-
ocArgs = @opt[:operation_args] || []
|
90
|
-
catch(:close) do
|
91
|
-
while true
|
92
|
-
begin
|
93
|
-
blk = ber_read(@io)
|
94
|
-
asn1 = OpenSSL::ASN1::decode(blk)
|
95
|
-
# Debugging:
|
96
|
-
# puts "Request: #{blk.unpack("H*")}\n#{asn1.inspect}" if $debug
|
97
|
-
|
98
|
-
raise LDAP::ResultError::ProtocolError, "LDAPMessage must be SEQUENCE" unless asn1.is_a?(OpenSSL::ASN1::Sequence)
|
99
|
-
raise LDAP::ResultError::ProtocolError, "Bad Message ID" unless asn1.value[0].is_a?(OpenSSL::ASN1::Integer)
|
100
|
-
messageId = asn1.value[0].value
|
101
|
-
|
102
|
-
protocolOp = asn1.value[1]
|
103
|
-
raise LDAP::ResultError::ProtocolError, "Bad protocolOp" unless protocolOp.is_a?(OpenSSL::ASN1::ASN1Data)
|
104
|
-
raise LDAP::ResultError::ProtocolError, "Bad protocolOp tag class" unless protocolOp.tag_class == :APPLICATION
|
105
|
-
|
106
|
-
# controls are not properly implemented
|
107
|
-
c = asn1.value[2]
|
108
|
-
if c.is_a?(OpenSSL::ASN1::ASN1Data) and c.tag_class == :APPLICATION and c.tag == 0
|
109
|
-
controls = c.value
|
110
|
-
end
|
111
|
-
|
112
|
-
case protocolOp.tag
|
113
|
-
when 0 # BindRequest
|
114
|
-
abandon_all
|
115
|
-
@binddn, @version = operationClass.new(self,messageId,*ocArgs).
|
116
|
-
do_bind(protocolOp, controls)
|
117
|
-
|
118
|
-
when 2 # UnbindRequest
|
119
|
-
throw(:close)
|
120
|
-
|
121
|
-
when 3 # SearchRequest
|
122
|
-
# Note: RFC 2251 4.4.4.1 says behaviour is undefined if
|
123
|
-
# client sends an overlapping request with same message ID,
|
124
|
-
# so we don't have to worry about the case where there is
|
125
|
-
# already a thread with this id in @active_reqs.
|
126
|
-
# However, to avoid a race we copy messageId/
|
127
|
-
# protocolOp/controls into thread-local variables, because
|
128
|
-
# they will change when the next request comes in.
|
129
|
-
#
|
130
|
-
# There is a theoretical race condition here: a client could
|
131
|
-
# send an abandon request before Thread.current is assigned to
|
132
|
-
# @active_reqs[thrm]. It's not a problem, because abandon isn't
|
133
|
-
# guaranteed to work anyway. Doing it this way ensures that
|
134
|
-
# @active_reqs does not leak memory on a long-lived connection.
|
135
|
-
|
136
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
137
|
-
begin
|
138
|
-
@active_reqs[thrm] = Thread.current
|
139
|
-
operationClass.new(self,thrm,*ocArgs).do_search(thrp, thrc)
|
140
|
-
ensure
|
141
|
-
@active_reqs.delete(thrm)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
when 6 # ModifyRequest
|
146
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
147
|
-
begin
|
148
|
-
@active_reqs[thrm] = Thread.current
|
149
|
-
operationClass.new(self,thrm,*ocArgs).do_modify(thrp, thrc)
|
150
|
-
ensure
|
151
|
-
@active_reqs.delete(thrm)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
when 8 # AddRequest
|
156
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
157
|
-
begin
|
158
|
-
@active_reqs[thrm] = Thread.current
|
159
|
-
operationClass.new(self,thrm,*ocArgs).do_add(thrp, thrc)
|
160
|
-
ensure
|
161
|
-
@active_reqs.delete(thrm)
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
when 10 # DelRequest
|
166
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
167
|
-
begin
|
168
|
-
@active_reqs[thrm] = Thread.current
|
169
|
-
operationClass.new(self,thrm,*ocArgs).do_del(thrp, thrc)
|
170
|
-
ensure
|
171
|
-
@active_reqs.delete(thrm)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
when 12 # ModifyDNRequest
|
176
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
177
|
-
begin
|
178
|
-
@active_reqs[thrm] = Thread.current
|
179
|
-
operationClass.new(self,thrm,*ocArgs).do_modifydn(thrp, thrc)
|
180
|
-
ensure
|
181
|
-
@active_reqs.delete(thrm)
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
when 14 # CompareRequest
|
186
|
-
Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
|
187
|
-
begin
|
188
|
-
@active_reqs[thrm] = Thread.current
|
189
|
-
operationClass.new(self,thrm,*ocArgs).do_compare(thrp, thrc)
|
190
|
-
ensure
|
191
|
-
@active_reqs.delete(thrm)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
when 16 # AbandonRequest
|
196
|
-
abandon(protocolOp.value)
|
197
|
-
|
198
|
-
else
|
199
|
-
raise LDAP::ResultError::ProtocolError, "Unrecognised protocolOp tag #{protocolOp.tag}"
|
200
|
-
end
|
201
|
-
|
202
|
-
rescue LDAP::ResultError::ProtocolError, OpenSSL::ASN1::ASN1Error => e
|
203
|
-
send_notice_of_disconnection(LDAP::ResultError::ProtocolError.new.to_i, e.message)
|
204
|
-
throw(:close)
|
205
|
-
|
206
|
-
# all other exceptions propagate up and are caught by tcpserver
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
abandon_all
|
211
|
-
end
|
212
|
-
|
213
|
-
def write(data)
|
214
|
-
@mutex.synchronize do
|
215
|
-
@io.write(data)
|
216
|
-
@io.flush
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def writelock
|
221
|
-
@mutex.synchronize do
|
222
|
-
yield @io
|
223
|
-
@io.flush
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
def abandon(messageID)
|
228
|
-
@mutex.synchronize do
|
229
|
-
thread = @active_reqs.delete(messageID)
|
230
|
-
thread.raise LDAP::Abandon if thread and thread.alive?
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def abandon_all
|
235
|
-
return if @active_reqs.size == 0
|
236
|
-
@mutex.synchronize do
|
237
|
-
@active_reqs.each do |id, thread|
|
238
|
-
thread.raise LDAP::Abandon if thread.alive?
|
239
|
-
end
|
240
|
-
@active_reqs = {}
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
def send_unsolicited_notification(resultCode, opt={})
|
245
|
-
protocolOp = [
|
246
|
-
OpenSSL::ASN1::Enumerated(resultCode),
|
247
|
-
OpenSSL::ASN1::OctetString(opt[:matchedDN] || ""),
|
248
|
-
OpenSSL::ASN1::OctetString(opt[:errorMessage] || ""),
|
249
|
-
]
|
250
|
-
if opt[:referral]
|
251
|
-
rs = opt[:referral].collect { |r| OpenSSL::ASN1::OctetString(r) }
|
252
|
-
protocolOp << OpenSSL::ASN1::Sequence(rs, 3, :IMPLICIT, :APPLICATION)
|
253
|
-
end
|
254
|
-
if opt[:responseName]
|
255
|
-
protocolOp << OpenSSL::ASN1::OctetString(opt[:responseName], 10, :IMPLICIT, :APPLICATION)
|
256
|
-
end
|
257
|
-
if opt[:response]
|
258
|
-
protocolOp << OpenSSL::ASN1::OctetString(opt[:response], 11, :IMPLICIT, :APPLICATION)
|
259
|
-
end
|
260
|
-
message = [
|
261
|
-
OpenSSL::ASN1::Integer(0),
|
262
|
-
OpenSSL::ASN1::Sequence(protocolOp, 24, :IMPLICIT, :APPLICATION),
|
263
|
-
]
|
264
|
-
message << opt[:controls] if opt[:controls]
|
265
|
-
write(OpenSSL::ASN1::Sequence(message).to_der)
|
266
|
-
end
|
267
|
-
|
268
|
-
def send_notice_of_disconnection(resultCode, errorMessage="")
|
269
|
-
send_unsolicited_notification(resultCode,
|
270
|
-
:errorMessage=>errorMessage,
|
271
|
-
:responseName=>"1.3.6.1.4.1.1466.20036"
|
272
|
-
)
|
273
|
-
end
|
274
|
-
end
|
275
|
-
end # class Server
|
276
|
-
end # module LDAP
|