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
@@ -22,7 +22,8 @@ class TestConnection < Test::Unit::TestCase
22
22
  exception = nil
23
23
  assert_raises(ArgumentError) do
24
24
  begin
25
- connector.establish_connection(:bind_format => "uid=%s,dc=test",
25
+ connector.establish_connection(:adapter => adapter,
26
+ :bind_format => "uid=%s,dc=test",
26
27
  :allow_anonymous => false)
27
28
  connector.connection.connect
28
29
  rescue Exception
@@ -3,6 +3,60 @@ require 'al-test-utils'
3
3
  class TestConnectionPerClass < Test::Unit::TestCase
4
4
  include AlTestUtils
5
5
 
6
+ priority :must
7
+ def test_multi_establish_connections_with_association
8
+ make_ou("Sub,ou=Users")
9
+ make_ou("Sub2,ou=Users")
10
+
11
+ sub_user_class = Class.new(@user_class)
12
+ sub_user_class.prefix = "ou=Sub"
13
+ sub2_user_class = Class.new(@user_class)
14
+ sub2_user_class.prefix = "ou=Sub2"
15
+ sub_user_class.has_many(:related_entries, :wrap => "seeAlso")
16
+ sub_user_class.set_associated_class(:related_entries, sub2_user_class)
17
+
18
+ make_temporary_user(:uid => "uid=user1,ou=Sub") do |user,|
19
+ make_temporary_user(:uid => "uid=user2,ou=Sub2") do |user2,|
20
+ sub_user = sub_user_class.find(user.uid)
21
+ sub2_user = sub2_user_class.find(user2.uid)
22
+ sub_user.see_also = sub2_user.dn
23
+ assert(sub_user.save)
24
+
25
+ sub_user = sub_user_class.find(user.uid)
26
+ assert_equal(["ou=Sub2"],
27
+ sub_user.related_entries.collect {|e| e.class.prefix})
28
+ end
29
+ end
30
+ end
31
+
32
+ priority :normal
33
+ def test_multi_establish_connections
34
+ make_ou("Sub")
35
+ make_ou("Sub2")
36
+ sub_class = ou_class("ou=Sub")
37
+ sub2_class = ou_class("ou=Sub2")
38
+
39
+ configuration = current_configuration.symbolize_keys
40
+ configuration[:scope] = :base
41
+ current_base = configuration[:base]
42
+ sub_configuration = configuration.dup
43
+ sub_base = "ou=Sub,#{current_base}"
44
+ sub_configuration[:base] = sub_base
45
+ sub2_configuration = configuration.dup
46
+ sub2_base = "ou=Sub2,#{current_base}"
47
+ sub2_configuration[:base] = sub2_base
48
+
49
+ sub_class.establish_connection(sub_configuration)
50
+ sub_class.prefix = nil
51
+ sub2_class.establish_connection(sub2_configuration)
52
+ sub2_class.prefix = nil
53
+
54
+ assert_equal([sub_base], sub_class.find(:all).collect(&:dn))
55
+ assert_equal([sub2_base], sub2_class.find(:all).collect(&:dn))
56
+ assert_equal([sub_base], sub_class.find(:all).collect(&:dn))
57
+ assert_equal([sub2_base], sub2_class.find(:all).collect(&:dn))
58
+ end
59
+
6
60
  def test_bind
7
61
  non_anon_class = ou_class("ou=NonAnonymous")
8
62
  anon_class = ou_class("ou=Anonymous")
@@ -52,7 +106,7 @@ class TestConnectionPerClass < Test::Unit::TestCase
52
106
 
53
107
  private
54
108
  def connect(klass, config)
55
- klass.establish_connection(config)
109
+ klass.establish_connection({:adapter => adapter}.merge(config))
56
110
  klass.connection.connect
57
111
  end
58
112
  end
@@ -4,9 +4,37 @@ class TestConnectionPerDN < Test::Unit::TestCase
4
4
  include AlTestUtils
5
5
 
6
6
  priority :must
7
+ def test_bind_with_empty_password
8
+ make_temporary_user do |user, password|
9
+ assert_equal(user.class.connection, user.connection)
10
+ assert_raises(ActiveLdap::AuthenticationError) do
11
+ user.bind("")
12
+ end
13
+ assert_equal(user.class.connection, user.connection)
14
+
15
+ assert_nothing_raised do
16
+ user.bind("", :allow_anonymous => true)
17
+ end
18
+ assert_not_equal(user.class.connection, user.connection)
19
+ end
20
+ end
7
21
 
8
22
  priority :normal
9
- def test_establish_connection
23
+ def test_rebind_with_invalid_password
24
+ make_temporary_user do |user, password|
25
+ assert_equal(user.class.connection, user.connection)
26
+ assert_nothing_raised do
27
+ user.bind(password)
28
+ end
29
+ assert_not_equal(user.class.connection, user.connection)
30
+
31
+ assert_raises(ActiveLdap::AuthenticationError) do
32
+ user.bind(password + "-WRONG")
33
+ end
34
+ end
35
+ end
36
+
37
+ def test_bind
10
38
  make_temporary_user do |user, password|
11
39
  assert_equal(user.class.connection, user.connection)
12
40
  assert_raises(ActiveLdap::AuthenticationError) do
@@ -4,8 +4,81 @@ class TestFind < Test::Unit::TestCase
4
4
  include AlTestUtils
5
5
 
6
6
  priority :must
7
+ def test_find_with_special_value_prefix
8
+ # \2C == ','
9
+ make_ou("a\\2Cb,ou=Users")
10
+ make_temporary_user(:uid => "user1,ou=a\\2Cb") do |user1,|
11
+ assert_equal([], @user_class.find(:all, :prefix => "ou=a,b").collect(&:dn))
12
+ params = {:prefix => "ou=a\\2Cb"}
13
+ assert_equal([user1.dn], @user_class.find(:all, params).collect(&:dn))
14
+ params = {:prefix => [["ou", "a,b"]]}
15
+ assert_equal([user1.dn], @user_class.find(:all, params).collect(&:dn))
16
+ end
17
+ end
18
+
19
+ def test_find_with_special_value_base
20
+ # \5C == '\'
21
+ make_ou("a\\5Cb,ou=Users")
22
+ make_temporary_user(:uid => "user1,ou=a\\5Cb") do |user1,|
23
+ base = @user_class.base
24
+ params = {:base => "ou=a\\b,#{base}"}
25
+ assert_equal([], @user_class.find(:all, params).collect(&:dn))
26
+ params = {:base => "ou=a\\5Cb,#{base}"}
27
+ assert_equal([user1.dn], @user_class.find(:all, params).collect(&:dn))
28
+ base_rdns = ActiveLdap::DN.parse(base).rdns
29
+ params = {:base => [["ou", "a\\b"]] + base_rdns}
30
+ assert_equal([user1.dn], @user_class.find(:all, params).collect(&:dn))
31
+ end
32
+ end
7
33
 
8
34
  priority :normal
35
+ def test_find_with_sort_by_in_ldap_mapping
36
+ @user_class.ldap_mapping(:dn_attribute => @user_class.dn_attribute,
37
+ :prefix => @user_class.prefix,
38
+ :scope => @user_class.scope,
39
+ :classes => @user_class_classes,
40
+ :sort_by => "uid",
41
+ :order => "desc")
42
+ make_temporary_user(:uid => "user1") do |user1,|
43
+ make_temporary_user(:uid => "user2") do |user2,|
44
+ make_temporary_user(:uid => "user3") do |user3,|
45
+ users = @user_class.find(:all)
46
+ assert_equal(["user3", "user2", "user1"],
47
+ users.collect {|u| u.uid})
48
+
49
+ users = @user_class.find(:all, :order => "asc")
50
+ assert_equal(["user1", "user2", "user3"],
51
+ users.collect {|u| u.uid})
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ def test_find_operational_attributes
58
+ make_temporary_user do |user, password|
59
+ found_user = @user_class.find(user.uid, :attributes => ["*", "+"])
60
+ assert_equal(Time.now.utc.iso8601,
61
+ found_user.modify_timestamp.utc.iso8601)
62
+ end
63
+ end
64
+
65
+ def test_find_with_attributes_without_object_class
66
+ make_temporary_user do |user, password|
67
+ found_user = @user_class.find(user.uid, :attributes => ["uidNumber"])
68
+ assert_equal(user.uid_number, found_user.uid_number)
69
+ assert_equal(user.classes, found_user.classes)
70
+ assert_nil(found_user.gid_number)
71
+ end
72
+ end
73
+
74
+ def test_find_with_integer_value
75
+ make_temporary_user do |user, password|
76
+ found_user = @user_class.find(:attribute => "gidNumber",
77
+ :value => user.gid_number)
78
+ assert_equal(user.dn, found_user.dn)
79
+ end
80
+ end
81
+
9
82
  def test_find_with_limit
10
83
  make_temporary_user(:uid => "user1") do |user1,|
11
84
  make_temporary_user(:uid => "user2") do |user2,|
@@ -1,35 +1,1849 @@
1
1
  require 'al-test-utils'
2
2
 
3
3
  class TestLDIF < Test::Unit::TestCase
4
- include AlTestUtils
4
+ include ActiveLdap::GetTextSupport
5
+ include AlTestUtils::Assertions
6
+ include AlTestUtils::Config
7
+ include AlTestUtils::ExampleFile
5
8
 
6
- def setup
9
+ priority :must
10
+
11
+ priority :normal
12
+ def test_unknown_change_type
13
+ ldif_source = <<-EOL
14
+ version: 1
15
+ dn: ou=Product Development, dc=airius, dc=com
16
+ changetype: XXX
17
+ EOL
18
+
19
+ ldif_source_with_error_mark = <<-EOL
20
+ changetype: |@|XXX
21
+ EOL
22
+
23
+ assert_invalid_ldif(["unknown change type: %s", "XXX"],
24
+ ldif_source, 3, 13, ldif_source_with_error_mark)
7
25
  end
8
26
 
9
- def teardown
27
+ def test_unknown_modify_type
28
+ ldif_source = <<-EOL
29
+ version: 1
30
+ dn: ou=Product Development, dc=airius, dc=com
31
+ changetype: modify
32
+ XXX: postaladdress
33
+ -
34
+ EOL
35
+
36
+ ldif_source_with_error_mark = <<-EOL
37
+ |@|XXX: postaladdress
38
+ EOL
39
+
40
+ assert_invalid_ldif(["unknown modify type: %s", "XXX"],
41
+ ldif_source, 4, 1, ldif_source_with_error_mark)
42
+ end
43
+
44
+ def test_modify_spec_block_separator_is_missing
45
+ ldif_source = <<-EOL
46
+ version: 1
47
+ dn: ou=Product Development, dc=airius, dc=com
48
+ changetype: modify
49
+ add: postaladdress
50
+ EOL
51
+
52
+ ldif_source_with_error_mark = <<-EOL.chomp
53
+ add: postaladdress
54
+ |@|
55
+ EOL
56
+
57
+ assert_invalid_ldif("'-' is missing",
58
+ ldif_source, 5, 1, ldif_source_with_error_mark)
59
+ end
60
+
61
+ def test_modify_spec_first_line_separator_is_missing
62
+ ldif_source = <<-EOL.chomp
63
+ version: 1
64
+ dn: ou=Product Development, dc=airius, dc=com
65
+ changetype: modify
66
+ add: postaladdress
67
+ EOL
68
+
69
+ ldif_source_with_error_mark = <<-EOL.chomp
70
+ add: postaladdress|@|
71
+ EOL
72
+
73
+ assert_invalid_ldif("separator is missing",
74
+ ldif_source, 4, 19, ldif_source_with_error_mark)
75
+ end
76
+
77
+ def test_modify_target_attribute_name_is_missing
78
+ ldif_source = <<-EOL
79
+ version: 1
80
+ dn: ou=Product Development, dc=airius, dc=com
81
+ changetype: modify
82
+ add:
83
+ -
84
+ EOL
85
+
86
+ ldif_source_with_error_mark = <<-EOL
87
+ add:|@|
88
+ EOL
89
+
90
+ assert_invalid_ldif("attribute type is missing",
91
+ ldif_source, 4, 5, ldif_source_with_error_mark)
92
+ end
93
+
94
+ def test_newsuperior_separator_is_missing
95
+ ldif_source = <<-EOL.chomp
96
+ version: 1
97
+ dn: ou=Product Development, dc=airius, dc=com
98
+ changetype: moddn
99
+ newrdn: cn=Paula Jensen
100
+ deleteoldrdn: 1
101
+ newsuperior: ou=Accounting, dc=airius, dc=com
102
+ EOL
103
+
104
+ ldif_source_with_error_mark = <<-EOL.chomp
105
+ newsuperior: ou=Accounting, dc=airius, dc=com|@|
106
+ EOL
107
+
108
+ assert_invalid_ldif("separator is missing",
109
+ ldif_source, 6, 46, ldif_source_with_error_mark)
110
+ end
111
+
112
+ def test_newsuperior_value_is_missing
113
+ ldif_source = <<-EOL
114
+ version: 1
115
+ dn: ou=Product Development, dc=airius, dc=com
116
+ changetype: moddn
117
+ newrdn: cn=Paula Jensen
118
+ deleteoldrdn: 1
119
+ newsuperior:
120
+ EOL
121
+
122
+ ldif_source_with_error_mark = <<-EOL
123
+ newsuperior:|@|
124
+ EOL
125
+
126
+ assert_invalid_ldif("new superior value is missing",
127
+ ldif_source, 6, 13, ldif_source_with_error_mark)
128
+ end
129
+
130
+ def test_deleteoldrdn_separator_is_missing
131
+ ldif_source = <<-EOL.chomp
132
+ version: 1
133
+ dn: ou=Product Development, dc=airius, dc=com
134
+ changetype: moddn
135
+ newrdn: cn=Paula Jensen
136
+ deleteoldrdn: 1
137
+ EOL
138
+
139
+ ldif_source_with_error_mark = <<-EOL.chomp
140
+ deleteoldrdn: 1|@|
141
+ EOL
142
+
143
+ assert_invalid_ldif("separator is missing",
144
+ ldif_source, 5, 16, ldif_source_with_error_mark)
145
+ end
146
+
147
+ def test_invalid_deleteoldrdn_value
148
+ ldif_source = <<-EOL
149
+ version: 1
150
+ dn: ou=Product Development, dc=airius, dc=com
151
+ changetype: moddn
152
+ newrdn: cn=Paula Jensen
153
+ deleteoldrdn: x
154
+ EOL
155
+
156
+ ldif_source_with_error_mark = <<-EOL
157
+ deleteoldrdn: |@|x
158
+ EOL
159
+
160
+ assert_invalid_ldif("delete old RDN value is missing",
161
+ ldif_source, 5, 15, ldif_source_with_error_mark)
162
+ end
163
+
164
+ def test_deleteoldrdn_value_is_missing
165
+ ldif_source = <<-EOL
166
+ version: 1
167
+ dn: ou=Product Development, dc=airius, dc=com
168
+ changetype: moddn
169
+ newrdn: cn=Paula Jensen
170
+ deleteoldrdn:
171
+ EOL
172
+
173
+ ldif_source_with_error_mark = <<-EOL
174
+ deleteoldrdn:|@|
175
+ EOL
176
+
177
+ assert_invalid_ldif("delete old RDN value is missing",
178
+ ldif_source, 5, 14, ldif_source_with_error_mark)
179
+ end
180
+
181
+ def test_deleteoldrdn_mark_is_missing
182
+ ldif_source = <<-EOL
183
+ version: 1
184
+ dn: ou=Product Development, dc=airius, dc=com
185
+ changetype: moddn
186
+ newrdn: cn=Paula Jensen
187
+ EOL
188
+
189
+ ldif_source_with_error_mark = <<-EOL.chomp
190
+ newrdn: cn=Paula Jensen
191
+ |@|
192
+ EOL
193
+
194
+ assert_invalid_ldif("'deleteoldrdn:' is missing",
195
+ ldif_source, 5, 1, ldif_source_with_error_mark)
196
+ end
197
+
198
+ def test_newrdn_separator_is_missing
199
+ ldif_source = <<-EOL.chomp
200
+ version: 1
201
+ dn: ou=Product Development, dc=airius, dc=com
202
+ changetype: moddn
203
+ newrdn: cn=Paula Jensen
204
+ EOL
205
+
206
+ ldif_source_with_error_mark = <<-EOL.chomp
207
+ newrdn: cn=Paula Jensen|@|
208
+ EOL
209
+
210
+ assert_invalid_ldif("separator is missing",
211
+ ldif_source, 4, 24, ldif_source_with_error_mark)
212
+ end
213
+
214
+ def test_newrdn_value_is_missing
215
+ ldif_source = <<-EOL
216
+ version: 1
217
+ dn: ou=Product Development, dc=airius, dc=com
218
+ changetype: moddn
219
+ newrdn:
220
+ EOL
221
+
222
+ ldif_source_with_error_mark = <<-EOL
223
+ newrdn:|@|
224
+ EOL
225
+
226
+ assert_invalid_ldif("new RDN value is missing",
227
+ ldif_source, 4, 8, ldif_source_with_error_mark)
228
+ end
229
+
230
+ def test_newrdn_mark_is_missing
231
+ ldif_source = <<-EOL
232
+ version: 1
233
+ dn: ou=Product Development, dc=airius, dc=com
234
+ changetype: moddn
235
+ EOL
236
+
237
+ ldif_source_with_error_mark = <<-EOL.chomp
238
+ changetype: moddn
239
+ |@|
240
+ EOL
241
+
242
+ assert_invalid_ldif("'newrdn:' is missing",
243
+ ldif_source, 4, 1, ldif_source_with_error_mark)
244
+ end
245
+
246
+ def test_add_change_type_without_attribute
247
+ ldif_source = <<-EOL
248
+ version: 1
249
+ dn: ou=Product Development, dc=airius, dc=com
250
+ changetype: add
251
+ EOL
252
+
253
+ ldif_source_with_error_mark = <<-EOL.chomp
254
+ changetype: add
255
+ |@|
256
+ EOL
257
+
258
+ assert_invalid_ldif("attribute spec is missing",
259
+ ldif_source, 4, 1, ldif_source_with_error_mark)
260
+ end
261
+
262
+ def test_change_type_with_an_extra_space
263
+ ldif_source = <<-EOL
264
+ version: 1
265
+ dn: ou=Product Development, dc=airius, dc=com
266
+ changetype: delete
267
+ EOL
268
+
269
+ ldif_source_with_error_mark = <<-EOL
270
+ changetype: delete|@|
271
+ EOL
272
+
273
+ assert_invalid_ldif("separator is missing",
274
+ ldif_source, 3, 19, ldif_source_with_error_mark)
275
+ end
276
+
277
+ def test_change_type_separator_is_missing
278
+ ldif_source = <<-EOL.chomp
279
+ version: 1
280
+ dn: ou=Product Development, dc=airius, dc=com
281
+ changetype: delete
282
+ EOL
283
+
284
+ ldif_source_with_error_mark = <<-EOL.chomp
285
+ changetype: delete|@|
286
+ EOL
287
+
288
+ assert_invalid_ldif("separator is missing",
289
+ ldif_source, 3, 19, ldif_source_with_error_mark)
290
+ end
291
+
292
+ def test_change_type_value_is_missing
293
+ ldif_source = <<-EOL
294
+ version: 1
295
+ dn: ou=Product Development, dc=airius, dc=com
296
+ changetype:
297
+ EOL
298
+
299
+ ldif_source_with_error_mark = <<-EOL
300
+ changetype:|@|
301
+ EOL
302
+
303
+ assert_invalid_ldif("change type value is missing",
304
+ ldif_source, 3, 12, ldif_source_with_error_mark)
305
+ end
306
+
307
+ def test_control_separator_is_missing
308
+ ldif_source = <<-EOL.chomp
309
+ version: 1
310
+ dn: ou=Product Development, dc=airius, dc=com
311
+ control: 1.2.840.113556.1.4.805 true
312
+ EOL
313
+
314
+ ldif_source_with_error_mark = <<-EOL.chomp
315
+ control: 1.2.840.113556.1.4.805 true|@|
316
+ EOL
317
+
318
+ assert_invalid_ldif("separator is missing",
319
+ ldif_source, 3, 37, ldif_source_with_error_mark)
320
+ end
321
+
322
+ def test_criticality_is_missing
323
+ ldif_source = <<-EOL
324
+ version: 1
325
+ dn: ou=Product Development, dc=airius, dc=com
326
+ control: 1.2.840.113556.1.4.805
327
+ EOL
328
+
329
+ ldif_source_with_error_mark = <<-EOL
330
+ control: 1.2.840.113556.1.4.805 |@|
331
+ EOL
332
+
333
+ assert_invalid_ldif("criticality is missing",
334
+ ldif_source, 3, 33, ldif_source_with_error_mark)
335
+ end
336
+
337
+ def test_control_type_is_missing
338
+ ldif_source = <<-EOL
339
+ version: 1
340
+ dn: ou=Product Development, dc=airius, dc=com
341
+ control:
342
+ EOL
343
+
344
+ ldif_source_with_error_mark = <<-EOL
345
+ control:|@|
346
+ EOL
347
+
348
+ assert_invalid_ldif("control type is missing",
349
+ ldif_source, 3, 9, ldif_source_with_error_mark)
350
+ end
351
+
352
+ def test_change_type_is_missing
353
+ ldif_source = <<-EOL
354
+ version: 1
355
+ dn: ou=Product Development, dc=airius, dc=com
356
+ control: 1.2.840.113556.1.4.805 true
357
+ EOL
358
+
359
+ ldif_source_with_error_mark = <<-EOL.chomp
360
+ control: 1.2.840.113556.1.4.805 true
361
+ |@|
362
+ EOL
363
+
364
+ assert_invalid_ldif("change type is missing",
365
+ ldif_source, 4, 1, ldif_source_with_error_mark)
366
+ end
367
+
368
+ def test_invalid_dn
369
+ ldif_source = <<-EOL
370
+ version: 1
371
+ dn: ou=o=Airius
372
+ EOL
373
+
374
+ ldif_source_with_error_mark = <<-EOL
375
+ dn: ou=o=Airius|@|
376
+ EOL
377
+
378
+ assert_invalid_ldif(["DN is invalid: %s: %s",
379
+ "ou=o=Airius", "attribute type is missing"],
380
+ ldif_source, 2, 16, ldif_source_with_error_mark)
381
+ end
382
+
383
+ def test_invalid_dn_value
384
+ ldif_source = <<-EOL
385
+ version: 1
386
+ # dn:: ou=<JapaneseOU>,o=Airius
387
+ dn: ou=営業部,o=Airius
388
+ EOL
389
+
390
+ ldif_source_with_error_mark = <<-EOL
391
+ dn: ou=|@|営業部,o=Airius
392
+ EOL
393
+
394
+ assert_invalid_ldif(["DN has an invalid character: %s", "営"],
395
+ ldif_source, 3, 8, ldif_source_with_error_mark)
396
+ end
397
+
398
+ def test_multi_records_without_separator
399
+ ldif_source = <<-EOL
400
+ version: 1
401
+ dn: ou=Product Development, dc=airius, dc=com
402
+ changetype: delete
403
+ dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
404
+ seealso:
405
+ description::
406
+ EOL
407
+
408
+ ldif_source_with_error_mark = <<-EOL
409
+ |@|dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
410
+ EOL
411
+
412
+ assert_invalid_ldif("separator is missing", ldif_source,
413
+ 4, 1, ldif_source_with_error_mark)
414
+ end
415
+
416
+ def test_to_s_with_blank_value
417
+ ldif_source = <<-EOL
418
+ version: 1
419
+ dn: ou=Product Development,dc=airius,dc=com
420
+ seealso:
421
+ description::
422
+ EOL
423
+
424
+ assert_ldif_to_s(<<-EOL, ldif_source)
425
+ version: 1
426
+ dn: ou=Product Development,dc=airius,dc=com
427
+ description:
428
+ seealso:
429
+ EOL
430
+ end
431
+
432
+ def test_to_s_with_last_space
433
+ ldif_source = <<-EOL
434
+ version: 1
435
+ # 'ou=Product Development,dc=airius,dc=com '
436
+ dn:: b3U9UHJvZHVjdCBEZXZlbG9wbWVudCwgZGM9YWlyaXVzLCBkYz1jb20g
437
+ # 'ou=Product Development,dc=airius,dc=com '
438
+ description:: b3U9UHJvZHVjdCBEZXZlbG9wbWVudCwgZGM9YWlyaXVzLCBkYz1jb20g
439
+ EOL
440
+
441
+ assert_ldif_to_s(<<-EOL, ldif_source)
442
+ version: 1
443
+ dn: ou=Product Development,dc=airius,dc="com "
444
+ description:: b3U9UHJvZHVjdCBEZXZlbG9wbWVudCwgZGM9YWlyaXVzLCBkYz1jb20g
445
+ EOL
446
+ end
447
+
448
+ def test_change_record_with_control
449
+ ldif_source = <<-EOL
450
+ version: 1
451
+ # Delete an entry. The operation will attach the LDAPv3
452
+ # Tree Delete Control defined in [9]. The criticality
453
+ # field is "true" and the controlValue field is
454
+ # absent, as required by [9].
455
+ dn: ou=Product Development, dc=airius, dc=com
456
+ control: 1.2.840.113556.1.4.805 true
457
+ changetype: delete
458
+ EOL
459
+
460
+ change_attributes = {
461
+ "dn" => "ou=Product Development,dc=airius,dc=com",
462
+ }
463
+
464
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
465
+ record = ldif.records[0]
466
+ assert_equal("delete", record.change_type)
467
+ assert_true(record.delete?)
468
+ assert_equal([{
469
+ :type => "1.2.840.113556.1.4.805",
470
+ :criticality => true,
471
+ :value => nil
472
+ }],
473
+ record.controls.collect {|control| control.to_hash})
474
+
475
+ control = record.controls[0]
476
+ assert_equal("1.2.840.113556.1.4.805", control.type)
477
+ assert_true(control.criticality?)
478
+ assert_nil(control.value)
479
+ end
480
+
481
+ def test_change_record_with_control_to_s
482
+ ldif_source = <<-EOL
483
+ version: 1
484
+ # Delete an entry. The operation will attach the LDAPv3
485
+ # Tree Delete Control defined in [9]. The criticality
486
+ # field is "true" and the controlValue field is
487
+ # absent, as required by [9].
488
+ dn: ou=Product Development, dc=airius, dc=com
489
+ control: 1.2.840.113556.1.4.805 true
490
+ changetype: delete
491
+ EOL
492
+
493
+ assert_ldif_to_s(<<-EOL, ldif_source)
494
+ version: 1
495
+ dn: ou=Product Development,dc=airius,dc=com
496
+ control: 1.2.840.113556.1.4.805 true
497
+ changetype: delete
498
+ EOL
499
+ end
500
+
501
+ def test_multi_change_type_records
502
+ ldif_source = <<-EOL
503
+ version: 1
504
+ # Add a new entry
505
+ dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
506
+ changetype: add
507
+ objectclass: top
508
+ objectclass: person
509
+ objectclass: organizationalPerson
510
+ cn: Fiona Jensen
511
+ sn: Jensen
512
+ uid: fiona
513
+ telephonenumber: +1 408 555 1212
514
+
515
+ # Delete an existing entry
516
+ dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
517
+ changetype: delete
518
+
519
+ # Modify an entry's relative distinguished name
520
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
521
+ changetype: modrdn
522
+ newrdn: cn=Paula Jensen
523
+ deleteoldrdn: 1
524
+
525
+ # Rename an entry and move all of its children to a new location in
526
+ # the directory tree (only implemented by LDAPv3 servers).
527
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
528
+ changetype: modrdn
529
+ newrdn: ou=Product Development Accountants
530
+ deleteoldrdn: 0
531
+ newsuperior: ou=Accounting, dc=airius, dc=com
532
+
533
+ # Modify an entry: add an additional value to the postaladdress
534
+ # attribute, completely delete the description attribute, replace
535
+ # the telephonenumber attribute with two values, and delete a specific
536
+ # value from the facsimiletelephonenumber attribute
537
+ dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
538
+ changetype: modify
539
+ add: postaladdress
540
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
541
+ -
542
+ delete: description
543
+ -
544
+ replace: telephonenumber
545
+ telephonenumber: +1 408 555 1234
546
+ telephonenumber: +1 408 555 5678
547
+ -
548
+ delete: facsimiletelephonenumber
549
+ facsimiletelephonenumber: +1 408 555 9876
550
+ -
551
+
552
+ # Modify an entry: replace the postaladdress attribute with an empty
553
+ # set of values (which will cause the attribute to be removed), and
554
+ # delete the entire description attribute. Note that the first will
555
+ # always succeed, while the second will only succeed if at least
556
+ # one value for the description attribute is present.
557
+ dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com
558
+ changetype: modify
559
+ replace: postaladdress
560
+ -
561
+ delete: description
562
+ -
563
+ EOL
564
+
565
+ change_attributes_add = {
566
+ "dn" => "cn=Fiona Jensen,ou=Marketing,dc=airius,dc=com",
567
+ "objectclass" => ["top", "person", "organizationalPerson"],
568
+ "cn" => ["Fiona Jensen"],
569
+ "sn" => ["Jensen"],
570
+ "uid" => ["fiona"],
571
+ "telephonenumber" => ["+1 408 555 1212"],
572
+ }
573
+
574
+ change_attributes_delete = {
575
+ "dn" => "cn=Robert Jensen,ou=Marketing,dc=airius,dc=com",
576
+ }
577
+
578
+ change_attributes_modrdn = {
579
+ "dn" => "cn=Paul Jensen,ou=Product Development,dc=airius,dc=com",
580
+ }
581
+
582
+ change_attributes_modrdn_with_new_superior = {
583
+ "dn" => "ou=PD Accountants,ou=Product Development,dc=airius,dc=com",
584
+ }
585
+
586
+ change_attributes_modify = {
587
+ "dn" => "cn=Paula Jensen,ou=Product Development,dc=airius,dc=com",
588
+ }
589
+
590
+ change_attributes_modify_with_empty_replace = {
591
+ "dn" => "cn=Ingrid Jensen,ou=Product Support,dc=airius,dc=com",
592
+ }
593
+
594
+ ldif = assert_ldif(1,
595
+ [change_attributes_add,
596
+ change_attributes_delete,
597
+ change_attributes_modrdn,
598
+ change_attributes_modrdn_with_new_superior,
599
+ change_attributes_modify,
600
+ change_attributes_modify_with_empty_replace],
601
+ ldif_source)
602
+ record = ldif.records[0]
603
+ assert_equal("add", record.change_type)
604
+ assert_true(record.add?)
605
+
606
+ record = ldif.records[1]
607
+ assert_equal("delete", record.change_type)
608
+ assert_true(record.delete?)
609
+
610
+ record = ldif.records[2]
611
+ assert_equal("modrdn", record.change_type)
612
+ assert_true(record.modify_rdn?)
613
+ assert_equal("cn=Paula Jensen", record.new_rdn)
614
+ assert_true(record.delete_old_rdn?)
615
+ assert_nil(record.new_superior)
616
+
617
+ record = ldif.records[3]
618
+ assert_equal("modrdn", record.change_type)
619
+ assert_true(record.modify_rdn?)
620
+ assert_equal("ou=Product Development Accountants", record.new_rdn)
621
+ assert_false(record.delete_old_rdn?)
622
+ assert_equal("ou=Accounting,dc=airius,dc=com", record.new_superior)
623
+
624
+ record = ldif.records[4]
625
+ assert_equal("modify", record.change_type)
626
+ assert_true(record.modify?)
627
+ operations = [
628
+ ["add", "postaladdress",
629
+ {"postaladdress" =>
630
+ ["123 Anystreet $ Sunnyvale, CA $ 94086"]}],
631
+ ["delete", "description", {}],
632
+ ["replace", "telephonenumber",
633
+ {"telephonenumber" => [
634
+ "+1 408 555 1234",
635
+ "+1 408 555 5678",
636
+ ]}],
637
+ ["delete", "facsimiletelephonenumber",
638
+ {"facsimiletelephonenumber" => ["+1 408 555 9876"]}],
639
+ ]
640
+ i = -1
641
+ actual = record.operations.collect do |operation|
642
+ i += 1
643
+ type = operations[i][0]
644
+ [operation.send("#{type}?"),
645
+ [operation.type, operation.attribute, operation.attributes]]
646
+ end
647
+ assert_equal(operations.collect {|operation| [true, operation]},
648
+ actual)
649
+
650
+ record = ldif.records[5]
651
+ assert_equal("modify", record.change_type)
652
+ assert_true(record.modify?)
653
+ operations = [
654
+ ["replace", "postaladdress", {}],
655
+ ["delete", "description", {}],
656
+ ]
657
+ i = -1
658
+ actual = record.operations.collect do |operation|
659
+ i += 1
660
+ type = operations[i][0]
661
+ [operation.send("#{type}?"),
662
+ [operation.type, operation.attribute, operation.attributes]]
663
+ end
664
+ assert_equal(operations.collect {|operation| [true, operation]},
665
+ actual)
666
+ end
667
+
668
+ def test_multi_change_type_records
669
+ ldif_source = <<-EOL
670
+ version: 1
671
+ # Add a new entry
672
+ dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
673
+ changetype: add
674
+ objectclass: top
675
+ objectclass: person
676
+ objectclass: organizationalPerson
677
+ cn: Fiona Jensen
678
+ sn: Jensen
679
+ uid: fiona
680
+ telephonenumber: +1 408 555 1212
681
+
682
+ # Delete an existing entry
683
+ dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
684
+ changetype: delete
685
+
686
+ # Modify an entry's relative distinguished name
687
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
688
+ changetype: modrdn
689
+ newrdn: cn=Paula Jensen
690
+ deleteoldrdn: 1
691
+
692
+ # Rename an entry and move all of its children to a new location in
693
+ # the directory tree (only implemented by LDAPv3 servers).
694
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
695
+ changetype: modrdn
696
+ newrdn: ou=Product Development Accountants
697
+ deleteoldrdn: 0
698
+ newsuperior: ou=Accounting, dc=airius, dc=com
699
+
700
+ # Modify an entry: add an additional value to the postaladdress
701
+ # attribute, completely delete the description attribute, replace
702
+ # the telephonenumber attribute with two values, and delete a specific
703
+ # value from the facsimiletelephonenumber attribute
704
+ dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
705
+ changetype: modify
706
+ add: postaladdress
707
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
708
+ -
709
+ delete: description
710
+ -
711
+ replace: telephonenumber
712
+ telephonenumber: +1 408 555 1234
713
+ telephonenumber: +1 408 555 5678
714
+ -
715
+ delete: facsimiletelephonenumber
716
+ facsimiletelephonenumber: +1 408 555 9876
717
+ -
718
+
719
+ # Modify an entry: replace the postaladdress attribute with an empty
720
+ # set of values (which will cause the attribute to be removed), and
721
+ # delete the entire description attribute. Note that the first will
722
+ # always succeed, while the second will only succeed if at least
723
+ # one value for the description attribute is present.
724
+ dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com
725
+ changetype: modify
726
+ replace: postaladdress
727
+ -
728
+ delete: description
729
+ -
730
+ EOL
731
+
732
+ assert_ldif_to_s(<<-EOL, ldif_source)
733
+ version: 1
734
+ dn: cn=Fiona Jensen,ou=Marketing,dc=airius,dc=com
735
+ changetype: add
736
+ cn: Fiona Jensen
737
+ objectclass: organizationalPerson
738
+ objectclass: person
739
+ objectclass: top
740
+ sn: Jensen
741
+ telephonenumber: +1 408 555 1212
742
+ uid: fiona
743
+
744
+ dn: cn=Robert Jensen,ou=Marketing,dc=airius,dc=com
745
+ changetype: delete
746
+
747
+ dn: cn=Paul Jensen,ou=Product Development,dc=airius,dc=com
748
+ changetype: modrdn
749
+ newrdn: cn=Paula Jensen
750
+ deleteoldrdn: 1
751
+
752
+ dn: ou=PD Accountants,ou=Product Development,dc=airius,dc=com
753
+ changetype: modrdn
754
+ newrdn: ou=Product Development Accountants
755
+ deleteoldrdn: 0
756
+ newsuperior: ou=Accounting,dc=airius,dc=com
757
+
758
+ dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com
759
+ changetype: modify
760
+ add: postaladdress
761
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
762
+ -
763
+ delete: description
764
+ -
765
+ replace: telephonenumber
766
+ telephonenumber: +1 408 555 1234
767
+ telephonenumber: +1 408 555 5678
768
+ -
769
+ delete: facsimiletelephonenumber
770
+ facsimiletelephonenumber: +1 408 555 9876
771
+ -
772
+
773
+ dn: cn=Ingrid Jensen,ou=Product Support,dc=airius,dc=com
774
+ changetype: modify
775
+ replace: postaladdress
776
+ -
777
+ delete: description
778
+ -
779
+ EOL
780
+ end
781
+
782
+ def test_modify_record
783
+ ldif_source = <<-EOL
784
+ version: 1
785
+ # Modify an entry: add an additional value to the postaladdress
786
+ # attribute, completely delete the description attribute, replace
787
+ # the telephonenumber attribute with two values, and delete a specific
788
+ # value from the facsimiletelephonenumber attribute
789
+ dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
790
+ changetype: modify
791
+ add: postaladdress
792
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
793
+ -
794
+ delete: description
795
+ -
796
+ replace: telephonenumber
797
+ telephonenumber: +1 408 555 1234
798
+ telephonenumber: +1 408 555 5678
799
+ -
800
+ delete: facsimiletelephonenumber
801
+ facsimiletelephonenumber: +1 408 555 9876
802
+ -
803
+ EOL
804
+
805
+ change_attributes = {
806
+ "dn" => "cn=Paula Jensen,ou=Product Development,dc=airius,dc=com",
807
+ }
808
+
809
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
810
+ record = ldif.records[0]
811
+ assert_equal("modify", record.change_type)
812
+ assert_true(record.modify?)
813
+
814
+ operations = [
815
+ ["add", "postaladdress",
816
+ {"postaladdress" =>
817
+ ["123 Anystreet $ Sunnyvale, CA $ 94086"]}],
818
+ ["delete", "description", {}],
819
+ ["replace", "telephonenumber",
820
+ {"telephonenumber" => [
821
+ "+1 408 555 1234",
822
+ "+1 408 555 5678",
823
+ ]}],
824
+ ["delete", "facsimiletelephonenumber",
825
+ {"facsimiletelephonenumber" => ["+1 408 555 9876"]}],
826
+ ]
827
+ i = -1
828
+ actual = record.operations.collect do |operation|
829
+ i += 1
830
+ type = operations[i][0]
831
+ [operation.send("#{type}?"),
832
+ [operation.type, operation.attribute, operation.attributes]]
833
+ end
834
+ assert_equal(operations.collect {|operation| [true, operation]},
835
+ actual)
836
+ end
837
+
838
+ def test_modify_record_to_s
839
+ ldif_source = <<-EOL
840
+ version: 1
841
+ # Modify an entry: add an additional value to the postaladdress
842
+ # attribute, completely delete the description attribute, replace
843
+ # the telephonenumber attribute with two values, and delete a specific
844
+ # value from the facsimiletelephonenumber attribute
845
+ dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com
846
+ changetype: modify
847
+ add: postaladdress
848
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
849
+ -
850
+ delete: description
851
+ -
852
+ replace: telephonenumber
853
+ telephonenumber: +1 408 555 1234
854
+ telephonenumber: +1 408 555 5678
855
+ -
856
+ delete: facsimiletelephonenumber
857
+ facsimiletelephonenumber: +1 408 555 9876
858
+ -
859
+ EOL
860
+
861
+ assert_ldif_to_s(<<-EOL, ldif_source)
862
+ version: 1
863
+ dn: cn=Paula Jensen,ou=Product Development,dc=airius,dc=com
864
+ changetype: modify
865
+ add: postaladdress
866
+ postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086
867
+ -
868
+ delete: description
869
+ -
870
+ replace: telephonenumber
871
+ telephonenumber: +1 408 555 1234
872
+ telephonenumber: +1 408 555 5678
873
+ -
874
+ delete: facsimiletelephonenumber
875
+ facsimiletelephonenumber: +1 408 555 9876
876
+ -
877
+ EOL
878
+ end
879
+
880
+ def test_modrdn_record_with_newsuperior
881
+ ldif_source = <<-EOL
882
+ version: 1
883
+ # Rename an entry and move all of its children to a new location in
884
+ # the directory tree (only implemented by LDAPv3 servers).
885
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
886
+ changetype: modrdn
887
+ newrdn: ou=Product Development Accountants
888
+ deleteoldrdn: 0
889
+ newsuperior: ou=Accounting, dc=airius, dc=com
890
+ EOL
891
+
892
+ change_attributes = {
893
+ "dn" => "ou=PD Accountants,ou=Product Development,dc=airius,dc=com",
894
+ }
895
+
896
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
897
+ record = ldif.records[0]
898
+ assert_equal("modrdn", record.change_type)
899
+ assert_true(record.modify_rdn?)
900
+ assert_equal("ou=Product Development Accountants", record.new_rdn)
901
+ assert_false(record.delete_old_rdn?)
902
+ assert_equal("ou=Accounting,dc=airius,dc=com", record.new_superior)
903
+ end
904
+
905
+ def test_modrdn_record_with_newsuperior_to_s
906
+ ldif_source = <<-EOL
907
+ version: 1
908
+ # Rename an entry and move all of its children to a new location in
909
+ # the directory tree (only implemented by LDAPv3 servers).
910
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
911
+ changetype: modrdn
912
+ newrdn: ou=Product Development Accountants
913
+ deleteoldrdn: 0
914
+ newsuperior: ou=Accounting, dc=airius, dc=com
915
+ EOL
916
+
917
+ assert_ldif_to_s(<<-EOL, ldif_source)
918
+ version: 1
919
+ dn: ou=PD Accountants,ou=Product Development,dc=airius,dc=com
920
+ changetype: modrdn
921
+ newrdn: ou=Product Development Accountants
922
+ deleteoldrdn: 0
923
+ newsuperior: ou=Accounting,dc=airius,dc=com
924
+ EOL
925
+ end
926
+
927
+ def test_modrdn_record
928
+ ldif_source = <<-EOL
929
+ version: 1
930
+ # Modify an entry's relative distinguished name
931
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
932
+ changetype: modrdn
933
+ newrdn: cn=Paula Jensen
934
+ deleteoldrdn: 1
935
+ EOL
936
+
937
+ change_attributes = {
938
+ "dn" => "cn=Paul Jensen,ou=Product Development,dc=airius,dc=com",
939
+ }
940
+
941
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
942
+ record = ldif.records[0]
943
+ assert_equal("modrdn", record.change_type)
944
+ assert_true(record.modify_rdn?)
945
+ assert_equal("cn=Paula Jensen", record.new_rdn)
946
+ assert_true(record.delete_old_rdn?)
947
+ assert_nil(record.new_superior)
948
+ end
949
+
950
+ def test_modrdn_record_to_s
951
+ ldif_source = <<-EOL
952
+ version: 1
953
+ # Modify an entry's relative distinguished name
954
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
955
+ changetype: modrdn
956
+ newrdn: cn=Paula Jensen
957
+ deleteoldrdn: 1
958
+ EOL
959
+
960
+ assert_ldif_to_s(<<-EOL, ldif_source)
961
+ version: 1
962
+ dn: cn=Paul Jensen,ou=Product Development,dc=airius,dc=com
963
+ changetype: modrdn
964
+ newrdn: cn=Paula Jensen
965
+ deleteoldrdn: 1
966
+ EOL
967
+ end
968
+
969
+ def test_moddn_record_with_newsuperior
970
+ ldif_source = <<-EOL
971
+ version: 1
972
+ # Rename an entry and move all of its children to a new location in
973
+ # the directory tree (only implemented by LDAPv3 servers).
974
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
975
+ changetype: moddn
976
+ newrdn: ou=Product Development Accountants
977
+ deleteoldrdn: 0
978
+ newsuperior: ou=Accounting, dc=airius, dc=com
979
+ EOL
980
+
981
+ change_attributes = {
982
+ "dn" => "ou=PD Accountants,ou=Product Development,dc=airius,dc=com",
983
+ }
984
+
985
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
986
+ record = ldif.records[0]
987
+ assert_equal("moddn", record.change_type)
988
+ assert_true(record.modify_dn?)
989
+ assert_equal("ou=Product Development Accountants", record.new_rdn)
990
+ assert_false(record.delete_old_rdn?)
991
+ assert_equal("ou=Accounting,dc=airius,dc=com", record.new_superior)
992
+ end
993
+
994
+ def test_moddn_record_with_newsuperior_to_s
995
+ ldif_source = <<-EOL
996
+ version: 1
997
+ # Rename an entry and move all of its children to a new location in
998
+ # the directory tree (only implemented by LDAPv3 servers).
999
+ dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com
1000
+ changetype: moddn
1001
+ newrdn: ou=Product Development Accountants
1002
+ deleteoldrdn: 0
1003
+ newsuperior: ou=Accounting, dc=airius, dc=com
1004
+ EOL
1005
+
1006
+ assert_ldif_to_s(<<-EOL, ldif_source)
1007
+ version: 1
1008
+ dn: ou=PD Accountants,ou=Product Development,dc=airius,dc=com
1009
+ changetype: moddn
1010
+ newrdn: ou=Product Development Accountants
1011
+ deleteoldrdn: 0
1012
+ newsuperior: ou=Accounting,dc=airius,dc=com
1013
+ EOL
1014
+ end
1015
+
1016
+ def test_moddn_record
1017
+ ldif_source = <<-EOL
1018
+ version: 1
1019
+ # Modify an entry's relative distinguished name
1020
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
1021
+ changetype: moddn
1022
+ newrdn: cn=Paula Jensen
1023
+ deleteoldrdn: 1
1024
+ EOL
1025
+
1026
+ change_attributes = {
1027
+ "dn" => "cn=Paul Jensen,ou=Product Development,dc=airius,dc=com",
1028
+ }
1029
+
1030
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
1031
+ record = ldif.records[0]
1032
+ assert_equal("moddn", record.change_type)
1033
+ assert_true(record.modify_dn?)
1034
+ assert_equal("cn=Paula Jensen", record.new_rdn)
1035
+ assert_true(record.delete_old_rdn?)
1036
+ assert_nil(record.new_superior)
1037
+ end
1038
+
1039
+ def test_moddn_record_to_s
1040
+ ldif_source = <<-EOL
1041
+ version: 1
1042
+ # Modify an entry's relative distinguished name
1043
+ dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com
1044
+ changetype: moddn
1045
+ newrdn: cn=Paula Jensen
1046
+ deleteoldrdn: 1
1047
+ EOL
1048
+
1049
+ assert_ldif_to_s(<<-EOL, ldif_source)
1050
+ version: 1
1051
+ dn: cn=Paul Jensen,ou=Product Development,dc=airius,dc=com
1052
+ changetype: moddn
1053
+ newrdn: cn=Paula Jensen
1054
+ deleteoldrdn: 1
1055
+ EOL
1056
+ end
1057
+
1058
+ def test_delete_record
1059
+ ldif_source = <<-EOL
1060
+ version: 1
1061
+ # Delete an existing entry
1062
+ dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
1063
+ changetype: delete
1064
+ EOL
1065
+
1066
+ change_attributes = {
1067
+ "dn" => "cn=Robert Jensen,ou=Marketing,dc=airius,dc=com",
1068
+ }
1069
+
1070
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
1071
+ record = ldif.records[0]
1072
+ assert_equal("delete", record.change_type)
1073
+ assert_true(record.delete?)
1074
+ end
1075
+
1076
+ def test_delete_record_to_s
1077
+ ldif_source = <<-EOL
1078
+ version: 1
1079
+ # Delete an existing entry
1080
+ dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com
1081
+ changetype: delete
1082
+ EOL
1083
+
1084
+ assert_ldif_to_s(<<-EOL, ldif_source)
1085
+ version: 1
1086
+ dn: cn=Robert Jensen,ou=Marketing,dc=airius,dc=com
1087
+ changetype: delete
1088
+ EOL
1089
+ end
1090
+
1091
+ def test_add_record
1092
+ ldif_source = <<-EOL
1093
+ version: 1
1094
+ # Add a new entry
1095
+ dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
1096
+ changetype: add
1097
+ objectclass: top
1098
+ objectclass: person
1099
+ objectclass: organizationalPerson
1100
+ cn: Fiona Jensen
1101
+ sn: Jensen
1102
+ uid: fiona
1103
+ telephonenumber: +1 408 555 1212
1104
+ EOL
1105
+
1106
+ change_attributes = {
1107
+ "dn" => "cn=Fiona Jensen,ou=Marketing,dc=airius,dc=com",
1108
+ "objectclass" => ["top", "person", "organizationalPerson"],
1109
+ "cn" => ["Fiona Jensen"],
1110
+ "sn" => ["Jensen"],
1111
+ "uid" => ["fiona"],
1112
+ "telephonenumber" => ["+1 408 555 1212"],
1113
+ }
1114
+
1115
+ ldif = assert_ldif(1, [change_attributes], ldif_source)
1116
+ record = ldif.records[0]
1117
+ assert_equal("add", record.change_type)
1118
+ assert_true(record.add?)
1119
+ end
1120
+
1121
+ def test_add_record_to_s
1122
+ ldif_source = <<-EOL
1123
+ version: 1
1124
+ # Add a new entry
1125
+ dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com
1126
+ changetype: add
1127
+ objectclass: top
1128
+ objectclass: person
1129
+ objectclass: organizationalPerson
1130
+ cn: Fiona Jensen
1131
+ sn: Jensen
1132
+ uid: fiona
1133
+ telephonenumber: +1 408 555 1212
1134
+ EOL
1135
+
1136
+ assert_ldif_to_s(<<-EOL, ldif_source)
1137
+ version: 1
1138
+ dn: cn=Fiona Jensen,ou=Marketing,dc=airius,dc=com
1139
+ changetype: add
1140
+ cn: Fiona Jensen
1141
+ objectclass: organizationalPerson
1142
+ objectclass: person
1143
+ objectclass: top
1144
+ sn: Jensen
1145
+ telephonenumber: +1 408 555 1212
1146
+ uid: fiona
1147
+ EOL
1148
+ end
1149
+
1150
+ def test_records_with_external_file_reference
1151
+ ldif_source = <<-EOL
1152
+ version: 1
1153
+ dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com
1154
+ objectclass: top
1155
+ objectclass: person
1156
+ objectclass: organizationalPerson
1157
+ cn: Horatio Jensen
1158
+ cn: Horatio N Jensen
1159
+ sn: Jensen
1160
+ uid: hjensen
1161
+ telephonenumber: +1 408 555 1212
1162
+ jpegphoto:< file://#{jpeg_photo_path}
1163
+ EOL
1164
+
1165
+ record = {
1166
+ "dn" => "cn=Horatio Jensen,ou=Product Testing,dc=airius,dc=com",
1167
+ "objectclass" => ["top", "person", "organizationalPerson"],
1168
+ "cn" => ["Horatio Jensen", "Horatio N Jensen"],
1169
+ "sn" => ["Jensen"],
1170
+ "uid" => ["hjensen"],
1171
+ "telephonenumber" => ["+1 408 555 1212"],
1172
+ "jpegphoto" => [jpeg_photo],
1173
+ }
1174
+
1175
+ assert_ldif(1, [record], ldif_source)
1176
+ end
1177
+
1178
+ def test_records_with_external_file_reference_to_s
1179
+ ldif_source = <<-EOL
1180
+ version: 1
1181
+ dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com
1182
+ objectclass: top
1183
+ objectclass: person
1184
+ objectclass: organizationalPerson
1185
+ cn: Horatio Jensen
1186
+ cn: Horatio N Jensen
1187
+ sn: Jensen
1188
+ uid: hjensen
1189
+ telephonenumber: +1 408 555 1212
1190
+ jpegphoto:< file://#{jpeg_photo_path}
1191
+ EOL
1192
+
1193
+ jpeg_photo_attribute = "jpegphoto:: "
1194
+ value = [jpeg_photo].pack("m").gsub(/\n/, '')
1195
+ first_line_value_size = 75 - jpeg_photo_attribute.size
1196
+ jpeg_photo_attribute << value[0, first_line_value_size] + "\n"
1197
+ value = value[first_line_value_size..-1]
1198
+ value.scan(/.{1,74}/).each do |line|
1199
+ jpeg_photo_attribute << " #{line}\n"
1200
+ end
1201
+ jpeg_photo_attribute = jpeg_photo_attribute.chomp
1202
+
1203
+ assert_ldif_to_s(<<-EOL, ldif_source)
1204
+ version: 1
1205
+ dn: cn=Horatio Jensen,ou=Product Testing,dc=airius,dc=com
1206
+ cn: Horatio Jensen
1207
+ cn: Horatio N Jensen
1208
+ #{jpeg_photo_attribute}
1209
+ objectclass: organizationalPerson
1210
+ objectclass: person
1211
+ objectclass: top
1212
+ sn: Jensen
1213
+ telephonenumber: +1 408 555 1212
1214
+ uid: hjensen
1215
+ EOL
1216
+ end
1217
+
1218
+ def test_records_with_option_attributes
1219
+ ldif_source = <<-EOL
1220
+ version: 1
1221
+ dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
1222
+ # dn:: ou=<JapaneseOU>,o=Airius
1223
+ objectclass: top
1224
+ objectclass: organizationalUnit
1225
+ ou:: 5Za25qWt6YOo
1226
+ # ou:: <JapaneseOU>
1227
+ ou;lang-ja:: 5Za25qWt6YOo
1228
+ # ou;lang-ja:: <JapaneseOU>
1229
+ ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2
1230
+ # ou;lang-ja:: <JapaneseOU_in_phonetic_representation>
1231
+ ou;lang-en: Sales
1232
+ description: Japanese office
1233
+
1234
+ dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
1235
+ # dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius
1236
+ userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
1237
+ objectclass: top
1238
+ objectclass: person
1239
+ objectclass: organizationalPerson
1240
+ objectclass: inetOrgPerson
1241
+ uid: rogasawara
1242
+ mail: rogasawara@airius.co.jp
1243
+ givenname;lang-ja:: 44Ot44OJ44OL44O8
1244
+ # givenname;lang-ja:: <JapaneseGivenname>
1245
+ sn;lang-ja:: 5bCP56yg5Y6f
1246
+ # sn;lang-ja:: <JapaneseSn>
1247
+ cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1248
+ # cn;lang-ja:: <JapaneseCn>
1249
+ title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw==
1250
+ # title;lang-ja:: <JapaneseTitle>
1251
+ preferredlanguage: ja
1252
+ givenname:: 44Ot44OJ44OL44O8
1253
+ # givenname:: <JapaneseGivenname>
1254
+ sn:: 5bCP56yg5Y6f
1255
+ # sn:: <JapaneseSn>
1256
+ cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1257
+ # cn:: <JapaneseCn>
1258
+ title:: 5Za25qWt6YOoIOmDqOmVtw==
1259
+ # title:: <JapaneseTitle>
1260
+ givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
1261
+ # givenname;lang-ja;phonetic::
1262
+ <JapaneseGivenname_in_phonetic_representation_kana>
1263
+ sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
1264
+ # sn;lang-ja;phonetic:: <JapaneseSn_in_phonetic_representation_kana>
1265
+ cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
1266
+ # cn;lang-ja;phonetic:: <JapaneseCn_in_phonetic_representation_kana>
1267
+ title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
1268
+ # title;lang-ja;phonetic::
1269
+ # <JapaneseTitle_in_phonetic_representation_kana>
1270
+ givenname;lang-en: Rodney
1271
+ sn;lang-en: Ogasawara
1272
+ cn;lang-en: Rodney Ogasawara
1273
+ title;lang-en: Sales, Director
1274
+ EOL
1275
+
1276
+ record1 = {
1277
+ "dn" => "ou=営業部,o=Airius",
1278
+ "objectclass" => ["top", "organizationalUnit"],
1279
+ "ou" => [
1280
+ "営業部",
1281
+ {"lang-ja" =>
1282
+ [
1283
+ "営業部",
1284
+ {"phonetic" => ["えいぎょうぶ"]},
1285
+ ],
1286
+ },
1287
+ {"lang-en" => ["Sales"]},
1288
+ ],
1289
+ "description" => ["Japanese office"],
1290
+ }
1291
+
1292
+ record2 = {
1293
+ "dn" => "uid=rogasawara,ou=営業部,o=Airius",
1294
+ "userpassword" => ["{SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM="],
1295
+ "objectclass" => ["top", "person",
1296
+ "organizationalPerson", "inetOrgPerson"],
1297
+ "uid" => ["rogasawara"],
1298
+ "mail" => ["rogasawara@airius.co.jp"],
1299
+ "givenname" => [
1300
+ {"lang-ja" =>
1301
+ [
1302
+ "ロドニー",
1303
+ {"phonetic" => ["ろどにー"]},
1304
+ ]
1305
+ },
1306
+ "ロドニー",
1307
+ {"lang-en" => ["Rodney"]},
1308
+ ],
1309
+ "sn" => [
1310
+ {"lang-ja" =>
1311
+ [
1312
+ "小笠原",
1313
+ {"phonetic" => ["おがさわら"]},
1314
+ ]
1315
+ },
1316
+ "小笠原",
1317
+ {"lang-en" => ["Ogasawara"]},
1318
+ ],
1319
+ "cn" => [
1320
+ {"lang-ja" =>
1321
+ [
1322
+ "小笠原 ロドニー",
1323
+ {"phonetic" => ["おがさわら ろどにー"]},
1324
+ ],
1325
+ },
1326
+ "小笠原 ロドニー",
1327
+ {"lang-en" => ["Rodney Ogasawara"]},
1328
+ ],
1329
+ "title" => [
1330
+ {"lang-ja" =>
1331
+ [
1332
+ "営業部 部長",
1333
+ {"phonetic" => ["えいぎょうぶ ぶちょう"]}
1334
+ ]
1335
+ },
1336
+ "営業部 部長",
1337
+ {"lang-en" => ["Sales, Director"]},
1338
+ ],
1339
+ "preferredlanguage" => ["ja"],
1340
+ }
1341
+
1342
+ assert_ldif(1, [record1, record2], ldif_source)
1343
+ end
1344
+
1345
+ def test_records_with_option_attributes_to_s
1346
+ ldif_source = <<-EOL
1347
+ version: 1
1348
+ dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
1349
+ # dn:: ou=<JapaneseOU>,o=Airius
1350
+ objectclass: top
1351
+ objectclass: organizationalUnit
1352
+ ou:: 5Za25qWt6YOo
1353
+ # ou:: <JapaneseOU>
1354
+ ou;lang-ja:: 5Za25qWt6YOo
1355
+ # ou;lang-ja:: <JapaneseOU>
1356
+ ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2
1357
+ # ou;lang-ja:: <JapaneseOU_in_phonetic_representation>
1358
+ ou;lang-en: Sales
1359
+ description: Japanese office
1360
+
1361
+ dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
1362
+ # dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius
1363
+ userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
1364
+ objectclass: top
1365
+ objectclass: person
1366
+ objectclass: organizationalPerson
1367
+ objectclass: inetOrgPerson
1368
+ uid: rogasawara
1369
+ mail: rogasawara@airius.co.jp
1370
+ givenname;lang-ja:: 44Ot44OJ44OL44O8
1371
+ # givenname;lang-ja:: <JapaneseGivenname>
1372
+ sn;lang-ja:: 5bCP56yg5Y6f
1373
+ # sn;lang-ja:: <JapaneseSn>
1374
+ cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1375
+ # cn;lang-ja:: <JapaneseCn>
1376
+ title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw==
1377
+ # title;lang-ja:: <JapaneseTitle>
1378
+ preferredlanguage: ja
1379
+ givenname:: 44Ot44OJ44OL44O8
1380
+ # givenname:: <JapaneseGivenname>
1381
+ sn:: 5bCP56yg5Y6f
1382
+ # sn:: <JapaneseSn>
1383
+ cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1384
+ # cn:: <JapaneseCn>
1385
+ title:: 5Za25qWt6YOoIOmDqOmVtw==
1386
+ # title:: <JapaneseTitle>
1387
+ givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
1388
+ # givenname;lang-ja;phonetic::
1389
+ <JapaneseGivenname_in_phonetic_representation_kana>
1390
+ sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
1391
+ # sn;lang-ja;phonetic:: <JapaneseSn_in_phonetic_representation_kana>
1392
+ cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
1393
+ # cn;lang-ja;phonetic:: <JapaneseCn_in_phonetic_representation_kana>
1394
+ title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
1395
+ # title;lang-ja;phonetic::
1396
+ # <JapaneseTitle_in_phonetic_representation_kana>
1397
+ givenname;lang-en: Rodney
1398
+ sn;lang-en: Ogasawara
1399
+ cn;lang-en: Rodney Ogasawara
1400
+ title;lang-en: Sales, Director
1401
+ EOL
1402
+
1403
+ assert_ldif_to_s(<<-EOL, ldif_source)
1404
+ version: 1
1405
+ dn:: b3U95Za25qWt6YOoLG89QWlyaXVz
1406
+ description: Japanese office
1407
+ objectclass: organizationalUnit
1408
+ objectclass: top
1409
+ ou:: 5Za25qWt6YOo
1410
+ ou;lang-en: Sales
1411
+ ou;lang-ja:: 5Za25qWt6YOo
1412
+ ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2
1413
+
1414
+ dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz
1415
+ cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1416
+ cn;lang-en: Rodney Ogasawara
1417
+ cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA==
1418
+ cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA==
1419
+ givenname:: 44Ot44OJ44OL44O8
1420
+ givenname;lang-en: Rodney
1421
+ givenname;lang-ja:: 44Ot44OJ44OL44O8
1422
+ givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8
1423
+ mail: rogasawara@airius.co.jp
1424
+ objectclass: inetOrgPerson
1425
+ objectclass: organizationalPerson
1426
+ objectclass: person
1427
+ objectclass: top
1428
+ preferredlanguage: ja
1429
+ sn:: 5bCP56yg5Y6f
1430
+ sn;lang-en: Ogasawara
1431
+ sn;lang-ja:: 5bCP56yg5Y6f
1432
+ sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ
1433
+ title:: 5Za25qWt6YOoIOmDqOmVtw==
1434
+ title;lang-en: Sales, Director
1435
+ title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw==
1436
+ title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg==
1437
+ uid: rogasawara
1438
+ userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM=
1439
+ EOL
1440
+ end
1441
+
1442
+ def test_an_record_with_base64_encoded_value
1443
+ ldif_source = <<-EOL
1444
+ version: 1
1445
+ dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com
1446
+ objectclass: top
1447
+ objectclass: person
1448
+ objectclass: organizationalPerson
1449
+ cn: Gern Jensen
1450
+ cn: Gern O Jensen
1451
+ sn: Jensen
1452
+ uid: gernj
1453
+ telephonenumber: +1 408 555 1212
1454
+ description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
1455
+ IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
1456
+ VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
1457
+ b3V0IG1vcmUu
1458
+ EOL
1459
+
1460
+ record = {
1461
+ "dn" => "cn=Gern Jensen,ou=Product Testing,dc=airius,dc=com",
1462
+ "objectclass" => ["top", "person", "organizationalPerson"],
1463
+ "cn" => ["Gern Jensen", "Gern O Jensen"],
1464
+ "sn" => ["Jensen"],
1465
+ "uid" => ["gernj"],
1466
+ "telephonenumber" => ["+1 408 555 1212"],
1467
+ "description" => ["What a careful reader you are! " +
1468
+ "This value is base-64-encoded because it has a " +
1469
+ "control character in it (a CR).\r By the way, " +
1470
+ "you should really get out more."],
1471
+ }
1472
+ assert_ldif(1, [record], ldif_source)
1473
+ end
1474
+
1475
+ def test_an_record_with_base64_encoded_value_to_s
1476
+ ldif_source = <<-EOL
1477
+ version: 1
1478
+ dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com
1479
+ objectclass: top
1480
+ objectclass: person
1481
+ objectclass: organizationalPerson
1482
+ cn: Gern Jensen
1483
+ cn: Gern O Jensen
1484
+ sn: Jensen
1485
+ uid: gernj
1486
+ telephonenumber: +1 408 555 1212
1487
+ description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVl
1488
+ IGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdG
1489
+ VyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQg
1490
+ b3V0IG1vcmUu
1491
+ EOL
1492
+
1493
+ assert_ldif_to_s(<<-EOL, ldif_source)
1494
+ version: 1
1495
+ dn: cn=Gern Jensen,ou=Product Testing,dc=airius,dc=com
1496
+ cn: Gern Jensen
1497
+ cn: Gern O Jensen
1498
+ description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVlIGlzI
1499
+ GJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdGVyIGluIGl
1500
+ 0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQgb3V0IG1vcmUu
1501
+ objectclass: organizationalPerson
1502
+ objectclass: person
1503
+ objectclass: top
1504
+ sn: Jensen
1505
+ telephonenumber: +1 408 555 1212
1506
+ uid: gernj
1507
+ EOL
1508
+ end
1509
+
1510
+ def test_an_record_with_folded_attribute_value
1511
+ ldif_source = <<-EOL
1512
+ version: 1
1513
+ dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1514
+ objectclass:top
1515
+ objectclass:person
1516
+ objectclass:organizationalPerson
1517
+ cn:Barbara Jensen
1518
+ cn:Barbara J Jensen
1519
+ cn:Babs Jensen
1520
+ sn:Jensen
1521
+ uid:bjensen
1522
+ telephonenumber:+1 408 555 1212
1523
+ description:Babs is a big sailing fan, and travels extensively in sea
1524
+ rch of perfect sailing conditions.
1525
+ title:Product Manager, Rod and Reel Division
1526
+ EOL
1527
+
1528
+ record = {
1529
+ "dn" => "cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com",
1530
+ "objectclass" => ["top", "person", "organizationalPerson"],
1531
+ "cn" => ["Barbara Jensen", "Barbara J Jensen", "Babs Jensen"],
1532
+ "sn" => ["Jensen"],
1533
+ "uid" => ["bjensen"],
1534
+ "telephonenumber" => ["+1 408 555 1212"],
1535
+ "description" => ["Babs is a big sailing fan, and travels extensively " +
1536
+ "in search of perfect sailing conditions."],
1537
+ "title" => ["Product Manager, Rod and Reel Division"],
1538
+ }
1539
+ assert_ldif(1, [record], ldif_source)
1540
+ end
1541
+
1542
+ def test_an_record_with_folded_attribute_value_to_s
1543
+ ldif_source = <<-EOL
1544
+ version: 1
1545
+ dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1546
+ objectclass:top
1547
+ objectclass:person
1548
+ objectclass:organizationalPerson
1549
+ cn:Barbara Jensen
1550
+ cn:Barbara J Jensen
1551
+ cn:Babs Jensen
1552
+ sn:Jensen
1553
+ uid:bjensen
1554
+ telephonenumber:+1 408 555 1212
1555
+ description:Babs is a big sailing fan, and travels extensively in sea
1556
+ rch of perfect sailing conditions.
1557
+ title:Product Manager, Rod and Reel Division
1558
+ EOL
1559
+
1560
+ assert_ldif_to_s(<<-EOL, ldif_source)
1561
+ version: 1
1562
+ dn: cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com
1563
+ cn: Babs Jensen
1564
+ cn: Barbara J Jensen
1565
+ cn: Barbara Jensen
1566
+ description: Babs is a big sailing fan, and travels extensively in search o
1567
+ f perfect sailing conditions.
1568
+ objectclass: organizationalPerson
1569
+ objectclass: person
1570
+ objectclass: top
1571
+ sn: Jensen
1572
+ telephonenumber: +1 408 555 1212
1573
+ title: Product Manager, Rod and Reel Division
1574
+ uid: bjensen
1575
+ EOL
1576
+ end
1577
+
1578
+ def test_records
1579
+ ldif_source = <<-EOL
1580
+ version: 1
1581
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1582
+ objectclass: top
1583
+ objectclass: person
1584
+ objectclass: organizationalPerson
1585
+ cn: Barbara Jensen
1586
+ cn: Barbara J Jensen
1587
+ cn: Babs Jensen
1588
+ sn: Jensen
1589
+ uid: bjensen
1590
+ telephonenumber: +1 408 555 1212
1591
+ description: A big sailing fan.
1592
+
1593
+ dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com
1594
+ objectclass: top
1595
+ objectclass: person
1596
+ objectclass: organizationalPerson
1597
+ cn: Bjorn Jensen
1598
+ sn: Jensen
1599
+ telephonenumber: +1 408 555 1212
1600
+ EOL
1601
+
1602
+ record1 = {
1603
+ "dn" => "cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com",
1604
+ "objectclass" => ["top", "person", "organizationalPerson"],
1605
+ "cn" => ["Barbara Jensen", "Barbara J Jensen", "Babs Jensen"],
1606
+ "sn" => ["Jensen"],
1607
+ "uid" => ["bjensen"],
1608
+ "telephonenumber" => ["+1 408 555 1212"],
1609
+ "description" => ["A big sailing fan."],
1610
+ }
1611
+ record2 = {
1612
+ "dn" => "cn=Bjorn Jensen,ou=Accounting,dc=airius,dc=com",
1613
+ "objectclass" => ["top", "person", "organizationalPerson"],
1614
+ "cn" => ["Bjorn Jensen"],
1615
+ "sn" => ["Jensen"],
1616
+ "telephonenumber" => ["+1 408 555 1212"],
1617
+ }
1618
+ assert_ldif(1, [record1, record2], ldif_source)
1619
+ end
1620
+
1621
+ def test_records_to_s
1622
+ ldif_source = <<-EOL
1623
+ version: 1
1624
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1625
+ objectclass: top
1626
+ objectclass: person
1627
+ objectclass: organizationalPerson
1628
+ cn: Barbara Jensen
1629
+ cn: Barbara J Jensen
1630
+ cn: Babs Jensen
1631
+ sn: Jensen
1632
+ uid: bjensen
1633
+ telephonenumber: +1 408 555 1212
1634
+ description: A big sailing fan.
1635
+
1636
+ dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com
1637
+ objectclass: top
1638
+ objectclass: person
1639
+ objectclass: organizationalPerson
1640
+ cn: Bjorn Jensen
1641
+ sn: Jensen
1642
+ telephonenumber: +1 408 555 1212
1643
+ EOL
1644
+
1645
+ assert_ldif_to_s(<<-EOL, ldif_source)
1646
+ version: 1
1647
+ dn: cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com
1648
+ cn: Babs Jensen
1649
+ cn: Barbara J Jensen
1650
+ cn: Barbara Jensen
1651
+ description: A big sailing fan.
1652
+ objectclass: organizationalPerson
1653
+ objectclass: person
1654
+ objectclass: top
1655
+ sn: Jensen
1656
+ telephonenumber: +1 408 555 1212
1657
+ uid: bjensen
1658
+
1659
+ dn: cn=Bjorn Jensen,ou=Accounting,dc=airius,dc=com
1660
+ cn: Bjorn Jensen
1661
+ objectclass: organizationalPerson
1662
+ objectclass: person
1663
+ objectclass: top
1664
+ sn: Jensen
1665
+ telephonenumber: +1 408 555 1212
1666
+ EOL
1667
+ end
1668
+
1669
+ def test_an_record
1670
+ ldif_source = <<-EOL
1671
+ version: 1
1672
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1673
+ objectclass: top
1674
+ objectclass: person
1675
+ objectclass: organizationalPerson
1676
+ cn: Barbara Jensen
1677
+ cn: Barbara J Jensen
1678
+ cn: Babs Jensen
1679
+ sn: Jensen
1680
+ uid: bjensen
1681
+ telephonenumber: +1 408 555 1212
1682
+ description: A big sailing fan.
1683
+ EOL
1684
+
1685
+ record = {
1686
+ "dn" => "cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com",
1687
+ "objectclass" => ["top", "person", "organizationalPerson"],
1688
+ "cn" => ["Barbara Jensen", "Barbara J Jensen", "Babs Jensen"],
1689
+ "sn" => ["Jensen"],
1690
+ "uid" => ["bjensen"],
1691
+ "telephonenumber" => ["+1 408 555 1212"],
1692
+ "description" => ["A big sailing fan."],
1693
+ }
1694
+ assert_ldif(1, [record], ldif_source)
1695
+ end
1696
+
1697
+ def test_an_record_to_s
1698
+ ldif_source = <<-EOL
1699
+ version: 1
1700
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1701
+ objectclass: top
1702
+ objectclass: person
1703
+ objectclass: organizationalPerson
1704
+ cn: Barbara Jensen
1705
+ cn: Barbara J Jensen
1706
+ cn: Babs Jensen
1707
+ sn: Jensen
1708
+ uid: bjensen
1709
+ telephonenumber: +1 408 555 1212
1710
+ description: A big sailing fan.
1711
+ EOL
1712
+
1713
+ assert_ldif_to_s(<<-EOL, ldif_source)
1714
+ version: 1
1715
+ dn: cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com
1716
+ cn: Babs Jensen
1717
+ cn: Barbara J Jensen
1718
+ cn: Barbara Jensen
1719
+ description: A big sailing fan.
1720
+ objectclass: organizationalPerson
1721
+ objectclass: person
1722
+ objectclass: top
1723
+ sn: Jensen
1724
+ telephonenumber: +1 408 555 1212
1725
+ uid: bjensen
1726
+ EOL
1727
+ end
1728
+
1729
+ def test_comment
1730
+ ldif_source = <<-EOL
1731
+ version: 1
1732
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1733
+ objectclass: top
1734
+ # objectclass: person
1735
+ #objectcl
1736
+ ass: organizationalPerson
1737
+ EOL
1738
+
1739
+ record = {
1740
+ "dn" => "cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com",
1741
+ "objectclass" => ["top"],
1742
+ }
1743
+ assert_ldif(1, [record], ldif_source)
1744
+ end
1745
+
1746
+ def test_comment_to_s
1747
+ ldif_source = <<-EOL
1748
+ version: 1
1749
+ dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com
1750
+ objectclass: top
1751
+ # objectclass: person
1752
+ #objectcl
1753
+ ass: organizationalPerson
1754
+ EOL
1755
+
1756
+ assert_ldif_to_s(<<-EOL, ldif_source)
1757
+ version: 1
1758
+ dn: cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com
1759
+ objectclass: top
1760
+ EOL
1761
+ end
1762
+
1763
+ def test_dn_spec
1764
+ assert_invalid_ldif("'dn:' is missing",
1765
+ "version: 1\n", 2, 1, "version: 1\n|@|")
1766
+ assert_invalid_ldif("DN is missing",
1767
+ "version: 1\ndn:", 2, 4, "dn:|@|")
1768
+ assert_invalid_ldif("DN is missing",
1769
+ "version: 1\ndn::", 2, 5, "dn::|@|")
1770
+ assert_invalid_ldif("DN is missing",
1771
+ "version: 1\ndn:\n", 2, 4, "dn:|@|\n")
1772
+ assert_invalid_ldif("DN is missing",
1773
+ "version: 1\ndn: \n", 2, 5, "dn: |@|\n")
1774
+
1775
+ dn = "cn=Barbara Jensen,ou=Product Development,dc=example,dc=com"
1776
+ cn = "Barbara Jensen"
1777
+ assert_valid_dn(dn, "version: 1\ndn: #{dn}\ncn:#{cn}\n")
1778
+
1779
+ encoded_dn = Base64.encode64(dn).gsub(/\n/, "\n ")
1780
+ encoded_cn = Base64.encode64(cn).gsub(/\n/, "\n ")
1781
+ assert_valid_dn(dn, "version: 1\ndn:: #{encoded_dn}\ncn::#{encoded_cn}\n")
10
1782
  end
11
1783
 
12
- priority :must
13
1784
  def test_version_number
14
- assert_invalid_ldif("unsupported version: 0", "version: 0")
15
- assert_invalid_ldif("unsupported version: 0", "version: 0")
1785
+ assert_valid_version(1, "version: 1\ndn: dc=com\ndc: com")
1786
+ assert_valid_version(1, "version: 1\r\ndn: dc=com\ndc: com\n")
1787
+ assert_valid_version(1, "version: 1\r\n\n\r\n\ndn: dc=com\ndc: com\n")
1788
+
1789
+ assert_invalid_ldif(["unsupported version: %d", 0],
1790
+ "version: 0", 1, 11, "version: 0|@|")
1791
+ assert_invalid_ldif(["unsupported version: %d", 2],
1792
+ "version: 2", 1, 11, "version: 2|@|")
1793
+
1794
+ assert_invalid_ldif("separator is missing",
1795
+ "version: 1", 1, 11, "version: 1|@|")
16
1796
  end
17
1797
 
18
1798
  def test_version_spec
19
- assert_invalid_ldif("version spec is missing", "")
20
- assert_invalid_ldif("version spec is missing", "version:")
21
- assert_invalid_ldif("version spec is missing", "version: ")
22
- assert_invalid_ldif("version spec is missing", "version: XXX")
1799
+ assert_invalid_ldif("version spec is missing",
1800
+ "", 1, 1, "|@|")
1801
+ assert_invalid_ldif("version spec is missing",
1802
+ "VERSION: 1", 1, 1, "|@|VERSION: 1")
1803
+ assert_invalid_ldif("version number is missing",
1804
+ "version:", 1, 9, "version:|@|")
1805
+ assert_invalid_ldif("version number is missing",
1806
+ "version: ", 1, 10, "version: |@|")
1807
+ assert_invalid_ldif("version number is missing",
1808
+ "version: XXX", 1, 10, "version: |@|XXX")
23
1809
  end
24
1810
 
25
- priority :normal
26
-
27
1811
  private
28
- def assert_invalid_ldif(reason, ldif)
1812
+ def assert_ldif(version, records, ldif_source)
1813
+ ldif = ActiveLdap::Ldif.parse(ldif_source)
1814
+ assert_equal(version, ldif.version)
1815
+ assert_equal(records,
1816
+ ldif.records.collect {|record| record.to_hash})
1817
+
1818
+ reparsed_ldif = ActiveLdap::Ldif.parse(ldif.to_s)
1819
+ assert_equal(ldif, reparsed_ldif)
1820
+
1821
+ ldif
1822
+ end
1823
+
1824
+ def assert_valid_dn(dn, ldif_source)
1825
+ ldif = ActiveLdap::Ldif.parse(ldif_source)
1826
+ assert_equal([dn], ldif.records.collect {|record| record.dn})
1827
+ end
1828
+
1829
+ def assert_valid_version(version, ldif_source)
1830
+ ldif = ActiveLdap::Ldif.parse(ldif_source)
1831
+ assert_equal(version, ldif.version)
1832
+ end
1833
+
1834
+ def assert_invalid_ldif(reason, ldif, line, column, nearest)
29
1835
  exception = assert_raise(ActiveLdap::LdifInvalid) do
30
1836
  ActiveLdap::Ldif.parse(ldif)
31
1837
  end
32
- assert_equal(ldif, exception.ldif)
33
- assert_equal(_(reason), exception.reason)
1838
+ reason, *params = reason
1839
+ params = params.collect {|param| param.is_a?(String) ? _(param) : param}
1840
+ assert_equal([_(reason) % params, line, column, nearest, ldif],
1841
+ [exception.reason, exception.line, exception.column,
1842
+ exception.nearest, exception.ldif])
1843
+ end
1844
+
1845
+ def assert_ldif_to_s(expected_ldif_source, original_ldif_source)
1846
+ ldif = ActiveLdap::Ldif.parse(original_ldif_source)
1847
+ assert_equal(expected_ldif_source, ldif.to_s)
34
1848
  end
35
1849
  end