ruby-ldapserver 0.3.1 → 0.4.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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ldap-server-primitive.gemspec
4
+ gemspec
data/Rakefile CHANGED
@@ -1,22 +1 @@
1
- require 'rubygems'
2
- require 'hoe'
3
- require File.join(File.dirname(__FILE__), 'lib', 'ldap', 'server', 'version')
4
-
5
- RDOC_OPTS = ['--quiet', '--title', "ruby-ldapserver",
6
- "--opname", "index.html",
7
- "--line-numbers",
8
- "--main", "README",
9
- "--inline-source"]
10
-
11
- # Generate all the Rake tasks
12
- hoe = Hoe.new('ruby-ldapserver', ENV['VERSION'] || LDAP::Server::VERSION::STRING) do |p|
13
- p.rubyforge_name = 'ruby-ldapserver'
14
- p.summary = "A pure-Ruby framework for building LDAP servers"
15
- p.description = "ruby-ldapserver is a lightweight, pure-Ruby skeleton for implementing LDAP
16
- server applications."
17
- p.author = 'Brian Candler'
18
- p.email = 'B.Candler@pobox.com'
19
- p.url = 'http://rubyforge.org/projects/ruby-ldapserver'
20
- p.test_globs = ["test/**/*_test.rb"]
21
- p.changes = p.paragraphs_of('ChangeLog', 0..1).join("\n\n")
22
- end
1
+ require "bundler/gem_tasks"
@@ -19,22 +19,30 @@ class Server
19
19
  @io = io
20
20
  @opt = opt
21
21
  @mutex = Mutex.new
22
- @active_reqs = {} # map message ID to thread object
22
+ @threadgroup = ThreadGroup.new
23
23
  @binddn = nil
24
24
  @version = 3
25
- @logger = @opt[:logger] || $stderr
25
+ @logger = @opt[:logger]
26
26
  @ssl = false
27
27
 
28
28
  startssl if @opt[:ssl_on_connect]
29
29
  end
30
30
 
31
- def log(msg)
32
- @logger << "[#{@io.peeraddr[3]}]: #{msg}\n"
31
+ def log(msg, severity = Logger::INFO)
32
+ @logger.add(severity, msg, @io.peeraddr[3])
33
+ end
34
+
35
+ def debug msg
36
+ log msg, Logger::DEBUG
37
+ end
38
+
39
+ def log_exception(e)
40
+ log "#{e}: #{e.backtrace.join("\n\tfrom ")}", Logger::ERROR
33
41
  end
34
42
 
35
43
  def startssl # :yields:
36
44
  @mutex.synchronize do
37
- raise LDAP::ResultError::OperationsError if @ssl or @active_reqs.size > 0
45
+ raise LDAP::ResultError::OperationsError if @ssl or @threadgroup.list.size > 0
38
46
  yield if block_given?
39
47
  @io = OpenSSL::SSL::SSLSocket.new(@io, @opt[:ssl_ctx])
40
48
  @io.sync_close = true
@@ -49,8 +57,11 @@ class Server
49
57
  def ber_read(io)
50
58
  blk = io.read(2) # minimum: short tag, short length
51
59
  throw(:close) if blk.nil?
52
- tag = blk[0] & 0x1f
53
- len = blk[1]
60
+
61
+ codepoints = blk.respond_to?(:codepoints) ? blk.codepoints.to_a : blk
62
+
63
+ tag = codepoints[0] & 0x1f
64
+ len = codepoints[1]
54
65
 
55
66
  if tag == 0x1f # long form
56
67
  tag = 0
@@ -116,78 +127,22 @@ class Server
116
127
  throw(:close)
117
128
 
118
129
  when 3 # SearchRequest
