activeldap 0.9.0 → 0.10.0

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 (120) hide show
  1. data/CHANGES +61 -0
  2. data/README +8 -1
  3. data/Rakefile +4 -1
  4. data/benchmark/bench-al.rb +12 -2
  5. data/examples/al-admin/app/controllers/account_controller.rb +4 -3
  6. data/examples/al-admin/app/controllers/application.rb +5 -2
  7. data/examples/al-admin/app/controllers/directory_controller.rb +3 -1
  8. data/examples/al-admin/app/controllers/users_controller.rb +19 -4
  9. data/examples/al-admin/app/controllers/welcome_controller.rb +4 -2
  10. data/examples/al-admin/app/helpers/application_helper.rb +7 -1
  11. data/examples/al-admin/app/helpers/url_helper.rb +4 -0
  12. data/examples/al-admin/app/models/ldap_user.rb +4 -0
  13. data/examples/al-admin/app/views/_entry/{_attributes_information.rhtml → _attributes_information.html.erb} +0 -0
  14. data/examples/al-admin/app/views/_entry/{_entry.rhtml → _entry.html.erb} +0 -0
  15. data/examples/al-admin/app/views/_schema/{_aliases.rhtml → _aliases.html.erb} +0 -0
  16. data/examples/al-admin/app/views/_switcher/{_after.rhtml → _after.html.erb} +0 -0
  17. data/examples/al-admin/app/views/_switcher/{_before.rhtml → _before.html.erb} +0 -0
  18. data/examples/al-admin/app/views/account/{login.rhtml → login.html.erb} +0 -0
  19. data/examples/al-admin/app/views/account/{sign_up.rhtml → sign_up.html.erb} +0 -0
  20. data/examples/al-admin/app/views/attributes/{_attributes.rhtml → _attributes.html.erb} +0 -0
  21. data/examples/al-admin/app/views/attributes/{_detail.rhtml → _detail.html.erb} +0 -0
  22. data/examples/al-admin/app/views/attributes/{index.rhtml → index.html.erb} +0 -0
  23. data/examples/al-admin/app/views/attributes/{show.rhtml → show.html.erb} +0 -0
  24. data/examples/al-admin/app/views/directory/{_tree.rhtml → _tree.html.erb} +0 -0
  25. data/examples/al-admin/app/views/directory/{_tree_view_js.rhtml → _tree_view_js.html.erb} +4 -5
  26. data/examples/al-admin/app/views/directory/{index.rhtml → index.html.erb} +0 -0
  27. data/examples/al-admin/app/views/directory/{populate.rhtml → populate.html.erb} +0 -0
  28. data/examples/al-admin/app/views/layouts/{_footer.rhtml → _footer.html.erb} +0 -0
  29. data/examples/al-admin/app/views/layouts/{_header_menu.rhtml → _header_menu.html.erb} +0 -0
  30. data/examples/al-admin/app/views/layouts/{_main_menu.rhtml → _main_menu.html.erb} +0 -0
  31. data/examples/al-admin/app/views/layouts/{application.rhtml → application.html.erb} +3 -2
  32. data/examples/al-admin/app/views/object_classes/{_attributes.rhtml → _attributes.html.erb} +0 -0
  33. data/examples/al-admin/app/views/object_classes/{_object_classes.rhtml → _object_classes.html.erb} +0 -0
  34. data/examples/al-admin/app/views/object_classes/{index.rhtml → index.html.erb} +0 -0
  35. data/examples/al-admin/app/views/object_classes/{show.rhtml → show.html.erb} +0 -0
  36. data/examples/al-admin/app/views/syntaxes/{_detail.rhtml → _detail.html.erb} +0 -0
  37. data/examples/al-admin/app/views/syntaxes/{_syntaxes.rhtml → _syntaxes.html.erb} +0 -0
  38. data/examples/al-admin/app/views/syntaxes/{index.rhtml → index.html.erb} +0 -0
  39. data/examples/al-admin/app/views/syntaxes/{show.rhtml → show.html.erb} +0 -0
  40. data/examples/al-admin/app/views/users/{_attributes_update_form.rhtml → _attributes_update_form.html.erb} +0 -0
  41. data/examples/al-admin/app/views/users/{_form.rhtml → _form.html.erb} +0 -0
  42. data/examples/al-admin/app/views/users/{_object_classes_update_form.rhtml → _object_classes_update_form.html.erb} +7 -1
  43. data/examples/al-admin/app/views/users/{_password_change_form.rhtml → _password_change_form.html.erb} +0 -0
  44. data/examples/al-admin/app/views/users/{edit.rhtml → edit.html.erb} +0 -0
  45. data/examples/al-admin/app/views/users/{index.rhtml → index.html.erb} +0 -0
  46. data/examples/al-admin/app/views/users/{show.rhtml → show.html.erb} +0 -0
  47. data/examples/al-admin/app/views/welcome/{index.rhtml → index.html.erb} +0 -0
  48. data/examples/al-admin/config/boot.rb +96 -32
  49. data/examples/al-admin/config/environment.rb +30 -36
  50. data/examples/al-admin/config/environments/development.rb +2 -5
  51. data/examples/al-admin/config/environments/production.rb +1 -0
  52. data/examples/al-admin/config/environments/test.rb +4 -1
  53. data/examples/al-admin/config/initializers/exception_notifier.rb +2 -0
  54. data/examples/al-admin/config/initializers/gettext.rb +1 -0
  55. data/examples/al-admin/config/initializers/inflections.rb +10 -0
  56. data/examples/al-admin/config/initializers/mime_types.rb +5 -0
  57. data/examples/al-admin/config/initializers/ralative_url_support.rb +1 -0
  58. data/examples/al-admin/config/routes.rb +24 -12
  59. data/examples/al-admin/lib/authenticated_system.rb +1 -1
  60. data/examples/al-admin/lib/tasks/gettext.rake +1 -1
  61. data/examples/al-admin/po/en/al-admin.po +102 -100
  62. data/examples/al-admin/po/ja/al-admin.po +112 -110
  63. data/examples/al-admin/po/nl/al-admin.po +117 -110
  64. data/examples/al-admin/public/javascripts/controls.js +484 -354
  65. data/examples/al-admin/public/javascripts/dragdrop.js +88 -58
  66. data/examples/al-admin/public/javascripts/effects.js +396 -364
  67. data/examples/al-admin/public/javascripts/prototype.js +2817 -1107
  68. data/examples/al-admin/public/stylesheets/base.css +5 -0
  69. data/examples/al-admin/script/performance/request +3 -0
  70. data/lib/active_ldap.rb +13 -10
  71. data/lib/active_ldap/adapter/base.rb +159 -43
  72. data/lib/active_ldap/adapter/jndi.rb +175 -0
  73. data/lib/active_ldap/adapter/jndi_connection.rb +180 -0
  74. data/lib/active_ldap/adapter/ldap.rb +91 -46
  75. data/lib/active_ldap/adapter/ldap_ext.rb +19 -5
  76. data/lib/active_ldap/adapter/net_ldap.rb +52 -44
  77. data/lib/active_ldap/association/has_many_wrap.rb +1 -1
  78. data/lib/active_ldap/attributes.rb +20 -95
  79. data/lib/active_ldap/base.rb +195 -186
  80. data/lib/active_ldap/callbacks.rb +33 -0
  81. data/lib/active_ldap/command.rb +3 -3
  82. data/lib/active_ldap/connection.rb +21 -3
  83. data/lib/active_ldap/distinguished_name.rb +18 -11
  84. data/lib/active_ldap/entry_attribute.rb +78 -0
  85. data/lib/active_ldap/human_readable.rb +20 -0
  86. data/lib/active_ldap/ldif.rb +860 -10
  87. data/lib/active_ldap/object_class.rb +6 -4
  88. data/lib/active_ldap/operations.rb +129 -22
  89. data/lib/active_ldap/schema.rb +118 -9
  90. data/lib/active_ldap/schema/syntaxes.rb +33 -16
  91. data/lib/active_ldap/validations.rb +74 -65
  92. data/po/en/active-ldap.po +378 -768
  93. data/po/ja/active-ldap.po +935 -868
  94. data/rails/plugin/active_ldap/init.rb +40 -2
  95. data/test/al-test-utils.rb +78 -58
  96. data/test/command.rb +51 -1
  97. data/test/test-unit-ext/priority.rb +29 -6
  98. data/test/test_adapter.rb +21 -2
  99. data/test/test_attributes.rb +13 -0
  100. data/test/test_base.rb +51 -1
  101. data/test/test_connection.rb +2 -1
  102. data/test/test_connection_per_class.rb +55 -1
  103. data/test/test_connection_per_dn.rb +29 -1
  104. data/test/test_find.rb +73 -0
  105. data/test/test_ldif.rb +1829 -15
  106. data/test/test_load.rb +126 -0
  107. data/test/test_object_class.rb +23 -5
  108. data/test/test_schema.rb +28 -0
  109. data/test/test_syntax.rb +22 -11
  110. data/test/test_user.rb +16 -25
  111. data/test/test_useradd-binary.rb +1 -1
  112. data/test/test_usermod-binary-add-time.rb +1 -1
  113. data/test/test_usermod-binary-add.rb +1 -1
  114. data/test/test_validation.rb +100 -22
  115. metadata +77 -71
  116. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  117. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  118. data/examples/al-admin/app/views/layouts/_flash_box.rhtml +0 -4
  119. data/examples/al-admin/public/stylesheets/common.css +0 -2
  120. data/examples/al-admin/script/breakpointer +0 -3
