activeldap 1.0.1 → 1.0.2

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