activeldap 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. data/CHANGES +61 -0
  2. data/README +8 -1
  3. data/Rakefile +4 -1
  4. data/benchmark/bench-al.rb +12 -2
  5. data/examples/al-admin/app/controllers/account_controller.rb +4 -3
  6. data/examples/al-admin/app/controllers/application.rb +5 -2
  7. data/examples/al-admin/app/controllers/directory_controller.rb +3 -1
  8. data/examples/al-admin/app/controllers/users_controller.rb +19 -4
  9. data/examples/al-admin/app/controllers/welcome_controller.rb +4 -2
  10. data/examples/al-admin/app/helpers/application_helper.rb +7 -1
  11. data/examples/al-admin/app/helpers/url_helper.rb +4 -0
  12. data/examples/al-admin/app/models/ldap_user.rb +4 -0
  13. data/examples/al-admin/app/views/_entry/{_attributes_information.rhtml → _attributes_information.html.erb} +0 -0
  14. data/examples/al-admin/app/views/_entry/{_entry.rhtml → _entry.html.erb} +0 -0
  15. data/examples/al-admin/app/views/_schema/{_aliases.rhtml → _aliases.html.erb} +0 -0
  16. data/examples/al-admin/app/views/_switcher/{_after.rhtml → _after.html.erb} +0 -0
  17. data/examples/al-admin/app/views/_switcher/{_before.rhtml → _before.html.erb} +0 -0
  18. data/examples/al-admin/app/views/account/{login.rhtml → login.html.erb} +0 -0
  19. data/examples/al-admin/app/views/account/{sign_up.rhtml → sign_up.html.erb} +0 -0
  20. data/examples/al-admin/app/views/attributes/{_attributes.rhtml → _attributes.html.erb} +0 -0
  21. data/examples/al-admin/app/views/attributes/{_detail.rhtml → _detail.html.erb} +0 -0
  22. data/examples/al-admin/app/views/attributes/{index.rhtml → index.html.erb} +0 -0
  23. data/examples/al-admin/app/views/attributes/{show.rhtml → show.html.erb} +0 -0
  24. data/examples/al-admin/app/views/directory/{_tree.rhtml → _tree.html.erb} +0 -0
  25. data/examples/al-admin/app/views/directory/{_tree_view_js.rhtml → _tree_view_js.html.erb} +4 -5
  26. data/examples/al-admin/app/views/directory/{index.rhtml → index.html.erb} +0 -0
  27. data/examples/al-admin/app/views/directory/{populate.rhtml → populate.html.erb} +0 -0
  28. data/examples/al-admin/app/views/layouts/{_footer.rhtml → _footer.html.erb} +0 -0
  29. data/examples/al-admin/app/views/layouts/{_header_menu.rhtml → _header_menu.html.erb} +0 -0
  30. data/examples/al-admin/app/views/layouts/{_main_menu.rhtml → _main_menu.html.erb} +0 -0
  31. data/examples/al-admin/app/views/layouts/{application.rhtml → application.html.erb} +3 -2
  32. data/examples/al-admin/app/views/object_classes/{_attributes.rhtml → _attributes.html.erb} +0 -0
  33. data/examples/al-admin/app/views/object_classes/{_object_classes.rhtml → _object_classes.html.erb} +0 -0
  34. data/examples/al-admin/app/views/object_classes/{index.rhtml → index.html.erb} +0 -0
  35. data/examples/al-admin/app/views/object_classes/{show.rhtml → show.html.erb} +0 -0
  36. data/examples/al-admin/app/views/syntaxes/{_detail.rhtml → _detail.html.erb} +0 -0
  37. data/examples/al-admin/app/views/syntaxes/{_syntaxes.rhtml → _syntaxes.html.erb} +0 -0
  38. data/examples/al-admin/app/views/syntaxes/{index.rhtml → index.html.erb} +0 -0
  39. data/examples/al-admin/app/views/syntaxes/{show.rhtml → show.html.erb} +0 -0
  40. data/examples/al-admin/app/views/users/{_attributes_update_form.rhtml → _attributes_update_form.html.erb} +0 -0
  41. data/examples/al-admin/app/views/users/{_form.rhtml → _form.html.erb} +0 -0
  42. data/examples/al-admin/app/views/users/{_object_classes_update_form.rhtml → _object_classes_update_form.html.erb} +7 -1
  43. data/examples/al-admin/app/views/users/{_password_change_form.rhtml → _password_change_form.html.erb} +0 -0
  44. data/examples/al-admin/app/views/users/{edit.rhtml → edit.html.erb} +0 -0
  45. data/examples/al-admin/app/views/users/{index.rhtml → index.html.erb} +0 -0
  46. data/examples/al-admin/app/views/users/{show.rhtml → show.html.erb} +0 -0
  47. data/examples/al-admin/app/views/welcome/{index.rhtml → index.html.erb} +0 -0
  48. data/examples/al-admin/config/boot.rb +96 -32
  49. data/examples/al-admin/config/environment.rb +30 -36
  50. data/examples/al-admin/config/environments/development.rb +2 -5
  51. data/examples/al-admin/config/environments/production.rb +1 -0
  52. data/examples/al-admin/config/environments/test.rb +4 -1
  53. data/examples/al-admin/config/initializers/exception_notifier.rb +2 -0
  54. data/examples/al-admin/config/initializers/gettext.rb +1 -0
  55. data/examples/al-admin/config/initializers/inflections.rb +10 -0
  56. data/examples/al-admin/config/initializers/mime_types.rb +5 -0
  57. data/examples/al-admin/config/initializers/ralative_url_support.rb +1 -0
  58. data/examples/al-admin/config/routes.rb +24 -12
  59. data/examples/al-admin/lib/authenticated_system.rb +1 -1
  60. data/examples/al-admin/lib/tasks/gettext.rake +1 -1
  61. data/examples/al-admin/po/en/al-admin.po +102 -100
  62. data/examples/al-admin/po/ja/al-admin.po +112 -110
  63. data/examples/al-admin/po/nl/al-admin.po +117 -110
  64. data/examples/al-admin/public/javascripts/controls.js +484 -354
  65. data/examples/al-admin/public/javascripts/dragdrop.js +88 -58
  66. data/examples/al-admin/public/javascripts/effects.js +396 -364
  67. data/examples/al-admin/public/javascripts/prototype.js +2817 -1107
  68. data/examples/al-admin/public/stylesheets/base.css +5 -0
  69. data/examples/al-admin/script/performance/request +3 -0
  70. data/lib/active_ldap.rb +13 -10
  71. data/lib/active_ldap/adapter/base.rb +159 -43
  72. data/lib/active_ldap/adapter/jndi.rb +175 -0
  73. data/lib/active_ldap/adapter/jndi_connection.rb +180 -0
  74. data/lib/active_ldap/adapter/ldap.rb +91 -46
  75. data/lib/active_ldap/adapter/ldap_ext.rb +19 -5
  76. data/lib/active_ldap/adapter/net_ldap.rb +52 -44
  77. data/lib/active_ldap/association/has_many_wrap.rb +1 -1
  78. data/lib/active_ldap/attributes.rb +20 -95
  79. data/lib/active_ldap/base.rb +195 -186
  80. data/lib/active_ldap/callbacks.rb +33 -0
  81. data/lib/active_ldap/command.rb +3 -3
  82. data/lib/active_ldap/connection.rb +21 -3
  83. data/lib/active_ldap/distinguished_name.rb +18 -11
  84. data/lib/active_ldap/entry_attribute.rb +78 -0
  85. data/lib/active_ldap/human_readable.rb +20 -0
  86. data/lib/active_ldap/ldif.rb +860 -10
  87. data/lib/active_ldap/object_class.rb +6 -4
  88. data/lib/active_ldap/operations.rb +129 -22
  89. data/lib/active_ldap/schema.rb +118 -9
  90. data/lib/active_ldap/schema/syntaxes.rb +33 -16
  91. data/lib/active_ldap/validations.rb +74 -65
  92. data/po/en/active-ldap.po +378 -768
  93. data/po/ja/active-ldap.po +935 -868
  94. data/rails/plugin/active_ldap/init.rb +40 -2
  95. data/test/al-test-utils.rb +78 -58
  96. data/test/command.rb +51 -1
  97. data/test/test-unit-ext/priority.rb +29 -6
  98. data/test/test_adapter.rb +21 -2
  99. data/test/test_attributes.rb +13 -0
  100. data/test/test_base.rb +51 -1
  101. data/test/test_connection.rb +2 -1
  102. data/test/test_connection_per_class.rb +55 -1
  103. data/test/test_connection_per_dn.rb +29 -1
  104. data/test/test_find.rb +73 -0
  105. data/test/test_ldif.rb +1829 -15
  106. data/test/test_load.rb +126 -0
  107. data/test/test_object_class.rb +23 -5
  108. data/test/test_schema.rb +28 -0
  109. data/test/test_syntax.rb +22 -11
  110. data/test/test_user.rb +16 -25
  111. data/test/test_useradd-binary.rb +1 -1
  112. data/test/test_usermod-binary-add-time.rb +1 -1
  113. data/test/test_usermod-binary-add.rb +1 -1
  114. data/test/test_validation.rb +100 -22
  115. metadata +77 -71
  116. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  117. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  118. data/examples/al-admin/app/views/layouts/_flash_box.rhtml +0 -4
  119. data/examples/al-admin/public/stylesheets/common.css +0 -2
  120. data/examples/al-admin/script/breakpointer +0 -3
