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,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