activeldap 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/CHANGES +34 -0
  2. data/README +13 -0
  3. data/Rakefile +2 -1
  4. data/TODO +6 -0
  5. data/benchmark/bench-al.rb +68 -17
  6. data/examples/al-admin/app/helpers/application_helper.rb +3 -5
  7. data/examples/al-admin/app/views/layouts/_footer.html.erb +2 -0
  8. data/examples/al-admin/config/boot.rb +7 -7
  9. data/examples/al-admin/config/environment.rb +27 -12
  10. data/examples/al-admin/config/environments/development.rb +0 -1
  11. data/examples/al-admin/config/environments/production.rb +6 -1
  12. data/examples/al-admin/config/environments/test.rb +1 -1
  13. data/examples/al-admin/config/initializers/gettext.rb +15 -1
  14. data/examples/al-admin/po/en/al-admin.po +1 -1
  15. data/examples/al-admin/po/ja/al-admin.po +1 -1
  16. data/examples/al-admin/po/nl/al-admin.po +1 -1
  17. data/examples/al-admin/public/dispatch.cgi +0 -0
  18. data/examples/al-admin/public/dispatch.fcgi +0 -0
  19. data/examples/al-admin/public/dispatch.rb +0 -0
  20. data/examples/al-admin/public/javascripts/controls.js +73 -73
  21. data/examples/al-admin/public/javascripts/dragdrop.js +166 -165
  22. data/examples/al-admin/public/javascripts/effects.js +174 -166
  23. data/examples/al-admin/public/javascripts/prototype.js +362 -267
  24. data/examples/al-admin/script/about +0 -0
  25. data/examples/al-admin/script/console +0 -0
  26. data/examples/al-admin/script/dbconsole +3 -0
  27. data/examples/al-admin/script/destroy +0 -0
  28. data/examples/al-admin/script/generate +0 -0
  29. data/examples/al-admin/script/performance/benchmarker +0 -0
  30. data/examples/al-admin/script/performance/profiler +0 -0
  31. data/examples/al-admin/script/performance/request +0 -0
  32. data/examples/al-admin/script/plugin +0 -0
  33. data/examples/al-admin/script/process/inspector +0 -0
  34. data/examples/al-admin/script/process/reaper +0 -0
  35. data/examples/al-admin/script/process/spawner +0 -0
  36. data/examples/al-admin/script/runner +0 -0
  37. data/examples/al-admin/script/server +0 -0
  38. data/examples/al-admin/test/run-test.sh +0 -0
  39. data/examples/groupadd +0 -0
  40. data/examples/groupdel +0 -0
  41. data/examples/groupls +0 -0
  42. data/examples/groupmod +0 -0
  43. data/examples/lpasswd +0 -0
  44. data/examples/ouadd +0 -0
  45. data/examples/useradd +0 -0
  46. data/examples/useradd-binary +0 -0
  47. data/examples/userdel +0 -0
  48. data/examples/userls +0 -0
  49. data/examples/usermod +0 -0
  50. data/examples/usermod-binary-add +0 -0
  51. data/examples/usermod-binary-add-time +0 -0
  52. data/examples/usermod-binary-del +0 -0
  53. data/examples/usermod-lang-add +0 -0
  54. data/lib/active_ldap.rb +10 -4
  55. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +28 -9
  56. data/lib/active_ldap/adapter/base.rb +30 -17
  57. data/lib/active_ldap/adapter/jndi.rb +5 -1
  58. data/lib/active_ldap/adapter/ldap.rb +5 -1
  59. data/lib/active_ldap/association/has_many_utils.rb +7 -1
  60. data/lib/active_ldap/associations.rb +10 -5
  61. data/lib/active_ldap/attributes.rb +6 -1
  62. data/lib/active_ldap/base.rb +154 -52
  63. data/lib/active_ldap/configuration.rb +1 -1
  64. data/lib/active_ldap/connection.rb +7 -4
  65. data/lib/active_ldap/get_text.rb +11 -3
  66. data/lib/active_ldap/ldif.rb +16 -4
  67. data/lib/active_ldap/operations.rb +13 -5
  68. data/lib/active_ldap/schema.rb +6 -2
  69. data/lib/active_ldap/schema/syntaxes.rb +15 -3
  70. data/lib/active_ldap/user_password.rb +4 -4
  71. data/lib/active_ldap/validations.rb +32 -44
  72. data/lib/active_ldap/xml.rb +125 -0
  73. data/po/en/active-ldap.po +740 -85
  74. data/po/ja/active-ldap.po +748 -547
  75. data/rails/README +54 -0
  76. data/rails/init.rb +33 -0
  77. data/rails/plugin/active_ldap/generators/README +2 -0
  78. data/rails/plugin/active_ldap/generators/model_active_ldap/model_active_ldap_generator.rb +1 -1
  79. data/rails/plugin/active_ldap/init.rb +3 -0
  80. data/rails_generators/model_active_ldap/USAGE +17 -0
  81. data/rails_generators/model_active_ldap/model_active_ldap_generator.rb +69 -0
  82. data/rails_generators/model_active_ldap/templates/model_active_ldap.rb +3 -0
  83. data/rails_generators/model_active_ldap/templates/unit_test.rb +8 -0
  84. data/rails_generators/scaffold_active_ldap/scaffold_active_ldap_generator.rb +7 -0
  85. data/rails_generators/scaffold_active_ldap/templates/ldap.yml +18 -0
  86. data/rails_generators/scaffold_al/scaffold_al_generator.rb +20 -0
  87. data/test-unit/History.txt +50 -1
  88. data/test-unit/Manifest.txt +22 -12
  89. data/test-unit/README.txt +31 -12
  90. data/test-unit/Rakefile +14 -1
  91. data/test-unit/TODO +5 -0
  92. data/test-unit/bin/testrb +0 -0
  93. data/test-unit/lib/test/unit.rb +62 -0
  94. data/test-unit/lib/test/unit/assertions.rb +419 -75
  95. data/test-unit/lib/test/unit/autorunner.rb +70 -13
  96. data/test-unit/lib/test/unit/collector.rb +1 -1
  97. data/test-unit/lib/test/unit/collector/load.rb +1 -1
  98. data/test-unit/lib/test/unit/color-scheme.rb +86 -0
  99. data/test-unit/lib/test/unit/color.rb +40 -5
  100. data/test-unit/lib/test/unit/diff.rb +14 -0
  101. data/test-unit/lib/test/unit/fixture.rb +7 -16
  102. data/test-unit/lib/test/unit/notification.rb +9 -0
  103. data/test-unit/lib/test/unit/omission.rb +14 -0
  104. data/test-unit/lib/test/unit/pending.rb +16 -0
  105. data/test-unit/lib/test/unit/priority.rb +17 -2
  106. data/test-unit/lib/test/unit/runner/console.rb +8 -2
  107. data/test-unit/lib/test/unit/testcase.rb +188 -2
  108. data/test-unit/lib/test/unit/ui/console/testrunner.rb +51 -26
  109. data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
  110. data/test-unit/lib/test/unit/version.rb +1 -1
  111. data/test-unit/sample/test_user.rb +22 -0
  112. data/test-unit/test/collector/{test_descendant.rb → test-descendant.rb} +0 -0
  113. data/test-unit/test/collector/{test_load.rb → test-load.rb} +1 -1
  114. data/test-unit/test/run-test.rb +0 -0
  115. data/test-unit/test/{test_attribute.rb → test-attribute.rb} +0 -0
  116. data/test-unit/test/test-color-scheme.rb +56 -0
  117. data/test-unit/test/{test_color.rb → test-color.rb} +10 -0
  118. data/test-unit/test/{test_diff.rb → test-diff.rb} +0 -0
  119. data/test-unit/test/{test_emacs_runner.rb → test-emacs-runner.rb} +0 -0
  120. data/test-unit/test/test-fixture.rb +287 -0
  121. data/test-unit/test/{test_notification.rb → test-notification.rb} +4 -4
  122. data/test-unit/test/{test_omission.rb → test-omission.rb} +6 -6
  123. data/test-unit/test/{test_pending.rb → test-pending.rb} +12 -6
  124. data/test-unit/test/{test_priority.rb → test-priority.rb} +30 -0
  125. data/test-unit/test/test_assertions.rb +411 -69
  126. data/test-unit/test/test_testcase.rb +70 -3
  127. data/test-unit/test/{testunit_test_util.rb → testunit-test-util.rb} +4 -2
  128. data/test-unit/test/ui/test_testrunmediator.rb +1 -1
  129. data/test-unit/test/util/test-method-owner-finder.rb +38 -0
  130. data/test/run-test.rb +0 -0
  131. data/test/test_adapter.rb +3 -0
  132. data/test/test_associations.rb +50 -7
  133. data/test/test_base.rb +193 -11
  134. data/test/test_connection_per_dn.rb +1 -1
  135. data/test/test_ldif.rb +86 -0
  136. data/test/test_load.rb +7 -0
  137. data/test/test_schema.rb +31 -1
  138. data/test/test_syntax.rb +20 -0
  139. data/test/test_user_password.rb +22 -14
  140. data/test/test_validation.rb +70 -29
  141. metadata +99 -77
  142. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  143. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  144. data/examples/al-admin/config/initializers/ralative_url_support.rb +0 -1
  145. data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +0 -9
  146. data/test-unit-ext/misc/rd2html.rb +0 -42
  147. data/test-unit/test/test_fixture.rb +0 -275