@@ -29,7 +29,11 @@ module ActiveLdap
29
29
  }
30
30
  config[:encryption] = {:method => method} if method
31
31
  begin
32
- Net::LDAP::Connection.new(config)
32
+ uri = construct_uri(host, port, method == :simple_tls)
33
+ with_start_tls = method == :start_tls
34
+ info = {:uri => uri, :with_start_tls => with_start_tls}
35
+ [log("connect", info) {Net::LDAP::Connection.new(config)},
36
+ uri, with_start_tls]
33
37
  rescue Net::LDAP::LdapError
34
38
  raise ConnectionError, $!.message
35
39
  end
@@ -37,7 +41,7 @@ module ActiveLdap
37
41
  end
38
42
 
39
43
  def unbind(options={})
40
- @bound = false
44
+ log("unbind") {@bound = false}
41
45
  end
42
46
 
43
47
  def bind(options={})
@@ -52,7 +56,7 @@ module ActiveLdap
52
56
  def bind_as_anonymous(options={})
53
57
  super do
54
58
  @bound = false
55
- execute(:bind, :method => :anonymous)
59
+ execute(:bind, {:name => "bind: anonymous"}, {:method => :anonymous})
56
60
  @bound = true
57
61
  end
58
62
  end
@@ -70,7 +74,11 @@ module ActiveLdap
70
74
  :attributes => attrs,
71
75
  :size => limit,
72
76
  }
73
- execute(:search, args) do |entry|
77
+ info = {
78
+ :base => base, :scope => scope_name(scope),
79
+ :filter => filter, :attributes => attrs,
80
+ }
81
+ execute(:search, info, args) do |entry|
74
82
  attributes = {}
75
83
  entry.original_attribute_names.each do |name|
76
84
  attributes[name] = entry[name]
@@ -80,31 +88,11 @@ module ActiveLdap
80
88
  end
81
89
  end
82
90
 
83
- def to_ldif(dn, attributes)
84
- entry = Net::LDAP::Entry.new(dn.dup)
85
- attributes.each do |key, values|
86
- entry[key] = values.flatten
87
- end
88
- entry.to_ldif
89
- end
90
-
91
- def load(ldifs, options={})
92
- super do |ldif|
93
- entry = Net::LDAP::Entry.from_single_ldif_string(ldif)
94
- attributes = {}
95
- entry.each do |name, values|
96
- attributes[name] = values
97
- end
98
- attributes.delete(:dn)
99
- execute(:add,
100
- :dn => entry.dn,
101
- :attributes => attributes)
102
- end
103
- end
104
-
105
91
  def delete(targets, options={})
106
92
  super do |target|
107
- execute(:delete, :dn => target)
93
+ args = {:dn => target}
94
+ info = args.dup
95
+ execute(:delete, info, args)
108
96
  end
109
97
  end
110
98
 
@@ -116,21 +104,38 @@ module ActiveLdap
116
104
  attributes[name] = values
117
105
  end
118
106
  end
119
- execute(:add, :dn => dn, :attributes => attributes)
107
+ args = {:dn => dn, :attributes => attributes}
108
+ info = args.dup
109
+ execute(:add, info, args)
120
110
  end
121
111
  end
122
112
 
