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,422 @@
1
+ module ActiveLdap
2
+ class Schema
3
+ module Syntaxes
4
+ class << self
5
+ def [](id)
6
+ syntax = Base::SYNTAXES[id]
7
+ if syntax
8
+ syntax.new
9
+ else
10
+ nil
11
+ end
12
+ end
13
+ end
14
+
15
+ class Base
16
+ include GetTextSupport
17
+ SYNTAXES = {}
18
+
19
+ printable_character_source = "a-zA-Z\\d\"()+,\\-.\\/:? "
20
+ PRINTABLE_CHARACTER = /[#{printable_character_source}]/ #
21
+ UNPRINTABLE_CHARACTER = /[^#{printable_character_source}]/ #
22
+
23
+ def type_cast(value)
24
+ value
25
+ end
26
+
27
+ def valid?(value)
28
+ validate(value).nil?
29
+ end
30
+
31
+ def validate(value)
32
+ validate_normalized_value(normalize_value(value), value)
33
+ end
34
+
35
+ def normalize_value(value)
36
+ value
37
+ end
38
+ end
39
+
40
+ class BitString < Base
41
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.6"] = self
42
+
43
+ def type_cast(value)
44
+ return nil if value.nil?
45
+ if /\A'([01]*)'B\z/ =~ value.to_s
46
+ $1
47
+ else
48
+ value
49
+ end
50
+ end
51
+
52
+ def normalize_value(value)
53
+ if value.is_a?(String) and /\A[01]*\z/ =~ value
54
+ "'#{value}'B"
55
+ else
56
+ value
57
+ end
58
+ end
59
+
60
+ private
61
+ def validate_normalized_value(value, original_value)
62
+ if /\A'/ !~ value
63
+ return _("%s doesn't have the first \"'\"") % original_value.inspect
64
+ end
65
+
66
+ if /'B\z/ !~ value
67
+ return _("%s doesn't have the last \"'B\"") % original_value.inspect
68
+ end
69
+
70
+ if /([^01])/ =~ value[1..-3]
71
+ return _("%s has invalid character '%s'") % [value.inspect, $1]
72
+ end
73
+
74
+ nil
75
+ end
76
+ end
77
+
78
+ class Boolean < Base
79
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.7"] = self
80
+
81
+ def type_cast(value)
82
+ case value
83
+ when "TRUE"
84
+ true
85
+ when "FALSE"
86
+ false
87
+ else
88
+ value
89
+ end
90
+ end
91
+
92
+ def normalize_value(value)
93
+ case value
94
+ when true, "1"
95
+ "TRUE"
96
+ when false, "0"
97
+ "FALSE"
98
+ else
99
+ value
100
+ end
101
+ end
102
+
103
+ private
104
+ def validate_normalized_value(value, original_value)
105
+ if %w(TRUE FALSE).include?(value)
106
+ nil
107
+ else
108
+ _("%s should be TRUE or FALSE") % original_value.inspect
109
+ end
110
+ end
111
+ end
112
+
113
+ class CountryString < Base
114
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.11"] = self
115
+
116
+ private
117
+ def validate_normalized_value(value, original_value)
118
+ if /\A#{PRINTABLE_CHARACTER}{2,2}\z/i =~ value
119
+ nil
120
+ else
121
+ format = _("%s should be just 2 printable characters")
122
+ format % original_value.inspect
123
+ end
124
+ end
125
+ end
126
+
127
+ class DistinguishedName < Base
128
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.12"] = self
129
+
130
+ def type_cast(value)
131
+ return nil if value.nil?
132
+ DN.parse(value)
133
+ rescue DistinguishedNameInvalid
134
+ value
135
+ end
136
+
137
+ def normalize_value(value)
138
+ if value.is_a?(DN)
139
+ value.to_s
140
+ else
141
+ value
142
+ end
143
+ end
144
+
145
+ private
146
+ def validate_normalized_value(value, original_value)
147
+ DN.parse(value)
148
+ nil
149
+ rescue DistinguishedNameInvalid
150
+ $!.message
151
+ end
152
+ end
153
+
154
+ class DirectoryString < Base
155
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.15"] = self
156
+
157
+ private
158
+ def validate_normalized_value(value, original_value)
159
+ value.unpack("U*")
160
+ nil
161
+ rescue ArgumentError
162
+ _("%s has invalid UTF-8 character") % original_value.inspect
163
+ end
164
+ end
165
+
166
+ class GeneralizedTime < Base
167
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.24"] = self
168
+ FORMAT = /\A
169
+ (\d{4,4})?
170
+ (\d{2,2})?
171
+ (\d{2,2})?
172
+ (\d{2,2})?
173
+ (\d{2,2})?
174
+ (\d{2,2})?
175
+ ([,.]\d+)?
176
+ ([+-]\d{4,4}|Z)?
177
+ \z/x
178
+
179
+ def type_cast(value)
180
+ return value if value.nil? or value.is_a?(Time)
181
+ match_data = FORMAT.match(value)
182
+ if match_data
183
+ required_components = match_data.to_a[1, 5]
184
+ return value if required_components.any?(&:nil?)
185
+ year, month, day, hour, minute = required_components.collect(&:to_i)
186
+ second = match_data[-3].to_i
187
+ fraction = match_data[-2]
188
+ fraction = fraction.to_f if fraction
189
+ time_zone = match_data[-1]
190
+ arguments = [
191
+ year, month, day, hour, minute, second, fraction, time_zone,
192
+ Time.now
193
+ ]
194
+ if Time.method(:make_time).arity == 10
195
+ arguments.unshift(value)
196
+ end
197
+ begin
198
+ Time.send(:make_time, *arguments)
199
+ rescue ArgumentError
200
+ raise if year >= 1700
201
+ out_of_range_messages = ["argument out of range",
202
+ "time out of range"]
203
+ raise unless out_of_range_messages.include?($!.message)
204
+ Time.at(0)
205
+ rescue RangeError
206
+ raise if year >= 1700
207
+ raise if $!.message != "bignum too big to convert into `long'"
208
+ Time.at(0)
209
+ end
210
+ else
211
+ value
212
+ end
213
+ end
214
+
215
+ def normalize_value(value)
216
+ if value.is_a?(Time)
217
+ normalized_value = value.strftime("%Y%m%d%H%M%S")
218
+ if value.gmt?
219
+ normalized_value + "Z"
220
+ else
221
+ normalized_value + ("%+03d%02d" % value.gmtoff.divmod(3600))
222
+ end
223
+ else
224
+ value
225
+ end
226
+ end
227
+
228
+ private
229
+ def validate_normalized_value(value, original_value)
230
+ match_data = FORMAT.match(value)
231
+ if match_data
232
+ date_data = match_data.to_a[1..-1]
233
+ missing_components = []
234
+ required_components = %w(year month day hour minute)
235
+ required_components.each_with_index do |component, i|
236
+ missing_components << component unless date_data[i]
237
+ end
238
+ if missing_components.empty?
239
+ nil
240
+ else
241
+ params = [original_value.inspect, missing_components.join(", ")]
242
+ _("%s has missing components: %s") % params
243
+ end
244
+ else
245
+ _("%s is invalid time format") % original_value.inspect
246
+ end
247
+ end
248
+ end
249
+
250
+ class Integer < Base
251
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.27"] = self
252
+
253
+ def type_cast(value)
254
+ return value if value.nil?
255
+ begin
256
+ Integer(value)
257
+ rescue ArgumentError
258
+ value
259
+ end
260
+ end
261
+
262
+ def normalize_value(value)
263
+ if value.is_a?(::Integer)
264
+ value.to_s
265
+ else
266
+ value
267
+ end
268
+ end
269
+
270
+ private
271
+ def validate_normalized_value(value, original_value)
272
+ Integer(value)
273
+ nil
274
+ rescue ArgumentError
275
+ _("%s is invalid integer format") % original_value.inspect
276
+ end
277
+ end
278
+
279
+ class JPEG < Base
280
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.28"] = self
281
+
282
+ private
283
+ def validate_normalized_value(value, original_value)
284
+ if value.unpack("n")[0] == 0xffd8
285
+ nil
286
+ else
287
+ _("invalid JPEG format")
288
+ end
289
+ end
290
+ end
291
+
292
+ class NameAndOptionalUID < Base
293
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.34"] = self
294
+
295
+ private
296
+ def validate_normalized_value(value, original_value)
297
+ separator_index = value.rindex("#")
298
+ if separator_index
299
+ dn = value[0, separator_index]
300
+ bit_string = value[(separator_index + 1)..-1]
301
+ bit_string_reason = BitString.new.validate(bit_string)
302
+ dn_reason = DistinguishedName.new.validate(dn)
303
+ if bit_string_reason
304
+ if dn_reason
305
+ value_reason = DistinguishedName.new.validate(value)
306
+ return nil unless value_reason
307
+ dn_reason
308
+ else
309
+ bit_string_reason
310
+ end
311
+ else
312
+ dn_reason
313
+ end
314
+ else
315
+ DistinguishedName.new.validate(value)
316
+ end
317
+ end
318
+ end
319
+
320
+ class NumericString < Base
321
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.36"] = self
322
+
323
+ private
324
+ def validate_normalized_value(value, original_value)
325
+ if /\A\d+\z/ =~ value
326
+ nil
327
+ else
328
+ _("%s is invalid numeric format") % original_value.inspect
329
+ end
330
+ end
331
+ end
332
+
333
+ class OID < Base
334
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.38"] = self
335
+
336
+ private
337
+ def validate_normalized_value(value, original_value)
338
+ DN.parse("#{value}=dummy")
339
+ nil
340
+ rescue DistinguishedNameInvalid
341
+ reason = $!.reason
342
+ if reason
343
+ _("%s is invalid OID format: %s") % [original_value.inspect, reason]
344
+ else
345
+ _("%s is invalid OID format") % original_value.inspect
346
+ end
347
+ end
348
+ end
349
+
350
+ class OtherMailbox < Base
351
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.39"] = self
352
+
353
+ private
354
+ def validate_normalized_value(value, original_value)
355
+ type, mailbox = value.split('$', 2)
356
+
357
+ if type.empty?
358
+ return _("%s has no mailbox type") % original_value.inspect
359
+ end
360
+
361
+ if /(#{UNPRINTABLE_CHARACTER})/i =~ type
362
+ format = _("%s has unprintable character in mailbox type: '%s'")
363
+ return format % [original_value.inspect, $1]
364
+ end
365
+
366
+ if mailbox.blank?
367
+ return _("%s has no mailbox") % original_value.inspect
368
+ end
369
+
370
+ nil
371
+ end
372
+ end
373
+
374
+ class PostalAddress < Base
375
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.41"] = self
376
+
377
+ private
378
+ def validate_normalized_value(value, original_value)
379
+ if value.blank?
380
+ return _("empty string")
381
+ end
382
+
383
+ begin
384
+ value.unpack("U*")
385
+ rescue ArgumentError
386
+ return _("%s has invalid UTF-8 character") % original_value.inspect
387
+ end
388
+
389
+ nil
390
+ end
391
+ end
392
+
393
+ class PrintableString < Base
394
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.44"] = self
395
+
396
+ private
397
+ def validate_normalized_value(value, original_value)
398
+ if value.blank?
399
+ return _("empty string")
400
+ end
401
+
402
+ if /(#{UNPRINTABLE_CHARACTER})/i =~ value
403
+ format = _("%s has unprintable character: '%s'")
404
+ return format % [original_value.inspect, $1]
405
+ end
406
+
407
+ nil
408
+ end
409
+ end
410
+
411
+ class TelephoneNumber < PrintableString
412
+ SYNTAXES["1.3.6.1.4.1.1466.115.121.1.50"] = self
413
+
414
+ private
415
+ def validate_normalized_value(value, original_value)
416
+ return nil if value.blank?
417
+ super
418
+ end
419
+ end
420
+ end
421
+ end
422
+ end
@@ -0,0 +1,75 @@
1
+ require 'timeout'
2
+
3
+ module Timeout
4
+
5
+ # A forking timeout implementation that relies on
6
+ # signals to interrupt blocking I/O instead of passing
7
+ # that code to run in a separate process.
8
+ #
9
+ # A process is fork()ed, sleeps for _sec_,
10
+ # then sends a ALRM signal to the Process.ppid
11
+ # process. ALRM is used to avoid conflicts with sleep()
12
+ #
13
+ # This overwrites any signal
14
+ def Timeout.alarm(sec, exception=Timeout::Error, &block)
15
+ return block.call if sec == nil or sec.zero?
16
+
17
+
18
+ # Trap an alarm in case it comes before we're ready
19
+ orig_alrm = trap(:ALRM, 'IGNORE')
20
+
21
+ # Setup a fallback in case of a race condition of an
22
+ # alarm before we set the other trap
23
+ trap(:ALRM) do
24
+ # Don't leave zombies
25
+ Process.wait2()
26
+ # Restore the original handler
27
+ trap('ALRM', orig_alrm)
28
+ # Now raise an exception!
29
+ raise exception, 'execution expired'
30
+ end
31
+
32
+ # Spawn the sleeper
33
+ pid = Process.fork {
34
+ begin
35
+ # Sleep x seconds then send SIGALRM
36
+ sleep(sec)
37
+ # Send alarm!
38
+ Process.kill(:ALRM, Process.ppid)
39
+ end
40
+ exit! 0
41
+ }
42
+
43
+ # Setup the real handler
44
+ trap(:ALRM) do
45
+ # Make sure we clean up any zombies
46
+ Process.waitpid(pid)
47
+ # Restore the original handler
48
+ trap(:ALRM, orig_alrm)
49
+ # Now raise an exception!
50
+ raise exception, 'execution expired'
51
+ end
52
+
53
+ begin
54
+ # Run the code!
55
+ return block.call
56
+ ensure
57
+ # Restore old alarm handler since we're done
58
+ trap(:ALRM, orig_alrm)
59
+ # Make sure the process is dead
60
+ # This may be run twice (trap occurs during execution) so ignore ESRCH
61
+ Process.kill(:TERM, pid) rescue Errno::ESRCH
62
+ # Don't leave zombies
63
+ Process.waitpid(pid) rescue Errno::ECHILD
64
+ end
65
+ end
66
+ end # Timeout
67
+
68
+ if __FILE__ == $0
69
+ require 'time'
70
+ Timeout.alarm(2) do
71
+ loop do
72
+ p Time.now
73
+ end
74
+ end
75
+ end