activeldap 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/CHANGES +15 -0
  2. data/README +4 -1
  3. data/Rakefile +1 -1
  4. data/TODO +1 -2
  5. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  6. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  7. data/examples/al-admin/app/models/user.rb +1 -1
  8. data/examples/al-admin/config/boot.rb +1 -1
  9. data/examples/al-admin/config/environment.rb +1 -15
  10. data/examples/al-admin/config/initializers/session_store.rb +23 -0
  11. data/examples/al-admin/config/session_secret.txt +1 -0
  12. data/examples/al-admin/db/schema.rb +23 -0
  13. data/examples/al-admin/locale/en/LC_MESSAGES/al-admin.mo +0 -0
  14. data/examples/al-admin/locale/ja/LC_MESSAGES/al-admin.mo +0 -0
  15. data/examples/al-admin/locale/nl/LC_MESSAGES/al-admin.mo +0 -0
  16. data/lib/active_ldap.rb +3 -3
  17. data/lib/active_ldap/acts/tree.rb +1 -1
  18. data/lib/active_ldap/adapter/base.rb +9 -1
  19. data/lib/active_ldap/adapter/ldap.rb +7 -1
  20. data/lib/active_ldap/association/belongs_to_many.rb +6 -2
  21. data/lib/active_ldap/association/children.rb +1 -1
  22. data/lib/active_ldap/association/has_many_utils.rb +2 -2
  23. data/lib/active_ldap/association/has_many_wrap.rb +6 -2
  24. data/lib/active_ldap/attributes.rb +5 -1
  25. data/lib/active_ldap/base.rb +133 -59
  26. data/lib/active_ldap/connection.rb +10 -3
  27. data/lib/active_ldap/distinguished_name.rb +33 -1
  28. data/lib/active_ldap/ldif.rb +1 -0
  29. data/lib/active_ldap/operations.rb +33 -14
  30. data/lib/active_ldap/populate.rb +21 -12
  31. data/lib/active_ldap/schema.rb +21 -0
  32. data/lib/active_ldap/validations.rb +49 -4
  33. data/po/active-ldap.pot +4030 -0
  34. data/po/en/active-ldap.po +57 -37
  35. data/po/ja/active-ldap.po +58 -38
  36. data/test-unit/History.txt +1 -1
  37. data/test-unit/Manifest.txt +2 -0
  38. data/test-unit/README.txt +1 -1
  39. data/test-unit/lib/test/unit/assertions.rb +1 -1
  40. data/test-unit/lib/test/unit/autorunner.rb +19 -4
  41. data/test-unit/lib/test/unit/collector/load.rb +3 -1
  42. data/test-unit/lib/test/unit/color-scheme.rb +5 -1
  43. data/test-unit/lib/test/unit/error.rb +7 -5
  44. data/test-unit/lib/test/unit/runner/tap.rb +8 -0
  45. data/test-unit/lib/test/unit/ui/console/testrunner.rb +63 -8
  46. data/test-unit/lib/test/unit/ui/tap/testrunner.rb +92 -0
  47. data/test-unit/test/collector/test-load.rb +1 -5
  48. data/test-unit/test/test-color-scheme.rb +4 -0
  49. data/test/al-test-utils.rb +30 -2
  50. data/test/test_acts_as_tree.rb +6 -3
  51. data/test/test_associations.rb +3 -2
  52. data/test/test_base.rb +104 -5
  53. data/test/test_dn.rb +10 -0
  54. data/test/test_groupls.rb +1 -1
  55. data/test/test_lpasswd.rb +1 -1
  56. data/test/test_reflection.rb +23 -16
  57. data/test/test_schema.rb +33 -1
  58. data/test/test_useradd-binary.rb +1 -1
  59. data/test/test_useradd.rb +1 -1
  60. data/test/test_userdel.rb +1 -1
  61. data/test/test_userls.rb +1 -1
  62. data/test/test_usermod-binary-add-time.rb +1 -1
  63. data/test/test_usermod-binary-add.rb +1 -1
  64. data/test/test_usermod-binary-del.rb +1 -1
  65. data/test/test_usermod-lang-add.rb +1 -1
  66. data/test/test_usermod.rb +1 -1
  67. data/test/test_validation.rb +48 -10
  68. metadata +44 -35