119
- # Note: RFC 2251 4.4.4.1 says behaviour is undefined if
120
- # client sends an overlapping request with same message ID,
121
- # so we don't have to worry about the case where there is
122
- # already a thread with this id in @active_reqs.
123
- # However, to avoid a race we copy messageId/
124
- # protocolOp/controls into thread-local variables, because
125
- # they will change when the next request comes in.
126
- #
127
- # There is a theoretical race condition here: a client could
128
- # send an abandon request before Thread.current is assigned to
129
- # @active_reqs[thrm]. It's not a problem, because abandon isn't
130
- # guaranteed to work anyway. Doing it this way ensures that
131
- # @active_reqs does not leak memory on a long-lived connection.
132
-
133
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
134
- begin
135
- @active_reqs[thrm] = Thread.current
136
- operationClass.new(self,thrm,*ocArgs).do_search(thrp, thrc)
137
- ensure
138
- @active_reqs.delete(thrm)
139
- end
140
- end
130
+ start_op(messageId,protocolOp,controls,:do_search)
141
131
 
142
132
  when 6 # ModifyRequest
143
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
144
- begin
145
- @active_reqs[thrm] = Thread.current
146
- operationClass.new(self,thrm,*ocArgs).do_modify(thrp, thrc)
147
- ensure
148
- @active_reqs.delete(thrm)
149
- end
150
- end
133
+ start_op(messageId,protocolOp,controls,:do_modify)
151
134
 
152
135
  when 8 # AddRequest
153
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
154
- begin
155
- @active_reqs[thrm] = Thread.current
156
- operationClass.new(self,thrm,*ocArgs).do_add(thrp, thrc)
157
- ensure
158
- @active_reqs.delete(thrm)
159
- end
160
- end
136
+ start_op(messageId,protocolOp,controls,:do_add)
161
137
 
162
138
  when 10 # DelRequest
163
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
164
- begin
165
- @active_reqs[thrm] = Thread.current
166
- operationClass.new(self,thrm,*ocArgs).do_del(thrp, thrc)
167
- ensure
168
- @active_reqs.delete(thrm)
169
- end
170
- end
139
+ start_op(messageId,protocolOp,controls,:do_del)
171
140
 
172
141
  when 12 # ModifyDNRequest
173
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
174
- begin
175
- @active_reqs[thrm] = Thread.current
176
- operationClass.new(self,thrm,*ocArgs).do_modifydn(thrp, thrc)
177
- ensure
178
- @active_reqs.delete(thrm)
179
- end
180
- end
142
+ start_op(messageId,protocolOp,controls,:do_modifydn)
181
143
 
182
144
  when 14 # CompareRequest
183
- Thread.new(messageId,protocolOp,controls) do |thrm,thrp,thrc|
184
- begin
185
- @active_reqs[thrm] = Thread.current
186
- operationClass.new(self,thrm,*ocArgs).do_compare(thrp, thrc)
187
- ensure
188
- @active_reqs.delete(thrm)
189
- end
190
- end
145
+ start_op(messageId,protocolOp,controls,:do_compare)
191
146
 
192
147
  when 16 # AbandonRequest
193
148
  abandon(protocolOp.value)
@@ -207,6 +162,31 @@ class Server
207
162
  abandon_all
208
163
  end
209
164
 
165
+ # Start an operation in a Thread. Add this to a ThreadGroup to allow
166
+ # the operation to be abandoned later.
167
+ #
168
+ # When the thread terminates, it automatically drops out of the group.
169
+ #
170
+ # Note: RFC 2251 4.4.4.1 says behaviour is undefined if
171
+ # client sends an overlapping request with same message ID,
172
+ # so we don't have to worry about the case where there is
173
+ # already a thread with this messageId in @threadgroup.
174
+
175
+ def start_op(messageId,protocolOp,controls,meth)
176
+ operationClass = @opt[:operation_class]
177
+ ocArgs = @opt[:operation_args] || []
178
+ thr = Thread.new do
179
+ begin
180
+ operationClass.new(self,messageId,*ocArgs).
181
+ send(meth,protocolOp,controls)
182
+ rescue Exception => e
183
+ log_exception e
184
+ end
185
+ end
186
+ thr[:messageId] = messageId
187
+ @threadgroup.add(thr)
188
+ end
189
+
210
190
  def write(data)
211
191
  @mutex.synchronize do
212
192
  @io.write(data)
@@ -223,18 +203,16 @@ class Server
223
203
 
224
204
  def abandon(messageID)