@@ -13,7 +13,7 @@ module ActiveLdap
13
13
  end
14
14
 
15
15
  def add_class(*target_classes)
16
- replace_class((classes + target_classes.flatten).uniq)
16
+ replace_class(classes + target_classes)
17
17
  end
18
18
 
19
19
  def ensure_recommended_classes
@@ -21,16 +21,18 @@ module ActiveLdap
21
21
  end
22
22
 
23
23
  def remove_class(*target_classes)
24
- replace_class((classes - target_classes.flatten).uniq)
24
+ replace_class(classes - target_classes)
25
25
  end
26
26
 
27
27
  def replace_class(*target_classes)
28
- new_classes = target_classes.flatten.uniq
28
+ new_classes = target_classes.flatten.compact.uniq
29
29
  assert_object_classes(new_classes)
30
30
  if new_classes.sort != classes.sort
31
31
  set_attribute('objectClass', new_classes)
32
+ clear_object_class_based_cache
32
33
  end
33
34
  end
35
+ alias_method(:classes=, :replace_class)
34
36
 
35
37
  def classes
36
38
  (get_attribute('objectClass', true) || []).dup
@@ -50,7 +52,7 @@ module ActiveLdap
50
52
  unless invalid_classes.empty?
51
53
  format = _("Value in objectClass array is not a String: %s")
