powerhome-activeldap 3.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +6 -0
  3. data/COPYING +340 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +59 -0
  6. data/README.textile +140 -0
  7. data/TODO +32 -0
  8. data/benchmark/README.md +64 -0
  9. data/benchmark/bench-backend.rb +247 -0
  10. data/benchmark/bench-instantiate.rb +98 -0
  11. data/benchmark/config.yaml.sample +5 -0
  12. data/doc/text/development.textile +54 -0
  13. data/doc/text/news.textile +811 -0
  14. data/doc/text/rails.textile +144 -0
  15. data/doc/text/tutorial.textile +1010 -0
  16. data/examples/config.yaml.example +5 -0
  17. data/examples/example.der +0 -0
  18. data/examples/example.jpg +0 -0
  19. data/examples/groupadd +41 -0
  20. data/examples/groupdel +35 -0
  21. data/examples/groupls +49 -0
  22. data/examples/groupmod +42 -0
  23. data/examples/lpasswd +55 -0
  24. data/examples/objects/group.rb +13 -0
  25. data/examples/objects/ou.rb +4 -0
  26. data/examples/objects/user.rb +20 -0
  27. data/examples/ouadd +38 -0
  28. data/examples/useradd +45 -0
  29. data/examples/useradd-binary +53 -0
  30. data/examples/userdel +34 -0
  31. data/examples/userls +50 -0
  32. data/examples/usermod +42 -0
  33. data/examples/usermod-binary-add +50 -0
  34. data/examples/usermod-binary-add-time +54 -0
  35. data/examples/usermod-binary-del +48 -0
  36. data/examples/usermod-lang-add +43 -0
  37. data/lib/active_ldap.rb +85 -0
  38. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +55 -0
  39. data/lib/active_ldap/acts/tree.rb +78 -0
  40. data/lib/active_ldap/adapter/base.rb +707 -0
  41. data/lib/active_ldap/adapter/jndi.rb +184 -0
  42. data/lib/active_ldap/adapter/jndi_connection.rb +185 -0
  43. data/lib/active_ldap/adapter/ldap.rb +290 -0
  44. data/lib/active_ldap/adapter/ldap_ext.rb +105 -0
  45. data/lib/active_ldap/adapter/net_ldap.rb +309 -0
  46. data/lib/active_ldap/adapter/net_ldap_ext.rb +23 -0
  47. data/lib/active_ldap/association/belongs_to.rb +47 -0
  48. data/lib/active_ldap/association/belongs_to_many.rb +58 -0
  49. data/lib/active_ldap/association/children.rb +21 -0
  50. data/lib/active_ldap/association/collection.rb +105 -0
  51. data/lib/active_ldap/association/has_many.rb +31 -0
  52. data/lib/active_ldap/association/has_many_utils.rb +44 -0
  53. data/lib/active_ldap/association/has_many_wrap.rb +75 -0
  54. data/lib/active_ldap/association/proxy.rb +107 -0
  55. data/lib/active_ldap/associations.rb +205 -0
  56. data/lib/active_ldap/attribute_methods.rb +23 -0
  57. data/lib/active_ldap/attribute_methods/before_type_cast.rb +24 -0
  58. data/lib/active_ldap/attribute_methods/dirty.rb +43 -0
  59. data/lib/active_ldap/attribute_methods/query.rb +31 -0
  60. data/lib/active_ldap/attribute_methods/read.rb +44 -0
  61. data/lib/active_ldap/attribute_methods/write.rb +38 -0
  62. data/lib/active_ldap/attributes.rb +176 -0
  63. data/lib/active_ldap/base.rb +1410 -0
  64. data/lib/active_ldap/callbacks.rb +71 -0
  65. data/lib/active_ldap/command.rb +49 -0
  66. data/lib/active_ldap/compatible.rb +44 -0
  67. data/lib/active_ldap/configuration.rb +147 -0
  68. data/lib/active_ldap/connection.rb +299 -0
  69. data/lib/active_ldap/distinguished_name.rb +291 -0
  70. data/lib/active_ldap/entry_attribute.rb +78 -0
  71. data/lib/active_ldap/escape.rb +12 -0
  72. data/lib/active_ldap/get_text.rb +20 -0
  73. data/lib/active_ldap/get_text/parser.rb +161 -0
  74. data/lib/active_ldap/helper.rb +92 -0
  75. data/lib/active_ldap/human_readable.rb +133 -0
  76. data/lib/active_ldap/ldap_error.rb +74 -0
  77. data/lib/active_ldap/ldif.rb +930 -0
  78. data/lib/active_ldap/log_subscriber.rb +50 -0
  79. data/lib/active_ldap/object_class.rb +95 -0
  80. data/lib/active_ldap/operations.rb +624 -0
  81. data/lib/active_ldap/persistence.rb +100 -0
  82. data/lib/active_ldap/populate.rb +53 -0
  83. data/lib/active_ldap/railtie.rb +43 -0
  84. data/lib/active_ldap/railties/controller_runtime.rb +48 -0
  85. data/lib/active_ldap/schema.rb +701 -0
  86. data/lib/active_ldap/schema/syntaxes.rb +422 -0
  87. data/lib/active_ldap/timeout.rb +75 -0
  88. data/lib/active_ldap/timeout_stub.rb +17 -0
  89. data/lib/active_ldap/user_password.rb +99 -0
  90. data/lib/active_ldap/validations.rb +200 -0
  91. data/lib/active_ldap/version.rb +3 -0
  92. data/lib/active_ldap/xml.rb +139 -0
  93. data/lib/rails/generators/active_ldap/model/USAGE +18 -0
  94. data/lib/rails/generators/active_ldap/model/model_generator.rb +47 -0
  95. data/lib/rails/generators/active_ldap/model/templates/model_active_ldap.rb +3 -0
  96. data/lib/rails/generators/active_ldap/scaffold/scaffold_generator.rb +14 -0
  97. data/lib/rails/generators/active_ldap/scaffold/templates/ldap.yml +19 -0
  98. data/po/en/active-ldap.po +4029 -0
  99. data/po/ja/active-ldap.po +4060 -0
  100. data/test/add-phonetic-attribute-options-to-slapd.ldif +10 -0
  101. data/test/al-test-utils.rb +428 -0
  102. data/test/command.rb +111 -0
  103. data/test/config.yaml.sample +6 -0
  104. data/test/fixtures/lower_case_object_class_schema.rb +802 -0
  105. data/test/run-test.rb +34 -0
  106. data/test/test_acts_as_tree.rb +60 -0
  107. data/test/test_adapter.rb +121 -0
  108. data/test/test_associations.rb +701 -0
  109. data/test/test_attributes.rb +117 -0
  110. data/test/test_base.rb +1214 -0
  111. data/test/test_base_per_instance.rb +61 -0
  112. data/test/test_bind.rb +62 -0
  113. data/test/test_callback.rb +31 -0
  114. data/test/test_configuration.rb +40 -0
  115. data/test/test_connection.rb +82 -0
  116. data/test/test_connection_per_class.rb +112 -0
  117. data/test/test_connection_per_dn.rb +112 -0
  118. data/test/test_dirty.rb +98 -0
  119. data/test/test_dn.rb +172 -0
  120. data/test/test_find.rb +176 -0
  121. data/test/test_groupadd.rb +50 -0
  122. data/test/test_groupdel.rb +46 -0
  123. data/test/test_groupls.rb +107 -0
  124. data/test/test_groupmod.rb +51 -0
  125. data/test/test_ldif.rb +1890 -0
  126. data/test/test_load.rb +133 -0
  127. data/test/test_lpasswd.rb +75 -0
  128. data/test/test_object_class.rb +74 -0
  129. data/test/test_persistence.rb +131 -0
  130. data/test/test_reflection.rb +175 -0
  131. data/test/test_schema.rb +559 -0
  132. data/test/test_syntax.rb +444 -0
  133. data/test/test_user.rb +217 -0
  134. data/test/test_user_password.rb +108 -0
  135. data/test/test_useradd-binary.rb +62 -0
  136. data/test/test_useradd.rb +57 -0
  137. data/test/test_userdel.rb +48 -0
  138. data/test/test_userls.rb +91 -0
  139. data/test/test_usermod-binary-add-time.rb +65 -0
  140. data/test/test_usermod-binary-add.rb +64 -0
  141. data/test/test_usermod-binary-del.rb +66 -0
  142. data/test/test_usermod-lang-add.rb +59 -0
  143. data/test/test_usermod.rb +58 -0
  144. data/test/test_validation.rb +274 -0
  145. metadata +379 -0