File without changes
File without changes
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require File.dirname(__FILE__) + '/../config/boot'
3
+ require 'commands/dbconsole'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -905,12 +905,15 @@
905
905
  # package, and I'd like to see it prove helpful to more people than just myself.
906
906
  #
907
907
 
908
- require_gem_if_need = Proc.new do |library_name, gem_name|
908
+ require_gem_if_need = Proc.new do |library_name, gem_name, *gem_args|
909
909
  begin
910
+ if !gem_args.empty? and Object.const_defined?(:Gem)
911
+ gem gem_name, *gem_args
912
+ end
910
913
  require library_name
911
914
  rescue LoadError
912
915
  require 'rubygems'
913
- gem gem_name
916
+ gem gem_name, *gem_args
914
917
  require library_name
915
918
  end
916
919
  end
@@ -928,7 +931,7 @@ if dependencies.respond_to?(:load_paths)
928
931
  end
929
932
 
930
933
  module ActiveLdap
931
- VERSION = "1.0.1"
934
+ VERSION = "1.0.2"
932
935
  end
933
936
 
934
937
  if RUBY_PLATFORM.match('linux')
@@ -939,7 +942,8 @@ end
939
942
 
940
943
  require_gem_if_need.call("active_record", "activerecord")
941
944
  begin