225
205
  @mutex.synchronize do
226
- thread = @active_reqs.delete(messageID)
227
- thread.raise LDAP::Abandon if thread and thread.alive?
206
+ thread = @threadgroup.list.find { |t| t[:messageId] == messageID }
207
+ thread.raise LDAP::Abandon if thread
228
208
  end
229
209
  end
230
210
 
231
211
  def abandon_all
232
- return if @active_reqs.size == 0
233
212
  @mutex.synchronize do
234
- @active_reqs.each do |id, thread|
235
- thread.raise LDAP::Abandon if thread.alive?
213
+ @threadgroup.list.each do |thread|
214
+ thread.raise LDAP::Abandon
236
215
  end
237
- @active_reqs = {}
238
216
  end
239
217
  end
240
218
 
@@ -38,16 +38,14 @@ class Server
38
38
  @server = @connection.opt[:server]
39
39
  end
40
40
 
41
- # Send a log message
42
-
43
- def log(*args)
44
- @connection.log(*args)
45
- end
46
-
47
41
  # Send an exception report to the log
48
42
 
49
- def log_exception(e)
50
- @connection.log "#{e}: #{e.backtrace.join("\n\tfrom ")}"
43
+ def debug msg
44
+ @connection.debug msg
45
+ end
46
+
47
+ def log_exception
48
+ @connection.log_exception msg
51
49
  end
52
50
 
53
51
  ##################################################
@@ -298,7 +296,7 @@ class Server
298
296
  attr = @schema.find_attrtype(attr).to_s
299
297
  end
300
298
  vals = seq.value[1].value[1].value.collect { |v| v.value }
301
- case seq.value[0].value
299
+ case seq.value[0].value.to_i
302
300
  when 0
303
301
  modinfo[attr] = [:add] + vals
304
302
  when 1
@@ -436,7 +434,8 @@ class Server
436
434
  # care of, but you need to perform all authorisation checks yourself,
437
435
  # using @connection.binddn
438
436
 
439
- def search(basedn, scope, deref, filter, attrs)
437
+ def search(basedn, scope, deref, filter)
438
+ debug "search(#{basedn}, #{scope}, #{deref}, #{filter})"
440
439
  raise LDAP::ResultError::UnwillingToPerform, "search not implemented"
441
440
  end
442
441
 
@@ -22,7 +22,6 @@ class Server
22
22
  # :max_idle=>N - seconds
23
23
 
24
24
  def self.preforkserver(opt, &blk)
25
- logger = opt[:logger] || $stderr
26
25
  server = PreFork.new(opt[:bindaddr] || "0.0.0.0", opt[:port])
27
26
 
28
27
  # Drop privileges if requested
@@ -60,7 +59,7 @@ class Server
60
59
  # This exception can be raised to shut the server down
61
60
  server.stop
62
61
  rescue Exception => e
63
- logger << "[#{s.peeraddr[3]}]: #{e}: #{e.backtrace[0]}\n"
62
+ opt[:logger].error(s.peeraddr[3]) { "#{e}: #{e.backtrace[0]}" }
64
63
  ensure
65
64
  s.close
66
65
  end
@@ -1,6 +1,7 @@
1
1
  require 'ldap/server/connection'
2
2
  require 'ldap/server/operation'
3
3
  require 'openssl'
4
+ require 'logger'
4
5
 
5
6
  module LDAP
6
7
  class Server
@@ -20,12 +21,19 @@ class Server
20
21
  # :ssl_ca_path=>directory - verify peer certificates
21
22
  # :schema=>Schema - Schema object
22
23
  # :namingContexts=>[dn, ...] - base DN(s) we answer
24
+
25
+ attr_reader :logger
23
26
 
24
27
  def initialize(opt = DEFAULT_OPT)
25
28
  @opt = opt
26
29
  @opt[:server] = self
27
30
  @opt[:operation_class] ||= LDAP::Server::Operation
28
31
  @opt[:operation_args] ||= []
