activeldap 0.9.0 → 0.10.0

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