942
- require_gem_if_need.call("gettext/active_record", "gettext")
945
+ require_gem_if_need.call("locale")
946
+ require_gem_if_need.call("gettext", "gettext", ">= 1.94")
943
947
  rescue LoadError
944
948
  end
945
949
  require 'active_ldap/get_text'
@@ -948,6 +952,7 @@ require 'active_ldap/base'
948
952
 
949
953
  require 'active_ldap/distinguished_name'
950
954
  require 'active_ldap/ldif'
955
+ require 'active_ldap/xml'
951
956
 
952
957
  require 'active_ldap/associations'
953
958
  require 'active_ldap/attributes'
@@ -961,6 +966,7 @@ require 'active_ldap/acts/tree'
961
966
 
962
967
  require 'active_ldap/populate'
963
968
  require 'active_ldap/escape'
969
+ require 'active_ldap/user_password'
964
970
  require 'active_ldap/helper'
965
971
 
966
972
  require 'active_ldap/validations'
@@ -3,22 +3,31 @@ module ActiveLdap
3
3
  module LdapBenchmarking
4
4
  def self.included(base)
5
5
  base.class_eval do
6
- alias_method_chain :render, :active_ldap_benchmark
7
- alias_method_chain :rendering_runtime, :active_ldap
6
+ alias_method_chain :render_with_benchmark, :active_ldap
7
+ if private_method_defined?(:view_runtime)
8
+ alias_method_chain :view_runtime, :active_ldap
9
+ else
10
+ alias_method_chain :rendering_runtime, :active_ldap
11
+ end
8
12
  end
9
13
  end
10
14
 
11
15
  protected
12
- def render_with_active_ldap_benchmark(*args, &block)
16
+ def render_with_benchmark_with_active_ldap(*args, &block)
13
17
  if logger
14
- @ldap_runtime_before_render = ActiveLdap::Base.reset_runtime
15
- result = render_without_active_ldap_benchmark(*args, &block)
18
+ ldap_runtime_before_render = ActiveLdap::Base.reset_runtime
19
+ end
20
+ result = render_with_benchmark_without_active_ldap(*args, &block)
21
+ if logger
22
+ @ldap_runtime_before_render = ldap_runtime_before_render
16
23
  @ldap_runtime_after_render = ActiveLdap::Base.reset_runtime
17
- @rendering_runtime -= @ldap_runtime_after_render
18
- result
19
- else
20
- render_without_active_ldap_benchmark(*args, &block)
24
+ if defined?(@rendering_runtime)
25
+ @rendering_runtime -= @ldap_runtime_after_render
26
+ else
27
+ @view_runtime -= @ldap_runtime_after_render
28
+ end
21
29
  end
30
+ result
22
31
  end
23
32
 
24
33
  private
@@ -30,6 +39,16 @@ module ActiveLdap
30
39
  ldap_percentage = ldap_runtime * 100 / runtime