123
113
  def modify(dn, entries, options={})
124
114
  super do |dn, entries|
125
- execute(:modify,
115
+ info = {:dn => dn, :attributes => entries}
116
+ execute(:modify, info,
126
117
  :dn => dn,
127
118
  :operations => parse_entries(entries))
128
119
  end
129
120
  end
130
121
 
122
+ def modify_rdn(dn, new_rdn, delete_old_rdn, new_superior, options={})
123
+ super do |dn, new_rdn, delete_old_rdn, new_superior|
124
+ info = {
125
+ :name => "modify: RDN", :dn => dn, :new_rdn => new_rdn,
126
+ :delete_old_rdn => delete_old_rdn,
127
+ }
128
+ execute(:rename, info,
129
+ :olddn => dn,
130
+ :newrdn => new_rdn,
131
+ :delete_attributes => delete_old_rdn)
132
+ end
133
+ end
134
+
131
135
  private
132
- def execute(method, *args, &block)
133
- result = @connection.send(method, *args, &block)
136
+ def execute(method, info=nil, *args, &block)
137
+ name = (info || {}).delete(:name) || method
138
+ result = log(name, info) {@connection.send(method, *args, &block)}
134
139
  message = nil
135
140
  if result.is_a?(Hash)
136
141
  message = result[:errorMessage]
@@ -139,16 +144,8 @@ module ActiveLdap
139
144
  unless result.zero?
140
145
  klass = LdapError::ERRORS[result]
141
146
  klass ||= LdapError
142
- raise klass,
143
- [Net::LDAP.result2string(result), message].compact.join(": ")
144
- end
145
- end
146
-
147
- def root_dse(attrs, options={})
148
- search(:base => "",
149
- :scope => :base,
150
- :attributes => attrs).collect do |dn, attributes|
151
- attributes
147
+ message = [Net::LDAP.result2string(result), message].compact.join(": ")
148
+ raise klass, message
152
149
  end
153
150
  end
154
151
 
@@ -177,6 +174,14 @@ module ActiveLdap
177
174
  value
178
175
  end
179
176
 
177
+ def scope_name(scope)
178
+ {
179
+ Net::LDAP::SearchScope_BaseObject => :base,
180
+ Net::LDAP::SearchScope_WholeSubtree => :sub,
181
+ Net::LDAP::SearchScope_SingleLevel => :one,
182
+ }[scope]
183
+ end
184
+
180
185
  def sasl_bind(bind_dn, options={})
181
186
  super do |bind_dn, mechanism, quiet|
182
187
  normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
@@ -191,7 +196,10 @@ module ActiveLdap
191
196
  :challenge_response => challenge_response,
192
197
  }
193
198
  @bound = false
194
- execute(:bind, args)
199
+ info = {
200
+ :name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism,
201
+ }
202
+ execute(:bind, info, args)
195
203
  @bound = true
196
204
  end
197
205
  end
@@ -263,7 +271,7 @@ module ActiveLdap
263
271
  :password => passwd,
264
272
  }
265
273
  @bound = false
266
- execute(:bind, args)
274
+ execute(:bind, {:dn => bind_dn}, args)
267
275
  @bound = true
268
276
  end
269
277
  end
@@ -281,7 +289,7 @@ module ActiveLdap
281
289
 
282
290
  def ensure_mod_type(type)
283
291
  case type
284
- when :replace, :add
292
+ when :replace, :add, :delete
285
293
  type
286
294
  else
287
295
  raise ArgumentError, _("unknown type: %s") % type
@@ -33,7 +33,7 @@ module ActiveLdap
33
33
  found_targets = {}
34
34
  foreign_base_key = primary_key
35
35
  targets.each do |target|
36
- found_targets[target.send(foreign_base_key)] ||= target
36
+ found_targets[target[foreign_base_key]] ||= target
37
37
  end
38
38
 
39
39
  klass = foreign_class
@@ -3,8 +3,8 @@ module ActiveLdap
3
3
  def self.included(base)
4
4
  base.class_eval do
5
5
  extend(ClassMethods)
6
- extend(Normalize)
7
- include(Normalize)
6
+ extend(Normalizable)
7
+ include(Normalizable)
8
8
  end
9
9
  end
10
10
 
@@ -19,9 +19,20 @@ module ActiveLdap
19
19
  result + ancestor.instance_eval {@attr_protected ||= []}
20
20
  end
21
21
  end
22
+
23
+ def blank_value?(value)
24
+ case value
25
+ when Hash
26
+ value.values.all? {|val| blank_value?(val)}
27
+ when Array
28
+ value.all? {|val| blank_value?(val)}
29
+ else
30
+ value.blank?
31
+ end
32
+ end
22
33
  end
23
34
 
24
- module Normalize
35
+ module Normalizable
25
36
  def normalize_attribute_name(name)
26
37
  name.to_s.downcase
27
38
  end
@@ -36,13 +47,7 @@ module ActiveLdap
36
47
  end
37
48
 
38
49
  name = normalize_attribute_name(name)
39
- rubyish_class_name = Inflector.underscore(value.class.name)
40
- handler = "normalize_attribute_value_of_#{rubyish_class_name}"
41
- if respond_to?(handler, true)
42
- [name, send(handler, name, value)]
43
- else
44
- [name, [schema.attribute(name).normalize_value(value)]]
45
- end
50
+ [name, schema.attribute(name).normalize_value(value)]
46
51
  end
47
52
 
48
53
  def unnormalize_attributes(attributes)
@@ -59,7 +64,7 @@ module ActiveLdap
59
64
  else
60
65
  values.each do |value|
61
66
  if value.is_a?(Hash)
62
- suffix, real_value = extract_attribute_options(value)
67
+ suffix, real_value = unnormalize_attribute_options(value)
63
68
  new_name = name + suffix
64
69
  result[new_name] ||= []
65
70
  result[new_name].concat(real_value)
@@ -72,86 +77,6 @@ module ActiveLdap
72
77
  result
73
78
  end
74
79
 