52
54
  invalid_classes_info = invalid_classes.collect do |invalid_class|
53
- "#{invalid_class.class}:#{invalid_class.inspect}"
55
+ "#{invalid_class.class}: #{invalid_class.inspect}"
54
56
  end.join(", ")
55
57
  raise TypeError, format % invalid_classes_info
56
58
  end
@@ -33,18 +33,14 @@ module ActiveLdap
33
33
  classes = options[:classes]
34
34
 
35
35
  value = value.first if value.is_a?(Array) and value.first.size == 1
36
- if filter.nil? and !value.is_a?(String)
37
- message = _("Search value must be a String: %s") % value.inspect
38
- raise ArgumentError, message
39
- end
40
36
 
41
37
  _attr, value, _prefix = split_search_value(value)
42
38
  attr ||= _attr || ensure_search_attribute
43
39
  prefix ||= _prefix
44
40
  filter ||= [attr, value]
45
41
  filter = [:and, filter, *object_class_filters(classes)]
46
- _base = options[:base]
47
- _base ||= [prefix, base].compact.reject{|x| x.empty?}.join(",")
42
+ _base = options[:base] ? [options[:base]] : [prefix, base]
43
+ _base = prepare_search_base(_base)
48
44
  if options.has_key?(:ldap_scope)
49
45
  logger.warning do
50
46
  _(":ldap_scope search option is deprecated. Use :scope instead.")
@@ -136,14 +132,37 @@ module ActiveLdap
136
132
  end
