activeldap 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +61 -0
- data/README +8 -1
- data/Rakefile +4 -1
- data/benchmark/bench-al.rb +12 -2
- data/examples/al-admin/app/controllers/account_controller.rb +4 -3
- data/examples/al-admin/app/controllers/application.rb +5 -2
- data/examples/al-admin/app/controllers/directory_controller.rb +3 -1
- data/examples/al-admin/app/controllers/users_controller.rb +19 -4
- data/examples/al-admin/app/controllers/welcome_controller.rb +4 -2
- data/examples/al-admin/app/helpers/application_helper.rb +7 -1
- data/examples/al-admin/app/helpers/url_helper.rb +4 -0
- data/examples/al-admin/app/models/ldap_user.rb +4 -0
- data/examples/al-admin/app/views/_entry/{_attributes_information.rhtml → _attributes_information.html.erb} +0 -0
- data/examples/al-admin/app/views/_entry/{_entry.rhtml → _entry.html.erb} +0 -0
- data/examples/al-admin/app/views/_schema/{_aliases.rhtml → _aliases.html.erb} +0 -0
- data/examples/al-admin/app/views/_switcher/{_after.rhtml → _after.html.erb} +0 -0
- data/examples/al-admin/app/views/_switcher/{_before.rhtml → _before.html.erb} +0 -0
- data/examples/al-admin/app/views/account/{login.rhtml → login.html.erb} +0 -0
- data/examples/al-admin/app/views/account/{sign_up.rhtml → sign_up.html.erb} +0 -0
- data/examples/al-admin/app/views/attributes/{_attributes.rhtml → _attributes.html.erb} +0 -0
- data/examples/al-admin/app/views/attributes/{_detail.rhtml → _detail.html.erb} +0 -0
- data/examples/al-admin/app/views/attributes/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/app/views/attributes/{show.rhtml → show.html.erb} +0 -0
- data/examples/al-admin/app/views/directory/{_tree.rhtml → _tree.html.erb} +0 -0
- data/examples/al-admin/app/views/directory/{_tree_view_js.rhtml → _tree_view_js.html.erb} +4 -5
- data/examples/al-admin/app/views/directory/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/app/views/directory/{populate.rhtml → populate.html.erb} +0 -0
- data/examples/al-admin/app/views/layouts/{_footer.rhtml → _footer.html.erb} +0 -0
- data/examples/al-admin/app/views/layouts/{_header_menu.rhtml → _header_menu.html.erb} +0 -0
- data/examples/al-admin/app/views/layouts/{_main_menu.rhtml → _main_menu.html.erb} +0 -0
- data/examples/al-admin/app/views/layouts/{application.rhtml → application.html.erb} +3 -2
- data/examples/al-admin/app/views/object_classes/{_attributes.rhtml → _attributes.html.erb} +0 -0
- data/examples/al-admin/app/views/object_classes/{_object_classes.rhtml → _object_classes.html.erb} +0 -0
- data/examples/al-admin/app/views/object_classes/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/app/views/object_classes/{show.rhtml → show.html.erb} +0 -0
- data/examples/al-admin/app/views/syntaxes/{_detail.rhtml → _detail.html.erb} +0 -0
- data/examples/al-admin/app/views/syntaxes/{_syntaxes.rhtml → _syntaxes.html.erb} +0 -0
- data/examples/al-admin/app/views/syntaxes/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/app/views/syntaxes/{show.rhtml → show.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{_attributes_update_form.rhtml → _attributes_update_form.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{_form.rhtml → _form.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{_object_classes_update_form.rhtml → _object_classes_update_form.html.erb} +7 -1
- data/examples/al-admin/app/views/users/{_password_change_form.rhtml → _password_change_form.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{edit.rhtml → edit.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/app/views/users/{show.rhtml → show.html.erb} +0 -0
- data/examples/al-admin/app/views/welcome/{index.rhtml → index.html.erb} +0 -0
- data/examples/al-admin/config/boot.rb +96 -32
- data/examples/al-admin/config/environment.rb +30 -36
- data/examples/al-admin/config/environments/development.rb +2 -5
- data/examples/al-admin/config/environments/production.rb +1 -0
- data/examples/al-admin/config/environments/test.rb +4 -1
- data/examples/al-admin/config/initializers/exception_notifier.rb +2 -0
- data/examples/al-admin/config/initializers/gettext.rb +1 -0
- data/examples/al-admin/config/initializers/inflections.rb +10 -0
- data/examples/al-admin/config/initializers/mime_types.rb +5 -0
- data/examples/al-admin/config/initializers/ralative_url_support.rb +1 -0
- data/examples/al-admin/config/routes.rb +24 -12
- data/examples/al-admin/lib/authenticated_system.rb +1 -1
- data/examples/al-admin/lib/tasks/gettext.rake +1 -1
- data/examples/al-admin/po/en/al-admin.po +102 -100
- data/examples/al-admin/po/ja/al-admin.po +112 -110
- data/examples/al-admin/po/nl/al-admin.po +117 -110
- data/examples/al-admin/public/javascripts/controls.js +484 -354
- data/examples/al-admin/public/javascripts/dragdrop.js +88 -58
- data/examples/al-admin/public/javascripts/effects.js +396 -364
- data/examples/al-admin/public/javascripts/prototype.js +2817 -1107
- data/examples/al-admin/public/stylesheets/base.css +5 -0
- data/examples/al-admin/script/performance/request +3 -0
- data/lib/active_ldap.rb +13 -10
- data/lib/active_ldap/adapter/base.rb +159 -43
- data/lib/active_ldap/adapter/jndi.rb +175 -0
- data/lib/active_ldap/adapter/jndi_connection.rb +180 -0
- data/lib/active_ldap/adapter/ldap.rb +91 -46
- data/lib/active_ldap/adapter/ldap_ext.rb +19 -5
- data/lib/active_ldap/adapter/net_ldap.rb +52 -44
- data/lib/active_ldap/association/has_many_wrap.rb +1 -1
- data/lib/active_ldap/attributes.rb +20 -95
- data/lib/active_ldap/base.rb +195 -186
- data/lib/active_ldap/callbacks.rb +33 -0
- data/lib/active_ldap/command.rb +3 -3
- data/lib/active_ldap/connection.rb +21 -3
- data/lib/active_ldap/distinguished_name.rb +18 -11
- data/lib/active_ldap/entry_attribute.rb +78 -0
- data/lib/active_ldap/human_readable.rb +20 -0
- data/lib/active_ldap/ldif.rb +860 -10
- data/lib/active_ldap/object_class.rb +6 -4
- data/lib/active_ldap/operations.rb +129 -22
- data/lib/active_ldap/schema.rb +118 -9
- data/lib/active_ldap/schema/syntaxes.rb +33 -16
- data/lib/active_ldap/validations.rb +74 -65
- data/po/en/active-ldap.po +378 -768
- data/po/ja/active-ldap.po +935 -868
- data/rails/plugin/active_ldap/init.rb +40 -2
- data/test/al-test-utils.rb +78 -58
- data/test/command.rb +51 -1
- data/test/test-unit-ext/priority.rb +29 -6
- data/test/test_adapter.rb +21 -2
- data/test/test_attributes.rb +13 -0
- data/test/test_base.rb +51 -1
- data/test/test_connection.rb +2 -1
- data/test/test_connection_per_class.rb +55 -1
- data/test/test_connection_per_dn.rb +29 -1
- data/test/test_find.rb +73 -0
- data/test/test_ldif.rb +1829 -15
- data/test/test_load.rb +126 -0
- data/test/test_object_class.rb +23 -5
- data/test/test_schema.rb +28 -0
- data/test/test_syntax.rb +22 -11
- data/test/test_user.rb +16 -25
- data/test/test_useradd-binary.rb +1 -1
- data/test/test_usermod-binary-add-time.rb +1 -1
- data/test/test_usermod-binary-add.rb +1 -1
- data/test/test_validation.rb +100 -22
- metadata +77 -71
- data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
- data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
- data/examples/al-admin/app/views/layouts/_flash_box.rhtml +0 -4
- data/examples/al-admin/public/stylesheets/common.css +0 -2
- data/examples/al-admin/script/breakpointer +0 -3
data/lib/active_ldap.rb
CHANGED
@@ -192,9 +192,9 @@
|
|
192
192
|
#
|
193
193
|
# Under ou=People I store user objects, and under ou=Groups, I store group
|
194
194
|
# objects. What |ldap_mapping| has done is mapped the class in to the LDAP tree
|
195
|
-
# abstractly. With the given :
|
196
|
-
# under ou=Groups,dc=dataspill,dc=org using the primary attribute 'cn'
|
197
|
-
# beginning of the distinguished name.
|
195
|
+
# abstractly. With the given :dn_attributes and :prefix, it will only work for
|
196
|
+
# entries under ou=Groups,dc=dataspill,dc=org using the primary attribute 'cn'
|
197
|
+
# as the beginning of the distinguished name.
|
198
198
|
#
|
199
199
|
# Just for clarity, here's how the arguments map out:
|
200
200
|
#
|
@@ -205,7 +205,8 @@
|
|
205
205
|
# :base from configuration.rb
|
206
206
|
#
|
207
207
|
# :scope tells ActiveLdap to only search under ou=Groups, and not to look deeper
|
208
|
-
# for
|
208
|
+
# for dn_attribute matches.
|
209
|
+
# (e.g. cn=develop,ou=DevGroups,ou=Groups,dc=dataspill,dc=org)
|
209
210
|
#
|
210
211
|
# Something's missing: :classes. :classes is used to tell Ruby/ActiveLdap what
|
211
212
|
# the minimum requirement is when creating a new object. LDAP uses objectClasses
|
@@ -293,7 +294,7 @@
|
|
293
294
|
# mind, the above definition could become:
|
294
295
|
#
|
295
296
|
# irb> class User < ActiveLdap::Base
|
296
|
-
# irb* ldap_mapping :
|
297
|
+
# irb* ldap_mapping :dn_attribute => 'uid', :prefix => 'People', :classes => ['top','account']
|
297
298
|
# irb* belongs_to :groups, :class => 'Group', :many => 'memberUid'
|
298
299
|
# irb* end
|
299
300
|
#
|
@@ -348,8 +349,8 @@
|
|
348
349
|
# => "root"
|
349
350
|
#
|
350
351
|
# In this simple example, Group.find took the search string of 'deve*' and
|
351
|
-
# searched for the first match in Group where the
|
352
|
-
# is the simplest example of .find.
|
352
|
+
# searched for the first match in Group where the dn_attribute matched the
|
353
|
+
# query. This is the simplest example of .find.
|
353
354
|
#
|
354
355
|
# irb> Group.find(:all, '*').collect {|group| group.cn}
|
355
356
|
# => ["root", "daemon", "bin", "sys", "adm", "tty", ..., "develop"]
|
@@ -921,7 +922,7 @@ if Dependencies.respond_to?(:load_paths)
|
|
921
922
|
end
|
922
923
|
|
923
924
|
module ActiveLdap
|
924
|
-
VERSION = "0.
|
925
|
+
VERSION = "0.10.0"
|
925
926
|
end
|
926
927
|
|
927
928
|
if RUBY_PLATFORM.match('linux')
|
@@ -939,6 +940,9 @@ require 'active_ldap/get_text'
|
|
939
940
|
|
940
941
|
require 'active_ldap/base'
|
941
942
|
|
943
|
+
require 'active_ldap/distinguished_name'
|
944
|
+
require 'active_ldap/ldif'
|
945
|
+
|
942
946
|
require 'active_ldap/associations'
|
943
947
|
require 'active_ldap/attributes'
|
944
948
|
require 'active_ldap/configuration'
|
@@ -949,7 +953,6 @@ require 'active_ldap/human_readable'
|
|
949
953
|
|
950
954
|
require 'active_ldap/acts/tree'
|
951
955
|
|
952
|
-
require 'active_ldap/distinguished_name'
|
953
956
|
require 'active_ldap/populate'
|
954
957
|
require 'active_ldap/escape'
|
955
958
|
require 'active_ldap/helper'
|
@@ -974,7 +977,7 @@ ActiveLdap::Base.class_eval do
|
|
974
977
|
end
|
975
978
|
|
976
979
|
unless defined?(ACTIVE_LDAP_CONNECTION_ADAPTERS)
|
977
|
-
ACTIVE_LDAP_CONNECTION_ADAPTERS = %w(ldap net_ldap)
|
980
|
+
ACTIVE_LDAP_CONNECTION_ADAPTERS = %w(ldap net_ldap jndi)
|
978
981
|
end
|
979
982
|
|
980
983
|
ACTIVE_LDAP_CONNECTION_ADAPTERS.each do |adapter|
|
@@ -1,4 +1,7 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
1
3
|
require 'active_ldap/schema'
|
4
|
+
require 'active_ldap/entry_attribute'
|
2
5
|
require 'active_ldap/ldap_error'
|
3
6
|
|
4
7
|
module ActiveLdap
|
@@ -13,9 +16,15 @@ module ActiveLdap
|
|
13
16
|
:sasl_mechanisms, :sasl_quiet,
|
14
17
|
:allow_anonymous, :store_password,
|
15
18
|
:scope]
|
19
|
+
|
20
|
+
@@row_even = true
|
21
|
+
|
22
|
+
attr_reader :runtime
|
16
23
|
def initialize(configuration={})
|
24
|
+
@runtime = 0
|
17
25
|
@connection = nil
|
18
26
|
@disconnected = false
|
27
|
+
@entry_attributes = {}
|
19
28
|
@configuration = configuration.dup
|
20
29
|
@logger = @configuration.delete(:logger)
|
21
30
|
@configuration.assert_valid_keys(VALID_ADAPTER_CONFIGURATION_KEYS)
|
@@ -24,12 +33,17 @@ module ActiveLdap
|
|
24
33
|
end
|
25
34
|
end
|
26
35
|
|
36
|
+
def reset_runtime
|
37
|
+
runtime, @runtime = @runtime, 0
|
38
|
+
runtime
|
39
|
+
end
|
40
|
+
|
27
41
|
def connect(options={})
|
28
42
|
host = options[:host] || @host
|
29
43
|
port = options[:port] || @port
|
30
44
|
method = ensure_method(options[:method] || @method)
|
31
45
|
@disconnected = false
|
32
|
-
@connection = yield(host, port, method)
|
46
|
+
@connection, @uri, @with_start_tls = yield(host, port, method)
|
33
47
|
prepare_connection(options)
|
34
48
|
bind(options)
|
35
49
|
end
|
@@ -37,7 +51,7 @@ module ActiveLdap
|
|
37
51
|
def disconnect!(options={})
|
38
52
|
return if @connection.nil?
|
39
53
|
unbind(options)
|
40
|
-
@connection = nil
|
54
|
+
@connection = @uri = @with_start_tls = nil
|
41
55
|
end
|
42
56
|
|
43
57
|
def rebind(options={})
|
@@ -53,20 +67,21 @@ module ActiveLdap
|
|
53
67
|
else
|
54
68
|
allow_anonymous = @allow_anonymous
|
55
69
|
end
|
70
|
+
options = options.merge(:allow_anonymous => allow_anonymous)
|
56
71
|
|
57
72
|
# Rough bind loop:
|
58
73
|
# Attempt 1: SASL if available
|
59
74
|
# Attempt 2: SIMPLE with credentials if password block
|
60
75
|
# Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail (or pwblock returns '')
|
61
76
|
if try_sasl and sasl_bind(bind_dn, options)
|
62
|
-
@logger.info {_('Bound by SASL as %s') % bind_dn}
|
77
|
+
@logger.info {_('Bound to %s by SASL as %s') % [target, bind_dn]}
|
63
78
|
elsif simple_bind(bind_dn, options)
|
64
|
-
@logger.info {_('Bound by simple as %s') % bind_dn}
|
79
|
+
@logger.info {_('Bound to %s by simple as %s') % [target, bind_dn]}
|
65
80
|
elsif allow_anonymous and bind_as_anonymous(options)
|
66
|
-
@logger.info {_('Bound as anonymous')}
|
81
|
+
@logger.info {_('Bound to %s as anonymous') % target}
|
67
82
|
else
|
68
83
|
message = yield if block_given?
|
69
|
-
message ||= _('All authentication methods exhausted.')
|
84
|
+
message ||= _('All authentication methods for %s exhausted.') % target
|
70
85
|
raise AuthenticationError, message
|
71
86
|
end
|
72
87
|
|
@@ -109,12 +124,9 @@ module ActiveLdap
|
|
109
124
|
end
|
110
125
|
end
|
111
126
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
yield(ldif)
|
116
|
-
end
|
117
|
-
end
|
127
|
+
def entry_attribute(object_classes)
|
128
|
+
@entry_attributes[object_classes.uniq.sort] ||=
|
129
|
+
EntryAttribute.new(schema, object_classes)
|
118
130
|
end
|
119
131
|
|
120
132
|
def search(options={})
|
@@ -195,26 +207,41 @@ module ActiveLdap
|
|
195
207
|
end
|
196
208
|
end
|
197
209
|
|
210
|
+
def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={})
|
211
|
+
operation(options) do
|
212
|
+
yield(dn, new_rdn, delete_old_rdn, new_superior)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def log_info(name, runtime, info=nil)
|
217
|
+
return unless @logger
|
218
|
+
return unless @logger.debug?
|
219
|
+
message = "LDAP: #{name} (#{'%f' % runtime})"
|
220
|
+
@logger.debug(format_log_entry(message, info))
|
221
|
+
end
|
222
|
+
|
198
223
|
private
|
199
224
|
def prepare_connection(options)
|
200
225
|
end
|
201
226
|
|
202
227
|
def operation(options)
|
203
228
|
retried = false
|
229
|
+
options = options.dup
|
230
|
+
options[:try_reconnect] = true unless options.has_key?(:try_reconnect)
|
231
|
+
try_reconnect = false
|
204
232
|
begin
|
205
|
-
reconnect_if_need
|
206
|
-
try_reconnect =
|
207
|
-
options[:try_reconnect]
|
233
|
+
reconnect_if_need(options)
|
234
|
+
try_reconnect = options[:try_reconnect]
|
208
235
|
with_timeout(try_reconnect, options) do
|
209
236
|
yield
|
210
237
|
end
|
211
|
-
rescue Errno::EPIPE
|
212
|
-
if
|
213
|
-
raise
|
214
|
-
else
|
238
|
+
rescue Errno::EPIPE, ConnectionError
|
239
|
+
if try_reconnect and !retried
|
215
240
|
retried = true
|
216
241
|
@disconnected = true
|
217
242
|
retry
|
243
|
+
else
|
244
|
+
raise
|
218
245
|
end
|
219
246
|
end
|
220
247
|
end
|
@@ -253,7 +280,7 @@ module ActiveLdap
|
|
253
280
|
Timeout.alarm(@timeout, &block)
|
254
281
|
rescue Timeout::Error => e
|
255
282
|
@logger.error {_('Requested action timed out.')}
|
256
|
-
retry if
|
283
|
+
retry if @retry_on_timeout and try_reconnect and reconnect(options)
|
257
284
|
@logger.error {e.message}
|
258
285
|
raise TimeoutError, e.message
|
259
286
|
end
|
@@ -290,6 +317,16 @@ module ActiveLdap
|
|
290
317
|
passwd = password(bind_dn, options)
|
291
318
|
return false unless passwd
|
292
319
|
|
320
|
+
if passwd.empty?
|
321
|
+
if options[:allow_anonymous]
|
322
|
+
@logger.info {_("Skip simple bind with empty password.")}
|
323
|
+
return false
|
324
|
+
else
|
325
|
+
raise AuthenticationError,
|
326
|
+
_("Can't use empty password for simple bind.")
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
293
330
|
begin
|
294
331
|
operation(options) do
|
295
332
|
yield(bind_dn, passwd)
|
@@ -318,24 +355,7 @@ module ActiveLdap
|
|
318
355
|
construct_filter(components, operator)
|
319
356
|
else
|
320
357
|
operator, components = normalize_array_filter(filter, operator)
|
321
|
-
|
322
|
-
components = components.collect do |component|
|
323
|
-
if component.is_a?(Array) and component.size == 2
|
324
|
-
key, value = component
|
325
|
-
if filter_logical_operator?(key)
|
326
|
-
parse_filter(component)
|
327
|
-
elsif value.is_a?(Hash)
|
328
|
-
parse_filter(value, key)
|
329
|
-
else
|
330
|
-
construct_component(key, value, operator)
|
331
|
-
end
|
332
|
-
elsif component.is_a?(Symbol)
|
333
|
-
assert_filter_logical_operator(component)
|
334
|
-
nil
|
335
|
-
else
|
336
|
-
parse_filter(component, operator)
|
337
|
-
end
|
338
|
-
end
|
358
|
+
components = construct_components(components, operator)
|
339
359
|
construct_filter(components, operator)
|
340
360
|
end
|
341
361
|
end
|
@@ -358,6 +378,7 @@ module ActiveLdap
|
|
358
378
|
operator = filter_operator
|
359
379
|
else
|
360
380
|
components.unshift(filter_operator)
|
381
|
+
components = [components] unless filter_operator.is_a?(Array)
|
361
382
|
end
|
362
383
|
[operator, components]
|
363
384
|
end
|
@@ -370,22 +391,53 @@ module ActiveLdap
|
|
370
391
|
options = value[0]
|
371
392
|
value = value[1]
|
372
393
|
when "=", "~=", "<=", "=>"
|
373
|
-
options[:operator] = value[
|
374
|
-
value
|
394
|
+
options[:operator] = value[0]
|
395
|
+
if value.size > 2
|
396
|
+
value = value[1..-1]
|
397
|
+
else
|
398
|
+
value = value[1]
|
399
|
+
end
|
375
400
|
end
|
376
401
|
end
|
377
402
|
[value, options]
|
378
403
|
end
|
379
404
|
|
405
|
+
def construct_components(components, operator)
|
406
|
+
components.collect do |component|
|
407
|
+
if component.is_a?(Array)
|
408
|
+
if filter_logical_operator?(component[0])
|
409
|
+
parse_filter(component)
|
410
|
+
elsif component.size == 2
|
411
|
+
key, value = component
|
412
|
+
if value.is_a?(Hash)
|
413
|
+
parse_filter(value, key)
|
414
|
+
else
|
415
|
+
construct_component(key, value, operator)
|
416
|
+
end
|
417
|
+
else
|
418
|
+
construct_component(component[0], component[1..-1], operator)
|
419
|
+
end
|
420
|
+
elsif component.is_a?(Symbol)
|
421
|
+
assert_filter_logical_operator(component)
|
422
|
+
nil
|
423
|
+
else
|
424
|
+
parse_filter(component, operator)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
380
429
|
def construct_component(key, value, operator=nil)
|
381
430
|
value, options = extract_filter_value_options(value)
|
431
|
+
comparison_operator = options[:operator] || "="
|
382
432
|
if collection?(value)
|
433
|
+
return nil if value.empty?
|
434
|
+
operator, value = normalize_array_filter(value, operator)
|
383
435
|
values = []
|
384
436
|
value.each do |val|
|
385
437
|
if collection?(val)
|
386
|
-
values.concat(val.collect {|v| [key, v]})
|
438
|
+
values.concat(val.collect {|v| [key, comparison_operator, v]})
|
387
439
|
else
|
388
|
-
values << [key, val]
|
440
|
+
values << [key, comparison_operator, val]
|
389
441
|
end
|
390
442
|
end
|
391
443
|
values[0] = values[0][1] if filter_logical_operator?(values[0][1])
|
@@ -394,7 +446,7 @@ module ActiveLdap
|
|
394
446
|
[
|
395
447
|
"(",
|
396
448
|
escape_filter_key(key),
|
397
|
-
|
449
|
+
comparison_operator,
|
398
450
|
escape_filter_value(value, options),
|
399
451
|
")"
|
400
452
|
].join
|
@@ -526,6 +578,70 @@ module ActiveLdap
|
|
526
578
|
return [] if dse.nil?
|
527
579
|
dse[key] || dse[key.downcase] || []
|
528
580
|
end
|
581
|
+
|
582
|
+
def root_dse(attrs, options={})
|
583
|
+
search(:base => "",
|
584
|
+
:scope => :base,
|
585
|
+
:attributes => attrs).collect do |dn, attributes|
|
586
|
+
attributes
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
def construct_uri(host, port, ssl)
|
591
|
+
protocol = ssl ? "ldaps" : "ldap"
|
592
|
+
URI.parse("#{protocol}://#{host}:#{port}").to_s
|
593
|
+
end
|
594
|
+
|
595
|
+
def target
|
596
|
+
return nil if @uri.nil?
|
597
|
+
if @with_start_tls
|
598
|
+
"#{@uri}(StartTLS)"
|
599
|
+
else
|
600
|
+
@uri
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def log(name, info=nil)
|
605
|
+
if block_given?
|
606
|
+
if @logger and @logger.debug?
|
607
|
+
result = nil
|
608
|
+
runtime = Benchmark.realtime {result = yield}
|
609
|
+
@runtime += runtime
|
610
|
+
log_info(name, runtime, info)
|
611
|
+
result
|
612
|
+
else
|
613
|
+
yield
|
614
|
+
end
|
615
|
+
else
|
616
|
+
log_info(name, info, 0)
|
617
|
+
nil
|
618
|
+
end
|
619
|
+
rescue Exception
|
620
|
+
log_info("#{name}: FAILED", 0,
|
621
|
+
(info || {}).merge(:error => $!.class.name,
|
622
|
+
:error_message => $!.message))
|
623
|
+
raise
|
624
|
+
end
|
625
|
+
|
626
|
+
def format_log_entry(message, info=nil)
|
627
|
+
if ActiveLdap::Base.colorize_logging
|
628
|
+
if @@row_even
|
629
|
+
message_color, dump_color = "4;36;1", "0;1"
|
630
|
+
else
|
631
|
+
@@row_even = true
|
632
|
+
message_color, dump_color = "4;35;1", "0"
|
633
|
+
end
|
634
|
+
@@row_even = !@@row_even
|
635
|
+
|
636
|
+
log_entry = " \e[#{message_color}m#{message}\e[0m"
|
637
|
+
log_entry << ": \e[#{dump_color}m#{info.inspect}\e[0m" if info
|
638
|
+
log_entry
|
639
|
+
else
|
640
|
+
log_entry = message
|
641
|
+
log_entry += ": #{info.inspect}" if info
|
642
|
+
log_entry
|
643
|
+
end
|
644
|
+
end
|
529
645
|
end
|
530
646
|
end
|
531
647
|
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require 'active_ldap/adapter/base'
|
2
|
+
|
3
|
+
module ActiveLdap
|
4
|
+
module Adapter
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def jndi_connection(options)
|
8
|
+
require 'active_ldap/adapter/jndi_connection'
|
9
|
+
Jndi.new(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Jndi < Base
|
15
|
+
METHOD = {
|
16
|
+
:ssl => :ssl,
|
17
|
+
:tls => :start_tls,
|
18
|
+
:plain => nil,
|
19
|
+
}
|
20
|
+
|
21
|
+
def connect(options={})
|
22
|
+
super do |host, port, method|
|
23
|
+
uri = construct_uri(host, port, method == :ssl)
|
24
|
+
with_start_tls = method == :start_tls
|
25
|
+
info = {:uri => uri, :with_start_tls => with_start_tls}
|
26
|
+
[log("connect", info) {JndiConnection.new(host, port, method)},
|
27
|
+
uri, with_start_tls]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def unbind(options={})
|
32
|
+
return unless bound?
|
33
|
+
operation(options) do
|
34
|
+
execute(:unbind)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def bind_as_anonymous(options={})
|
39
|
+
super do
|
40
|
+
execute(:bind_as_anonymous, :name => "bind: anonymous")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def bound?
|
45
|
+
connecting? and @connection.bound?
|
46
|
+
end
|
47
|
+
|
48
|
+
def search(options={}, &block)
|
49
|
+
super(options) do |base, scope, filter, attrs, limit, callback|
|
50
|
+
info = {
|
51
|
+
:base => base, :scope => scope_name(scope), :filter => filter,
|
52
|
+
:attributes => attrs,
|
53
|
+
}
|
54
|
+
execute(:search, info,
|
55
|
+
base, scope, filter, attrs, limit, callback, &block)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def delete(targets, options={})
|
60
|
+
super do |target|
|
61
|
+
execute(:delete, {:dn => target}, target)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def add(dn, entries, options={})
|
66
|
+
super do |dn, entries|
|
67
|
+
info = {:dn => dn, :attributes => entries}
|
68
|
+
execute(:add, info, dn, parse_entries(entries))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def modify(dn, entries, options={})
|
73
|
+
super do |dn, entries|
|
74
|
+
info = {:dn => dn, :attributes => entries}
|
75
|
+
execute(:modify, info, dn, parse_entries(entries))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={})
|
80
|
+
super do |dn, new_rdn, delete_old_rdn, new_superior|
|
81
|
+
info = {
|
82
|
+
:name => "modify: RDN", :dn => dn, :new_rdn => new_rdn,
|
83
|
+
:delete_old_rdn => delete_old_rdn,
|
84
|
+
}
|
85
|
+
execute(:modify_rdn, dn, new_rdn, delete_old_rdn)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def execute(method, info=nil, *args, &block)
|
91
|
+
name = (info || {}).delete(:name) || method
|
92
|
+
log(name, info) {@connection.send(method, *args, &block)}
|
93
|
+
rescue JndiConnection::NamingException
|
94
|
+
if /\[LDAP: error code (\d+) - ([^\]]+)\]/ =~ $!.to_s
|
95
|
+
message = $2
|
96
|
+
klass = LdapError::ERRORS[Integer($1)]
|
97
|
+
klass ||= ActiveLdap::LdapError
|
98
|
+
raise klass, message
|
99
|
+
end
|
100
|
+
raise
|
101
|
+
end
|
102
|
+
|
103
|
+
def ensure_method(method)
|
104
|
+
method ||= "plain"
|
105
|
+
normalized_method = method.to_s.downcase.to_sym
|
106
|
+
return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
|
107
|
+
|
108
|
+
available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
|
109
|
+
format = _("%s is not one of the available connect methods: %s")
|
110
|
+
raise ConfigurationError, format % [method.inspect, available_methods]
|
111
|
+
end
|
112
|
+
|
113
|
+
def ensure_scope(scope)
|
114
|
+
scope_map = {
|
115
|
+
:base => 0,
|
116
|
+
:one => 1,
|
117
|
+
:sub => 2,
|
118
|
+
}
|
119
|
+
value = scope_map[scope || :sub]
|
120
|
+
if value.nil?
|
121
|
+
available_scopes = scope_map.keys.inspect
|
122
|
+
format = _("%s is not one of the available LDAP scope: %s")
|
123
|
+
raise ArgumentError, format % [scope.inspect, available_scopes]
|
124
|
+
end
|
125
|
+
value
|
126
|
+
end
|
127
|
+
|
128
|
+
def scope_name(scope)
|
129
|
+
{
|
130
|
+
0 => :base,
|
131
|
+
1 => :one,
|
132
|
+
2 => :sub,
|
133
|
+
}[scope]
|
134
|
+
end
|
135
|
+
|
136
|
+
def sasl_bind(bind_dn, options={})
|
137
|
+
super do |bind_dn, mechanism, quiet|
|
138
|
+
info = {:name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism}
|
139
|
+
execute(:sasl_bind, info, bind_dn, mechanism, quiet)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def simple_bind(bind_dn, options={})
|
144
|
+
super do |bind_dn, passwd|
|
145
|
+
info = {:name => "bind", :dn => bind_dn}
|
146
|
+
execute(:simple_bind, info, bind_dn, passwd)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_entries(entries)
|
151
|
+
result = []
|
152
|
+
entries.each do |type, key, attributes|
|
153
|
+
mod_type = ensure_mod_type(type)
|
154
|
+
binary = schema.attribute(key).binary?
|
155
|
+
attributes.each do |name, values|
|
156
|
+
result << JndiConnection::ModifyRecord.new(mod_type, name,
|
157
|
+
values, binary)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
result
|
161
|
+
end
|
162
|
+
|
163
|
+
def ensure_mod_type(type)
|
164
|
+
case type
|
165
|
+
when :replace, :add
|
166
|
+
type
|
167
|
+
when :delete
|
168
|
+
:remove
|
169
|
+
else
|
170
|
+
raise ArgumentError, _("unknown type: %s") % type
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|