powerhome-activeldap 3.2.3

Sign up to get free protection for your applications and to get access to all the features.
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