75
- private
76
- def normalize_attribute_value_of_array(name, value)
77
- attribute = schema.attribute(name)
78
- if value.size > 1 and attribute.single_value?
79
- format = _("Attribute %s can only have a single value")
80
- message = format % self.class.human_attribute_name(attribute)
81
- raise TypeError, message
82
- end
83
- if value.empty?
84
- if schema.attribute(name).binary_required?
85
- [{'binary' => value}]
86
- else
87
- value
88
- end
89
- else
90
- value.collect do |entry|
91
- normalize_attribute(name, entry)[1][0]
92
- end
93
- end
94
- end
95
-
96
- def normalize_attribute_value_of_hash(name, value)
97
- if value.keys.size > 1
98
- format = _("Hashes must have one key-value pair only: %s")
99
- raise TypeError, format % value.inspect
100
- end
101
- unless value.keys[0].match(/^(lang-[a-z][a-z]*)|(binary)$/)
102
- logger.warn do
103
- format = _("unknown option did not match lang-* or binary: %s")
104
- format % value.keys[0]
105
- end
106
- end
107
- # Contents MUST be a String or an Array
108
- if !value.has_key?('binary') and schema.attribute(name).binary_required?
109
- suffix, real_value = extract_attribute_options(value)
110
- name, values =
111
- normalize_attribute_options("#{name}#{suffix};binary", real_value)
112
- values
113
- else
114
- [value]
115
- end
116
- end
117
-
118
- def normalize_attribute_value_of_nil_class(name, value)
119
- if schema.attribute(name).binary_required?
120
- [{'binary' => []}]
121
- else
122
- []
123
- end
124
- end
125
-
126
- def normalize_attribute_value_of_string(name, value)
127
- if schema.attribute(name).binary_required?
128
- [{'binary' => [value]}]
129
- else
130
- [value]
131
- end
132
- end
133
-
134
- def normalize_attribute_value_of_date(name, value)
135
- new_value = sprintf('%.04d%.02d%.02d%.02d%.02d%.02d%s',
136
- value.year, value.month, value.mday, 0, 0, 0,
137
- '+0000')
138
- normalize_attribute_value_of_string(name, new_value)
139
- end
140
-
141
- def normalize_attribute_value_of_time(name, value)
142
- new_value = sprintf('%.04d%.02d%.02d%.02d%.02d%.02d%s',
143
- 0, 0, 0, value.hour, value.min, value.sec,
144
- value.zone)
145
- normalize_attribute_value_of_string(name, new_value)
146
- end
147
-
148
- def normalize_attribute_value_of_date_time(name, value)
149
- new_value = sprintf('%.04d%.02d%.02d%.02d%.02d%.02d%s',
150
- value.year, value.month, value.mday, value.hour,
151
- value.min, value.sec, value.zone)
152
- normalize_attribute_value_of_string(name, new_value)
153
- end
154
-
155
80
  # normalize_attribute_options
156
81
  #
157
82
  # Makes the Hashized value from the full attribute name
@@ -165,18 +90,18 @@ module ActiveLdap
165
90
  [options.reverse.inject(value) {|result, option| {option => result}}]]
166
91
  end
167
92
 
168
- # extract_attribute_options
93
+ # unnormalize_attribute_options
169
94
  #
170
- # Extracts all of the subtypes from a given set of nested hashes
95
+ # Unnormalizes all of the subtypes from a given set of nested hashes
171
96
  # and returns the attribute suffix and the final true value
172
- def extract_attribute_options(value)
97
+ def unnormalize_attribute_options(value)
173
98
  options = ''
174
99
  ret_val = value
175
100
  if value.class == Hash
176
101
  options = ';' + value.keys[0]
177
102
  ret_val = value[value.keys[0]]
178
103
  if ret_val.class == Hash
179
- sub_options, ret_val = extract_attribute_options(ret_val)
104
+ sub_options, ret_val = unnormalize_attribute_options(ret_val)
180
105
  options += sub_options
181
106
  end
182
107
  end
@@ -123,16 +123,63 @@ module ActiveLdap
123
123
  end
124
124
 
125
125
  class LdifInvalid < Error
126
- attr_reader :ldif, :reason
127
- def initialize(ldif, reason=nil)
126
+ attr_reader :ldif, :reason, :line, :column, :nearest
127
+ def initialize(ldif, reason=nil, line=nil, column=nil)
128
128
  @ldif = ldif
129
129
  @reason = reason
130
+ @line = line
131
+ @column = column
132
+ @nearest = nil
130
133
  if @reason
131
- message = _("%s is invalid LDIF: %s") % [@ldif, @reason]
134
+ message = _("invalid LDIF: %s:") % @reason
132
135
  else
133
- message = _("%s is invalid LDIF") % @ldif
136
+ message = _("invalid LDIF:")
134
137
  end
135
- super(message)
138
+ if @line and @column
139
+ @nearest = detect_nearest(@line, @column)
140
+ snippet = generate_snippet
141
+ message << "\n#{snippet}\n"
142
+ end
143
+ super("#{message}\n#{numbered_ldif}")
144
+ end
145
+
146
+ NEAREST_MARK = "|@|"
147
+ private
148
+ def detect_nearest(line, column)
149
+ nearest = @ldif.to_a[line - 1] || ""
150
+ if column - 1 == nearest.size # for JRuby 1.0.2 :<
151
+ nearest << NEAREST_MARK
152
+ else
153
+ nearest[column - 1, 0] = NEAREST_MARK
154
+ end
155
+ nearest = "#{@ldif.to_a[line - 2]}#{nearest}" if nearest == NEAREST_MARK
156
+ nearest
157
+ end
158
+
159
+ def generate_snippet
160
+ nearest = @nearest.chomp
161
+ column_column = ":#{@column}"
162
+ target_position_info = "#{@line}#{column_column}: "
163
+ if /\n/ =~ nearest
164
+ snippet = "%#{Math.log10(@line).truncate}d" % (@line - 1)
165
+ snippet << " " * column_column.size
166
+ snippet << ": "
167
+ snippet << nearest.gsub(/\n/, "\n#{target_position_info}")
168
+ else
169
+ snippet = "#{target_position_info}#{nearest}"
170
+ end
171
+ snippet
172
+ end
173
+
174
+ def numbered_ldif
175
+ return @ldif if @ldif.blank?
176
+ lines = @ldif.to_a
177
+ format = "%#{Math.log10(lines.size).truncate + 1}d: %s"
178
+ i = 0
179
+ lines.collect do |line|
180
+ i += 1
181
+ format % [i, line]
182
+ end.join
136
183
  end
137
184
  end
138
185
 
@@ -173,6 +220,15 @@ module ActiveLdap
173
220
  end
174
221
  end
175
222
 
