activeldap 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/CHANGES +34 -0
  2. data/README +13 -0
  3. data/Rakefile +2 -1
  4. data/TODO +6 -0
  5. data/benchmark/bench-al.rb +68 -17
  6. data/examples/al-admin/app/helpers/application_helper.rb +3 -5
  7. data/examples/al-admin/app/views/layouts/_footer.html.erb +2 -0
  8. data/examples/al-admin/config/boot.rb +7 -7
  9. data/examples/al-admin/config/environment.rb +27 -12
  10. data/examples/al-admin/config/environments/development.rb +0 -1
  11. data/examples/al-admin/config/environments/production.rb +6 -1
  12. data/examples/al-admin/config/environments/test.rb +1 -1
  13. data/examples/al-admin/config/initializers/gettext.rb +15 -1
  14. data/examples/al-admin/po/en/al-admin.po +1 -1
  15. data/examples/al-admin/po/ja/al-admin.po +1 -1
  16. data/examples/al-admin/po/nl/al-admin.po +1 -1
  17. data/examples/al-admin/public/dispatch.cgi +0 -0
  18. data/examples/al-admin/public/dispatch.fcgi +0 -0
  19. data/examples/al-admin/public/dispatch.rb +0 -0
  20. data/examples/al-admin/public/javascripts/controls.js +73 -73
  21. data/examples/al-admin/public/javascripts/dragdrop.js +166 -165
  22. data/examples/al-admin/public/javascripts/effects.js +174 -166
  23. data/examples/al-admin/public/javascripts/prototype.js +362 -267
  24. data/examples/al-admin/script/about +0 -0
  25. data/examples/al-admin/script/console +0 -0
  26. data/examples/al-admin/script/dbconsole +3 -0
  27. data/examples/al-admin/script/destroy +0 -0
  28. data/examples/al-admin/script/generate +0 -0
  29. data/examples/al-admin/script/performance/benchmarker +0 -0
  30. data/examples/al-admin/script/performance/profiler +0 -0
  31. data/examples/al-admin/script/performance/request +0 -0
  32. data/examples/al-admin/script/plugin +0 -0
  33. data/examples/al-admin/script/process/inspector +0 -0
  34. data/examples/al-admin/script/process/reaper +0 -0
  35. data/examples/al-admin/script/process/spawner +0 -0
  36. data/examples/al-admin/script/runner +0 -0
  37. data/examples/al-admin/script/server +0 -0
  38. data/examples/al-admin/test/run-test.sh +0 -0
  39. data/examples/groupadd +0 -0
  40. data/examples/groupdel +0 -0
  41. data/examples/groupls +0 -0
  42. data/examples/groupmod +0 -0
  43. data/examples/lpasswd +0 -0
  44. data/examples/ouadd +0 -0
  45. data/examples/useradd +0 -0
  46. data/examples/useradd-binary +0 -0
  47. data/examples/userdel +0 -0
  48. data/examples/userls +0 -0
  49. data/examples/usermod +0 -0
  50. data/examples/usermod-binary-add +0 -0
  51. data/examples/usermod-binary-add-time +0 -0
  52. data/examples/usermod-binary-del +0 -0
  53. data/examples/usermod-lang-add +0 -0
  54. data/lib/active_ldap.rb +10 -4
  55. data/lib/active_ldap/action_controller/ldap_benchmarking.rb +28 -9
  56. data/lib/active_ldap/adapter/base.rb +30 -17
  57. data/lib/active_ldap/adapter/jndi.rb +5 -1
  58. data/lib/active_ldap/adapter/ldap.rb +5 -1
  59. data/lib/active_ldap/association/has_many_utils.rb +7 -1
  60. data/lib/active_ldap/associations.rb +10 -5
  61. data/lib/active_ldap/attributes.rb +6 -1
  62. data/lib/active_ldap/base.rb +154 -52
  63. data/lib/active_ldap/configuration.rb +1 -1
  64. data/lib/active_ldap/connection.rb +7 -4
  65. data/lib/active_ldap/get_text.rb +11 -3
  66. data/lib/active_ldap/ldif.rb +16 -4
  67. data/lib/active_ldap/operations.rb +13 -5
  68. data/lib/active_ldap/schema.rb +6 -2
  69. data/lib/active_ldap/schema/syntaxes.rb +15 -3
  70. data/lib/active_ldap/user_password.rb +4 -4
  71. data/lib/active_ldap/validations.rb +32 -44
  72. data/lib/active_ldap/xml.rb +125 -0
  73. data/po/en/active-ldap.po +740 -85
  74. data/po/ja/active-ldap.po +748 -547
  75. data/rails/README +54 -0
  76. data/rails/init.rb +33 -0
  77. data/rails/plugin/active_ldap/generators/README +2 -0
  78. data/rails/plugin/active_ldap/generators/model_active_ldap/model_active_ldap_generator.rb +1 -1
  79. data/rails/plugin/active_ldap/init.rb +3 -0
  80. data/rails_generators/model_active_ldap/USAGE +17 -0
  81. data/rails_generators/model_active_ldap/model_active_ldap_generator.rb +69 -0
  82. data/rails_generators/model_active_ldap/templates/model_active_ldap.rb +3 -0
  83. data/rails_generators/model_active_ldap/templates/unit_test.rb +8 -0
  84. data/rails_generators/scaffold_active_ldap/scaffold_active_ldap_generator.rb +7 -0
  85. data/rails_generators/scaffold_active_ldap/templates/ldap.yml +18 -0
  86. data/rails_generators/scaffold_al/scaffold_al_generator.rb +20 -0
  87. data/test-unit/History.txt +50 -1
  88. data/test-unit/Manifest.txt +22 -12
  89. data/test-unit/README.txt +31 -12
  90. data/test-unit/Rakefile +14 -1
  91. data/test-unit/TODO +5 -0
  92. data/test-unit/bin/testrb +0 -0
  93. data/test-unit/lib/test/unit.rb +62 -0
  94. data/test-unit/lib/test/unit/assertions.rb +419 -75
  95. data/test-unit/lib/test/unit/autorunner.rb +70 -13
  96. data/test-unit/lib/test/unit/collector.rb +1 -1
  97. data/test-unit/lib/test/unit/collector/load.rb +1 -1
  98. data/test-unit/lib/test/unit/color-scheme.rb +86 -0
  99. data/test-unit/lib/test/unit/color.rb +40 -5
  100. data/test-unit/lib/test/unit/diff.rb +14 -0
  101. data/test-unit/lib/test/unit/fixture.rb +7 -16
  102. data/test-unit/lib/test/unit/notification.rb +9 -0
  103. data/test-unit/lib/test/unit/omission.rb +14 -0
  104. data/test-unit/lib/test/unit/pending.rb +16 -0
  105. data/test-unit/lib/test/unit/priority.rb +17 -2
  106. data/test-unit/lib/test/unit/runner/console.rb +8 -2
  107. data/test-unit/lib/test/unit/testcase.rb +188 -2
  108. data/test-unit/lib/test/unit/ui/console/testrunner.rb +51 -26
  109. data/test-unit/lib/test/unit/util/method-owner-finder.rb +28 -0
  110. data/test-unit/lib/test/unit/version.rb +1 -1
  111. data/test-unit/sample/test_user.rb +22 -0
  112. data/test-unit/test/collector/{test_descendant.rb → test-descendant.rb} +0 -0
  113. data/test-unit/test/collector/{test_load.rb → test-load.rb} +1 -1
  114. data/test-unit/test/run-test.rb +0 -0
  115. data/test-unit/test/{test_attribute.rb → test-attribute.rb} +0 -0
  116. data/test-unit/test/test-color-scheme.rb +56 -0
  117. data/test-unit/test/{test_color.rb → test-color.rb} +10 -0
  118. data/test-unit/test/{test_diff.rb → test-diff.rb} +0 -0
  119. data/test-unit/test/{test_emacs_runner.rb → test-emacs-runner.rb} +0 -0
  120. data/test-unit/test/test-fixture.rb +287 -0
  121. data/test-unit/test/{test_notification.rb → test-notification.rb} +4 -4
  122. data/test-unit/test/{test_omission.rb → test-omission.rb} +6 -6
  123. data/test-unit/test/{test_pending.rb → test-pending.rb} +12 -6
  124. data/test-unit/test/{test_priority.rb → test-priority.rb} +30 -0
  125. data/test-unit/test/test_assertions.rb +411 -69
  126. data/test-unit/test/test_testcase.rb +70 -3
  127. data/test-unit/test/{testunit_test_util.rb → testunit-test-util.rb} +4 -2
  128. data/test-unit/test/ui/test_testrunmediator.rb +1 -1
  129. data/test-unit/test/util/test-method-owner-finder.rb +38 -0
  130. data/test/run-test.rb +0 -0
  131. data/test/test_adapter.rb +3 -0
  132. data/test/test_associations.rb +50 -7
  133. data/test/test_base.rb +193 -11
  134. data/test/test_connection_per_dn.rb +1 -1
  135. data/test/test_ldif.rb +86 -0
  136. data/test/test_load.rb +7 -0
  137. data/test/test_schema.rb +31 -1
  138. data/test/test_syntax.rb +20 -0
  139. data/test/test_user_password.rb +22 -14
  140. data/test/test_validation.rb +70 -29
  141. metadata +99 -77
  142. data/data/locale/en/LC_MESSAGES/active-ldap.mo +0 -0
  143. data/data/locale/ja/LC_MESSAGES/active-ldap.mo +0 -0
  144. data/examples/al-admin/config/initializers/ralative_url_support.rb +0 -1
  145. data/examples/al-admin/lib/accept_http_rails_relative_url_root.rb +0 -9
  146. data/test-unit-ext/misc/rd2html.rb +0 -42
  147. data/test-unit/test/test_fixture.rb +0 -275