32
+ unless @opt[:logger]
33
+ @opt[:logger] ||= Logger.new($stderr)
34
+ @opt[:logger].level = Logger::INFO
35
+ end
36
+ @logger = @opt[:logger]
29
37
  LDAP::Server.ssl_prepare(@opt)
30
38
  @schema = opt[:schema] # may be nil
31
39
  @root_dse = Hash.new { |h,k| h[k] = [] }.merge({
@@ -47,12 +55,19 @@ class Server
47
55
  ctx = OpenSSL::SSL::SSLContext.new
48
56
  ctx.key = OpenSSL::PKey::RSA.new(File::read(opt[:ssl_key_file]))
49
57
  ctx.cert = OpenSSL::X509::Certificate.new(File::read(opt[:ssl_cert_file]))
58
+ if opt[:ssl_dhparams]
59
+ ctx.tmp_dh_callback = proc { |*args|
60
+ OpenSSL::PKey::DH.new(
61
+ File.read(opt[:ssl_dhparams])
62
+ )
63
+ }
64
+ end
50
65
  if opt[:ssl_ca_path]
51
66
  ctx.ca_path = opt[:ssl_ca_path]
52
- ctx.verify_mode =
67
+ ctx.verify_mode = opt[:ssl_verify_mode] ||
53
68
  OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
54
- else
55
- $stderr.puts "Warning: SSL peer certificate won't be verified"
69
+ elsif opt[:ssl_verify_mode] != 0
70
+ $stderr.puts "Warning: No ssl_ca_path, peer certificate won't be verified"
56
71
  end
57
72
  opt[:ssl_ctx] = ctx
58
73
  end
@@ -22,7 +22,6 @@ class Server
22
22
  # :nodelay=>true - set TCP_NODELAY option
23
23
 
24
24
  def self.tcpserver(opt, &blk)
25
- logger = opt[:logger] || $stderr
26
25
  server = TCPServer.new(opt[:bindaddr] || "0.0.0.0", opt[:port])
27
26
 
28
27
  # Drop privileges if requested
@@ -51,8 +50,7 @@ class Server
51
50
  begin
52
51
  s.instance_eval(&blk)
53
52
  rescue Exception => e
54
- logger << "[#{s.peeraddr[3]}]: #{e}: #{e.backtrace[0]}\n"
55
- #logger << "[#{s.peeraddr[3]}]: #{e}: #{e.backtrace.join("\n\tfrom ")}\n"
53
+ opt[:logger].error(s.peeraddr[3]) {"#{e}: #{e.backtrace[0]}"}
56
54
  ensure
57
55
  s.close
58
56
  end
@@ -1,11 +1,5 @@
1
1
  module LDAP #:nodoc:
2
2
  class Server #:nodoc:
3
- class VERSION #:nodoc:
4
- MAJOR = 0
5
- MINOR = 3
6
- TINY = 1
7
-
8
- STRING = [MAJOR, MINOR, TINY].join('.')
9
- end
3
+ VERSION = '0.4.0'
10
4
  end
11
5
  end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ldap/server/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{ruby-ldapserver}
8
+ s.version = LDAP::Server::VERSION
9
+
10
+ s.authors = ["Brian Candler"]
11
+ s.description = %q{ruby-ldapserver is a lightweight, pure-Ruby skeleton for implementing LDAP server applications.}
12
+ s.email = %q{B.Candler@pobox.com}
13
+ s.files = `git ls-files`.split($/)
14
+ s.homepage = %q{https://github.com/inscitiv/ruby-ldapserver}
15
+ s.rdoc_options = ["--main", "README.txt"]
16
+ s.require_paths = ["lib"]
17
+ s.summary = %q{A pure-Ruby framework for building LDAP servers}
18
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
19
+
20
+ s.add_development_dependency 'bundler', '~> 1.3'
21
+ s.add_development_dependency 'rake', '~> 10.0'
22
+ end
metadata CHANGED
@@ -1,38 +1,59 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: ruby-ldapserver
3
- version: !ruby/object:Gem::Version
4
- version: 0.3.1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
+ authors:
7
8
  - Brian Candler
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
-
12
- date: 2008-01-16 00:00:00 -05:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: hoe
17
- version_requirement:
18
- version_requirements: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - ">="
21
- - !ruby/object:Gem::Version
22
- version: 1.4.0
23
- version:
24
- description: ruby-ldapserver is a lightweight, pure-Ruby skeleton for implementing LDAP server applications.
12
+ date: 2013-03-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ description: ruby-ldapserver is a lightweight, pure-Ruby skeleton for implementing
47
+ LDAP server applications.
25
48
  email: B.Candler@pobox.com
26
49
  executables: []
27
-
28
50
  extensions: []
29
-
30
- extra_rdoc_files:
31
- - Manifest.txt
32
- files:
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
33
54
  - COPYING
34
55
  - ChangeLog
35
- - Manifest.txt
56
+ - Gemfile
36
57
  - README
37
58
  - Rakefile
38
59
  - examples/README
@@ -54,6 +75,7 @@ files:
54
75
  - lib/ldap/server/tcpserver.rb
55
76
  - lib/ldap/server/util.rb
56
77
  - lib/ldap/server/version.rb
78
+ - ruby-ldapserver.gemspec
57
79
  - test/core.schema
58
80
  - test/encoding_test.rb
59
81
  - test/filter_test.rb
@@ -62,37 +84,44 @@ files:
62
84
  - test/syntax_test.rb
63
85
  - test/test_helper.rb
64
86
  - test/util_test.rb
65
- has_rdoc: true
66
- homepage: http://rubyforge.org/projects/ruby-ldapserver
87
+ homepage: https://github.com/inscitiv/ruby-ldapserver
88
+ licenses: []
67
89
  post_install_message:
68
- rdoc_options:
90
+ rdoc_options:
69
91
  - --main
70
92
  - README.txt
71
- require_paths:
93
+ require_paths:
72
94
  - lib
73
- required_ruby_version: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - ">="
76
- - !ruby/object:Gem::Version
77
- version: "0"
78
- version:
79
- required_rubygems_version: !ruby/object:Gem::Requirement
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- version: "0"
84
- version:
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ segments:
102
+ - 0
103
+ hash: 2115768966675035117
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ segments:
111
+ - 0
112
+ hash: 2115768966675035117
85
113
  requirements: []
86
-
87
- rubyforge_project: ruby-ldapserver
88
- rubygems_version: 1.0.1
114
+ rubyforge_project:
115
+ rubygems_version: 1.8.25
89
116
  signing_key:
90
- specification_version: 2
117
+ specification_version: 3
91
118
  summary: A pure-Ruby framework for building LDAP servers
92
- test_files:
119
+ test_files:
120
+ - test/core.schema
93
121
  - test/encoding_test.rb
94
122
  - test/filter_test.rb
95
123
  - test/match_test.rb
96
124
  - test/schema_test.rb
97
125
  - test/syntax_test.rb
126
+ - test/test_helper.rb
98
127
  - test/util_test.rb
data/Manifest.txt DELETED
@@ -1,32 +0,0 @@
1
- COPYING
2
- ChangeLog
3
- Manifest.txt
4
- README
5
- Rakefile
6
- examples/README
7
- examples/mkcert.rb
8
- examples/rbslapd1.rb
9
- examples/rbslapd2.rb
10
- examples/rbslapd3.rb
11
- examples/speedtest.rb
12
- lib/ldap/server.rb
13
- lib/ldap/server/connection.rb
14
- lib/ldap/server/filter.rb
15
- lib/ldap/server/match.rb
16
- lib/ldap/server/operation.rb
17
- lib/ldap/server/preforkserver.rb
18
- lib/ldap/server/result.rb
19
- lib/ldap/server/schema.rb
20
- lib/ldap/server/server.rb
21
- lib/ldap/server/syntax.rb
22
- lib/ldap/server/tcpserver.rb
23
- lib/ldap/server/util.rb
24
- lib/ldap/server/version.rb
25
- test/core.schema
26
- test/encoding_test.rb
27
- test/filter_test.rb
28
- test/match_test.rb
29
- test/schema_test.rb
30
- test/syntax_test.rb
31
- test/test_helper.rb
32
- test/util_test.rb