31
40
  result + (" | LDAP: %.5f (%d%%)" % [ldap_runtime, ldap_percentage])
32
41
  end
42
+
43
+ def view_runtime_with_active_ldap
44
+ result = view_runtime_without_active_ldap
45
+ ldap_runtime = ActiveLdap::Base.reset_runtime
46
+ @ldap_runtime_before_render ||= 0
47
+ @ldap_runtime_after_render ||= 0
48
+ ldap_runtime += @ldap_runtime_before_render
49
+ ldap_runtime += @ldap_runtime_after_render
50
+ result + (", LDAP: %.0f" % (ldap_runtime * 1000))
51
+ end
33
52
  end
34
53
  end
35
54
  end
@@ -40,8 +40,9 @@ module ActiveLdap
40
40
 
41
41
  def connect(options={})
42
42
  host = options[:host] || @host
43
- port = options[:port] || @port
44
- method = ensure_method(options[:method] || @method)
43
+ method = options[:method] || @method || :plain
44
+ port = options[:port] || @port || ensure_port(method)
45
+ method = ensure_method(method)
45
46
  @disconnected = false
46
47
  @connection, @uri, @with_start_tls = yield(host, port, method)
47
48
  prepare_connection(options)
@@ -167,7 +168,11 @@ module ActiveLdap
167
168
  begin
168
169
  operation(options) do
169
170
  targets.each do |target|
170
- yield(target)
171
+ begin
172
+ yield(target)
173
+ rescue LdapError::UnwillingToPerform, LdapError::InsufficientAccess
174
+ raise OperationNotPermitted, _("%s: %s") % [$!.message, target]
175
+ end
171
176
  end
172
177
  end
173
178
  rescue LdapError::NoSuchObject
@@ -198,7 +203,11 @@ module ActiveLdap
198
203
  def modify(dn, entries, options={})
199
204
  begin
200
205
  operation(options) do
201
- yield(dn, entries)
206
+ begin
207
+ yield(dn, entries)
208
+ rescue LdapError::UnwillingToPerform, LdapError::InsufficientAccess
209
+ raise OperationNotPermitted, _("%s: %s") % [$!.message, target]
210
+ end
202
211
  end
203
212
  rescue LdapError::UndefinedType
204
213
  raise
@@ -213,14 +222,22 @@ module ActiveLdap
213
222
  end
214
223
  end
215
224
 
216
- def log_info(name, runtime, info=nil)
225
+ def log_info(name, runtime_in_seconds, info=nil)
217
226
  return unless @logger
218
227
  return unless @logger.debug?
219
- message = "LDAP: #{name} (#{'%f' % runtime})"
228
+ message = "LDAP: #{name} (#{'%.1f' % (runtime_in_seconds * 1000)}ms)"
220
229
  @logger.debug(format_log_entry(message, info))
221
230
  end
222
231
 
223
232
  private
233
+ def ensure_port(method)
234
+ if method == :ssl
235
+ URI::LDAPS::DEFAULT_PORT
236
+ else
237
+ URI::LDAP::DEFAULT_PORT
238
+ end
239
+ end
240
+
224
241
  def prepare_connection(options)
225
242
  end
226
243
 
@@ -388,7 +405,7 @@ module ActiveLdap
388
405
  when Hash
389
406
  options = value[0]
390
407
  value = value[1]
391
- when "=", "~=", "<=", "=>"
408
+ when "=", "~=", "<=", ">="
392
409
  options[:operator] = value[0]
393
410
  if value.size > 2
394
411
  value = value[1..-1]
@@ -601,17 +618,13 @@ module ActiveLdap
601
618
 
602
619
  def log(name, info=nil)
603
620
  if block_given?
604
- if @logger and @logger.debug?
605
- result = nil
606
- runtime = Benchmark.realtime {result = yield}
607
- @runtime += runtime
608
- log_info(name, runtime, info)
609
- result
610
- else
611
- yield
612
- end
621
+ result = nil
622
+ seconds = Benchmark.realtime {result = yield}
623
+ @runtime += seconds
624
+ log_info(name, seconds, info)
625
+ result
613
626
  else
614
- log_info(name, info, 0)
627
+ log_info(name, 0, info)
615
628
  nil
616
629
  end
617
630
  rescue Exception
@@ -153,8 +153,12 @@ module ActiveLdap
153
153
  mod_type = ensure_mod_type(type)
154
154
  binary = schema.attribute(key).binary?
155
155
  attributes.each do |name, values|