@@ -23,7 +23,7 @@ module ActiveLdap
23
23
 
24
24
  DEFAULT_CONFIG = {}
25
25
  DEFAULT_CONFIG[:host] = '127.0.0.1'
26
- DEFAULT_CONFIG[:port] = 389
26
+ DEFAULT_CONFIG[:port] = nil
27
27
  DEFAULT_CONFIG[:method] = :plain # :ssl, :tls, :plain allowed
28
28
 
29
29
  DEFAULT_CONFIG[:bind_dn] = nil
@@ -213,11 +213,14 @@ module ActiveLdap
213
213
  conn = @connection
214
214
  return conn if conn
215
215
 
216
- if @dn or
217
- (attribute_name_resolvable_without_connection? and
218
- get_attribute_before_type_cast(dn_attribute)[1])
219
- conn = self.class.active_connections[dn] || retrieve_connection
216
+ have_dn = !@dn.nil?
217
+ if !have_dn and attribute_name_resolvable_without_connection?
218
+ begin
219
+ have_dn = !get_attribute_before_type_cast(dn_attribute)[1].nil?
220
+ rescue DistinguishedNameInvalid
221
+ end
220
222
  end
223
+ conn = self.class.active_connections[dn] || retrieve_connection if have_dn
221
224
  conn || self.class.connection