137
133
  end
138
134
 
135
+ def prepare_search_base(components)
136
+ components.compact.collect do |component|
137
+ if component.is_a?(String)
138
+ component
139
+ else
140
+ DN.new(*component).to_s
141
+ end
142
+ end.reject{|x| x.empty?}.join(",")
143
+ end
144
+
139
145
  def object_class_filters(classes=nil)
140
- (classes || required_classes).collect do |name|
141
- ["objectClass", Escape.ldap_filter_escape(name)]
146
+ expected_classes = (classes || required_classes).collect do |name|
147
+ Escape.ldap_filter_escape(name)
148
+ end
149
+ unexpected_classes = excluded_classes.collect do |name|
150
+ Escape.ldap_filter_escape(name)
142
151
  end
152
+ filters = []
153
+ unless expected_classes.empty?
154
+ filters << ["objectClass", "=", *expected_classes]
155
+ end
156
+ unless unexpected_classes.empty?
157
+ filters << [:not, [:or, ["objectClass", "=", *unexpected_classes]]]
158
+ end
159
+ filters
143
160
  end
144
161
 
145
162
  def split_search_value(value)
146
163
  attr = prefix = nil
164
+ return [attr, value, prefix] unless value.is_a?(String)
165
+
147
166
  begin
148
167
  dn = DN.parse(value)
149
168
  attr, value = dn.rdns.first.to_a.first
@@ -204,9 +223,10 @@ module ActiveLdap
204
223
 
205
224
  def find_every(options)
206
225
  options = options.dup
207
- sort_by = options.delete(:sort_by) || sort_by
208
- order = options.delete(:order) || order
226
+ sort_by = options.delete(:sort_by) || self.sort_by
227
+ order = options.delete(:order) || self.order
209
228
  limit = options.delete(:limit) if sort_by or order
229
+ options[:attributes] |= ["objectClass"] if options[:attributes]
210
230
 
211
231
  results = search(options).collect do |dn, attrs|
212
232
  instantiate([dn, attrs, {:connection => options[:connection]}])
@@ -302,24 +322,101 @@ module ActiveLdap
302
322
 
303
323
  module LDIF
304
324
  def dump(options={})
305
- ldifs = []
325
+ ldif = Ldif.new
306
326
  options = {:base => base, :scope => scope}.merge(options)
307
327
  options[:connection] ||= connection
308
328
  options[:connection].search(options) do |dn, attributes|
309
- ldifs << to_ldif(dn, attributes)
329
+ ldif << Ldif::Record.new(dn, attributes)
310
330
  end
311
- ldifs.join("\n")
331
+ return "" if ldif.records.empty?
332
+ ldif.to_s
312
333
  end
313
334
 
314
- def to_ldif(dn, attributes, options={})
315
- options[:connection] ||= connection
316
- options[:connection].to_ldif(dn, unnormalize_attributes(attributes))
335
+ def to_ldif(dn, attributes)
336
+ record = Ldif::Record.new(dn, attributes)
337
+ Ldif.new([record]).to_s
317
338
  end
318
339
 
319
- def load(ldifs, options={})
320
- options[:connection] ||= connection
321
- options[:connection].load(ldifs)
340
+ def load(ldif, options={})
341
+ return if ldif.blank?
342
+ Ldif.parse(ldif).each do |record|
343
+ record.load(self, options)
344
+ end
345
+ end
346
+
347
+ module ContentRecordLoadable
348
+ def load(operator, options)
349
+ operator.add_entry(dn, attributes, options)
350
+ end
351
+ end
352
+ Ldif::ContentRecord.send(:include, ContentRecordLoadable)
353
+
354
+ module AddRecordLoadable
355
+ def load(operator, options)
356
+ entries = attributes.collect do |key, value|
357
+ [:add, key, value]
358
+ end
359
+ options = {:controls => controls}.merge(options)
360
+ operator.modify_entry(dn, entries, options)
361
+ end
362
+ end
363
+ Ldif::AddRecord.send(:include, AddRecordLoadable)
364
+
365
+ module DeleteRecordLoadable
366
+ def load(operator, options)
367
+ operator.delete_entry(dn, {:controls => controls}.merge(options))
368
+ end
322
369
  end