223
+ class AttributeValueInvalid < Error
224
+ attr_reader :attribute, :value
225
+ def initialize(attribute, value, message)
226
+ @attribute = attribute
227
+ @value = value
228
+ super(message)
229
+ end
230
+ end
231
+
176
232
  # Base
177
233
  #
178
234
  # Base is the primary class which contains all of the core
@@ -190,9 +246,12 @@ module ActiveLdap
190
246
  end
191
247
  end
192
248
 
249
+ cattr_accessor :colorize_logging, :instance_writer => false
250
+ @@colorize_logging = true
251
+
193
252
  VALID_LDAP_MAPPING_OPTIONS = [:dn_attribute, :prefix, :scope,
194
253
  :classes, :recommended_classes,
195
- :sort_by, :order]
254
+ :excluded_classes, :sort_by, :order]
196
255
 
197
256
  cattr_accessor :logger
198
257
  cattr_accessor :configurations
@@ -228,6 +287,7 @@ module ActiveLdap
228
287
  class_local_attr_accessor false, :prefix, :base
229
288
  class_local_attr_accessor true, :dn_attribute, :scope, :sort_by, :order
230
289
  class_local_attr_accessor true, :required_classes, :recommended_classes
290
+ class_local_attr_accessor true, :excluded_classes
231
291
 
232
292
  class << self
233
293
  # Hide new in Base
@@ -297,6 +357,7 @@ module ActiveLdap
297
357
  self.scope = options[:scope]
298
358
  self.required_classes = options[:classes]
299
359
  self.recommended_classes = options[:recommended_classes]
360
+ self.excluded_classes = options[:excluded_classes]
300
361
  self.sort_by = options[:sort_by]
301
362
  self.order = options[:order]
302
363
 
@@ -323,6 +384,16 @@ module ActiveLdap
323
384
  end.join(",")
324
385
  end
325
386
 
387
+ alias_method :base_without_parsed_cache_clear=, :base=
388
+ def base=(value)
389
+ self.base_without_parsed_cache_clear = value
390
+ @parsed_base = nil
391
+ end
392
+
393
+ def parsed_base
394
+ @parsed_base ||= DN.parse(base)
395
+ end
396
+
326
397
  alias_method :scope_without_validation=, :scope=
327
398
  def scope=(scope)
328
399
  validate_scope(scope)
@@ -410,6 +481,7 @@ module ActiveLdap
410
481
  self.scope = :sub
411
482
  self.required_classes = ['top']
412
483
  self.recommended_classes = []
484
+ self.excluded_classes = []
413
485
 
414
486
  include Enumerable
415
487
 
@@ -424,25 +496,26 @@ module ActiveLdap
424
496
  init_base
425
497
  @new_entry = true
426
498
  initial_classes = required_classes | recommended_classes
427
- if attributes.nil?
428
- apply_object_class(initial_classes)
429
- elsif attributes.is_a?(String) or attributes.is_a?(Array)
430
- apply_object_class(initial_classes)
499
+ case attributes
500
+ when nil
501
+ self.classes = initial_classes
502
+ when String, Array, DN
503
+ self.classes = initial_classes
431
504
  self.dn = attributes
432
- elsif attributes.is_a?(Hash)
505
+ when Hash
433
506
  classes, attributes = extract_object_class(attributes)
434
- apply_object_class(classes | initial_classes)
507
+ self.classes = classes | initial_classes
435
508
  normalized_attributes = {}
436
509
  attributes.each do |key, value|
437
510
  real_key = to_real_attribute_name(key) || key
438
511
  normalized_attributes[real_key] = value
439
512
  end
440
- self.dn = normalized_attributes[dn_attribute]
513
+ self.dn = normalized_attributes.delete(dn_attribute)
441
514
  self.attributes = normalized_attributes
442
515
  else
443
- message = _("'%s' must be either nil, DN value as String or Array " \
444
- "or attributes as Hash") % attributes.inspect
445
- raise ArgumentError, message
516
+ format = _("'%s' must be either nil, DN value as ActiveLdap::DN, " \
517
+ "String or Array or attributes as Hash")
518
+ raise ArgumentError, format % attributes.inspect
446
519
  end
447
520
  yield self if block_given?
448
521
  assert_dn_attribute
@@ -471,13 +544,11 @@ module ActiveLdap
471
544
  end
472
545
 
473
546
  def may
474
- ensure_apply_object_class
475
- @may
547
+ entry_attribute.may
476
548
  end
477
549
 
478
550
  def must
479
- ensure_apply_object_class
480
- @must
551
+ entry_attribute.must
481
552
  end
482
553
 
483
554
  # attributes
@@ -485,15 +556,7 @@ module ActiveLdap
485
556
  # Return attribute methods so that a program can determine available
486
557
  # attributes dynamically without schema awareness
487
558
  def attribute_names(normalize=false)
488
- ensure_apply_object_class
489
- names = @attribute_names.keys
490
- if normalize
491
- names.collect do |name|
492
- to_real_attribute_name(name)
493
- end.uniq
494
- else
495
- names
496
- end
559
+ entry_attribute.names(normalize)
497
560
  end
498
561
 
499
562
  def attribute_present?(name)
@@ -520,16 +583,7 @@ module ActiveLdap
520
583
  #
521
584
  # Return the authoritative dn
522
585
  def dn
523
- return base if @dn_is_base
524
-
525
- dn_value = id
526
- if dn_value.nil?
527
- raise DistinguishedNameNotSetError.new,
528
- _("%s's DN attribute (%s) isn't set") % [self, dn_attribute]
529
- end
530
- _base = base
531
- _base = nil if _base.empty?
532
- ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
586
+ @dn ||= compute_dn
533
587
  end
534
588
 
535
589
  def id
@@ -542,6 +596,7 @@ module ActiveLdap
542
596
 
543
597
  def dn=(value)
544
598
  set_attribute(dn_attribute, value)
599
+ @dn = nil
545
600
  end
546
601
  alias_method(:id=, :dn=)
547
602
 
@@ -593,8 +648,6 @@ module ActiveLdap
593
648
  # using class_eval instead of using method_missing. This would
594
649
  # give tab completion in irb.
595
650
  def method_missing(name, *args, &block)
596
- ensure_apply_object_class
597
-
598
651
  key = name.to_s
599
652
  case key
600
653
  when /=$/
@@ -629,8 +682,7 @@ module ActiveLdap
629
682
 