data/CHANGES CHANGED
@@ -1,5 +1,20 @@
1
1
  = CHANGES
2
2
 
3
+ == 1.2.0: 2009-09-22
4
+
5
+ * Supported ActiveRecord 2.3.4 and Rails 2.3.4.
6
+ * [IMCOMPATIBLE]
7
+ [#23932] Inconsistant DN handling in object attributes [Marc Dequènes]
8
+ (ActiveLdap::Base#dn and ActiveLdap::Base#base return
9
+ ActiveLdap::DN not String)
10
+ * [#26824] support operational attributes detection [Marc Dequènes]
11
+ (added ActiveLdap::Schema::Attribute#directory_operation?)
12
+ * [#27] Error saving an ActiveLDAP user [brad@lucky-dip.net]
13
+ * [#29] Raised on modify_rdn_entry when rdn already exists [Alexey.Chebotar]
14
+ * Added ActiveLdap::DN.parent.
15
+ * Supported renaming an entry. Renaming other DTI is only supported by
16
+ JNDI backend.
17
+
3
18
  == 1.1.0: 2009-07-18
4
19
 
5
20
  * Improved tutorial. [Kazuaki Takase]
data/README CHANGED
@@ -133,4 +133,7 @@ list, please point out.
133
133
  * Tim Hermans: A bug report.
134
134
  * Joe Francis: A suggestion.
135
135
  * Tiago Fernandes: Bug reports.
136
- * achemze. A suggestion.
136
+ * achemze: A suggestion.
137
+ * George Montana Harkin: A suggestion.
138
+ * Marc Dequènes: Bug reports.
139
+ * brad@lucky-dip.net: A bug report.
data/Rakefile CHANGED
@@ -65,7 +65,7 @@ project = Hoe.spec('activeldap') do
65
65
  self.changes = self.paragraphs_of('CHANGES', 1..2).join("\n\n")
66
66
  self.extra_deps = [
67
67
  # ['ruby-ldap', '= 0.9.9'],
68
- ['activerecord', '= 2.3.2'],
68
+ ['activerecord', '= 2.3.4'],
69
69
  ['locale', '= 2.0.4'],
70
70
  ['gettext', '= 2.0.4'],
71
71
  ['gettext_activerecord', '= 2.0.4'],
data/TODO CHANGED
@@ -1,6 +1,5 @@
1
- - [1.1.1] add :readonly option to :has_many.
1
+ - [1.1.x] add :readonly option to :has_many.
2
2
  - Work as framework on Rails.
3
- - [1.1.x] ActiveLdap::Base#dn returns ActiveLdap::DN. [#23932] [Marc Dequènes]
4
3
  - How to support dSCorePropagationData? ignore it?
5
4
  all systemOnly == "TRUE" attribute can be ignored?
6
5
  - Add parsing position to DistinguishedNameInvalid error like
@@ -82,7 +82,7 @@ class User < ActiveRecord::Base
82
82
  else
83
83
  begin
84
84
  ldap_user = LdapUser.find(login)
85
- self.dn = ldap_user.dn
85
+ self.dn = ldap_user.dn.to_s
86
86
  rescue ActiveLdap::EntryNotFound
87
87
  self.dn = nil
88
88
  end
@@ -82,8 +82,8 @@ module Rails
82
82
  end
83
83
 
84
84
  def load_rubygems
85
+ min_version = '1.3.2'
85
86
  require 'rubygems'
86
- min_version = '1.3.1'
87
87
  unless rubygems_version >= min_version
88
88
  $stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
89
89
  exit 1
@@ -5,7 +5,7 @@
5
5
  # ENV['RAILS_ENV'] ||= 'production'
6
6
 
7
7
  # Specifies gem version of Rails to use when vendor/rails is not present
8
- RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION
8
+ RAILS_GEM_VERSION = '2.3.4' unless defined? RAILS_GEM_VERSION
9
9
 
10
10
  # Bootstrap the Rails environment, frameworks, and default configuration
11
11
  require File.join(File.dirname(__FILE__), 'boot')
@@ -55,20 +55,6 @@ Rails::Initializer.run do |config|
55
55
  # config.i18n.load_path << Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.{rb,yml}')]
56
56
  # config.i18n.default_locale = :de
57
57
 
58
- # Your secret key for verifying cookie session data integrity.
59
- # If you change this key, all old sessions will become invalid!
60
- # Make sure the secret is at least 30 characters and all random,
61
- # no regular words or you'll be exposed to dictionary attacks.
62
- config.action_controller.session = {
63
- :session_key => '_al_admin_session',
64
- :secret => '581ed74d26a88caa7cb2ff6d0ea0f0aeea1a49f98641ee6d3e7ba1dfcf6154e26cd2b3f9636c9cc02ed8139a4f74c64fdb529a53dcfd1b7ff7aa763f91083aad'
65
- }
66
-
67
- # Use the database for sessions instead of the cookie-based default,
68
- # which shouldn't be used to store highly confidential information
69
- # (create the session table with "rake db:sessions:create")
70
- # config.action_controller.session_store = :active_record_store
71
-
72
58
  # Use SQL instead of Active Record's schema dumper when creating the test database.
73
59
  # This is necessary if your schema can't be completely dumped by the schema dumper,
74
60
  # like if you have constraints or database-specific column types
@@ -0,0 +1,23 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key for verifying cookie session data integrity.
4
+ # If you change this key, all old sessions will become invalid!
5
+ # Make sure the secret is at least 30 characters and all random,
6
+ # no regular words or you'll be exposed to dictionary attacks.
7
+ session_secret_file = File.join(RAILS_ROOT, "config", "session_secret.txt")
8
+ unless File.exist?(session_secret_file)
9
+ File.open(session_secret_file, "w") do |file|
10
+ file.puts(ActiveSupport::SecureRandom.hex(64))
11
+ end
12
+ end
13
+ session_secret = File.read(session_secret_file).strip
14
+
15
+ ActionController::Base.session = {
16
+ :key => '_al_admin_session',
17
+ :secret => session_secret,
18
+ }
19
+
20
+ # Use the database for sessions instead of the cookie-based default,
21
+ # which shouldn't be used to store highly confidential information
22
+ # (create the session table with "rake db:sessions:create")
23
+ # ActionController::Base.session_store = :active_record_store
@@ -0,0 +1 @@
1
+ 455ed531e0b1f453d98bdf1bffc9941d659ae54b8ed7506fa63edab05c04516f8888540d4ef6870fa660fe525668f59ebfcc2408446f17e57fa41243e6edb0c6
@@ -0,0 +1,23 @@
1
+ # This file is auto-generated from the current state of the database. Instead of editing this file,
2
+ # please use the migrations feature of Active Record to incrementally modify your database, and
3
+ # then regenerate this schema definition.
4
+ #
5
+ # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6
+ # to create the application database on another system, you should be using db:schema:load, not running
7
+ # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
9
+ #
10
+ # It's strongly recommended to check this file into your version control system.
11
+
12
+ ActiveRecord::Schema.define(:version => 1) do
13
+
14
+ create_table "users", :force => true do |t|
15
+ t.string "login"
16
+ t.string "dn"
17
+ t.datetime "updated_at"
18
+ t.string "salt"
19
+ t.string "remember_token"
20
+ t.datetime "remember_token_expires_at"
21
+ end
22
+
23
+ end
@@ -901,7 +901,7 @@ require_gem_if_need = Proc.new do |library_name, gem_name, *gem_args|
901
901
  end
902
902
  end
903
903
 
904
- require_gem_if_need.call("active_support", "activesupport", "= 2.3.2")
904
+ require_gem_if_need.call("active_support", "activesupport", "= 2.3.4")
905
905
 
906
906
  if ActiveSupport.const_defined?(:Dependencies)
907
907
  dependencies = ActiveSupport::Dependencies
@@ -914,7 +914,7 @@ if dependencies.respond_to?(:load_paths)
914
914
  end
915
915
 
916
916
  module ActiveLdap
917
- VERSION = "1.1.0"
917
+ VERSION = "1.2.0"
918
918
  end
919
919
 
920
920
  if RUBY_PLATFORM.match('linux')
@@ -923,7 +923,7 @@ else
923
923
  require 'active_ldap/timeout_stub'
924
924
  end
925
925
 
926
- require_gem_if_need.call("active_record", "activerecord", "= 2.3.2")
926
+ require_gem_if_need.call("active_record", "activerecord", "= 2.3.4")
927
927
  begin
928
928
  require_gem_if_need.call("locale", nil, "= 2.0.4")
929
929
  require_gem_if_need.call("gettext", nil, "= 2.0.4")
@@ -55,7 +55,7 @@ module ActiveLdap
55
55
  end
56
56
 
57
57
  def parent=(entry)
58
- if entry.is_a?(String)
58
+ if entry.is_a?(String) or entry.is_a?(DN)
59
59
  base = entry
60
60
  elsif entry.respond_to?(:dn)
61
61
  base = entry.dn
@@ -139,6 +139,10 @@ module ActiveLdap
139
139
  end
140
140
  end
141
141
 
142
+ def naming_contexts
143
+ root_dse_values('namingContexts')
144
+ end
145
+
142
146
  def entry_attribute(object_classes)
143
147
  @entry_attributes[object_classes.uniq.sort] ||=
144
148
  EntryAttribute.new(schema, object_classes)
@@ -615,7 +619,11 @@ module ActiveLdap
615
619
  def root_dse_values(key, options={})
616
620
  dse = root_dse([key], options)[0]
617
621
  return [] if dse.nil?
618
- dse[key] || dse[key.downcase] || []
622
+ normalized_key = key.downcase
623
+ dse.each do |_key, _value|
624
+ return _value if _key.downcase == normalized_key
625
+ end
626
+ []
619
627
  end
620
628
 
621
629
  def root_dse(attrs, options={})
@@ -158,9 +158,15 @@ module ActiveLdap
158
158
 
159
159
  def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={})
160
160
  super do |_dn, _new_rdn, _delete_old_rdn, _new_superior|
161
+ if _new_superior
162
+ raise NotImplemented.new(_("modify RDN with new superior"))
163
+ end
161
164
  info = {
162
165
  :name => "modify: RDN",
163
- :dn => _dn, :new_rdn => _new_rdn, :delete_old_rdn => _delete_old_rdn
166
+ :dn => _dn,
167
+ :new_rdn => _new_rdn,
168
+ :new_superior => _new_superior,
169
+ :delete_old_rdn => _delete_old_rdn
164
170
  }
165
171
  execute(:modrdn, info, _dn, _new_rdn, _delete_old_rdn)
166
172
  end
@@ -10,7 +10,9 @@ module ActiveLdap
10
10
  if primary_key_name == "dn"
11
11
  old_value = dn_values_to_string_values(old_value)
12
12
  end
13
- new_value = old_value + @owner[primary_key_name, true]
13
+ current_value = @owner[primary_key_name, true]
14
+ current_value = dn_values_to_string_values(current_value)
15
+ new_value = old_value + current_value
14
16
  new_value = new_value.uniq.sort
15
17
  if old_value != new_value
16
18
  entry[@options[:many]] = new_value
@@ -25,7 +27,9 @@ module ActiveLdap
25
27
  if primary_key_name == "dn"
26
28
  old_value = dn_values_to_string_values(old_value)
27
29
  end
28
- new_value = old_value - @owner[primary_key_name, true]
30
+ current_value = @owner[primary_key_name, true]
31
+ current_value = dn_values_to_string_values(current_value)
32
+ new_value = old_value - current_value
29
33
  new_value = new_value.uniq.sort
30
34
  if old_value != new_value
31
35
  entry[@options[:many]] = new_value
@@ -5,7 +5,7 @@ module ActiveLdap
5
5
  class Children < Collection
6
6
  private
7
7
  def insert_entry(entry)
8
- entry.dn = [entry.id, @owner.dn].join(",")
8
+ entry.dn = [entry.id, @owner.dn.to_s].join(",")
9
9
  entry.save
10
10
  end
11
11
 
@@ -14,9 +14,9 @@ module ActiveLdap
14
14
  elsif foreign_base_key == "dn"
15
15
  requested_targets = requested_targets.collect do |target|
16
16
  if target.is_a?(DN)
17
- target.to_s
18
- else
19
17
  target
18
+ else
19
+ DN.parse(target)
20
20
  end
21
21
  end
22
22
  targets = []
@@ -13,7 +13,9 @@ module ActiveLdap
13
13
  if _primary_key == "dn"
14
14
  old_value = dn_values_to_string_values(old_value)
15
15
  end
16
- new_value = (old_value + entry[_primary_key, true]).uniq.sort
16
+ current_value = entry[_primary_key, true]
17
+ current_value = dn_values_to_string_values(current_value)
18
+ new_value = (old_value + current_value).uniq.sort
17
19
  if old_value != new_value
18
20
  @owner[@options[:wrap]] = new_value
19
21
  @owner.save
@@ -26,7 +28,9 @@ module ActiveLdap
26
28
  if _primary_key == "dn"
27
29
  old_value = dn_values_to_string_values(old_value)
28
30
  end
29
- new_value = old_value - entries.collect {|entry| entry[_primary_key]}
31
+ current_value = entries.collect {|entry| entry[_primary_key]}
32
+ current_value = dn_values_to_string_values(current_value)
33
+ new_value = old_value - current_value
30
34
  new_value = new_value.uniq.sort
31
35
  if old_value != new_value
32
36
  @owner[@options[:wrap]] = new_value
@@ -105,7 +105,11 @@ module ActiveLdap
105
105
  result[new_name].concat(real_value)
106
106
  else
107
107
  result[name] ||= []
108
- result[name] << value.dup
108
+ if value.is_a?(DN)
109
+ result[name] << value.to_s
110
+ else
111
+ result[name] << value.dup
112
+ end
109
113
  end
110
114
  end
111
115
  end
@@ -259,6 +259,14 @@ module ActiveLdap
259
259
  end
260
260
  end
261
261
 
262
+ class NotImplemented < Error
263
+ attr_reader :target
264
+ def initialize(target)
265
+ @target = target
266
+ super(_("not implemented: %s") % @target)
267
+ end
268
+ end
269
+
262
270
  # Base
263
271
  #
264
272
  # Base is the primary class which contains all of the core
@@ -313,7 +321,7 @@ module ActiveLdap
313
321
  end
314
322
  end
315
323
 
316
- class_local_attr_accessor false, :prefix, :base
324
+ class_local_attr_accessor false, :inheritable_prefix, :inheritable_base
317
325
  class_local_attr_accessor true, :dn_attribute, :scope, :sort_by, :order
318
326
  class_local_attr_accessor true, :required_classes, :recommended_classes
319
327
  class_local_attr_accessor true, :excluded_classes
@@ -412,7 +420,6 @@ module ActiveLdap
412
420
  public_class_method :new
413
421
  end
414
422
 
415
- alias_method :base_inheritable, :base
416
423
  # Base.base
417
424
  #
418
425
  # This method when included into Base provides
@@ -424,22 +431,23 @@ module ActiveLdap
424
431
  # configuration.rb into this class.
425
432
  # When subclassing, the specified prefix will be concatenated.
426
433
  def base
427
- _base = base_inheritable
428
- _base = configuration[:base] if _base.nil? and configuration
429
- _base ||= base_inheritable(true)
430
- [prefix, _base].find_all do |component|
431
- !component.blank?
432
- end.join(",")
434
+ @base ||= compute_base
433
435
  end
436
+ alias_method :parsed_base, :base # for backward compatibility
434
437
 
435
- alias_method :base_without_parsed_cache_clear=, :base=
436
438
  def base=(value)
437
- self.base_without_parsed_cache_clear = value
438
- @parsed_base = nil
439
+ self.inheritable_base = value
440
+ @base = nil
439
441
  end
440
442
 
441
- def parsed_base
442
- @parsed_base ||= DN.parse(base)
443
+ def prefix
444
+ @prefix ||= inheritable_prefix and DN.parse(inheritable_prefix)
445
+ end
446
+
447
+ def prefix=(value)
448
+ self.inheritable_prefix = value
449
+ @prefix = nil
450
+ @base = nil
443
451
  end
444
452
 
445
453
  alias_method :scope_without_validation=, :scope=
@@ -473,17 +481,23 @@ module ActiveLdap
473
481
  elsif abstract_class?
474
482
  "#{super}(abstract)"
475
483
  else
476
- class_names = []
477
- must = []
478
- may = []
479
- class_names = classes.collect do |object_class|
480
- must.concat(object_class.must)
481
- may.concat(object_class.may)
482
- object_class.name
484
+ detail = nil
485
+ begin
486
+ must = []
487
+ may = []
488
+ class_names = classes.collect do |object_class|
489
+ must.concat(object_class.must)
490
+ may.concat(object_class.may)
491
+ object_class.name
492
+ end
493
+ detail = ["objectClass:<#{class_names.join(', ')}>",
494
+ "must:<#{inspect_attributes(must)}>",
495
+ "may:<#{inspect_attributes(may)}>"].join(", ")
496
+ rescue ActiveLdap::ConnectionNotSetup
497
+ detail = "not-connected"
498
+ rescue ActiveLdap::Error
499
+ detail = "connection-failure"
483
500
  end
484
- detail = ["objectClass:<#{class_names.join(', ')}>",
485
- "must:<#{inspect_attributes(must)}>",
486
- "may:<#{inspect_attributes(may)}>"].join(", ")
487
501
  "#{super}(#{detail})"
488
502
  end
489
503
  end
@@ -615,6 +629,28 @@ module ActiveLdap
615
629
  "ou=#{name.demodulize.pluralize}"
616
630
  end
617
631
  end
632
+
633
+ def compute_base
634
+ _base = inheritable_base
635
+ _base = configuration[:base] if _base.nil? and configuration
636
+ if _base.nil?
637
+ target = superclass
638
+ loop do
639
+ break unless target.respond_to?(:base)
640
+ _base = target.base
641
+ break if _base
642
+ target = target.superclass
643
+ end
644
+ end
645
+ _prefix = prefix
646
+
647
+ _base ||= connection.naming_contexts.first
648
+ return _prefix if _base.blank?
649
+
650
+ _base = DN.parse(_base)
651
+ _base = _prefix + _base if _prefix
652
+ _base
653
+ end
618
654
  end
619
655
 
620
656
  self.scope = :sub
@@ -763,12 +799,8 @@ module ActiveLdap
763
799
  #
764
800
  # Delete this entry from LDAP
765
801
  def destroy
766
- begin
767
- self.class.delete(dn)
768
- @new_entry = true
769
- rescue Error
770
- raise DeleteError.new(_("Failed to delete LDAP entry: %s") % dn)
771
- end
802
+ self.class.delete(dn)
803
+ @new_entry = true
772
804
  end
773
805
 
774
806
  def delete(options={})
@@ -844,9 +876,9 @@ module ActiveLdap
844
876
  return true if super
845
877
 
846
878
  name = name.to_s
847
- return true if have_attribute?(name)
879
+ return true if have_attribute?(name, ["objectClass"])
848
880
  return false if /(?:=|\?|_before_type_cast)$/ !~ name
849
- have_attribute?($PREMATCH)
881
+ have_attribute?($PREMATCH, ["objectClass"])
850
882
  end
851
883
 
852
884
  # Updates a given attribute and saves immediately
@@ -870,7 +902,7 @@ module ActiveLdap
870
902
  # This returns the key value pairs in @data with all values
871
903
  # cloned
872
904
  def attributes
873
- Marshal.load(Marshal.dump(@data))
905
+ @data.clone
874
906
  end
875
907
 
876
908
  # This allows a bulk update to the attributes of a record
@@ -1006,18 +1038,15 @@ module ActiveLdap
1006
1038
  @schema ||= super
1007
1039
  end
1008
1040
 
1009
- alias_method :base_of_class, :base
1010
1041
  def base
1011
- ensure_update_dn
1012
- [@base, base_of_class].find_all do |component|
1013
- not component.blank?
1014
- end.join(",")
1042
+ @base ||= compute_base
1015
1043
  end
1016
1044
 
1017
1045
  def base=(object_local_base)
1018
1046
  ensure_update_dn
1019
1047
  @dn = nil
1020
- @base = object_local_base
1048
+ @base = nil
1049
+ @base_value = object_local_base
1021
1050
  end
1022
1051
 
1023
1052
  alias_method :scope_of_class, :scope
@@ -1030,6 +1059,14 @@ module ActiveLdap
1030
1059
  @scope = scope
1031
1060
  end
1032
1061
 
1062
+ def delete_all(options={})
1063
+ super({:base => dn}.merge(options || {}))
1064
+ end
1065
+
1066
+ def destroy_all(options={})
1067
+ super({:base => dn}.merge(options || {}))
1068
+ end
1069
+
1033
1070
  def inspect
1034
1071
  object_classes = entry_attribute.object_classes
1035
1072
  inspected_object_classes = object_classes.collect do |object_class|
@@ -1106,7 +1143,10 @@ module ActiveLdap
1106
1143
  init_base
1107
1144
  dn = Compatible.convert_to_utf8_encoded_object(dn)
1108
1145
  attributes = Compatible.convert_to_utf8_encoded_object(attributes)
1146
+ @original_dn = dn.clone
1109
1147
  @dn = dn
1148
+ @base = nil
1149
+ @base_value = nil
1110
1150
  @new_entry = false
1111
1151
  @dn_is_base = false
1112
1152
  @ldap_data = attributes
@@ -1259,6 +1299,7 @@ module ActiveLdap
1259
1299
  if new_name.nil? and new_value.nil?
1260
1300
  @dn_is_base = true
1261
1301
  @base = nil
1302
+ @base_value = nil
1262
1303
  attr, value = bases[0].to_a[0]
1263
1304
  @dn_attribute = attr
1264
1305
  else
@@ -1271,10 +1312,11 @@ module ActiveLdap
1271
1312
  new_bases = bases.empty? ? nil : DN.new(*bases).to_s
1272
1313
  dn_components = ["#{new_name}=#{new_value}",
1273
1314
  new_bases,
1274
- base_of_class]
1315
+ self.class.base.to_s]
1275
1316
  dn_components = dn_components.find_all {|component| !component.blank?}
1276
1317
  DN.parse(dn_components.join(','))
1277
- @base = new_bases
1318
+ @base = nil
1319
+ @base_value = new_bases
1278
1320
  @dn_attribute = new_name
1279
1321
  end
1280
1322
  end
@@ -1294,7 +1336,9 @@ module ActiveLdap
1294
1336
 
1295
1337
  val = bases = nil
1296
1338
  begin
1297
- relative_dn_value = dn_value - self.class.parsed_base
1339
+ relative_dn_value = dn_value
1340
+ base_of_class = self.class.base
1341
+ relative_dn_value -= base_of_class if base_of_class
1298
1342
  if relative_dn_value.rdns.empty?
1299
1343
  val = []
1300
1344
  bases = dn_value.rdns
@@ -1328,7 +1372,7 @@ module ActiveLdap
1328
1372
  end
1329
1373
  end
1330
1374
 
1331
- def compute_dn(escape_dn_value=false)
1375
+ def compute_dn
1332
1376
  return base if @dn_is_base
1333
1377
 
1334
1378
  ensure_update_dn
@@ -1338,14 +1382,21 @@ module ActiveLdap
1338
1382
  message = format % [self.inspect, dn_attribute]
1339
1383
  raise DistinguishedNameNotSetError.new, message
1340
1384
  end
1341
- dn_value = DN.escape_value(dn_value.to_s) if escape_dn_value
1385
+ dn_value = DN.escape_value(dn_value.to_s)
1342
1386
  _base = base
1343
1387
  _base = nil if _base.blank?
1344
- ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
1388
+ DN.parse(["#{dn_attribute}=#{dn_value}", _base].compact.join(","))
1345
1389
  end
1346
1390
 
1347
- def escaped_dn
1348
- compute_dn(true)
1391
+ def compute_base
1392
+ base_of_class = self.class.base
1393
+ if @base_value.nil?
1394
+ base_of_class
1395
+ else
1396
+ base_of_object = DN.parse(@base_value)
1397
+ base_of_object += base_of_class if base_of_class
1398
+ base_of_object
1399
+ end
1349
1400
  end
1350
1401
 
1351
1402
  # array_of
@@ -1393,7 +1444,11 @@ module ActiveLdap
1393
1444
  end
1394
1445
 
1395
1446
  def collect_modified_attributes(ldap_data, data)
1447
+ klass = self.class
1448
+ _dn_attribute = dn_attribute
1449
+ new_dn_value = nil
1396
1450
  attributes = []
1451
+
1397
1452
  # Now that all the options will be treated as unique attributes
1398
1453
  # we can see what's changed and add anything that is brand-spankin'
1399
1454
  # new.
@@ -1402,23 +1457,26 @@ module ActiveLdap
1402
1457
 
1403
1458
  next if v == value
1404
1459
 
1405
- x = value
1406
- value = self.class.remove_blank_value(value) || []
1460
+ value = klass.remove_blank_value(value) || []
1407
1461
  next if v == value
1408
1462
 
1409
- if self.class.blank_value?(value) and
1463
+ if klass.blank_value?(value) and
1410
1464
  schema.attribute(k).binary_required?
1411
1465
  value = [{'binary' => []}]
1412
1466
  end
1413
- attributes.push([:replace, k, value])
1467
+ if k == _dn_attribute
1468
+ new_dn_value = value[0]
1469
+ else
1470
+ attributes.push([:replace, k, value])
1471
+ end
1414
1472
  end
1473
+
1415
1474
  data.each do |k, v|
1416
1475
  value = v || []
1417
1476
  next if ldap_data.has_key?(k)
1418
1477
 
1419
- value = self.class.remove_blank_value(value) || []
1420
- next if self.class.blank_value?(value)
1421
-
1478
+ value = klass.remove_blank_value(value) || []
1479
+ next if klass.blank_value?(value)
1422
1480
 
1423
1481
  # Detect subtypes and account for them
1424
1482
  # REPLACE will function like ADD, but doesn't hit EQUALITY problems
@@ -1426,7 +1484,7 @@ module ActiveLdap
1426
1484
  attributes.push([:replace, k, value])
1427
1485
  end
1428
1486
 
1429
- attributes
1487
+ [new_dn_value, attributes]
1430
1488
  end
1431
1489
 
1432
1490
  def collect_all_attributes(data)
@@ -1460,18 +1518,21 @@ module ActiveLdap
1460
1518
  ldap_data = normalize_data(@ldap_data)
1461
1519
 
1462
1520
  # Expand subtypes to real data attributes, but leave @data alone
1463
- bad_attrs = @data.keys - attribute_names
1521
+ original_attributes =
1522
+ connection.entry_attribute(@ldap_data["objectClass"] || []).names
1523
+ bad_attrs = original_attributes - entry_attribute.names
1464
1524
  data = normalize_data(@data, bad_attrs)
1465
1525
 
1466
1526
  success = yield(data, ldap_data)
1467
1527
 
1468
1528
  if success
1469
- @ldap_data = Marshal.load(Marshal.dump(data))
1529
+ @ldap_data = data.clone
1470
1530
  # Delete items disallowed by objectclasses.
1471
1531
  # They should have been removed from ldap.
1472
1532
  bad_attrs.each do |remove_me|
1473
1533
  @ldap_data.delete(remove_me)
1474
1534
  end
1535
+ @original_dn = dn.clone
1475
1536
  end
1476
1537
 
1477
1538
  success
@@ -1480,7 +1541,7 @@ module ActiveLdap
1480
1541
  def create
1481
1542
  prepare_data_for_saving do |data, ldap_data|
1482
1543
  attributes = collect_all_attributes(data)
1483
- add_entry(escaped_dn, attributes)
1544
+ add_entry(dn, attributes)
1484
1545
  @new_entry = false
1485
1546
  true
1486
1547
  end
@@ -1488,8 +1549,21 @@ module ActiveLdap
1488
1549
 
1489
1550
  def update
1490
1551
  prepare_data_for_saving do |data, ldap_data|
1491
- attributes = collect_modified_attributes(ldap_data, data)
1492
- modify_entry(escaped_dn, attributes)
1552
+ new_dn_value, attributes = collect_modified_attributes(ldap_data, data)
1553
+ modify_entry(@original_dn, attributes)
1554
+ if new_dn_value
1555
+ old_dn_base = DN.parse(@original_dn).parent
1556
+ new_dn_base = dn.clone.parent
1557
+ if old_dn_base == new_dn_base
1558
+ new_superior = nil
1559
+ else
1560
+ new_superior = new_dn_base
1561
+ end
1562
+ modify_rdn_entry(@original_dn,
1563
+ "#{dn_attribute}=#{DN.escape_value(new_dn_value)}",
1564
+ true,
1565
+ new_superior)
1566
+ end
1493
1567
  true
1494
1568
  end
1495
1569
  end