Package ldaptor :: Package test :: Module test_ldifprotocol
[hide private]
[frames] | no frames]

Source Code for Module ldaptor.test.test_ldifprotocol

  1  """ 
  2  Test cases for ldaptor.protocols.ldap.ldif module. 
  3  """ 
  4   
  5  from twisted.trial import unittest 
  6  import sets 
  7  from ldaptor.protocols.ldap import ldifprotocol, distinguishedname 
  8   
9 -class LDIFDriver(ldifprotocol.LDIF):
10 - def __init__(self):
11 self.listOfCompleted = []
12 - def gotEntry(self, obj):
13 self.listOfCompleted.append(obj)
14
15 -class TestLDIFParsing(unittest.TestCase):
16 - def testFromLDIF(self):
17 proto = LDIFDriver() 18 for line in ( 19 20 "dn: cn=foo,dc=example,dc=com", 21 "objectClass: a", 22 "objectClass: b", 23 "aValue: a", 24 "aValue: b", 25 "bValue: c", 26 "", 27 28 "dn: cn=bar,dc=example,dc=com", 29 "objectClass: c", 30 "aValue:: IEZPTyE=", 31 "aValue: b", 32 "bValue: C", 33 "", 34 35 ): 36 proto.lineReceived(line) 37 38 self.failUnlessEqual(len(proto.listOfCompleted), 2) 39 40 o = proto.listOfCompleted.pop(0) 41 self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') 42 self.failUnlessEqual(o['objectClass'], ['a', 'b']) 43 self.failUnlessEqual(o['aValue'], ['a', 'b']) 44 self.failUnlessEqual(o['bValue'], ['c']) 45 46 o = proto.listOfCompleted.pop(0) 47 self.failUnlessEqual(str(o.dn), 'cn=bar,dc=example,dc=com') 48 self.failUnlessEqual(o['objectClass'], ['c']) 49 self.failUnlessEqual(o['aValue'], [' FOO!', 'b']) 50 self.failUnlessEqual(o['bValue'], ['C']) 51 52 self.failUnlessEqual(proto.listOfCompleted, [])
53
54 - def testSplitLines(self):
55 proto = LDIFDriver() 56 for line in ( 57 58 "dn: cn=foo,dc=ex", 59 " ample,dc=com", 60 "objectClass: a", 61 "ob", 62 " jectClass: b", 63 "", 64 65 ): 66 proto.lineReceived(line) 67 68 self.failUnlessEqual(len(proto.listOfCompleted), 1) 69 70 o = proto.listOfCompleted.pop(0) 71 self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') 72 self.failUnlessEqual(o['objectClass'], ['a', 'b']) 73 74 self.failUnlessEqual(proto.listOfCompleted, [])
75
77 proto = LDIFDriver() 78 proto.dataReceived("""\ 79 dn: cn=foo,dc=example,dc=com 80 objectClass: a 81 obJeCtClass: b 82 cn: foo 83 avalue: a 84 aValUe: b 85 86 """) 87 88 self.failUnlessEqual(len(proto.listOfCompleted), 1) 89 90 o = proto.listOfCompleted.pop(0) 91 self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') 92 self.failUnlessEqual(o['objectClass'], ['a', 'b']) 93 self.failUnlessEqual(o['CN'], ['foo']) 94 self.failUnlessEqual(o['aValue'], ['a', 'b']) 95 96 self.failUnlessEqual(proto.listOfCompleted, [])
97
98 - def testVersion1(self):
99 proto = LDIFDriver() 100 proto.dataReceived("""\ 101 version: 1 102 dn: cn=foo,dc=example,dc=com 103 objectClass: a 104 objectClass: b 105 aValue: a 106 aValue: b 107 bValue: c 108 109 """) 110 111 self.failUnlessEqual(len(proto.listOfCompleted), 1) 112 113 o = proto.listOfCompleted.pop(0) 114 self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') 115 self.failUnlessEqual(o['objectClass'], ['a', 'b']) 116 self.failUnlessEqual(o['aValue'], ['a', 'b']) 117 self.failUnlessEqual(o['bValue'], ['c'])
118
119 - def testVersionInvalid(self):
120 proto = LDIFDriver() 121 self.assertRaises(ldifprotocol.LDIFVersionNotANumberError, 122 proto.dataReceived, 123 """\ 124 version: junk 125 dn: cn=foo,dc=example,dc=com 126 objectClass: a 127 objectClass: b 128 aValue: a 129 aValue: b 130 bValue: c 131 132 """)
133
134 - def testVersion2(self):
135 proto = LDIFDriver() 136 self.assertRaises(ldifprotocol.LDIFUnsupportedVersionError, 137 proto.dataReceived, 138 """\ 139 version: 2 140 dn: cn=foo,dc=example,dc=com 141 objectClass: a 142 objectClass: b 143 aValue: a 144 aValue: b 145 bValue: c 146 147 """)
148
149 - def testNoSpaces(self):
150 proto = LDIFDriver() 151 proto.dataReceived("""\ 152 dn:cn=foo,dc=example,dc=com 153 objectClass:a 154 obJeCtClass:b 155 cn:foo 156 avalue:a 157 aValUe:b 158 159 """) 160 161 self.failUnlessEqual(len(proto.listOfCompleted), 1) 162 163 o = proto.listOfCompleted.pop(0) 164 self.failUnlessEqual(str(o.dn), 'cn=foo,dc=example,dc=com') 165 self.failUnlessEqual(o['objectClass'], ['a', 'b']) 166 self.failUnlessEqual(o['CN'], ['foo']) 167 self.failUnlessEqual(o['aValue'], ['a', 'b']) 168 169 self.failUnlessEqual(proto.listOfCompleted, [])
170
171 - def testTruncatedFailure(self):
172 proto = LDIFDriver() 173 proto.dataReceived("""\ 174 version: 1 175 dn: cn=foo,dc=example,dc=com 176 objectClass: a 177 objectClass: b 178 aValue: a 179 aValue: b 180 bValue: c 181 """) 182 183 self.failUnlessEqual(len(proto.listOfCompleted), 0) 184 185 self.assertRaises(ldifprotocol.LDIFTruncatedError, 186 proto.connectionLost)
187
188 -class RFC2849_Examples(unittest.TestCase):
189 examples = [ 190 ( """Example 1: An simple LDAP file with two entries""", 191 """\ 192 version: 1 193 dn: cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com 194 objectclass: top 195 objectclass: person 196 objectclass: organizationalPerson 197 cn: Barbara Jensen 198 cn: Barbara J Jensen 199 cn: Babs Jensen 200 sn: Jensen 201 uid: bjensen 202 telephonenumber: +1 408 555 1212 203 description: A big sailing fan. 204 205 dn: cn=Bjorn Jensen, ou=Accounting, dc=airius, dc=com 206 objectclass: top 207 objectclass: person 208 objectclass: organizationalPerson 209 cn: Bjorn Jensen 210 sn: Jensen 211 telephonenumber: +1 408 555 1212 212 213 """, 214 [ ( 'cn=Barbara Jensen,ou=Product Development,dc=airius,dc=com', 215 { 'objectClass': ['top', 'person', 'organizationalPerson'], 216 'cn': ['Barbara Jensen', 217 'Barbara J Jensen', 218 'Babs Jensen'], 219 'sn': ['Jensen'], 220 'uid': ['bjensen'], 221 'telephonenumber': ['+1 408 555 1212'], 222 'description': ['A big sailing fan.'], 223 }), 224 225 ( 'cn=Bjorn Jensen,ou=Accounting,dc=airius,dc=com', 226 { 'objectClass': ['top', 'person', 'organizationalPerson'], 227 'cn': ['Bjorn Jensen'], 228 'sn': ['Jensen'], 229 'telephonenumber': ['+1 408 555 1212'], 230 }), 231 ]), 232 233 ( """Example 2: A file containing an entry with a folded attribute value""", 234 """\ 235 version: 1 236 dn:cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com 237 objectclass:top 238 objectclass:person 239 objectclass:organizationalPerson 240 cn:Barbara Jensen 241 cn:Barbara J Jensen 242 cn:Babs Jensen 243 sn:Jensen 244 uid:bjensen 245 telephonenumber:+1 408 555 1212 246 description:Babs is a big sailing fan, and travels extensively in sea 247 rch of perfect sailing conditions. 248 title:Product Manager, Rod and Reel Division 249 250 """, 251 [ ( 'cn=Barbara Jensen, ou=Product Development, dc=airius, dc=com', 252 { 'objectclass': ['top', 'person', 'organizationalPerson'], 253 'cn': ['Barbara Jensen', 'Barbara J Jensen', 'Babs Jensen'], 254 'sn': ['Jensen'], 255 'uid': ['bjensen'], 256 'telephonenumber': ['+1 408 555 1212'], 257 'description': ['Babs is a big sailing fan, and travels extensively in search of perfect sailing conditions.'], 258 'title': ['Product Manager, Rod and Reel Division'], 259 }), 260 ]), 261 262 ( """Example 3: A file containing a base-64-encoded value""", 263 """\ 264 version: 1 265 dn: cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com 266 objectclass: top 267 objectclass: person 268 objectclass: organizationalPerson 269 cn: Gern Jensen 270 cn: Gern O Jensen 271 sn: Jensen 272 uid: gernj 273 telephonenumber: +1 408 555 1212 274 description:: V2hhdCBhIGNhcmVmdWwgcmVhZGVyIHlvdSBhcmUhICBUaGlzIHZhbHVlIGlzIGJhc2UtNjQtZW5jb2RlZCBiZWNhdXNlIGl0IGhhcyBhIGNvbnRyb2wgY2hhcmFjdGVyIGluIGl0IChhIENSKS4NICBCeSB0aGUgd2F5LCB5b3Ugc2hvdWxkIHJlYWxseSBnZXQgb3V0IG1vcmUu 275 276 """, 277 [ ( 'cn=Gern Jensen, ou=Product Testing, dc=airius, dc=com', 278 { 'objectclass': ['top', 'person', 'organizationalPerson'], 279 'cn': ['Gern Jensen', 'Gern O Jensen'], 280 'sn': ['Jensen'], 281 'uid': ['gernj'], 282 'telephonenumber': ['+1 408 555 1212'], 283 'description': ['What a careful reader you are! This value is base-64-encoded because it has a control character in it (a CR).\r By the way, you should really get out more.'], 284 }), 285 ]), 286 287 ] 288
289 - def testExamples(self):
290 for name, data, expected in self.examples: 291 proto = LDIFDriver() 292 proto.dataReceived(data) 293 294 self.failUnlessEqual(len(proto.listOfCompleted), len(expected)) 295 296 for dn, attr in expected: 297 o = proto.listOfCompleted.pop(0) 298 self.failUnlessEqual(o.dn, distinguishedname.DistinguishedName(dn)) 299 300 got = sets.Set([x.lower() for x in o.keys()]) 301 want = sets.Set([x.lower() for x in attr.keys()]) 302 self.failUnlessEqual(got, want) 303 304 for k, v in attr.items(): 305 self.failUnlessEqual(o[k], v) 306 307 self.failUnlessEqual(proto.listOfCompleted, [])
308 309 """ 310 TODO more tests from RFC2849: 311 312 Example 4: A file containing an entries with UTF-8-encoded attribute 313 values, including language tags. Comments indicate the contents 314 of UTF-8-encoded attributes and distinguished names. 315 316 version: 1 317 dn:: b3U95Za25qWt6YOoLG89QWlyaXVz 318 # dn:: ou=<JapaneseOU>,o=Airius 319 objectclass: top 320 objectclass: organizationalUnit 321 ou:: 5Za25qWt6YOo 322 # ou:: <JapaneseOU> 323 ou;lang-ja:: 5Za25qWt6YOo 324 # ou;lang-ja:: <JapaneseOU> 325 ou;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2 326 327 # ou;lang-ja:: <JapaneseOU_in_phonetic_representation> 328 ou;lang-en: Sales 329 description: Japanese office 330 331 dn:: dWlkPXJvZ2FzYXdhcmEsb3U95Za25qWt6YOoLG89QWlyaXVz 332 # dn:: uid=<uid>,ou=<JapaneseOU>,o=Airius 333 userpassword: {SHA}O3HSv1MusyL4kTjP+HKI5uxuNoM= 334 objectclass: top 335 objectclass: person 336 objectclass: organizationalPerson 337 objectclass: inetOrgPerson 338 uid: rogasawara 339 mail: rogasawara@airius.co.jp 340 givenname;lang-ja:: 44Ot44OJ44OL44O8 341 # givenname;lang-ja:: <JapaneseGivenname> 342 sn;lang-ja:: 5bCP56yg5Y6f 343 # sn;lang-ja:: <JapaneseSn> 344 cn;lang-ja:: 5bCP56yg5Y6fIOODreODieODi+ODvA== 345 # cn;lang-ja:: <JapaneseCn> 346 title;lang-ja:: 5Za25qWt6YOoIOmDqOmVtw== 347 # title;lang-ja:: <JapaneseTitle> 348 preferredlanguage: ja 349 givenname:: 44Ot44OJ44OL44O8 350 # givenname:: <JapaneseGivenname> 351 sn:: 5bCP56yg5Y6f 352 # sn:: <JapaneseSn> 353 cn:: 5bCP56yg5Y6fIOODreODieODi+ODvA== 354 # cn:: <JapaneseCn> 355 title:: 5Za25qWt6YOoIOmDqOmVtw== 356 # title:: <JapaneseTitle> 357 givenname;lang-ja;phonetic:: 44KN44Gp44Gr44O8 358 # givenname;lang-ja;phonetic:: 359 <JapaneseGivenname_in_phonetic_representation_kana> 360 sn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJ 361 # sn;lang-ja;phonetic:: <JapaneseSn_in_phonetic_representation_kana> 362 cn;lang-ja;phonetic:: 44GK44GM44GV44KP44KJIOOCjeOBqeOBq+ODvA== 363 # cn;lang-ja;phonetic:: <JapaneseCn_in_phonetic_representation_kana> 364 title;lang-ja;phonetic:: 44GI44GE44GO44KH44GG44G2IOOBtuOBoeOCh+OBhg== 365 # title;lang-ja;phonetic:: 366 # <JapaneseTitle_in_phonetic_representation_kana> 367 givenname;lang-en: Rodney 368 sn;lang-en: Ogasawara 369 cn;lang-en: Rodney Ogasawara 370 title;lang-en: Sales, Director 371 """ 372 373 """ 374 Example 5: A file containing a reference to an external file 375 376 version: 1 377 dn: cn=Horatio Jensen, ou=Product Testing, dc=airius, dc=com 378 objectclass: top 379 objectclass: person 380 objectclass: organizationalPerson 381 cn: Horatio Jensen 382 383 cn: Horatio N Jensen 384 sn: Jensen 385 uid: hjensen 386 telephonenumber: +1 408 555 1212 387 jpegphoto:< file:///usr/local/directory/photos/hjensen.jpg 388 """ 389 390 """ 391 Example 6: A file containing a series of change records and comments 392 393 version: 1 394 # Add a new entry 395 dn: cn=Fiona Jensen, ou=Marketing, dc=airius, dc=com 396 changetype: add 397 objectclass: top 398 objectclass: person 399 objectclass: organizationalPerson 400 cn: Fiona Jensen 401 sn: Jensen 402 uid: fiona 403 telephonenumber: +1 408 555 1212 404 jpegphoto:< file:///usr/local/directory/photos/fiona.jpg 405 406 # Delete an existing entry 407 dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com 408 changetype: delete 409 410 # Modify an entry's relative distinguished name 411 dn: cn=Paul Jensen, ou=Product Development, dc=airius, dc=com 412 changetype: modrdn 413 newrdn: cn=Paula Jensen 414 deleteoldrdn: 1 415 416 # Rename an entry and move all of its children to a new location in 417 # the directory tree (only implemented by LDAPv3 servers). 418 dn: ou=PD Accountants, ou=Product Development, dc=airius, dc=com 419 changetype: modrdn 420 newrdn: ou=Product Development Accountants 421 deleteoldrdn: 0 422 newsuperior: ou=Accounting, dc=airius, dc=com 423 424 # Modify an entry: add an additional value to the postaladdress 425 # attribute, completely delete the description attribute, replace 426 # the telephonenumber attribute with two values, and delete a specific 427 # value from the facsimiletelephonenumber attribute 428 dn: cn=Paula Jensen, ou=Product Development, dc=airius, dc=com 429 changetype: modify 430 add: postaladdress 431 postaladdress: 123 Anystreet $ Sunnyvale, CA $ 94086 432 - 433 delete: description 434 - 435 replace: telephonenumber 436 telephonenumber: +1 408 555 1234 437 telephonenumber: +1 408 555 5678 438 - 439 delete: facsimiletelephonenumber 440 facsimiletelephonenumber: +1 408 555 9876 441 - 442 443 # Modify an entry: replace the postaladdress attribute with an empty 444 # set of values (which will cause the attribute to be removed), and 445 # delete the entire description attribute. Note that the first will 446 # always succeed, while the second will only succeed if at least 447 # one value for the description attribute is present. 448 dn: cn=Ingrid Jensen, ou=Product Support, dc=airius, dc=com 449 changetype: modify 450 replace: postaladdress 451 - 452 delete: description 453 - 454 """ 455 456 """ 457 Example 7: An LDIF file containing a change record with a control 458 version: 1 459 # Delete an entry. The operation will attach the LDAPv3 460 # Tree Delete Control defined in [9]. The criticality 461 # field is "true" and the controlValue field is 462 # absent, as required by [9]. 463 dn: ou=Product Development, dc=airius, dc=com 464 control: 1.2.840.113556.1.4.805 true 465 changetype: delete 466 467 """ 468