370
+ Ldif::DeleteRecord.send(:include, DeleteRecordLoadable)
371
+
372
+ module ModifyNameRecordLoadable
373
+ def load(operator, options)
374
+ operator.modify_rdn_entry(dn, new_rdn, delete_old_rdn?, new_superior,
375
+ {:controls => controls}.merge(options))
376
+ end
377
+ end
378
+ Ldif::ModifyNameRecord.send(:include, ModifyNameRecordLoadable)
379
+
380
+ module ModifyRecordLoadable
381
+ def load(operator, options)
382
+ each do |operation|
383
+ operator.modify_entry(dn, operation.to_modify_entries,
384
+ {:controls => controls}.merge(options))
385
+ end
386
+ end
387
+
388
+ module AddOperationModifiable
389
+ def to_modify_entries
390
+ attributes.collect do |key, value|
391
+ [:add, key, value]
392
+ end
393
+ end
394
+ end
395
+ Ldif::ModifyRecord::AddOperation.send(:include, AddOperationModifiable)
396
+
397
+ module DeleteOperationModifiable
398
+ def to_modify_entries
399
+ return [[:delete, full_attribute_name, []]] if attributes.empty?
400
+ attributes.collect do |key, value|
401
+ [:delete, key, value]
402
+ end
403
+ end
404
+ end
405
+ Ldif::ModifyRecord::DeleteOperation.send(:include,
406
+ DeleteOperationModifiable)
407
+
408
+ module ReplaceOperationModifiable
409
+ def to_modify_entries
410
+ return [[:replace, full_attribute_name, []]] if attributes.empty?
411
+ attributes.collect do |key, value|
412
+ [:replace, key, value]
413
+ end
414
+ end
415
+ end
416
+ Ldif::ModifyRecord::ReplaceOperation.send(:include,
417
+ ReplaceOperationModifiable)
418
+ end
419
+ Ldif::ModifyRecord.send(:include, ModifyRecordLoadable)
323
420
  end
324
421
 
325
422
  module Delete
@@ -349,8 +446,12 @@ module ActiveLdap
349
446
  targets = targets.collect do |target|
350
447
  ensure_dn_attribute(ensure_base(target))
351
448
  end
449
+ delete_entry(targets, options)
450
+ end
451
+
452
+ def delete_entry(dn, options={})
352
453
  options[:connection] ||= connection
353
- options[:connection].delete(targets, options)
454
+ options[:connection].delete(dn, options)
354
455
  end
355
456
 
356
457
  def delete_all(filter=nil, options={})
@@ -370,8 +471,8 @@ module ActiveLdap
370
471
 
371
472
  module Update
372
473
  def add_entry(dn, attributes, options={})
373
- unnormalized_attributes = attributes.collect do |type, key, value|
374
- [type, key, unnormalize_attribute(key, value)]
474
+ unnormalized_attributes = attributes.collect do |key, value|
475
+ [:add, key, unnormalize_attribute(key, value)]
375
476
  end
376
477
  options[:connection] ||= connection
377
478
  options[:connection].add(dn, unnormalized_attributes, options)
@@ -385,6 +486,12 @@ module ActiveLdap
385
486
  options[:connection].modify(dn, unnormalized_attributes, options)
386
487
  end
387
488
 
489
+ def modify_rdn_entry(dn, new_rdn, delete_old_rdn, new_superior, options={})
490
+ options[:connection] ||= connection
491
+ options[:connection].modify_rdn(dn, new_rdn, delete_old_rdn,
492
+ new_superior, options)
493
+ end
494
+
388
495
  def update(dn, attributes, options={})
389
496
  if dn.is_a?(Array)
390
497
  i = -1
@@ -349,6 +349,9 @@ module ActiveLdap
349
349
  end
350
350
 
351
351
  class Attribute < Entry
352
+ include GetTextSupport
353
+ include HumanReadable
354
+
352
355
  attr_reader :super_attribute
353
356
  def initialize(name, schema)
354
357
  super(name, schema, "attributeTypes")
@@ -387,7 +390,7 @@ module ActiveLdap
387
390
  end
388
391
 
389
392
  def syntax