630
683
  # Add available attributes to the methods
631
684
  def methods(inherited_too=true)
632
- ensure_apply_object_class
633
- target_names = @attribute_names.keys + @attribute_aliases.keys
685
+ target_names = entry_attribute.all_names
634
686
  target_names -= ['objectClass', Inflector.underscore('objectClass')]
635
687
  super + target_names.uniq.collect do |x|
636
688
  [x, "#{x}=", "#{x}?", "#{x}_before_type_cast"]
@@ -639,10 +691,12 @@ module ActiveLdap
639
691
 
640
692
  alias_method :respond_to_without_attributes?, :respond_to?
641
693
  def respond_to?(name, include_priv=false)
642
- have_attribute?(name.to_s) or
643
- (/(?:=|\?|_before_type_cast)$/ =~ name.to_s and
644
- have_attribute?($PREMATCH)) or
645
- super
694
+ return true if super
695
+
696
+ name = name.to_s
697
+ return true if have_attribute?(name)
698
+ return false if /(?:=|\?|_before_type_cast)$/ !~ name
699
+ have_attribute?($PREMATCH)
646
700
  end
647
701
 
648
702
  # Updates a given attribute and saves immediately
@@ -677,7 +731,7 @@ module ActiveLdap
677
731
  # Do not let URL/form hackers supply the keys.
678
732
  def attributes=(new_attributes)
679
733
  return if new_attributes.nil?
680
- _schema = nil
734
+ _schema = _local_entry_attribute = nil
681
735
  targets = remove_attributes_protected_from_mass_assignment(new_attributes)
682
736
  targets.each do |key, value|
683
737
  setter = "#{key}="
@@ -685,14 +739,15 @@ module ActiveLdap
685
739
  _schema ||= schema
686
740
  attribute = _schema.attribute(key)
687
741
  next if attribute.id.nil?
688
- define_attribute_methods(attribute)
742
+ _local_entry_attribute ||= local_entry_attribute
743
+ _local_entry_attribute.register(attribute)
689
744
  end
690
745
  send(setter, value)
691
746
  end
692
747
  end
693
748
 
694
749
  def to_ldif
695
- super(dn, normalize_data(@data))
750
+ super(dn, @data)
696
751
  end
697
752
 
698
753
  def to_xml(options={})
@@ -735,7 +790,7 @@ module ActiveLdap
735
790
 
736
791
  @ldap_data.update(attributes)
737
792
  classes, attributes = extract_object_class(attributes)
738
- apply_object_class(classes)
793
+ self.classes = classes
739
794
  self.attributes = attributes
740
795
  @new_entry = false
741
796
  self
@@ -759,11 +814,9 @@ module ActiveLdap
759
814
  end
760
815
  end
761
816
 
762
- def bind(config_or_password={}, &block)
817
+ def bind(config_or_password={}, config_or_ignore=nil, &block)
763
818
  if config_or_password.is_a?(String)
764
- config = {:password => config_or_password}
765
- elsif config_or_password.respond_to?(:call)
766
- config = {:password_block => config_or_password}
819
+ config = (config_or_ignore || {}).merge(:password => config_or_password)
767
820
  else
768
821
  config = config_or_password
769
822
  end
@@ -776,7 +829,7 @@ module ActiveLdap
776
829
  @connection = nil
777
830
  connection.connect
778
831
  @connection = connection
779
- @schema = nil
832
+ clear_connection_based_cache
780
833
  clear_association_cache
781
834
  rescue ActiveLdap::Error
782
835
  remove_connection
@@ -786,6 +839,17 @@ module ActiveLdap
786
839
  true
787
840
  end
788
841
 
842
+ def clear_connection_based_cache
843
+ @schema = nil
844
+ @local_entry_attribute = nil
845
+ clear_object_class_based_cache
846
+ end
847
+
848
+ def clear_object_class_based_cache
849
+ @entry_attribute = nil
850
+ @real_names = {}
851
+ end
852
+
789
853
  def schema
790
854
  @schema ||= super
791
855
  end
@@ -797,6 +861,7 @@ module ActiveLdap
797
861
 
798
862
  undef_method :base=
799
863
  def base=(object_local_base)
864
+ @dn = nil
800
865
  @base = object_local_base
801
866
  end
802
867
 
@@ -824,42 +889,50 @@ module ActiveLdap
824
889
  end
825
890
 
826
891
  private
892
+ def attribute_name_resolvable_without_connection?
893
+ @entry_attribute and @local_entry_attribute
894
+ end
895
+
896
+ def entry_attribute
897
+ @entry_attribute ||= connection.entry_attribute(@data["objectClass"] || [])
898
+ end
899
+
900
+ def local_entry_attribute
901
+ @local_entry_attribute ||= connection.entry_attribute([])
902
+ end
903
+
827
904
  def abbreviate_instance_variables
828
905
  @abbreviating ||= nil
829
906
  connection, @connection = @connection, nil
830
907
  schema, @schema = @schema, nil
831
- attribute_schemata, @attribute_schemata = @attribute_schemata, nil
832
- must, may = @must, @may
833
- object_classes = @object_classes
908
+ entry_attribute, @entry_attribute = @entry_attribute, nil
909
+ local_entry_attribute, @local_entry_attribute = @local_entry_attribute, nil
910
+ real_names, @real_names = @real_names, nil
834
911
  unless @abbreviating
835
912
  @abbreviating = true
836
- @must, @may = @must.collect(&:name), @may.collect(&:name)
837
- @object_classes = @object_classes.collect(&:name)
838
913
  end
839
914
  yield
840
915
  ensure
841
916
  @connection = connection
842
917
  @schema = schema
843
- @attribute_schemata = attribute_schemata
844
- @must = must
845
- @may = may
846
- @object_classes = object_classes
918
+ @entry_attribute = entry_attribute
919
+ @local_entry_attribute = local_entry_attribute
920
+ @real_names = real_names
847
921
  @abbreviating = false
848
922
  end
849
923
 
850
924
  def extract_object_class(attributes)
851
925
  classes = []
852
- attrs = attributes.stringify_keys.reject do |key, value|
853
- if key == 'objectClass' or
854
- key.underscore == 'object_class' or
855
- key.downcase == 'objectclass'
856
- classes |= [value].flatten
857
- true
926
+ attrs = {}
927
+ attributes.each do |key, value|
928
+ key = key.to_s
929
+ if /\Aobject_?class\z/i =~ key
930
+ classes.concat(value.to_a)
858
931
  else