156
+ real_binary = binary
157
+ if values.any? {|value| Ldif::Attribute.binary_value?(value)}
158
+ real_binary = true
159
+ end
156
160
  result << JndiConnection::ModifyRecord.new(mod_type, name,
157
- values, binary)
161
+ values, real_binary)
158
162
  end
159
163
  end
160
164
  result
@@ -257,7 +257,11 @@ module ActiveLdap
257
257
  binary = schema.attribute(key).binary?
258
258
  mod_type |= LDAP::LDAP_MOD_BVALUES if binary
259
259
  attributes.each do |name, values|
260
- result << LDAP.mod(mod_type, name, values)
260
+ additional_mod_type = 0
261
+ if values.any? {|value| Ldif::Attribute.binary_value?(value)}
262
+ additional_mod_type |= LDAP::LDAP_MOD_BVALUES
263
+ end
264
+ result << LDAP.mod(mod_type | additional_mod_type, name, values)
261
265
  end
262
266
  end
263
267
  result
@@ -19,7 +19,13 @@ module ActiveLdap
19
19
  target
20
20
  end
21
21
  end
22
- targets = foreign_class.find(requested_targets, find_options)
22
+ targets = []
23
+ requested_targets.each do |target|
24
+ begin
25
+ targets << foreign_class.find(target, find_options)
26
+ rescue EntryNotFound
27
+ end
28
+ end
23
29
  else
24
30
  components = requested_targets.collect do |value|
25
31
  [foreign_base_key, value]
@@ -31,7 +31,8 @@ module ActiveLdap
31
31
  #
32
32
  # This defines a method for an extension class map its DN key
33
33
  # attribute value on to multiple items which reference it by
34
- # |:foreign_key| in the other LDAP entry covered by class |:class_name|.
34
+ # |:foreign_key| in the other LDAP entry covered by class
35
+ # |:class_name|.
35
36
  #
36
37
  # Example:
37
38
  # belongs_to :groups, :class_name => "Group",
@@ -44,7 +45,8 @@ module ActiveLdap
44
45
  #
45
46
  def belongs_to(association_id, options={})
46
47
  validate_belongs_to_options(options)
47
- klass = options[:class] || association_id.to_s.classify
48
+ klass = options[:class]
49
+ klass ||= (options[:class_name] || association_id.to_s).classify
48
50
  foreign_key = options[:foreign_key]
49
51
  primary_key = options[:primary_key]
50
52
  many = options[:many]
@@ -96,7 +98,8 @@ module ActiveLdap
96
98
  # :wrap => "memberUid" # Group#memberUid
97
99
  def has_many(association_id, options = {})
98
100
  validate_has_many_options(options)
99
- klass = options[:class] || association_id.to_s.classify
101
+ klass = options[:class]
102
+ klass ||= (options[:class_name] || association_id.to_s).classify
100
103
  foreign_key = options[:foreign_key] || "#{association_id}_id"
101
104
  primary_key = options[:primary_key]
102
105
  set_associated_class(association_id, klass)
@@ -149,13 +152,15 @@ module ActiveLdap
149
152
  EOM
150
153
  end
151
154
 
