activeldap 0.9.0 → 0.10.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/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
|