859
- false
932
+ attrs[key] = value
860
933
  end
861
934
  end
862
- [classes, attrs]
935
+ [classes, attributes]
863
936
  end
864
937
 
865
938
  def init_base
@@ -868,11 +941,12 @@ module ActiveLdap
868
941
 
869
942
  def initialize_by_ldap_data(dn, attributes)
870
943
  init_base
944
+ @dn = dn
871
945
  @new_entry = false
872
946
  @dn_is_base = false
873
947
  @ldap_data = attributes
874
948
  classes, attributes = extract_object_class(attributes)
875
- apply_object_class(classes)
949
+ self.classes = classes
876
950
  self.dn = dn
877
951
  self.attributes = attributes
878
952
  yield self if block_given?
@@ -893,32 +967,22 @@ module ActiveLdap
893
967
 
894
968
  def to_real_attribute_name(name, allow_normalized_name=false)
895
969
  return name if name.nil?
896
- ensure_apply_object_class
897
- name = name.to_s
898
- real_name = @attribute_names[name]
899
- real_name ||= @attribute_aliases[Inflector.underscore(name)]
900
- if real_name
901
- real_name
902
- elsif allow_normalized_name
903
- @normalized_attribute_names[normalize_attribute_name(name)]
970
+ if allow_normalized_name
971
+ entry_attribute.normalize(name, allow_normalized_name) ||
972
+ local_entry_attribute.normalize(name, allow_normalized_name)
904
973
  else
905
- nil
974
+ @real_names[name] ||=
975
+ entry_attribute.normalize(name, false) ||
976
+ local_entry_attribute.normalize(name, false)
906
977
  end
907
978
  end
908
979
 
909
- def ensure_apply_object_class
910
- current_object_class = @data['objectClass']
911
- return if current_object_class.nil? or current_object_class == @last_oc
912
- apply_object_class(current_object_class)
913
- end
914
-
915
980
  # enforce_type
916
981
  #
917
982
  # enforce_type applies your changes without attempting to write to LDAP.
918
983
  # This means that if you set userCertificate to somebinary value, it will
919
984
  # wrap it up correctly.
920
985
  def enforce_type(key, value)
921
- ensure_apply_object_class
922
986
  # Enforce attribute value formatting
923
987
  normalize_attribute(key, value)[1]
924
988
  end
@@ -927,60 +991,12 @@ module ActiveLdap
927
991
  @mutex = Mutex.new
928
992
  @data = {} # where the r/w entry data is stored
929
993
  @ldap_data = {} # original ldap entry data
930
- @attribute_schemata = {}
931
- @attribute_names = {} # list of valid method calls for attributes used
932
- # for dereferencing
933
- @normalized_attribute_names = {} # list of normalized attribute name
934
- @attribute_aliases = {} # aliases of @attribute_names
935
- @last_oc = false # for use in other methods for "caching"
936
994
  @dn_attribute = nil
937
995
  @base = nil
938
996
  @scope = nil
997
+ @dn = nil
939
998
  @connection ||= nil
940
- end
941
-
942
- # apply_object_class
943
- #
944
- # objectClass= special case for updating appropriately
945
- # This updates the objectClass entry in @data. It also
946
- # updating all required and allowed attributes while
947
- # removing defined attributes that are no longer valid
948
- # given the new objectclasses.
949
- def apply_object_class(val)
950
- new_oc = val
951
- new_oc = [val] if new_oc.class != Array
952
- new_oc = new_oc.uniq
953
- return new_oc if @last_oc == new_oc
954
-
955
- # Store for caching purposes
956
- @last_oc = new_oc.dup
957
-
958
- # Set the actual objectClass data
959
- define_attribute_methods(schema.attribute('objectClass'))
960
- replace_class(*new_oc)
961
-
962
- # Build |data| from schema
963
- # clear attribute name mapping first
964
- @attribute_schemata = {}
965
- @attribute_names = {}
966
- @normalized_attribute_names = {}
967
- @attribute_aliases = {}
968
- @must = []
969
- @may = []
970
- @object_classes = []
971
- new_oc.each do |objc|
972
- # get all attributes for the class
973
- object_class = schema.object_class(objc)
974
- @object_classes << object_class
975
- @must.concat(object_class.must)
976
- @may.concat(object_class.may)
977
- end
978
- @must.uniq!
979
- @may.uniq!
980
- (@must + @may).each do |attr|
981
- # Update attr_method with appropriate
982
- define_attribute_methods(attr)
983
- end
999
+ clear_connection_based_cache
984
1000
  end
985
1001
 
986
1002
  # get_attribute
@@ -999,7 +1015,11 @@ module ActiveLdap
999
1015
  value.each do |option, val|
1000
1016
  result[option] = type_cast(attribute, val)
1001
1017
  end
1002
- result
1018
+ if result.size == 1 and result.has_key?("binary")
1019
+ result["binary"]
1020
+ else
1021
+ result
1022
+ end
1003
1023
  when Array
1004
1024
  value.collect do |val|
1005
1025
  type_cast(attribute, val)
@@ -1013,11 +1033,7 @@ module ActiveLdap
1013
1033
  name = to_real_attribute_name(name)
1014
1034
 
1015
1035
  value = @data[name] || []
1016
- if force_array
1017
- [name, value.dup]
1018
- else
1019
- [name, array_of(value.dup, false)]
1020
- end
1036
+ [name, array_of(value, force_array)]
1021
1037
  end
1022
1038
 
1023
1039
  def get_attribute_as_query(name, force_array=false)
@@ -1042,35 +1058,25 @@ module ActiveLdap
1042
1058
  attr, value = update_dn(attr, value) if attr == dn_attribute
1043
1059
  raise UnknownAttribute.new(name) if attr.nil?
1044
1060
 
1045
- case value
1046
- when nil, ""
1047
- value = []
1048
- when Array
1049
- value = value.collect {|c| c.blank? ? [] : c}.flatten
1050
- when String
1051
- value = [value]
1052
- when Numeric
1053
- value = [value.to_s]
1054
- end
1055
-
1056
- @data[attr] = enforce_type(attr, value)
1061
+ @data[attr] = value
1057
1062
  end
1058
1063
 
1059
1064
  def update_dn(attr, value)
1065
+ @dn = nil
1060
1066
  @dn_is_base = false