390
- (@derived_syntax ||= [derived_syntax])[0]
393
+ @derived_syntax
391
394
  end
392
395
 
393
396
  def valid?(value)
@@ -395,7 +398,14 @@ module ActiveLdap
395
398
  end
396
399
 
397
400
  def validate(value)
398
- send_to_syntax(nil, :validate, value)
401
+ error_info = validate_each_value(value)
402
+ return error_info if error_info
403
+ begin
404
+ normalize_value(value)
405
+ nil
406
+ rescue AttributeValueInvalid
407
+ [$!.message]
408
+ end
399
409
  end
400
410
 
401
411
  def type_cast(value)
@@ -403,13 +413,21 @@ module ActiveLdap
403
413
  end
404
414
 
405
415
  def normalize_value(value)
406
- send_to_syntax(value, :normalize_value, value)
416
+ normalize_value_internal(value, false)
407
417
  end
408
418
 
409
419
  def syntax_description
410
420
  send_to_syntax(nil, :description)
411
421
  end
412
422
 
423
+ def human_attribute_name
424
+ self.class.human_attribute_name(self)
425
+ end
426
+
427
+ def human_attribute_description
428
+ self.class.human_attribute_description(self)
429
+ end
430
+
413
431
  private
414
432
  def attribute(attribute_name, name=@name)
415
433
  @schema.attribute_type(name, attribute_name)
@@ -429,18 +447,15 @@ module ActiveLdap
429
447
  if @syntax
430
448
  @binary_required = @syntax.binary_transfer_required?
431
449
  @binary = (@binary_required or !@syntax.human_readable?)
450
+ @derived_syntax = @syntax
432
451
  else
433
452
  @binary_required = false
434
453
  @binary = false
454
+ @derived_syntax = nil
455
+ @derived_syntax = @super_attribute.syntax if @super_attribute
435
456
  end
436
457
  end
437
458
 
438
- def derived_syntax
439
- return @syntax if @syntax
440
- return @super_attribute.syntax if @super_attribute
441
- nil
442
- end
443
-
444
459
  def send_to_syntax(default_value, method_name, *args)
445
460
  _syntax = syntax
446
461
  if _syntax
@@ -449,6 +464,100 @@ module ActiveLdap
449
464
  default_value
450
465
  end
451
466
  end
467
+
468
+ def validate_each_value(value, option=nil)
469
+ failed_reason = nil
470
+ case value
471
+ when Hash
472
+ original_option = option
473
+ value.each do |sub_option, val|
474
+ opt = [original_option, sub_option].compact.join(";")
475
+ failed_reason, option = validate_each_value(val, opt)
476
+ break if failed_reason
477
+ end
478
+ when Array
479
+ original_option = option
480
+ value.each do |val|
481
+ failed_reason, option = validate_each_value(val, original_option)
482
+ break if failed_reason
483
+ end
484
+ else
485
+ failed_reason = send_to_syntax(nil, :validate, value)
486
+ end
487
+ return nil if failed_reason.nil?
488
+ [failed_reason, option]
489
+ end
490
+
491
+ def normalize_value_internal(value, have_binary_mark)
492
+ case value
493
+ when Array
494
+ normalize_array_value(value, have_binary_mark)
495
+ when Hash
496
+ normalize_hash_value(value, have_binary_mark)
497
+ else
498
+ if value.blank?
499
+ value = []
500
+ else
501
+ value = send_to_syntax(value, :normalize_value, value)
502
+ end
503
+ if !have_binary_mark and binary_required?
504
+ [{'binary' => value}]
505
+ else
506
+ value.is_a?(Array) ? value : [value]
507
+ end
508
+ end
509
+ end
510
+
511
+ def normalize_array_value(value, have_binary_mark)
512
+ if single_value? and value.reject {|v| v.is_a?(Hash)}.size > 1
513
+ format = _("Attribute %s can only have a single value: %s")
514
+ message = format % [human_attribute_name, value.inspect]
515
+ raise AttributeValueInvalid.new(self, value, message)
516
+ end
517
+ if value.empty?
518
+ if !have_binary_mark and binary_required?
519
+ [{'binary' => value}]
520
+ else
521
+ value
522
+ end
523
+ else
524
+ value.collect do |entry|
525
+ normalize_value_internal(entry, have_binary_mark)[0]
526
+ end
527
+ end
528
+ end
529
+
530
+ def normalize_hash_value(value, have_binary_mark)
531
+ if value.size > 1
532
+ format = _("Attribute %s: Hash must have one key-value pair only: %s")
533
+ message = format % [human_attribute_name, value.inspect]
534
+ raise AttributeValueInvalid.new(self, value, message)
535
+ end
536
+
537
+ if !have_binary_mark and binary_required? and !have_binary_key?(value)
538
+ [append_binary_key(value)]
539
+ else
540
+ key = value.keys[0]
541
+ have_binary_mark ||= key == "binary"
542
+ [{key => normalize_value_internal(value.values[0], have_binary_mark)}]
543
+ end
544
+ end
545
+
546
+ def have_binary_key?(hash)
547
+ key, value = hash.to_a[0]
548
+ return true if key == "binary"
549
+ return have_binary_key?(value) if value.is_a?(Hash)
550
+ false
551
+ end
552
+
553
+ def append_binary_key(hash)
554
+ key, value = hash.to_a[0]
555
+ if value.is_a?(Hash)
556
+ append_binary_key(value)
557
+ else
558
+ hash.merge(key => {"binary" => value})
559
+ end
560
+ end
452
561
  end