222
225
  end
223
226
 
@@ -1,7 +1,15 @@
1
- begin
2
- require "gettext/active_record"
1
+ if Object.const_defined?(:GetText)
2
+ require 'active_record/version'
3
+ active_record_version = [ActiveRecord::VERSION::MAJOR,
4
+ ActiveRecord::VERSION::MINOR,
5
+ ActiveRecord::VERSION::TINY]
6
+ if (active_record_version <=> [2, 2, 0]) < 0
7
+ require "gettext/active_record"
8
+ end
3
9
  ActiveLdap.const_set("GetText", GetText)
4
- rescue LoadError
10
+ end
11
+
12
+ unless ActiveLdap.const_defined?(:GetText)
5
13
  require 'active_ldap/get_text_fallback'
6
14
  end
7
15
 
@@ -31,11 +31,19 @@ module ActiveLdap
31
31
  SIZE = 75
32
32
 
33
33
  module_function
34
+ def binary_value?(value)
35
+ if /\A#{Parser::SAFE_STRING}\z/u =~ value
36
+ false
37
+ else
38
+ true
39
+ end
40
+ end
41
+
34
42
  def encode(name, value)
35
43
  return "#{name}:\n" if value.blank?
36
44
  result = "#{name}:"
37
45
 
38
- if value[-1, 1] == ' ' or /\A#{Parser::SAFE_STRING}\z/u !~ value
46
+ if value[-1, 1] == ' ' or binary_value?(value)
39
47
  result << ":"