@@ -0,0 +1,50 @@
1
+ module ActiveLdap
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ def self.runtime=(value)
4
+ Thread.current["active_ldap_runtime"] = value
5
+ end
6
+
7
+ def self.runtime
8
+ Thread.current["active_ldap_runtime"] ||= 0
9
+ end
10
+
11
+ def self.reset_runtime
12
+ rt, self.runtime = runtime, 0
13
+ rt
14
+ end
15
+
16
+ def initialize
17
+ super
18
+ @odd_or_even = false
19
+ end
20
+
21
+ def log_info(event)
22
+ self.class.runtime += event.duration
23
+ return unless logger.debug?
24
+
25
+ payload = event.payload
26
+ name = 'LDAP: %s (%.1fms)' % [payload[:name], event.duration]
27
+ info = payload[:info].inspect
28
+
29
+ if odd?
30
+ name_color, dump_color = "4;36;1", "0;1"
31
+ else
32
+ name_color, dump_color = "4;35;1", "0"
33
+ end
34
+
35
+ debug " \e[#{name_color}m#{name}\e[0m: \e[#{dump_color}m#{info}\e[0m"
36
+ end
37
+
38
+ def odd?
39
+ @odd_or_even = !@odd_or_even
40
+ end
41
+
42
+ def logger
43
+ ActiveLdap::Base.logger
44
+ end
45
+ end
46
+ end
47
+
48
+ ActiveLdap::LogSubscriber.attach_to :active_ldap
49
+
50
+
@@ -0,0 +1,95 @@
1
+ module ActiveLdap
2
+ module ObjectClass
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ end
6
+
7
+ module ClassMethods
8
+ def classes
9
+ required_classes.collect do |name|
10
+ schema.object_class(name)
11
+ end
12
+ end
13
+ end
14
+
15
+ def add_class(*target_classes)
16
+ replace_class(classes + target_classes)
17
+ end
18
+
19
+ def ensure_recommended_classes
20
+ add_class(self.class.recommended_classes)
21
+ end
22
+
23
+ def remove_class(*target_classes)
24
+ replace_class(classes - target_classes)
25
+ end
26
+
27
+ def replace_class(*target_classes)
28
+ new_classes = target_classes.flatten.compact.uniq
29
+ assert_object_classes(new_classes)
30
+ if new_classes.sort != classes.sort
31
+ set_attribute('objectClass', new_classes)
32
+ clear_object_class_based_cache
33
+ end
34
+ end
35
+ alias_method(:classes=, :replace_class)
36
+
37
+ def classes
38
+ (get_attribute('objectClass', true) || []).dup
39
+ end
40
+
41
+ private
42
+ def assert_object_classes(new_classes)
43
+ assert_valid_object_class_value_type(new_classes)
44
+ assert_valid_object_class_value(new_classes)
45
+ assert_have_all_required_classes(new_classes)
46
+ end
47
+
48
+ def assert_valid_object_class_value_type(new_classes)
49
+ invalid_classes = new_classes.reject do |new_class|
50
+ new_class.is_a?(String)
51
+ end
52
+ unless invalid_classes.empty?
53
+ format = _("Value in objectClass array is not a String: %s")
54
+ invalid_classes_info = invalid_classes.collect do |invalid_class|
55
+ "#{invalid_class.class}: #{invalid_class.inspect}"
56
+ end.join(", ")
57
+ raise TypeError, format % invalid_classes_info
58
+ end
59
+ end
60
+
61
+ def assert_valid_object_class_value(new_classes)
62
+ _schema = schema
63
+ invalid_classes = new_classes.reject do |new_class|
64
+ !_schema.object_class(new_class).id.nil?
65
+ end
66
+ unless invalid_classes.empty?
67
+ format = _("unknown objectClass in LDAP server: %s")
68
+ message = format % invalid_classes.join(', ')
69
+ raise ObjectClassError, message
70
+ end
71
+ end
72
+
73
+ def assert_have_all_required_classes(new_classes)
74
+ _schema = schema
75
+ normalized_new_classes = new_classes.collect(&:downcase)
76
+ required_classes = self.class.required_classes
77
+ required_classes = required_classes.reject do |required_class_name|
78
+ normalized_new_classes.include?(required_class_name.downcase) or
79
+ (normalized_new_classes.find do |new_class|
80
+ required_class = _schema.object_class(required_class_name)
81
+ _schema.object_class(new_class).super_class?(required_class)
82
+ end)
83
+ end
84
+ unless required_classes.empty?
85
+ format = _("Can't remove required objectClass: %s")
86
+ required_class_names = required_classes.collect do |required_class|
87
+ required_class = _schema.object_class(required_class)
88
+ self.class.human_object_class_name(required_class)
89
+ end
90
+ message = format % required_class_names.join(", ")
91
+ raise RequiredObjectClassMissed, message
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,624 @@
1
+ module ActiveLdap
2
+ module Operations
3
+ class << self
4
+ def included(base)
5
+ super
6
+ base.class_eval do
7
+ extend(Common)
8
+ extend(Find)
9
+ extend(LDIF)
10
+ extend(Delete)
11
+ extend(ClassOnlyDelete)
12
+ extend(Update)
13
+ extend(ClassOnlyUpdate)
14
+
15
+ include(Common)
16
+ include(Find)
17
+ include(LDIF)
18
+ include(Delete)
19
+ include(Update)
20
+ end
21
+ end
22
+ end
23
+
24
+ module Common
25
+ VALID_SEARCH_OPTIONS = [:attribute, :value, :filter, :prefix,
26
+ :classes, :scope, :limit, :attributes,
27
+ :sort_by, :order, :connection, :base, :offset]
28
+
29
+ def search(options={}, &block)
30
+ validate_search_options(options)
31
+ attr = options[:attribute]
32
+ value = options[:value] || '*'
33
+ filter = options[:filter]
34
+ prefix = options[:prefix]
35
+ classes = options[:classes]
36
+ requested_attributes = options[:attributes]
37
+
38
+ value = value.first if value.is_a?(Array) and value.first.size == 1
39
+
40
+ _attr = nil
41
+ _prefix = nil
42
+ if attr.nil? or attr == dn_attribute
43
+ _attr, value, _prefix = split_search_value(value)
44
+ end
45
+ attr ||= _attr || ensure_search_attribute
46
+ prefix ||= _prefix
47
+ filter ||= [attr, value]
48
+ filter = [:and, filter, *object_class_filters(classes)]
49
+ _base = options[:base] ? [options[:base]] : [prefix, base]
50
+ _base = prepare_search_base(_base)
51
+ if options.has_key?(:ldap_scope)
52
+ message = _(":ldap_scope search option is deprecated. " \
53
+ "Use :scope instead.")
54
+ ActiveSupport::Deprecation.warn(message)
55
+ options[:scope] ||= options[:ldap_scope]
56
+ end
57
+ search_options = {
58
+ :base => _base,
59
+ :scope => options[:scope] || scope,
60
+ :filter => filter,
61
+ :limit => options[:limit],
62
+ :attributes => requested_attributes,
63
+ :sort_by => options[:sort_by] || sort_by,
64
+ :order => options[:order] || order,
65
+ }
66
+ options[:connection] ||= connection
67
+ values = []
68
+ requested_all_attributes_p =
69
+ (requested_attributes.nil? or requested_attributes.include?('*'))
70
+ options[:connection].search(search_options) do |dn, attrs|
71
+ attributes = {}
72
+ attrs.each do |key, _value|
73
+ if requested_all_attributes_p or requested_attributes.include?(key)
74
+ normalized_attribute, normalized_value =
75
+ normalize_attribute_options(key, _value)
76
+ attributes[normalized_attribute] ||= []
77
+ attributes[normalized_attribute].concat(normalized_value)
78
+ else
79
+ next
80
+ end
81
+ end
82
+ values << [dn, attributes]
83
+ end
84
+ values = values.collect {|_value| yield(_value)} if block_given?
85
+ values
86
+ end
87
+
88
+ def exist?(dn, options={})
89
+ attr, value, prefix = split_search_value(dn)
90
+
91
+ options_for_leaf = {
92
+ :attribute => attr,
93
+ :value => value,
94
+ :prefix => prefix,
95
+ :limit => 1,
96
+ }
97
+
98
+ attribute = attr || ensure_search_attribute
99
+ options_for_non_leaf = {
100
+ :attribute => attr,
101
+ :value => value,
102
+ :prefix => ["#{attribute}=#{value}", prefix].compact.join(","),
103
+ :limit => 1,
104
+ :scope => :base,
105
+ }
106
+
107
+ !search(options_for_leaf.merge(options)).empty? or
108
+ !search(options_for_non_leaf.merge(options)).empty?
109
+ end
110
+ alias_method :exists?, :exist?
111
+
112
+ def count(options={})
113
+ search(options).size
114
+ end
115
+
116
+ private
117
+ def validate_search_options(options)
118
+ options.assert_valid_keys(VALID_SEARCH_OPTIONS)
119
+ end
120
+
121
+ def extract_options_from_args!(args)
122
+ args.last.is_a?(Hash) ? args.pop : {}
123
+ end
124
+
125
+ def ensure_search_attribute(*candidates)
126
+ default_search_attribute || "objectClass"
127
+ end
128
+
129
+ def ensure_dn_attribute(target)
130
+ "#{dn_attribute}=" +
131
+ target.gsub(/^\s*#{Regexp.escape(dn_attribute)}\s*=\s*/i, '')
132
+ end
133
+
134
+ def ensure_base(target)
135
+ [truncate_base(target), base.to_s].reject do |component|
136
+ component.blank?
137
+ end.join(',')
138
+ end
139
+
140
+ def truncate_base(target)
141
+ return nil if target.blank?
142
+ return target if base.nil?
143
+
144
+ parsed_target = nil
145
+ if target.is_a?(DN)
146
+ parsed_target = target
147
+ elsif /,/ =~ target
148
+ begin
149
+ parsed_target = DN.parse(target)
150
+ rescue DistinguishedNameInvalid
151
+ end
152
+ end
153
+
154
+ return target if parsed_target.nil?
155
+ begin
156
+ (parsed_target - base).to_s
157
+ rescue ArgumentError
158
+ target
159
+ end
160
+ end
161
+
162
+ def prepare_search_base(components)
163
+ components.compact.collect do |component|
164
+ case component
165
+ when String
166
+ component
167
+ when DN
168
+ component.to_s
169
+ else
170
+ DN.new(*component).to_s
171
+ end
172
+ end.reject{|x| x.empty?}.join(",")
173
+ end
174
+
175
+ def object_class_filters(classes=nil)
176
+ expected_classes = (classes || required_classes).collect do |name|
177
+ Escape.ldap_filter_escape(name)
178
+ end
179
+ unexpected_classes = excluded_classes.collect do |name|
180
+ Escape.ldap_filter_escape(name)
181
+ end
182
+ filters = []
183
+ unless expected_classes.empty?
184
+ filters << ["objectClass", "=", *expected_classes]
185
+ end
186
+ unless unexpected_classes.empty?
187
+ filters << [:not, [:or, ["objectClass", "=", *unexpected_classes]]]
188
+ end
189
+ filters
190
+ end
191
+
192
+ def split_search_value(value)
193
+ attr = prefix = nil
194
+
195
+ begin
196
+ dn = DN.parse(value)
197
+ attr, value = dn.rdns.first.to_a.first
198
+ rest = dn.rdns[1..-1]
199
+ prefix = DN.new(*rest).to_s unless rest.empty?
200
+ rescue DistinguishedNameInputInvalid
201
+ return [attr, value, prefix]
202
+ rescue DistinguishedNameInvalid
203
+ begin
204
+ dn = DN.parse("DUMMY=#{value}")
205
+ _, value = dn.rdns.first.to_a.first
206
+ rest = dn.rdns[1..-1]
207
+ prefix = DN.new(*rest).to_s unless rest.empty?
208
+ rescue DistinguishedNameInvalid
209
+ end
210
+ end
211
+
212
+ prefix = nil if prefix == base
213
+ prefix = truncate_base(prefix) if prefix
214
+ [attr, value, prefix]
215
+ end
216
+ end
217
+
218
+ module Find
219
+ # find
220
+ #
221
+ # Finds the first match for value where |value| is the value of some
222
+ # |field|, or the wildcard match. This is only useful for derived classes.
223
+ # usage: Subclass.find(:all, :attribute => "cn", :value => "some*val")
224
+ # Subclass.find(:all, 'some*val')
225
+ def find(*args)
226
+ options = extract_options_from_args!(args)
227
+ args = [:first] if args.empty? and !options.empty?
228
+ case args.first
229
+ when :first
230
+ options[:value] ||= args[1]
231
+ find_initial(options)
232
+ when :last
233
+ options[:value] ||= args[1]
234
+ find_last(options)
235
+ when :all
236
+ options[:value] ||= args[1]
237
+ find_every(options)
238
+ else
239
+ find_from_dns(args, options)
240
+ end
241
+ end
242
+
243
+ # A convenience wrapper for <tt>find(:first,
244
+ # *args)</tt>. You can pass in all the same arguments
245
+ # to this method as you can to <tt>find(:first)</tt>.
246
+ def first(*args)
247
+ find(:first, *args)
248
+ end
249
+
250
+ # A convenience wrapper for <tt>find(:last,
251
+ # *args)</tt>. You can pass in all the same arguments
252
+ # to this method as you can to <tt>find(:last)</tt>.
253
+ def last(*args)
254
+ find(:last, *args)
255
+ end
256
+
257
+ # This is an alias for find(:all). You can pass in
258
+ # all the same arguments to this method as you can
259
+ # to find(:all)
260
+ def all(*args)
261
+ find(:all, *args)
262
+ end
263
+
264
+ private
265
+ def find_initial(options)
266
+ find_every(options.merge(:limit => 1)).first
267
+ end
268
+
269
+ def find_last(options)
270
+ order = options[:order] || self.order || 'ascend'
271
+ order = normalize_sort_order(order) == :ascend ? :descend : :ascend
272
+ find_initial(options.merge(:order => order))
273
+ end
274
+
275
+ def normalize_sort_order(value)
276
+ case value.to_s
277
+ when /\Aasc(?:end)?\z/i
278
+ :ascend
279
+ when /\Adesc(?:end)?\z/i
280
+ :descend
281
+ else
282
+ raise ArgumentError, _("Invalid order: %s") % value.inspect
283
+ end
284
+ end
285
+
286
+ def find_every(options)
287
+ options = options.dup
288
+ sort_by = options.delete(:sort_by) || self.sort_by
289
+ order = options.delete(:order) || self.order
290
+ limit = options.delete(:limit) if sort_by or order
291
+ offset = options.delete(:offset) || offset
292
+ options[:attributes] = options.delete(:attributes) || ['*']
293
+ options[:attributes] |= ['objectClass']
294
+ results = search(options).collect do |dn, attrs|
295
+ instantiate([dn, attrs, {:connection => options[:connection]}])
296
+ end
297
+ return results if sort_by.nil? and order.nil?
298
+
299
+ sort_by ||= "dn"
300
+ if sort_by.downcase == "dn"
301
+ results = results.sort_by {|result| DN.parse(result.dn)}
302
+ else
303
+ results = results.sort_by {|result| result.send(sort_by)}
304
+ end
305
+
306
+ results.reverse! if normalize_sort_order(order || "ascend") == :descend
307
+ results = results[offset, results.size] if offset
308
+ results = results[0, limit] if limit
309
+ results
310
+ end
311
+
312
+ def find_from_dns(dns, options)
313
+ expects_array = dns.first.is_a?(Array)
314
+ return [] if expects_array and dns.first.empty?
315
+
316
+ dns = dns.flatten.compact.uniq
317
+
318
+ case dns.size
319
+ when 0
320
+ raise EntryNotFound, _("Couldn't find %s without a DN") % name
321
+ when 1
322
+ result = find_one(dns.first, options)
323
+ expects_array ? [result] : result
324
+ else
325
+ find_some(dns, options)
326
+ end
327
+ end
328
+
329
+ def find_one(dn, options)
330
+ attr, value, prefix = split_search_value(dn)
331
+ filter = [attr || ensure_search_attribute,
332
+ Escape.ldap_filter_escape(value)]
333
+ filter = [:and, filter, options[:filter]] if options[:filter]
334
+ options = {:prefix => prefix}.merge(options.merge(:filter => filter))
335
+ result = find_initial(options)
336
+ if result
337
+ result
338
+ else
339
+ args = [self.is_a?(Class) ? name : self.class.name,
340
+ dn]
341
+ if options[:filter]
342
+ format = _("Couldn't find %s: DN: %s: filter: %s")
343
+ args << options[:filter].inspect
344
+ else
345
+ format = _("Couldn't find %s: DN: %s")
346
+ end
347
+ raise EntryNotFound, format % args
348
+ end
349
+ end
350
+
351
+ def find_some(dns, options)
352
+ dn_filters = dns.collect do |dn|
353
+ attr, value, prefix = split_search_value(dn)
354
+ attr ||= ensure_search_attribute
355
+ filter = [attr, value]
356
+ if prefix
357
+ filter = [:and,
358
+ filter,
359
+ [dn, "*,#{Escape.ldap_filter_escape(prefix)},#{base}"]]
360
+ end
361
+ filter
362
+ end
363
+ filter = [:or, *dn_filters]
364
+ filter = [:and, filter, options[:filter]] if options[:filter]
365
+ result = find_every(options.merge(:filter => filter))
366
+ if result.size == dns.size
367
+ result
368
+ else
369
+ args = [self.is_a?(Class) ? name : self.class.name,
370
+ dns.join(", ")]
371
+ if options[:filter]
372
+ format = _("Couldn't find all %s: DNs (%s): filter: %s")
373
+ args << options[:filter].inspect
374
+ else
375
+ format = _("Couldn't find all %s: DNs (%s)")
376
+ end
377
+ raise EntryNotFound, format % args
378
+ end
379
+ end
380
+
381
+ def ensure_dn(target)
382
+ attr, value, prefix = split_search_value(target)
383
+ "#{attr || dn_attribute}=#{value},#{prefix || base}"
384
+ end
385
+ end
386
+
387
+ module LDIF
388
+ def dump(options={})
389
+ ldif = Ldif.new
390
+ options = {:base => base, :scope => scope}.merge(options)
391
+ options[:connection] ||= connection
392
+ options[:connection].search(options) do |dn, attributes|
393
+ ldif << Ldif::Record.new(dn, attributes)
394
+ end
395
+ return "" if ldif.records.empty?
396
+ ldif.to_s
397
+ end
398
+
399
+ def to_ldif_record(dn, attributes)
400
+ Ldif::Record.new(dn, attributes)
401
+ end
402
+
403
+ def to_ldif(dn, attributes)
404
+ Ldif.new([to_ldif_record(dn, attributes)]).to_s
405
+ end
406
+
407
+ def load(ldif, options={})
408
+ return if ldif.blank?
409
+ Ldif.parse(ldif).each do |record|
410
+ record.load(self, options)
411
+ end
412
+ end
413
+
414
+ module ContentRecordLoadable
415
+ def load(operator, options)
416
+ operator.add_entry(dn, attributes, options)
417
+ end
418
+ end
419
+ Ldif::ContentRecord.send(:include, ContentRecordLoadable)
420
+
421
+ module AddRecordLoadable
422
+ def load(operator, options)
423
+ entries = attributes.collect do |key, value|
424
+ [:add, key, value]
425
+ end
426
+ options = {:controls => controls}.merge(options)
427
+ operator.modify_entry(dn, entries, options)
428
+ end
429
+ end
430
+ Ldif::AddRecord.send(:include, AddRecordLoadable)
431
+
432
+ module DeleteRecordLoadable
433
+ def load(operator, options)
434
+ operator.delete_entry(dn, {:controls => controls}.merge(options))
435
+ end
436
+ end
437
+ Ldif::DeleteRecord.send(:include, DeleteRecordLoadable)
438
+
439
+ module ModifyNameRecordLoadable
440
+ def load(operator, options)
441
+ operator.modify_rdn_entry(dn, new_rdn, delete_old_rdn?, new_superior,
442
+ {:controls => controls}.merge(options))
443
+ end
444
+ end
445
+ Ldif::ModifyNameRecord.send(:include, ModifyNameRecordLoadable)
446
+
447
+ module ModifyRecordLoadable
448
+ def load(operator, options)
449
+ modify_entries = operations.inject([]) do |result, operation|
450
+ result + operation.to_modify_entries
451
+ end
452
+ return if modify_entries.empty?
453
+ operator.modify_entry(dn, modify_entries,
454
+ {:controls => controls}.merge(options))
455
+ end
456
+
457
+ module AddOperationModifiable
458
+ def to_modify_entries
459
+ attributes.collect do |key, value|
460
+ [:add, key, value]
461
+ end
462
+ end
463
+ end
464
+ Ldif::ModifyRecord::AddOperation.send(:include, AddOperationModifiable)
465
+
466
+ module DeleteOperationModifiable
467
+ def to_modify_entries
468
+ return [[:delete, full_attribute_name, []]] if attributes.empty?
469
+ attributes.collect do |key, value|
470
+ [:delete, key, value]
471
+ end
472
+ end
473
+ end
474
+ Ldif::ModifyRecord::DeleteOperation.send(:include,
475
+ DeleteOperationModifiable)
476
+
477
+ module ReplaceOperationModifiable
478
+ def to_modify_entries
479
+ return [[:replace, full_attribute_name, []]] if attributes.empty?
480
+ attributes.collect do |key, value|
481
+ [:replace, key, value]
482
+ end
483
+ end
484
+ end
485
+ Ldif::ModifyRecord::ReplaceOperation.send(:include,
486
+ ReplaceOperationModifiable)
487
+ end
488
+ Ldif::ModifyRecord.send(:include, ModifyRecordLoadable)
489
+ end
490
+
491
+ module Delete
492
+ def destroy_all(options_or_filter=nil, deprecated_options=nil)
493
+ if deprecated_options.nil?
494
+ if options_or_filter.is_a?(String)
495
+ options = {:filter => options_or_filter}
496
+ else
497
+ options = (options_or_filter || {}).dup
498
+ end
499
+ else
500
+ options = deprecated_options.merge(:filter => options_or_filter)
501
+ end
502
+
503
+ find(:all, options).sort_by do |target|
504
+ target.dn
505
+ end.each do |target|
506
+ target.destroy
507
+ end
508
+ end
509
+
510
+ def delete_all(options_or_filter=nil, deprecated_options=nil)
511
+ if deprecated_options.nil?
512
+ if options_or_filter.is_a?(String)
513
+ options = {:filter => options_or_filter}
514
+ else
515
+ options = (options_or_filter || {}).dup
516
+ end
517
+ else
518
+ options = deprecated_options.merge(:filter => options_or_filter)
519
+ end
520
+ targets = search(options).collect do |dn, attributes|
521
+ dn
522
+ end.sort_by do |dn|
523
+ dn.upcase.reverse
524
+ end.reverse
525
+
526
+ delete_entry(targets, options)
527
+ end
528
+
529
+ def delete_entry(dn, options={})
530
+ options[:connection] ||= connection
531
+ begin
532
+ options[:connection].delete(dn, options)
533
+ rescue Error
534
+ format = _("Failed to delete LDAP entry: <%s>: %s")
535
+ raise DeleteError.new(format % [dn.inspect, $!.message])
536
+ end
537
+ end
538
+ end
539
+
540
+ module ClassOnlyDelete
541
+ def destroy(targets, options={})
542
+ targets = [targets] unless targets.is_a?(Array)
543
+ targets.each do |target|
544
+ find(target, options).destroy
545
+ end
546
+ end
547
+
548
+ def delete(targets, options={})
549
+ targets = [targets] unless targets.is_a?(Array)
550
+ targets = targets.collect do |target|
551
+ ensure_dn_attribute(ensure_base(target))
552
+ end
553
+ delete_entry(targets, options)
554
+ end
555
+ end
556
+
557
+ module Update
558
+ def add_entry(dn, attributes, options={})
559
+ unnormalized_attributes = attributes.collect do |key, value|
560
+ [:add, key, unnormalize_attribute(key, value)]
561
+ end
562
+ options[:connection] ||= connection
563
+ options[:connection].add(dn, unnormalized_attributes, options)
564
+ end
565
+
566
+ def modify_entry(dn, attributes, options={})
567
+ return if attributes.empty?
568
+ unnormalized_attributes = attributes.collect do |type, key, value|
569
+ [type, key, unnormalize_attribute(key, value)]
570
+ end
571
+ options[:connection] ||= connection
572
+ options[:connection].modify(dn, unnormalized_attributes, options)
573
+ end
574
+
575
+ def modify_rdn_entry(dn, new_rdn, delete_old_rdn, new_superior, options={})
576
+ options[:connection] ||= connection
577
+ options[:connection].modify_rdn(dn, new_rdn, delete_old_rdn,
578
+ new_superior, options)
579
+ end
580
+
581
+ def update_all(attributes, filter=nil, options={})
582
+ search_options = options.dup
583
+ if filter
584
+ if filter.is_a?(String) and /[=\(\)&\|]/ !~ filter
585
+ search_options = search_options.merge(:value => filter)
586
+ else
587
+ search_options = search_options.merge(:filter => filter)
588
+ end
589
+ end
590
+ targets = search(search_options).collect do |dn, attrs|
591
+ dn
592
+ end
593
+
594
+ unnormalized_attributes = attributes.collect do |name, value|
595
+ normalized_name, normalized_value = normalize_attribute(name, value)
596
+ [:replace, normalized_name,
597
+ unnormalize_attribute(normalized_name, normalized_value)]
598
+ end
599
+ options[:connection] ||= connection
600
+ conn = options[:connection]
601
+ targets.each do |dn|
602
+ conn.modify(dn, unnormalized_attributes, options)
603
+ end
604
+ end
605
+ end
606
+
607
+ module ClassOnlyUpdate
608
+ def update(dn, attributes, options={})
609
+ if dn.is_a?(Array)
610
+ i = -1
611
+ dns = dn
612
+ dns.collect do |_dn|
613
+ i += 1
614
+ update(_dn, attributes[i], options)
615
+ end
616
+ else
617
+ object = find(dn, options)
618
+ object.update_attributes(attributes)
619
+ object
620
+ end
621
+ end
622
+ end
623
+ end
624
+ end