1# -*- coding: utf-8 -*- 2""" 3Automatic tests for module ldap0.ldapurl 4""" 5 6import unittest 7from urllib.request import quote as url_quote 8 9import ldap0 10import ldap0.ldapurl 11from ldap0.ldapurl import LDAPUrl 12 13 14class TestIsLDAPUrl(unittest.TestCase): 15 16 is_ldapurl_tests = { 17 # Examples from RFC2255 18 'ldap:///o=University%20of%20Michigan,c=US':1, 19 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US':1, 20 'ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,':1, 21 'ldap://host.com:6666/o=University%20of%20Michigan,':1, 22 'ldap://ldap.itd.umich.edu/c=GB?objectClass?one':1, 23 'ldap://ldap.question.com/o=Question%3f,c=US?mail':1, 24 'ldap://ldap.netscape.com/o=Babsco,c=US??(int=%5c00%5c00%5c00%5c04)':1, 25 'ldap:///??sub??bindname=cn=Manager%2co=Foo':1, 26 'ldap:///??sub??!bindname=cn=Manager%2co=Foo':1, 27 # More examples from various sources 28 'ldap://ldap.nameflow.net:1389/c%3dDE':1, 29 'ldap://root.openldap.org/dc=openldap,dc=org':1, 30 'ldap://root.openldap.org/dc=openldap,dc=org':1, 31 'ldap://x500.mh.se/o=Mitthogskolan,c=se????1.2.752.58.10.2=T.61':1, 32 'ldp://root.openldap.org/dc=openldap,dc=org':0, 33 'ldap://localhost:1389/ou%3DUnstructured%20testing%20tree%2Cdc%3Dexample%2Cdc%3Dcom??one':1, 34 'ldaps://ldap.example.com/c%3dDE':1, 35 'ldapi:///dc=example,dc=com????x-saslmech=EXTERNAL':1, 36 } 37 38 def test_is_ldapurl(self): 39 for ldap_url, expected in self.is_ldapurl_tests.items(): 40 result = ldap0.ldapurl.is_ldapurl(ldap_url) 41 self.assertEqual( 42 result, expected, 43 'is_ldapurl("%s") returns %d instead of %d.' % ( 44 ldap_url, result, expected, 45 ) 46 ) 47 48 49class TestParseLDAPUrl(unittest.TestCase): 50 51 parse_ldap_url_tests = [ 52 ( 53 'ldap://root.openldap.org/dc=openldap,dc=org', 54 LDAPUrl( 55 hostport='root.openldap.org', 56 dn='dc=openldap,dc=org' 57 ) 58 ), 59 ( 60 'ldap://root.openldap.org/dc%3dboolean%2cdc%3dnet???%28objectClass%3d%2a%29', 61 LDAPUrl( 62 hostport='root.openldap.org', 63 dn='dc=boolean,dc=net', 64 filterstr='(objectClass=*)' 65 ) 66 ), 67 ( 68 'ldap://root.openldap.org/dc=openldap,dc=org??sub?', 69 LDAPUrl( 70 hostport='root.openldap.org', 71 dn='dc=openldap,dc=org', 72 scope=ldap0.SCOPE_SUBTREE 73 ) 74 ), 75 ( 76 'ldap://root.openldap.org/dc=openldap,dc=org??one?', 77 LDAPUrl( 78 hostport='root.openldap.org', 79 dn='dc=openldap,dc=org', 80 scope=ldap0.SCOPE_ONELEVEL 81 ) 82 ), 83 ( 84 'ldap://root.openldap.org/dc=openldap,dc=org??base?', 85 LDAPUrl( 86 hostport='root.openldap.org', 87 dn='dc=openldap,dc=org', 88 scope=ldap0.SCOPE_BASE 89 ) 90 ), 91 ( 92 'ldap://x500.mh.se/o=Mitthogskolan,c=se????1.2.752.58.10.2=T.61', 93 LDAPUrl( 94 hostport='x500.mh.se', 95 dn='o=Mitthogskolan,c=se', 96 extensions=ldap0.ldapurl.LDAPUrlExtensions({ 97 '1.2.752.58.10.2':ldap0.ldapurl.LDAPUrlExtension( 98 critical=0,extype='1.2.752.58.10.2',exvalue='T.61' 99 ) 100 }) 101 ) 102 ), 103 ( 104 'ldap://localhost:12345/dc=example,dc=com????!bindname=cn=Michael%2Cdc=example%2Cdc=com,!X-BINDPW=secretpassword', 105 LDAPUrl( 106 hostport='localhost:12345', 107 dn='dc=example,dc=com', 108 extensions=ldap0.ldapurl.LDAPUrlExtensions({ 109 'bindname':ldap0.ldapurl.LDAPUrlExtension( 110 critical=1,extype='bindname',exvalue='cn=Michael,dc=example,dc=com' 111 ), 112 'X-BINDPW':ldap0.ldapurl.LDAPUrlExtension( 113 critical=1,extype='X-BINDPW',exvalue='secretpassword' 114 ), 115 }), 116 ) 117 ), 118 ( 119 'ldap://localhost:54321/dc=example,dc=com????bindname=cn=Michael%2Cdc=example%2Cdc=com,X-BINDPW=secretpassword', 120 LDAPUrl( 121 hostport='localhost:54321', 122 dn='dc=example,dc=com', 123 who='cn=Michael,dc=example,dc=com', 124 cred='secretpassword' 125 ) 126 ), 127 ( 128 'ldap://localhost:54321/cn=Michael Ströder+mail=michael@example.com,o=Test????bindname=cn=Michael%20Ströder%2bmail=michael@example.com%2Co=Test,X-BINDPW=secretpassword', 129 LDAPUrl( 130 hostport='localhost:54321', 131 dn='cn=Michael Ströder+mail=michael@example.com,o=Test', 132 who='cn=Michael Ströder+mail=michael@example.com,o=Test', 133 cred='secretpassword' 134 ) 135 ), 136 ( 137 'ldaps://localhost:12345/dc=example,dc=com', 138 LDAPUrl( 139 urlscheme='ldaps', 140 hostport='localhost:12345', 141 dn='dc=example,dc=com', 142 ), 143 ), 144 ( 145 'ldapi://%2ftmp%2fopenldap2-1389/dc=example,dc=com', 146 LDAPUrl( 147 urlscheme='ldapi', 148 hostport='/tmp/openldap2-1389', 149 dn='dc=example,dc=com', 150 ), 151 ), 152 ] 153 154 def test_ldapurl(self): 155 for ldap_url_str,test_ldap_url_obj in self.parse_ldap_url_tests: 156 ldap_url_obj = LDAPUrl(ldapUrl=ldap_url_str) 157 self.assertEqual( 158 ldap_url_obj, test_ldap_url_obj, 159 'Attributes of LDAPUrl(%r) are:\n%r\ninstead of:\n%r' % ( 160 ldap_url_str, 161 ldap_url_obj, 162 test_ldap_url_obj, 163 ) 164 ) 165 unparsed_ldap_url_str = test_ldap_url_obj.unparse() 166 unparsed_ldap_url_obj = LDAPUrl(ldapUrl=unparsed_ldap_url_str) 167 self.assertEqual( 168 unparsed_ldap_url_obj, test_ldap_url_obj, 169 'Attributes of LDAPUrl(%r) are:\n%r\ninstead of:\n%r' % ( 170 unparsed_ldap_url_str, 171 unparsed_ldap_url_obj, 172 test_ldap_url_obj, 173 ) 174 ) 175 176 177class TestLDAPUrl(unittest.TestCase): 178 179 def test_combo(self): 180 u = LDAPUrl( 181 'ldap://127.0.0.1:1234/' 182 'dc=example,dc=com' 183 '?attr1,attr2,attr3' 184 '?sub' 185 '?%28objectClass%3D%2A%29' 186 '?' 187 'bindname=cn%3Dfred%2Cc%3Dau,' 188 'X-BINDPW=%3F%3F%3F,' 189 'trace=8,' 190 'x-saslmech=DIGEST-MD5,' 191 'x-saslrealm=example.com,' 192 'x-saslauthzid=u:anna,' 193 'x-starttls=2,' 194 ) 195 self.assertEqual(u.urlscheme, "ldap") 196 self.assertEqual(u.hostport, "127.0.0.1:1234") 197 self.assertEqual(u.dn, "dc=example,dc=com") 198 self.assertEqual(u.attrs, ["attr1","attr2","attr3"]) 199 self.assertEqual(u.scope, ldap0.SCOPE_SUBTREE) 200 self.assertEqual(u.filterstr, "(objectClass=*)") 201 self.assertEqual(len(u.extensions), 7) 202 self.assertEqual(u.who, "cn=fred,c=au") 203 self.assertEqual(u.cred, "???") 204 self.assertEqual(u.trace_level, "8") 205 self.assertEqual(u.sasl_mech, 'DIGEST-MD5') 206 self.assertEqual(u.sasl_realm, 'example.com') 207 self.assertEqual(u.sasl_authzid, 'u:anna') 208 self.assertEqual(u.start_tls, '2') 209 with self.assertRaises(AttributeError): 210 u.foo 211 212 def test_parse_default_hostport(self): 213 u = LDAPUrl("ldap://") 214 self.assertEqual(u.urlscheme, "ldap") 215 self.assertEqual(u.hostport, "") 216 217 def test_parse_empty_dn(self): 218 u = LDAPUrl("ldap://") 219 self.assertEqual(u.dn, "") 220 u = LDAPUrl("ldap:///") 221 self.assertEqual(u.dn, "") 222 u = LDAPUrl("ldap:///?") 223 self.assertEqual(u.dn, "") 224 225 def test_parse_default_attrs(self): 226 u = LDAPUrl("ldap://") 227 self.assertIsNone(u.attrs) 228 229 def test_parse_default_scope(self): 230 u = LDAPUrl("ldap://") 231 self.assertIsNone(u.scope) # RFC4516 s3 232 233 def test_parse_default_filter(self): 234 u = LDAPUrl("ldap://") 235 self.assertIsNone(u.filterstr) # RFC4516 s3 236 237 def test_parse_default_extensions(self): 238 u = LDAPUrl("ldap://") 239 self.assertEqual(len(u.extensions), 0) 240 241 def test_parse_schemes(self): 242 u = LDAPUrl("ldap://") 243 self.assertEqual(u.urlscheme, "ldap") 244 u = LDAPUrl("ldapi://") 245 self.assertEqual(u.urlscheme, "ldapi") 246 u = LDAPUrl("ldaps://") 247 self.assertEqual(u.urlscheme, "ldaps") 248 249 def test_parse_hostport(self): 250 u = LDAPUrl("ldap://a") 251 self.assertEqual(u.hostport, "a") 252 u = LDAPUrl("ldap://a.b") 253 self.assertEqual(u.hostport, "a.b") 254 u = LDAPUrl("ldap://a.") 255 self.assertEqual(u.hostport, "a.") 256 u = LDAPUrl("ldap://%61%62:%32/") 257 self.assertEqual(u.hostport, "ab:2") 258 u = LDAPUrl("ldap://[::1]/") 259 self.assertEqual(u.hostport, "[::1]") 260 u = LDAPUrl("ldap://[::1]") 261 self.assertEqual(u.hostport, "[::1]") 262 u = LDAPUrl("ldap://[::1]:123/") 263 self.assertEqual(u.hostport, "[::1]:123") 264 u = LDAPUrl("ldap://[::1]:123") 265 self.assertEqual(u.hostport, "[::1]:123") 266 267 def test_parse_dn(self): 268 u = LDAPUrl("ldap:///") 269 self.assertEqual(u.dn, "") 270 u = LDAPUrl("ldap:///dn=foo") 271 self.assertEqual(u.dn, "dn=foo") 272 u = LDAPUrl("ldap:///dn=foo%2cdc=bar") 273 self.assertEqual(u.dn, "dn=foo,dc=bar") 274 u = LDAPUrl("ldap:///dn=foo%20bar") 275 self.assertEqual(u.dn, "dn=foo bar") 276 u = LDAPUrl("ldap:///dn=foo%2fbar") 277 self.assertEqual(u.dn, "dn=foo/bar") 278 u = LDAPUrl("ldap:///dn=foo%2fbar?") 279 self.assertEqual(u.dn, "dn=foo/bar") 280 u = LDAPUrl("ldap:///dn=foo%3f?") 281 self.assertEqual(u.dn, "dn=foo?") 282 u = LDAPUrl("ldap:///dn=foo%3f") 283 self.assertEqual(u.dn, "dn=foo?") 284 u = LDAPUrl("ldap:///dn=str%c3%b6der.com") 285 self.assertEqual(u.dn, "dn=ströder.com") 286 287 def test_parse_attrs(self): 288 u = LDAPUrl("ldap:///?") 289 self.assertEqual(u.attrs, None) 290 u = LDAPUrl("ldap:///??") 291 self.assertEqual(u.attrs, None) 292 u = LDAPUrl("ldap:///?*?") 293 self.assertEqual(u.attrs, ['*']) 294 u = LDAPUrl("ldap:///?*,*?") 295 self.assertEqual(u.attrs, ['*','*']) 296 u = LDAPUrl("ldap:///?a") 297 self.assertEqual(u.attrs, ['a']) 298 u = LDAPUrl("ldap:///?%61") 299 self.assertEqual(u.attrs, ['a']) 300 u = LDAPUrl("ldap:///?a,b") 301 self.assertEqual(u.attrs, ['a','b']) 302 u = LDAPUrl("ldap:///?a%3fb") 303 self.assertEqual(u.attrs, ['a?b']) 304 305 def test_parse_scope_default(self): 306 u = LDAPUrl("ldap:///??") 307 self.assertIsNone(u.scope) # on opposite to RFC4516 s3 for referral chasing 308 u = LDAPUrl("ldap:///???") 309 self.assertIsNone(u.scope) # on opposite to RFC4516 s3 for referral chasing 310 311 def test_parse_scope(self): 312 u = LDAPUrl("ldap:///??sub") 313 self.assertEqual(u.scope, ldap0.SCOPE_SUBTREE) 314 u = LDAPUrl("ldap:///??sub?") 315 self.assertEqual(u.scope, ldap0.SCOPE_SUBTREE) 316 u = LDAPUrl("ldap:///??base") 317 self.assertEqual(u.scope, ldap0.SCOPE_BASE) 318 u = LDAPUrl("ldap:///??base?") 319 self.assertEqual(u.scope, ldap0.SCOPE_BASE) 320 u = LDAPUrl("ldap:///??one") 321 self.assertEqual(u.scope, ldap0.SCOPE_ONELEVEL) 322 u = LDAPUrl("ldap:///??one?") 323 self.assertEqual(u.scope, ldap0.SCOPE_ONELEVEL) 324 u = LDAPUrl("ldap:///??subordinates") 325 self.assertEqual(u.scope, ldap0.SCOPE_SUBORDINATE) 326 u = LDAPUrl("ldap:///??subordinates?") 327 self.assertEqual(u.scope, ldap0.SCOPE_SUBORDINATE) 328 329 def test_parse_filter(self): 330 u = LDAPUrl("ldap:///???(cn=Bob)") 331 self.assertEqual(u.filterstr, "(cn=Bob)") 332 u = LDAPUrl("ldap:///???(cn=Bob)?") 333 self.assertEqual(u.filterstr, "(cn=Bob)") 334 u = LDAPUrl("ldap:///???(cn=Bob%20Smith)?") 335 self.assertEqual(u.filterstr, "(cn=Bob Smith)") 336 u = LDAPUrl("ldap:///???(cn=Bob/Smith)?") 337 self.assertEqual(u.filterstr, "(cn=Bob/Smith)") 338 u = LDAPUrl("ldap:///???(cn=Bob:Smith)?") 339 self.assertEqual(u.filterstr, "(cn=Bob:Smith)") 340 u = LDAPUrl("ldap:///???&(cn=Bob)(objectClass=user)?") 341 self.assertEqual(u.filterstr, "&(cn=Bob)(objectClass=user)") 342 u = LDAPUrl("ldap:///???|(cn=Bob)(objectClass=user)?") 343 self.assertEqual(u.filterstr, "|(cn=Bob)(objectClass=user)") 344 u = LDAPUrl("ldap:///???(cn=Q%3f)?") 345 self.assertEqual(u.filterstr, "(cn=Q?)") 346 u = LDAPUrl("ldap:///???(cn=Q%3f)") 347 self.assertEqual(u.filterstr, "(cn=Q?)") 348 u = LDAPUrl("ldap:///???(sn=Str%c3%b6der)") # (possibly bad?) 349 self.assertEqual(u.filterstr, "(sn=Ströder)") 350 u = LDAPUrl("ldap:///???(sn=Str\\c3\\b6der)") 351 self.assertEqual(u.filterstr, "(sn=Str\\c3\\b6der)") # (recommended) 352 u = LDAPUrl("ldap:///???(cn=*\\2a*)") 353 self.assertEqual(u.filterstr, "(cn=*\\2a*)") 354 u = LDAPUrl("ldap:///???(cn=*%5c2a*)") 355 self.assertEqual(u.filterstr, "(cn=*\\2a*)") 356 357 def test_parse_extensions(self): 358 u = LDAPUrl("ldap:///????") 359 self.assertIsNone(u.extensions) 360 self.assertIsNone(u.who) 361 u = LDAPUrl("ldap:///????bindname=cn=root") 362 self.assertEqual(len(u.extensions), 1) 363 self.assertEqual(u.who, "cn=root") 364 u = LDAPUrl("ldap:///????!bindname=cn=root") 365 self.assertEqual(len(u.extensions), 1) 366 self.assertEqual(u.who, "cn=root") 367 u = LDAPUrl("ldap:///????bindname=%3f,X-BINDPW=%2c") 368 self.assertEqual(len(u.extensions), 2) 369 self.assertEqual(u.who, "?") 370 self.assertEqual(u.cred, ",") 371 372 def test_parse_extensions_nulls(self): 373 u = LDAPUrl("ldap:///????bindname=%00name") 374 self.assertEqual(u.who, "\0name") 375 376 def test_parse_extensions_5questions(self): 377 u = LDAPUrl("ldap:///????bindname=?") 378 self.assertEqual(len(u.extensions), 1) 379 self.assertEqual(u.who, "?") 380 381 def test_parse_extensions_novalue(self): 382 u = LDAPUrl("ldap:///????bindname") 383 self.assertEqual(len(u.extensions), 1) 384 self.assertIsNone(u.who) 385 386 def test_bad_urls1(self): 387 failed_urls = [] 388 for bad in ( 389 '', 390 'ldap:', 391 'ldap:/', 392 ':///', 393 '://', 394 '///', 395 '//', 396 '/', 397 'LDAP://', 398 'invalid://', 399 'ldap:///??invalid_scope', 400 r'ldap:///??%00', # RFC4516 2.1 401 ): 402 try: 403 LDAPUrl(bad) 404 except ValueError: 405 pass 406 else: 407 failed_urls.append(bad) 408 if failed_urls: 409 self.fail("These LDAP URLs should have raised ValueError: %r" % failed_urls) 410 411 @unittest.expectedFailure 412 def test_bad_urls2(self): 413 failed_urls = [] 414 for bad in ( 415 #XXX-- the following should raise exceptions! 416 'ldap:///?????', # extension can't start with '?' 417 'ldap://:389/', # [host [COLON port]] 418 'ldap://a:/', # [host [COLON port]] 419 r'ldap://%%%/', # invalid URL encoding 420 'ldap:///?,', # attrdesc *(COMMA attrdesc) 421 'ldap:///?a,', # attrdesc *(COMMA attrdesc) 422 'ldap:///?,a', # attrdesc *(COMMA attrdesc) 423 'ldap:///?a,,b', # attrdesc *(COMMA attrdesc) 424 r'ldap://%00/', # RFC4516 2.1 425 r'ldap:///%00', # RFC4516 2.1 426 r'ldap:///?%00', # RFC4516 2.1 427 'ldap:///????0=0', # extype must start with Alpha 428 'ldap:///????a_b=0', # extype contains only [-a-zA-Z0-9] 429 'ldap:///????!!a=0', # only one exclamation allowed 430 ): 431 try: 432 LDAPUrl(bad) 433 except ValueError: 434 pass 435 else: 436 failed_urls.append(bad) 437 if failed_urls: 438 self.fail("These LDAP URLs should have raised ValueError: %r" % failed_urls) 439 440 def test_connect_uri(self): 441 for url, uri in ( 442 ('ldap:///o=University%20of%20Michigan,c=US', 'ldap://'), 443 ('ldap://ldap.itd.umich.edu/o=University%20of%20Michigan,c=US', 'ldap://ldap.itd.umich.edu'), 444 ('ldap://host.com:6666/o=University%20of%20Michigan,', 'ldap://host.com:6666'), 445 ('ldap://ldap.nameflow.net:1389/c%3dDE', 'ldap://ldap.nameflow.net:1389'), 446 ('ldap://root.openldap.org/dc=openldap,dc=org', 'ldap://root.openldap.org'), 447 ('ldap://x500.mh.se/o=Mitthogskolan,c=se????1.2.752.58.10.2=T.61', 'ldap://x500.mh.se'), 448 ('ldap://localhost:1389/ou%3DUnstructured%20testing%20tree??one', 'ldap://localhost:1389'), 449 ('ldapi:///dc=example,dc=com????x-saslmech=EXTERNAL', 'ldapi://'), 450 ('ldapi://%2Fopt%2Fae-dir%2Frun%2Fslapd%2Fldapi/ou=ae-dir????bindname=aead', 'ldapi://%2Fopt%2Fae-dir%2Frun%2Fslapd%2Fldapi'), 451 ): 452 self.assertEqual(LDAPUrl(url).connect_uri(), uri) 453 454 455if __name__ == '__main__': 456 unittest.main() 457