1061
- return [attr, value] if value.blank?
1067
+ return [attr, nil] if value.blank?
1062
1068
 
1063
- new_dn_attribute, new_value, base = split_dn_value(value)
1069
+ new_dn_attribute, new_value, bases = split_dn_value(value)
1064
1070
  if new_dn_attribute.nil? and new_value.nil?
1065
1071
  @dn_is_base = true
1066
1072
  @base = nil
1067
- attr, value = DN.parse(base).rdns[0].to_a[0]
1073
+ attr, value = bases[0].to_a[0]
1068
1074
  @dn_attribute = attr
1069
1075
  else
1070
1076
  new_dn_attribute = to_real_attribute_name(new_dn_attribute)
1071
1077
  if new_dn_attribute
1072
1078
  value = new_value
1073
- @base = base
1079
+ @base = bases.empty? ? nil : DN.new(*bases).to_s
1074
1080
  if dn_attribute != new_dn_attribute
1075
1081
  @dn_attribute = attr = new_dn_attribute
1076
1082
  end
@@ -1082,13 +1088,14 @@ module ActiveLdap
1082
1088
  def split_dn_value(value)
1083
1089
  dn_value = relative_dn_value = nil
1084
1090
  begin
1085
- dn_value = DN.parse(value)
1091
+ dn_value = value if value.is_a?(DN)
1092
+ dn_value ||= DN.parse(value)
1086
1093
  rescue DistinguishedNameInvalid
1087
1094
  dn_value = DN.parse("#{dn_attribute}=#{value}")
1088
1095
  end
1089
1096
 
1090
1097
  begin
1091
- relative_dn_value = dn_value - DN.parse(base_of_class)
1098
+ relative_dn_value = dn_value - self.class.parsed_base
1092
1099
  if relative_dn_value.rdns.empty?
1093
1100
  val = []
1094
1101
  bases = dn_value.rdns
@@ -1100,23 +1107,25 @@ module ActiveLdap
1100
1107
  end
1101
1108
 
1102
1109
  dn_attribute_name, dn_attribute_value = val.to_a[0]
1103
- [dn_attribute_name, dn_attribute_value,
1104
- bases.empty? ? nil : DN.new(*bases).to_s]
1110
+ [dn_attribute_name, dn_attribute_value, bases]
1105
1111
  end
1106
1112
 
1107
- # define_attribute_methods
1108
- #
1109
- # Make a method entry for _every_ alias of a valid attribute and map it
1110
- # onto the first attribute passed in.
1111
- def define_attribute_methods(attribute)
1112
- real_name = attribute.name
1113
- return if @attribute_schemata.has_key?(real_name)
1114
- @attribute_schemata[real_name] = attribute
1115
- ([real_name] + attribute.aliases).each do |name|
1116
- @attribute_names[name] = real_name
1117
- @attribute_aliases[Inflector.underscore(name)] = real_name
1118
- @normalized_attribute_names[normalize_attribute_name(name)] = real_name
1113
+ def compute_dn(escape_dn_value=false)
1114
+ return base if @dn_is_base
1115
+
1116
+ dn_value = id
1117
+ if dn_value.nil?
1118
+ raise DistinguishedNameNotSetError.new,
1119
+ _("%s's DN attribute (%s) isn't set") % [self, dn_attribute]
1119
1120
  end
1121
+ dn_value = DN.escape_value(dn_value) if escape_dn_value
1122
+ _base = base
1123
+ _base = nil if _base.empty?
1124
+ ["#{dn_attribute}=#{dn_value}", _base].compact.join(",")
1125
+ end
1126
+
1127
+ def escaped_dn
1128
+ compute_dn(true)
1120
1129
  end
1121
1130
 
1122
1131
  # array_of
@@ -1127,7 +1136,7 @@ module ActiveLdap
1127
1136
  case value
1128
1137
  when Array
1129
1138
  if to_a or value.size > 1
1130
- value.collect {|v| array_of(v, to_a)}
1139
+ value.collect {|v| array_of(v, false)}.compact
1131
1140
  else
1132
1141
  if value.empty?
1133
1142
  nil
@@ -1144,7 +1153,7 @@ module ActiveLdap
1144
1153
  result
1145
1154
  end
1146
1155
  else
1147
- to_a ? [value.to_s] : value.to_s
1156
+ to_a ? [value] : value
1148
1157
  end
1149
1158
  end
1150
1159
 
@@ -1158,7 +1167,7 @@ module ActiveLdap
1158
1167
  real_name ||= key
1159
1168
  next if _schema.attribute(real_name).id.nil?
1160
1169
  result[real_name] ||= []
1161
- result[real_name].concat(values)
1170
+ result[real_name].concat(enforce_type(real_name, values))
1162
1171
  end
1163
1172
  result
1164
1173
  end
@@ -1205,14 +1214,14 @@ module ActiveLdap
1205
1214
  dn_value = data[dn_attr]
1206
1215
 
1207
1216
  attributes = []
1208
- attributes.push([:add, dn_attr, dn_value])
1217
+ attributes.push([dn_attr, dn_value])
1209
1218
 
1210
1219
  oc_value = data['objectClass']
1211
- attributes.push([:add, 'objectClass', oc_value])
1220
+ attributes.push(['objectClass', oc_value])
1212
1221
  data.each do |key, value|
1213
1222
  next if value.empty? or key == 'objectClass' or key == dn_attr
1214
1223
 
1215
- attributes.push([:add, key, value])
1224
+ attributes.push([key, value])
1216
1225
  end
1217
1226
 
1218
1227
  attributes
@@ -1256,7 +1265,7 @@ module ActiveLdap
1256
1265
  def create
1257
1266
  prepare_data_for_saving do |data, ldap_data|
1258
1267
  attributes = collect_all_attributes(data)
1259
- add_entry(dn, attributes)
1268
+ add_entry(escaped_dn, attributes)
1260
1269
  @new_entry = false
1261
1270
  true
1262
1271
  end
@@ -1265,7 +1274,7 @@ module ActiveLdap
1265
1274
  def update
1266
1275
  prepare_data_for_saving do |data, ldap_data|
1267
1276
  attributes = collect_modified_attributes(ldap_data, data)
1268
- modify_entry(dn, attributes)
1277
+ modify_entry(escaped_dn, attributes)
1269
1278
  true
1270
1279
  end
1271
1280
  end