453
562
 
454
563
  class ObjectClass < Entry
@@ -165,12 +165,32 @@ module ActiveLdap
165
165
 
166
166
  class GeneralizedTime < Base
167
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
168
178
 
169
179
  def type_cast(value)
170
180
  return value if value.nil? or value.is_a?(Time)
171
- begin
172
- Time.parse(value)
173
- rescue ArgumentError
181
+ match_data = FORMAT.match(value)
182
+ if match_data
183
+ required_components = match_data.to_a[1, 6]
184
+ return value if required_components.any?(&:nil?)
185
+ year, month, day, hour, minute, second =
186
+ required_components.collect(&:to_i)
187
+ fraction = match_data[-2]
188
+ fraction = fraction.to_f if fraction
189
+ time_zone = match_data[-1]
190
+ Time.send(:make_time,
191
+ year, month, day, hour, minute, second, fraction,
192
+ time_zone, Time.now)
193
+ else
174
194
  value
175
195
  end
176
196
  end
@@ -190,20 +210,12 @@ module ActiveLdap
190
210
 
191
211
  private
192
212
  def validate_normalized_value(value, original_value)
193
- match_data = /\A
194
- (\d{4,4})?
195
- (\d{2,2})?
196
- (\d{2,2})?
197
- (\d{2,2})?
198
- (\d{2,2})?
199
- (\d{2,2}(?:[,.]\d+)?)?
200
- ([+-]\d{4,4}|Z)?
201
- \z/x.match(value)
213
+ match_data = FORMAT.match(value)
202
214
  if match_data
203
- year, month, day, hour, minute, second, time_zone =
215
+ year, month, day, hour, minute, second, fraction, time_zone =
204
216
  match_data.to_a[1..-1]
205
217
  missing_components = []
206
- %w(year month day hour minute).each do |component|
218
+ %w(year month day hour minute second).each do |component|
207
219
  missing_components << component unless eval(component)
208
220
  end
209
221
  if missing_components.empty?
@@ -231,7 +243,7 @@ module ActiveLdap
231
243
  end
232
244
 
233
245
  def normalize_value(value)
234
- if value.is_a?(Integer)
246
+ if value.is_a?(::Integer)
235
247
  value.to_s
236
248
  else
237
249
  value
@@ -309,7 +321,12 @@ module ActiveLdap
309
321
  DN.parse("#{value}=dummy")
310
322
  nil
311
323
  rescue DistinguishedNameInvalid
312
- _("%s is invalid OID format") % original_value.inspect
324
+ reason = $!.reason
325
+ if reason
326
+ _("%s is invalid OID format: %s") % [original_value.inspect, reason]
327
+ else
328
+ _("%s is invalid OID format") % original_value.inspect
329
+ end
313
330
  end
314
331
  end
315
332