40
48
  value = [value].pack("m").gsub(/\n/u, '')
41
49
  end
@@ -364,6 +372,9 @@ module ActiveLdap
364
372
  records << parse_record
365
373
  break if @scanner.eos?
366
374
  raise separator_is_missing if @scanner.scan_separator.nil?
375
+
376
+ break if @scanner.eos?
377
+ break if @scanner.scan_separators and @scanner.eos?
367
378
  end
368
379
  records
369
380
  end
@@ -475,6 +486,7 @@ module ActiveLdap
475
486
 
476
487
  class Scanner
477
488
  SEPARATOR = /(?:\r\n|\n)/u
489
+ SEPARATORS = /(?:(?:^#.*)?#{SEPARATOR})+/u
478
490
 
479
491
  def initialize(source)
480
492
  @source = source
@@ -506,13 +518,13 @@ module ActiveLdap
506
518
  end
507
519
 
508
520
  def scan_separators
509
- return @scanner.scan(/#{SEPARATOR}+/u) if @sub_scanner.eos?
521
+ return @scanner.scan(SEPARATORS) if @sub_scanner.eos?
510
522
 
511
- sub_result = scan(/#{SEPARATOR}+/u)
523
+ sub_result = scan(SEPARATORS)
512
524
  return nil if sub_result.nil?
513
525
  return sub_result unless @sub_scanner.eos?
514
526
 
515
- result = @scanner.scan(/#{SEPARATOR}+/u)
527
+ result = @scanner.scan(SEPARATORS)
516
528
  return sub_result if result.nil?
517
529
 
518
530
  sub_result + result
@@ -34,7 +34,11 @@ module ActiveLdap
34
34
 
35
35
  value = value.first if value.is_a?(Array) and value.first.size == 1
36
36
 
37
- _attr, value, _prefix = split_search_value(value)
37
+ _attr = nil
38
+ _prefix = nil
39
+ if attr.nil? or attr == dn_attribute
40
+ _attr, value, _prefix = split_search_value(value)
41
+ end
38
42
  attr ||= _attr || ensure_search_attribute
39
43
  prefix ||= _prefix
40
44
  filter ||= [attr, value]
@@ -117,7 +121,9 @@ module ActiveLdap
117
121
  end
118
122
 
119
123
  def ensure_base(target)
120
- [truncate_base(target), base].join(',')
124
+ [truncate_base(target), base].reject do |component|
125
+ component.blank?
126
+ end.join(',')
121
127
  end
122
128
 
123
129
  def truncate_base(target)
@@ -383,10 +389,12 @@ module ActiveLdap
383
389
 
384
390
  module ModifyRecordLoadable
385
391
  def load(operator, options)
386
- each do |operation|
387
- operator.modify_entry(dn, operation.to_modify_entries,
388
- {:controls => controls}.merge(options))
392
+ modify_entries = operations.inject([]) do |result, operation|
393
+ result + operation.to_modify_entries
389
394
  end
395
+ return if modify_entries.empty?
396
+ operator.modify_entry(dn, modify_entries,
397
+ {:controls => controls}.merge(options))
390
398
  end
391
399
 
392
400
  module AddOperationModifiable
@@ -189,7 +189,7 @@ module ActiveLdap
189
189
 
190
190
  def parse_attributes(str, attributes)
191
191
  str.scan(/([A-Z\-_]+)\s+
192
- (?:\(\s*([\w\-]+(?:\s+\$\s+[\w\-]+)+)\s*\)|
192
+ (?:\(\s*(\w[\w\-;]*(?:\s+\$\s+\w[\w\-;]*)*)\s*\)|
193
193
  \(\s*([^\)]*)\s*\)|
194
194
  '([^\']*)'|
195
195
  ((?!#{RESERVED_NAMES_RE})[a-zA-Z][a-zA-Z\d\-;]*)|
@@ -611,8 +611,12 @@ module ActiveLdap
611
611
  end
612
612
  end
613
613
 
614
+ UNWRITABLE_MUST_ATTRIBUTES = ["nTSecurityDescriptor"]
614
615
  def collect_attributes
615
- must = attribute('MUST').collect {|name| @schema.attribute(name)}
616
+ must = attribute('MUST').reject do |name|
617
+ UNWRITABLE_MUST_ATTRIBUTES.include?(name)
618
+ end
619
+ must = must.collect {|name| @schema.attribute(name)}
616
620
  may = attribute('MAY').collect {|name| @schema.attribute(name)}
617
621
 
618
622
  all_must = must.dup
@@ -187,9 +187,21 @@ module ActiveLdap
187
187
  fraction = match_data[-2]
188
188
  fraction = fraction.to_f if fraction
189
189
  time_zone = match_data[-1]
190
- Time.send(:make_time,
191
- year, month, day, hour, minute, second, fraction,
192
- time_zone, Time.now)
190
+ begin
191
+ Time.send(:make_time,
192
+ year, month, day, hour, minute, second, fraction,
193
+ time_zone, Time.now)
194
+ rescue ArgumentError
195
+ raise if year >= 1700
196
+ out_of_range_messages = ["argument out of range",
197
+ "time out of range"]
198
+ raise unless out_of_range_messages.include?($!.message)
199
+ Time.at(0)
200
+ rescue RangeError
201
+ raise if year >= 1700
202
+ raise if $!.message != "bignum too big to convert into `long'"
203
+ Time.at(0)
204
+ end
193
205
  else
194
206
  value
195
207
  end
@@ -43,7 +43,7 @@ module ActiveLdap
43
43
  end
44
44
 
45
45
  def md5(password)
46
- "{MD5}#{Base64.encode64(MD5.md5(password).digest).chomp}"
46
+ "{MD5}#{[MD5.md5(password).digest].pack('m').chomp}"
47
47
  end
48
48
 
49
49
  def smd5(password, salt=nil)
@@ -52,7 +52,7 @@ module ActiveLdap
52
52
  end
53
53
  salt ||= Salt.generate(4)
54
54
  md5_hash_with_salt = "#{MD5.md5(password + salt).digest}#{salt}"
55
- "{SMD5}#{Base64.encode64(md5_hash_with_salt).chomp}"
55
+ "{SMD5}#{[md5_hash_with_salt].pack('m').chomp}"
56
56
  end
57
57
 
58
58
  def extract_salt_for_smd5(smd5ed_password)
@@ -60,7 +60,7 @@ module ActiveLdap
60
60
  end
61
61
 
62
62
  def sha(password)
63
- "{SHA}#{Base64.encode64(SHA1.sha1(password).digest).chomp}"
63
+ "{SHA}#{[SHA1.sha1(password).digest].pack('m').chomp}"
64
64
  end
65
65
 
66
66
  def ssha(password, salt=nil)
@@ -69,7 +69,7 @@ module ActiveLdap
69
69
  end
70
70
  salt ||= Salt.generate(4)
71
71
  sha1_hash_with_salt = "#{SHA1.sha1(password + salt).digest}#{salt}"
72
- "{SSHA}#{Base64.encode64(sha1_hash_with_salt).chomp}"
72
+ "{SSHA}#{[sha1_hash_with_salt].pack('m').chomp}"
73
73
  end
74
74
 
75
75
  def extract_salt_for_ssha(sshaed_password)
@@ -34,6 +34,7 @@ module ActiveLdap
34
34
  end
35
35
 
36
36
  validate_on_create :validate_duplicated_dn_creation
37
+ validate :validate_dn
37
38
  validate :validate_excluded_classes
38
39
  validate :validate_required_ldap_values
39
40
  validate :validate_ldap_values
@@ -76,14 +77,24 @@ module ActiveLdap
76
77
 
77
78
  private
78
79
  def validate_duplicated_dn_creation
79
- if id and exist?
80
- if ActiveLdap.get_text_supported?
81
- format = _("%{fn} is duplicated: %s")
82
- else
83
- format = _("is duplicated: %s")
84
- end
85
- errors.add("dn", format % dn)
80
+ _dn = nil
81
+ begin
82
+ _dn = dn
83
+ rescue DistinguishedNameInvalid, DistinguishedNameNotSetError
84
+ return
86
85
  end
86
+ if _dn and exist?
87
+ format = _("is duplicated: %s")
88
+ errors.add("dn", format % _dn)
89
+ end
90
+ end
91
+
92
+ def validate_dn
93
+ dn
94
+ rescue DistinguishedNameInvalid
95
+ errors.add("dn", _("is invalid: %s") % $!.message)
96
+ rescue DistinguishedNameNotSetError
97
+ errors.add("dn", _("isn't set: %s") % $!.message)
87
98
  end
88
99
 
89
100
  def validate_excluded_classes
@@ -101,17 +112,9 @@ module ActiveLdap
101
112
  names = unexpected_classes.collect do |object_class|
102
113
  self.class.human_object_class_name(object_class)
103
114
  end
104
- if ActiveLdap.get_text_supported?
105
- format = n_("%{fn} has excluded value: %s",
106
- "%{fn} has excluded values: %s",
107
- names.size)
108
- else
109
- if names.size == 1
110
- format = "has excluded value: %s"
111
- else
112
- format = "has excluded values: %s"
113
- end
114
- end
115
+ format = n_("has excluded value: %s",
116
+ "has excluded values: %s",
117
+ names.size)
115
118
  errors.add("objectClass", format % names.join(", "))
116
119
  end
117
120
 
@@ -129,6 +132,8 @@ module ActiveLdap
129
132
  real_name = to_real_attribute_name(required_attribute.name, true)
130
133
  raise UnknownAttribute.new(required_attribute) if real_name.nil?
131
134
 
135
+ next if required_attribute.read_only?
136
+
132
137
  value = @data[real_name] || []
133
138
  next unless self.class.blank_value?(value)
134
139
 
@@ -137,20 +142,12 @@ module ActiveLdap
137
142
  self.class.human_attribute_name(name)
138
143
  end
139
144
  args = [self.class.human_object_class_name(object_class)]
140
- if ActiveLdap.get_text_supported?
141
- if aliases.empty?
142
- format = _("%{fn} is required attribute by objectClass '%s'")
143
- else
144
- format = _("%{fn} is required attribute by objectClass " \
145
- "'%s': aliases: %s")
146
- args << aliases.join(', ')
147
- end
145
+ if aliases.empty?
146
+ format = _("is required attribute by objectClass '%s'")
148
147
  else
149
- format = "is required attribute by objectClass '%s'"
150
- unless aliases.empty?
151
- format << ": aliases: %s"
152
- args << aliases.join(', ')
153
- end
148
+ format = _("is required attribute by objectClass " \
149
+ "'%s': aliases: %s")
150
+ args << aliases.join(', ')
154
151
  end
155
152
  errors.add(real_name, format % args)
156
153
  end
@@ -160,7 +157,7 @@ module ActiveLdap
160
157
  def validate_ldap_values
161
158
  entry_attribute.schemata.each do |name, attribute|
162
159
  value = self[name]
163
- next if value.nil?
160
+ next if value.blank?
164
161
  validate_ldap_value(attribute, name, value)
165
162
  end
166
163
  end
@@ -171,19 +168,10 @@ module ActiveLdap
171
168
  params = [self.class.human_readable_format(value),
172
169
  self.class.human_syntax_description(attribute.syntax),
173
170
  failed_reason]
174
- if ActiveLdap.get_text_supported?
175
- if option
176
- format =
177
- _("%{fn} (%s) has invalid format: %s: required syntax: %s: %s")
178
- else
179
- format = _("%{fn} has invalid format: %s: required syntax: %s: %s")
180
- end
171
+ if option
172
+ format = _("(%s) has invalid format: %s: required syntax: %s: %s")
181
173
  else
182
- if option
183
- format = _("(%s) has invalid format: %s: required syntax: %s: %s")
184
- else
185
- format = _("has invalid format: %s: required syntax: %s: %s")
186
- end
174
+ format = _("has invalid format: %s: required syntax: %s: %s")
187
175
  end
188
176
  params.unshift(option) if option
189
177
  errors.add(name, format % params)
@@ -0,0 +1,125 @@
1
+ require 'erb'
2
+
3
+ require 'active_ldap/ldif'
4
+
5
+ module ActiveLdap
6
+ class Xml
7
+ class Serializer
8
+ PRINTABLE_STRING = /[\x20-\x7e\w\s]*/um
9
+
10
+ def initialize(dn, attributes, schema, options={})
11
+ @dn = dn
12
+ @attributes = attributes
13
+ @schema = schema
14
+ @options = options
15
+ end
16
+
17
+ def to_s
18
+ root = @options[:root]
19
+ result = "<#{root}>\n"
20
+ target_attributes.each do |key, values|
21
+ values = normalize_values(values).sort_by {|value, _| value}
22
+ if @schema.attribute(key).single_value?
23
+ result << " #{serialize_attribute_value(key, *values[0])}\n"
24
+ else
25
+ result << serialize_attribute_values(key, values)
26
+ end
27
+ end
28
+ result << "</#{root}>\n"
29
+ result
30
+ end
31
+
32
+ private
33
+ def target_attributes
34
+ except_dn = false
35
+ attributes = @attributes.dup
36
+ (@options[:except] || []).each do |name|
37
+ if name == "dn"
38
+ except_dn = true
39
+ else
40
+ attributes.delete(name)
41
+ end
42
+ end
43
+ attributes = attributes.sort_by {|key, values| key}
44
+ attributes.unshift(["dn", [@dn]]) unless except_dn
45
+ attributes
46
+ end
47
+
48
+ def normalize_values(values)
49
+ targets = []
50
+ values.each do |value|
51
+ targets.concat(normalize_value(value))
52
+ end
53
+ targets
54
+ end
55
+
56
+ def normalize_value(value, options=[])
57
+ targets = []
58
+ if value.is_a?(Hash)
59
+ value.each do |real_option, real_value|
60
+ targets.concat(normalize_value(real_value, options + [real_option]))
61
+ end
62
+ elsif value.is_a?(Array)
63
+ value.each do |real_value|
64
+ targets.concat(normalize_value(real_value, options))
65
+ end
66
+ else
67
+ if /\A#{PRINTABLE_STRING}\z/u !~ value
68
+ value = [value].pack("m").gsub(/\n/u, '')
69
+ options += ["base64"]
70
+ end
71
+ xml_attributes = {}
72
+ options.each do |name, val|
73
+ xml_attributes[name] = val || "true"
74
+ end
75
+ targets << [value, xml_attributes]
76
+ end
77
+ targets
78
+ end
79
+
80
+ def serialize_attribute_values(name, values)
81
+ return "" if values.blank?
82
+
83
+ result = ""
84
+ if name == "dn" or @options[:type].to_s.downcase == "ldif"
85
+ values.collect do |value, xml_attributes|
86
+ xml = serialize_attribute_value(name, value, xml_attributes)
87
+ result << " #{xml}\n"
88
+ end
89
+ else
90
+ plural_name = name.pluralize
91
+ result << " <#{plural_name} type=\"array\">\n"
92
+ values.each do |value, xml_attributes|
93
+ xml = serialize_attribute_value(name, value, xml_attributes)
94
+ result << " #{xml}\n"
95
+ end
96
+ result << " </#{plural_name}>\n"
97
+ end
98
+ result
99
+ end
100
+
101
+ def serialize_attribute_value(name, value, xml_attributes)
102
+ if xml_attributes.blank?
103
+ xml_attributes = ""
104
+ else
105
+ xml_attributes = " " + xml_attributes.collect do |n, v|
106
+ "#{ERB::Util.h(n)}=\"#{ERB::Util.h(v)}\""
107
+ end.join(" ")
108
+ end
109
+ "<#{name}#{xml_attributes}>#{ERB::Util.h(value)}</#{name}>"
110
+ end
111
+ end
112
+
113
+ def initialize(dn, attributes, schema)
114
+ @dn = dn
115
+ @attributes = attributes
116
+ @schema = schema
117
+ end
118
+
119
+ def to_s(options={})
120
+ Serializer.new(@dn, @attributes, @schema, options).to_s
121
+ end
122
+ end
123
+
124
+ XML = Xml
125
+ end