152
- VALID_BELONGS_TO_OPTIONS = [:class, :foreign_key, :primary_key, :many,
155
+ VALID_BELONGS_TO_OPTIONS = [:class, :class_name,
156
+ :foreign_key, :primary_key, :many,
153
157
  :extend]
154
158
  def validate_belongs_to_options(options)
155
159
  options.assert_valid_keys(VALID_BELONGS_TO_OPTIONS)
156
160
  end
157
161
 
158
- VALID_HAS_MANY_OPTIONS = [:class, :foreign_key, :primary_key, :wrap,
162
+ VALID_HAS_MANY_OPTIONS = [:class, :class_name,
163
+ :foreign_key, :primary_key, :wrap,
159
164
  :extend]
160
165
  def validate_has_many_options(options)
161
166
  options.assert_valid_keys(VALID_HAS_MANY_OPTIONS)
@@ -126,7 +126,12 @@ module ActiveLdap
126
126
  end
127
127
 
128
128
  def attributes_protected_by_default
129
- [dn_attribute, 'objectClass']
129
+ _dn_attribute = nil
130
+ begin
131
+ _dn_attribute = dn_attribute_with_fallback
132
+ rescue DistinguishedNameInvalid
133
+ end
134
+ [_dn_attribute, 'objectClass'].compact
130
135
  end
131
136
 
132
137
  def normalize_attribute_name(name)
@@ -30,6 +30,7 @@
30
30
 
31
31
  require 'English'
32
32
  require 'thread'
33
+ require 'erb'
33
34
 
34
35
  module ActiveLdap
35
36
  # OO-interface to LDAP assuming pam/nss_ldap-style organization with
@@ -438,6 +439,8 @@ module ActiveLdap
438
439
  def inspect
439
440
  if self == Base
440
441
  super
442
+ elsif abstract_class?
443
+ "#{super}(abstract)"
441
444
  else
442
445
  class_names = []
443
446
  must = []
@@ -454,6 +457,48 @@ module ActiveLdap
454
457
  end
455
458
  end
456
459
 
460
+ attr_accessor :abstract_class
461
+ def abstract_class?
462
+ defined?(@abstract_class) && @abstract_class
463
+ end
464
+
465
+ def class_of_active_ldap_descendant(klass)
466
+ if klass.superclass == Base or klass.superclass.abstract_class?
467
+ klass
468
+ elsif klass.superclass.nil?
469
+ raise Error, _("%s doesn't belong in a hierarchy descending " \
470
+ "from ActiveLdap") % (name || to_s)
471
+ else
472
+ class_of_active_ldap_descendant(klass.superclass)
473
+ end
474
+ end
475
+
476
+ def self_and_descendents_from_active_ldap
477
+ klass = self
478
+ classes = [klass]
479
+ while klass != klass.base_class
480
+ classes << klass = klass.superclass
481
+ end
482
+ classes
483
+ rescue
484
+ [self]
485
+ end
486
+ alias_method(:self_and_descendents_from_active_record,
487
+ :self_and_descendents_from_active_ldap)
488
+
489
+ def human_name(options={})
490
+ defaults = self_and_descendents_from_active_ldap.collect do |klass|
491
+ if klass.name.blank?
492
+ nil
493
+ else
494
+ :"#{klass.name.underscore}"
495
+ end
496
+ end
497
+ defaults << name.humanize
498
+ defaults = defaults.compact
499
+ defaults.first || name || to_s
500
+ end
501
+
457
502
  private
458
503
  def inspect_attributes(attributes)
459
504
  inspected_attribute_names = {}
@@ -579,7 +624,6 @@ module ActiveLdap
579
624
  raise ArgumentError, format % attributes.inspect
580
625
  end
581
626
  yield self if block_given?
582
- assert_dn_attribute
583
627
  end
584
628
 
585
629
  # Returns true if the +comparison_object+ is the same object, or is of
@@ -648,7 +692,7 @@ module ActiveLdap
648
692
  end
649
693
 
650
694
  def id
651
- get_attribute(dn_attribute)
695
+ get_attribute(dn_attribute_with_fallback)
652
696
  end
653
697
 
654
698
  def to_param
@@ -656,13 +700,14 @@ module ActiveLdap
656
700
  end
657
701
 
658
702
  def dn=(value)
659
- set_attribute(dn_attribute, value)
703
+ set_attribute(dn_attribute_with_fallback, value)
660
704
  @dn = nil
661
705
  end
662
706
  alias_method(:id=, :dn=)
663
707
 
664
708
  alias_method(:dn_attribute_of_class, :dn_attribute)
665
709
  def dn_attribute
710
+ ensure_update_dn
666
711
  _dn_attribute = @dn_attribute || dn_attribute_of_class
667
712
  to_real_attribute_name(_dn_attribute) || _dn_attribute
668
713
  end
@@ -792,7 +837,7 @@ module ActiveLdap
792
837
  # Also be sure to only pass in key-value pairs of your choosing.
793
838
  # Do not let URL/form hackers supply the keys.
794
839
  def attributes=(new_attributes)
795
- return if new_attributes.nil?
840
+ return if new_attributes.blank?
796
841
  _schema = _local_entry_attribute = nil
797
842
  targets = remove_attributes_protected_from_mass_assignment(new_attributes)
798
843
  targets.each do |key, value|
@@ -817,26 +862,23 @@ module ActiveLdap
817
862
  end
818
863
 
819
864
  def to_xml(options={})
820
- root = options[:root] || self.class.name.underscore
821
- result = "<#{root}>\n"
822
- result << " <dn>#{dn}</dn>\n"
823
- normalize_data(@data).sort_by {|key, values| key}.each do |key, values|
824
- targets = []
825
- values.each do |value|
826
- if value.is_a?(Hash)
827
- value.each do |option, real_value|
828
- targets << [real_value, " #{option}=\"true\""]
829
- end
865
+ options = options.dup
866
+ options[:root] ||= self.class.name.underscore
867
+ except = options[:except]
868
+ if except
869
+ options[:except] = except.collect do |name|
870
+ if name.to_s.downcase == "dn"
871
+ "dn"
830
872
  else
831
- targets << [value]
873
+ to_real_attribute_name(name)
832
874
  end
833
- end
834
- targets.sort_by {|value, attr| value}.each do |value, attr|
835
- result << " <#{key}#{attr}>#{value}</#{key}>\n"
836
- end
875
+ end.compact
837
876
  end
838
- result << "</#{root}>\n"
839
- result
877
+ XML.new(dn, normalize_data(@data), schema).to_s(options)
878
+ end
879
+
880
+ def to_s
881
+ to_ldif
840
882
  end
841
883
 
842
884
  def have_attribute?(name, except=[])
@@ -922,7 +964,10 @@ module ActiveLdap
922
964
 
923
965
  alias_method :base_of_class, :base
924
966
  def base
925
- [@base, base_of_class].compact.join(",")
967
+ ensure_update_dn
968
+ [@base, base_of_class].find_all do |component|
969
+ not component.blank?
970
+ end.join(",")
926
971
  end
927
972
 
928
973
  undef_method :base=
@@ -959,6 +1004,17 @@ module ActiveLdap
959
1004
  end
960
1005
 
961
1006
  private
1007
+ def dn_attribute_with_fallback
1008
+ begin
1009
+ dn_attribute
1010
+ rescue DistinguishedNameInvalid
1011
+ _dn_attribute = @dn_attribute || dn_attribute_of_class
1012
+ _dn_attribute = to_real_attribute_name(_dn_attribute) || _dn_attribute
1013
+ raise if _dn_attribute.nil?
1014
+ _dn_attribute
1015
+ end
1016
+ end
1017
+
962
1018
  def inspect_attribute(name)
963
1019
  values = get_attribute(name, true)
964
1020
  values.collect do |value|
@@ -1014,7 +1070,6 @@ module ActiveLdap
1014
1070
  self.dn = dn
1015
1071
  self.attributes = attributes
1016
1072
  yield self if block_given?
1017
- assert_dn_attribute
1018
1073
  end
1019
1074
 
1020
1075
  def instantiate(args)
@@ -1059,6 +1114,8 @@ module ActiveLdap
1059
1114
  @base = nil
1060
1115
  @scope = nil
1061
1116
  @dn = nil
1117
+ @dn_is_base = false
1118
+ @dn_split_value = nil
1062
1119
  @connection ||= nil
1063
1120
  clear_connection_based_cache
1064
1121
  end
@@ -1119,35 +1176,61 @@ module ActiveLdap
1119
1176
  #
1120
1177
  # Set the value of the attribute called by method_missing?
1121
1178
  def set_attribute(name, value)
1122
- attr = to_real_attribute_name(name)
1123
- attr, value = update_dn(attr, value) if attr == dn_attribute
1124
- raise UnknownAttribute.new(name) if attr.nil?
1179
+ real_name = to_real_attribute_name(name)
1180
+ _dn_attribute = nil
1181
+ valid_dn_attribute = true
1182
+ begin
1183
+ _dn_attribute = dn_attribute
1184
+ rescue DistinguishedNameInvalid
1185
+ valid_dn_attribute = false
1186
+ end
1187
+ if valid_dn_attribute and real_name == _dn_attribute
1188
+ real_name, value = register_new_dn_attribute(real_name, value)
1189
+ end
1190
+ raise UnknownAttribute.new(name) if real_name.nil?
1125
1191
 
1126
- @data[attr] = value
1192
+ @data[real_name] = value
1127
1193
  end
1128
1194
 
1129
- def update_dn(attr, value)
1195
+ def register_new_dn_attribute(name, value)
1130
1196
  @dn = nil
1131
1197
  @dn_is_base = false
1132
- return [attr, nil] if value.blank?
1198
+ if value.blank?
1199
+ @dn_split_value = nil
1200
+ [name, nil]
1201
+ else
1202
+ new_name, new_value, raw_new_value, new_bases = split_dn_value(value)
1203
+ @dn_split_value = [new_name, new_value, new_bases]
1204
+ if new_name.nil? and new_value.nil?
1205
+ new_name, raw_new_value = new_bases[0].to_a[0]
1206
+ end
1207
+ [to_real_attribute_name(new_name) || name,
1208
+ raw_new_value || value]
1209
+ end
1210
+ end
1133
1211
 
1134
- new_dn_attribute, new_value, bases = split_dn_value(value)
1135
- if new_dn_attribute.nil? and new_value.nil?
1212
+ def update_dn(new_name, new_value, bases)
1213
+ if new_name.nil? and new_value.nil?
1136
1214
  @dn_is_base = true
1137
1215
  @base = nil
1138
1216
  attr, value = bases[0].to_a[0]
1139
1217
  @dn_attribute = attr
1140
1218
  else
1141
- new_dn_attribute = to_real_attribute_name(new_dn_attribute)
1142
- if new_dn_attribute
1143
- value = new_value
1144
- @base = bases.empty? ? nil : DN.new(*bases).to_s
1145
- if dn_attribute != new_dn_attribute
1146
- @dn_attribute = attr = new_dn_attribute
1147
- end
1219
+ new_name ||= @dn_attribute || dn_attribute_of_class
1220
+ new_name = to_real_attribute_name(new_name)
1221
+ if new_name.nil?
1222
+ new_name = @dn_attribute || dn_attribute_of_class
1223
+ new_name = to_real_attribute_name(new_name)
1148
1224
  end
1225
+ new_bases = bases.empty? ? nil : DN.new(*bases).to_s
1226
+ dn_components = ["#{new_name}=#{new_value}",
1227
+ new_bases,
1228
+ base_of_class]
1229
+ dn_components = dn_components.find_all {|component| !component.blank?}
1230
+ DN.parse(dn_components.join(','))
1231
+ @base = new_bases
1232
+ @dn_attribute = new_name
1149
1233
  end
1150
- [attr, value]
1151
1234
  end
1152
1235
 
1153
1236
  def split_dn_value(value)
@@ -1156,9 +1239,14 @@ module ActiveLdap
1156
1239
  dn_value = value if value.is_a?(DN)
1157
1240
  dn_value ||= DN.parse(value)
1158
1241
  rescue DistinguishedNameInvalid
1159
- dn_value = DN.parse("#{dn_attribute}=#{value}")
1242
+ begin
1243
+ dn_value = DN.parse("#{dn_attribute}=#{value}")
1244
+ rescue DistinguishedNameInvalid
1245
+ return [nil, value, value, []]
1246
+ end
1160
1247
  end
1161
1248
 
1249
+ val = bases = nil
1162
1250
  begin
1163
1251
  relative_dn_value = dn_value - self.class.parsed_base
1164
1252
  if relative_dn_value.rdns.empty?
@@ -1172,20 +1260,41 @@ module ActiveLdap
1172
1260
  end
1173
1261
 
1174
1262
  dn_attribute_name, dn_attribute_value = val.to_a[0]
1175
- [dn_attribute_name, dn_attribute_value, bases]
1263
+ escaped_dn_attribute_value = nil
1264
+ unless dn_attribute_value.nil?
1265
+ escaped_dn_attribute_value = DN.escape_value(dn_attribute_value)
1266
+ end
1267
+ [dn_attribute_name, escaped_dn_attribute_value,
1268
+ dn_attribute_value, bases]
1269
+ end
1270
+
1271
+ def need_update_dn?
1272
+ not @dn_split_value.nil?
1273
+ end
1274
+
1275
+ def ensure_update_dn
1276
+ return unless need_update_dn?
1277
+ @mutex.synchronize do
1278
+ if @dn_split_value
1279
+ update_dn(*@dn_split_value)
1280
+ @dn_split_value = nil
1281
+ end
1282
+ end
1176
1283
  end
1177
1284
 
1178
1285
  def compute_dn(escape_dn_value=false)
1179
1286
  return base if @dn_is_base
1180
1287
 
1288
+ ensure_update_dn
1181
1289
  dn_value = id
1182
1290
  if dn_value.nil?
1183
- raise DistinguishedNameNotSetError.new,
1184
- _("%s's DN attribute (%s) isn't set") % [self, dn_attribute]
1291
+ format =_("%s's DN attribute (%s) isn't set")
1292
+ message = format % [self.inspect, dn_attribute]
1293
+ raise DistinguishedNameNotSetError.new, message
1185
1294
  end
1186
- dn_value = DN.escape_value(dn_value) if escape_dn_value
1295
+ dn_value = DN.escape_value(dn_value.to_s) if escape_dn_value
1187
1296
  _base = base
1188
- _base = nil if _base.empty?
1297
+ _base = nil if _base.blank?
1189
1298
  ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
1190
1299
  end
1191
1300
 
@@ -1292,13 +1401,6 @@ module ActiveLdap
1292
1401
  attributes
1293
1402
  end
1294
1403
 
1295
- def assert_dn_attribute
1296
- unless dn_attribute
1297
- raise ConfigurationError,
1298
- _("dn_attribute isn't set for this class: %s") % self.class
1299
- end
1300
- end
1301
-
1302
1404
  def create_or_update
1303
1405
  new_entry? ? create : update
1304
1406
  end