1<?php 2 3require_once __DIR__ . '/TestBase.php'; 4 5/** 6 * Copyright 2010-2017 Horde LLC (http://www.horde.org/) 7 * 8 * @package Ldap 9 * @subpackage UnitTests 10 * @author Jan Schneider <jan@horde.org> 11 * @license http://www.gnu.org/licenses/lgpl-3.0.html LGPL-3.0 12 */ 13class Horde_Ldap_LdapTest extends Horde_Ldap_TestBase 14{ 15 public static function tearDownAfterClass() 16 { 17 if (!self::$ldapcfg) { 18 return; 19 } 20 21 $clean = array('cn=Horde_Ldap_TestEntry,', 22 'ou=Horde_Ldap_Test_subdelete,', 23 'ou=Horde_Ldap_Test_modify,', 24 'ou=Horde_Ldap_Test_search1,', 25 'ou=Horde_Ldap_Test_search2,', 26 'ou=Horde_Ldap_Test_exists,', 27 'ou=Horde_Ldap_Test_exists_2+l=somewhere,', 28 'ou=Horde_Ldap_Test_getEntry,', 29 'ou=Horde_Ldap_Test_move,', 30 'ou=Horde_Ldap_Test_pool,', 31 'ou=Horde_Ldap_Test_tgt,'); 32 try { 33 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 34 foreach ($clean as $dn) { 35 try { 36 $ldap->delete($dn . self::$ldapcfg['server']['basedn'], true); 37 } catch (Exception $e) { 38 } 39 } 40 } catch (Exception $e) { 41 } 42 } 43 44 /** 45 * Tests if the server can connect and bind correctly. 46 */ 47 public function testConnectAndPrivilegedBind() 48 { 49 // This connect is supposed to fail. 50 $lcfg = array( 51 'hostspec' => 'nonexistant.ldap.horde.org', 52 'timeout' => 1, 53 ); 54 try { 55 $ldap = new Horde_Ldap($lcfg); 56 $this->fail('Horde_Ldap_Exception expected.'); 57 } catch (Horde_Ldap_Exception $e) {} 58 59 // Failing with multiple hosts. 60 $lcfg = array( 61 'hostspec' => array( 62 'nonexistant1.ldap.horde.org', 63 'nonexistant2.ldap.horde.org' 64 ), 65 'timeout' => 1, 66 ); 67 try { 68 $ldap = new Horde_Ldap($lcfg); 69 $this->fail('Horde_Ldap_Exception expected.'); 70 } catch (Horde_Ldap_Exception $e) {} 71 72 // Simple working connect and privileged bind. 73 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 74 75 // Working connect and privileged bind with first host down. 76 $lcfg = array( 77 'hostspec' => array( 78 'nonexistant.ldap.horde.org', 79 self::$ldapcfg['server']['hostspec'] 80 ), 81 'port' => self::$ldapcfg['server']['port'], 82 'binddn' => self::$ldapcfg['server']['binddn'], 83 'bindpw' => self::$ldapcfg['server']['bindpw'], 84 'timeout' => 1, 85 ); 86 $ldap = new Horde_Ldap($lcfg); 87 } 88 89 /** 90 * Tests if the server can connect and bind anonymously, if supported. 91 */ 92 public function testConnectAndAnonymousBind() 93 { 94 if (!self::$ldapcfg['capability']['anonymous']) { 95 $this->markTestSkipped('Server does not support anonymous bind'); 96 } 97 98 // Simple working connect and anonymous bind. 99 $lcfg = array('hostspec' => self::$ldapcfg['server']['hostspec'], 100 'port' => self::$ldapcfg['server']['port']); 101 $ldap = new Horde_Ldap($lcfg); 102 } 103 104 /** 105 * Tests if the server can connect and bind, but not rebind with empty 106 * password. 107 * 108 * @expectedException Horde_Ldap_Exception 109 */ 110 public function testConnectAndEmptyRebind() 111 { 112 // Simple working connect and privileged bind. 113 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 114 $ldap->bind(self::$ldapcfg['server']['binddn'], ''); 115 } 116 117 /** 118 * Tests startTLS() if server supports it. 119 */ 120 public function testStartTLS() 121 { 122 if (!self::$ldapcfg['capability']['tls']) { 123 $this->markTestSkipped('Server does not support TLS'); 124 } 125 126 // Simple working connect and privileged bind. 127 $lcfg = array('starttls' => true) + self::$ldapcfg['server']; 128 $ldap = new Horde_Ldap($lcfg); 129 } 130 131 /** 132 * Test if adding and deleting a fresh entry works. 133 */ 134 public function testAdd() 135 { 136 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 137 138 // Adding a fresh entry. 139 $cn = 'Horde_Ldap_TestEntry'; 140 $dn = 'cn=' . $cn . ',' . self::$ldapcfg['server']['basedn']; 141 $fresh_entry = Horde_Ldap_Entry::createFresh( 142 $dn, 143 array('objectClass' => array('top', 'person'), 144 'cn' => $cn, 145 'sn' => 'TestEntry')); 146 $this->assertInstanceOf('Horde_Ldap_Entry', $fresh_entry); 147 $ldap->add($fresh_entry); 148 149 // Deleting this entry. 150 $ldap->delete($fresh_entry); 151 } 152 153 /** 154 * Basic deletion is tested in testAdd(), so here we just test if 155 * advanced deletion tasks work properly. 156 */ 157 public function testDelete() 158 { 159 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 160 161 // Some parameter checks. 162 try { 163 $ldap->delete(1234); 164 $this->fail('Horde_Ldap_Exception expected.'); 165 } catch (Horde_Ldap_Exception $e) {} 166 try { 167 $ldap->delete($ldap); 168 $this->fail('Horde_Ldap_Exception expected.'); 169 } catch (Horde_Ldap_Exception $e) {} 170 171 // In order to test subtree deletion, we need some little tree 172 // which we need to establish first. 173 $base = self::$ldapcfg['server']['basedn']; 174 $testdn = 'ou=Horde_Ldap_Test_subdelete,' . $base; 175 176 $ou = Horde_Ldap_Entry::createFresh( 177 $testdn, 178 array('objectClass' => array('top', 'organizationalUnit'), 179 'ou' => 'Horde_Ldap_Test_subdelete')); 180 $ou_1 = Horde_Ldap_Entry::createFresh( 181 'ou=test1,' . $testdn, 182 array('objectClass' => array('top', 'organizationalUnit'), 183 'ou' => 'test1')); 184 $ou_1_l1 = Horde_Ldap_Entry::createFresh( 185 'l=subtest,ou=test1,' . $testdn, 186 array('objectClass' => array('top', 'locality'), 187 'l' => 'test1')); 188 $ou_2 = Horde_Ldap_Entry::createFresh( 189 'ou=test2,' . $testdn, 190 array('objectClass' => array('top', 'organizationalUnit'), 191 'ou' => 'test2')); 192 $ou_3 = Horde_Ldap_Entry::createFresh( 193 'ou=test3,' . $testdn, 194 array('objectClass' => array('top', 'organizationalUnit'), 195 'ou' => 'test3')); 196 $ldap->add($ou); 197 $ldap->add($ou_1); 198 $ldap->add($ou_1_l1); 199 $ldap->add($ou_2); 200 $ldap->add($ou_3); 201 $this->assertTrue($ldap->exists($ou->dn())); 202 $this->assertTrue($ldap->exists($ou_1->dn())); 203 $this->assertTrue($ldap->exists($ou_1_l1->dn())); 204 $this->assertTrue($ldap->exists($ou_2->dn())); 205 $this->assertTrue($ldap->exists($ou_3->dn())); 206 // Tree established now. We can run some tests now :D 207 208 // Try to delete some non existent entry inside that subtree (fails). 209 try { 210 $ldap->delete('cn=not_existent,ou=test1,' . $testdn); 211 $this->fail('Horde_Ldap_Exception expected.'); 212 } catch (Horde_Ldap_Exception $e) { 213 $this->assertEquals('LDAP_NO_SUCH_OBJECT', Horde_Ldap::errorName($e->getCode())); 214 } 215 216 // Try to delete main test ou without recursive set (fails too). 217 try { 218 $ldap->delete($testdn); 219 $this->fail('Horde_Ldap_Exception expected.'); 220 } catch (Horde_Ldap_Exception $e) { 221 $this->assertEquals('LDAP_NOT_ALLOWED_ON_NONLEAF', Horde_Ldap::errorName($e->getCode())); 222 } 223 224 // Retry with subtree delete, this should work. 225 $ldap->delete($testdn, true); 226 227 // The DN is not allowed to exist anymore. 228 $this->assertFalse($ldap->exists($testdn)); 229 } 230 231 /** 232 * Test modify(). 233 */ 234 public function testModify() 235 { 236 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 237 238 // We need a test entry. 239 $local_entry = Horde_Ldap_Entry::createFresh( 240 'ou=Horde_Ldap_Test_modify,' . self::$ldapcfg['server']['basedn'], 241 array('objectClass' => array('top', 'organizationalUnit'), 242 'ou' => 'Horde_Ldap_Test_modify', 243 'street' => 'Beniroad', 244 'telephoneNumber' => array('1234', '5678'), 245 'postalcode' => '12345', 246 'postalAddress' => 'someAddress', 247 'st' => array('State 1', 'State 2'))); 248 $ldap->add($local_entry); 249 $this->assertTrue($ldap->exists($local_entry->dn())); 250 251 // Test invalid actions. 252 try { 253 $ldap->modify($local_entry, array('foo' => 'bar')); 254 $this->fail('Expected exception when passing invalid actions to modify().'); 255 } catch (Horde_Ldap_Exception $e) { 256 } 257 258 // Prepare some changes. 259 $changes = array( 260 'add' => array( 261 'businessCategory' => array('foocat', 'barcat'), 262 'description' => 'testval' 263 ), 264 'delete' => array('postalAddress'), 265 'replace' => array('telephoneNumber' => array('345', '567')), 266 'changes' => array( 267 'replace' => array('street' => 'Highway to Hell'), 268 'add' => array('l' => 'someLocality'), 269 'delete' => array( 270 'postalcode', 271 'st' => array('State 1')))); 272 273 // Perform those changes. 274 $ldap->modify($local_entry, $changes); 275 276 // Verify correct attribute changes. 277 $actual_entry = $ldap->getEntry($local_entry->dn(), 278 array('objectClass', 'ou', 279 'postalAddress', 'street', 280 'telephoneNumber', 'postalcode', 281 'st', 'l', 'businessCategory', 282 'description')); 283 $this->assertInstanceOf('Horde_Ldap_Entry', $actual_entry); 284 $expected_attributes = array( 285 'objectClass' => array('top', 'organizationalUnit'), 286 'ou' => 'Horde_Ldap_Test_modify', 287 'street' => 'Highway to Hell', 288 'l' => 'someLocality', 289 'telephoneNumber' => array('345', '567'), 290 'businessCategory' => array('foocat', 'barcat'), 291 'description' => 'testval', 292 'st' => 'State 2' 293 ); 294 295 $local_attributes = $local_entry->getValues(); 296 $actual_attributes = $actual_entry->getValues(); 297 298 // To enable easy check, we need to sort the values of the remaining 299 // multival attributes as well as the attribute names. 300 ksort($expected_attributes); 301 ksort($local_attributes); 302 ksort($actual_attributes); 303 sort($expected_attributes['businessCategory']); 304 sort($local_attributes['businessCategory']); 305 sort($actual_attributes['businessCategory']); 306 307 // The attributes must match the expected values. Both, the entry 308 // inside the directory and our local copy must reflect the same 309 // values. 310 $this->assertEquals($expected_attributes, $actual_attributes, 'The directory entries attributes are not OK!'); 311 $this->assertEquals($expected_attributes, $local_attributes, 'The local entries attributes are not OK!'); 312 } 313 314 /** 315 * Test search(). 316 */ 317 public function testSearch() 318 { 319 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 320 321 // Some testdata, so we can test sizelimit. 322 $base = self::$ldapcfg['server']['basedn']; 323 $ou1 = Horde_Ldap_Entry::createFresh( 324 'ou=Horde_Ldap_Test_search1,' . $base, 325 array('objectClass' => array('top','organizationalUnit'), 326 'ou' => 'Horde_Ldap_Test_search1')); 327 $ou1_1 = Horde_Ldap_Entry::createFresh( 328 'ou=Horde_Ldap_Test_search1_1,' . $ou1->dn(), 329 array('objectClass' => array('top','organizationalUnit'), 330 'ou' => 'Horde_Ldap_Test_search1_1')); 331 $ou2 = Horde_Ldap_Entry::createFresh( 332 'ou=Horde_Ldap_Test_search2,' . $base, 333 array('objectClass' => array('top','organizationalUnit'), 334 'ou' => 'Horde_Ldap_Test_search2')); 335 $ldap->add($ou1); 336 $this->assertTrue($ldap->exists($ou1->dn())); 337 $ldap->add($ou1_1); 338 $this->assertTrue($ldap->exists($ou1_1->dn())); 339 $ldap->add($ou2); 340 $this->assertTrue($ldap->exists($ou2->dn())); 341 342 343 // Search for test filter, should at least return our two test entries. 344 $res = $ldap->search(null, '(ou=Horde_Ldap*)', 345 array('attributes' => '1.1')); 346 $this->assertInstanceOf('Horde_Ldap_Search', $res); 347 $this->assertThat($res->count(), $this->greaterThanOrEqual(2)); 348 349 // Same, but with Horde_Ldap_Filter object. 350 $filtero = Horde_Ldap_Filter::create('ou', 'begins', 'Horde_Ldap'); 351 $this->assertInstanceOf('Horde_Ldap_Filter', $filtero); 352 $res = $ldap->search(null, $filtero, 353 array('attributes' => '1.1')); 354 $this->assertInstanceOf('Horde_Ldap_Search', $res); 355 $this->assertThat($res->count(), $this->greaterThanOrEqual(2)); 356 357 // Search using default filter for base-onelevel scope, should at least 358 // return our two test entries. 359 $res = $ldap->search(null, null, 360 array('scope' => 'one', 'attributes' => '1.1')); 361 $this->assertInstanceOf('Horde_Ldap_Search', $res); 362 $this->assertThat($res->count(), $this->greaterThanOrEqual(2)); 363 364 // Base-search using custom base (string), should only return the test 365 // entry $ou1 and not the entry below it. 366 $res = $ldap->search($ou1->dn(), null, 367 array('scope' => 'base', 'attributes' => '1.1')); 368 $this->assertInstanceOf('Horde_Ldap_Search', $res); 369 $this->assertEquals(1, $res->count()); 370 371 // Search using custom base, this time using an entry object. This 372 // tests if passing an entry object as base works, should only return 373 // the test entry $ou1. 374 $res = $ldap->search($ou1, '(ou=*)', 375 array('scope' => 'base', 'attributes' => '1.1')); 376 $this->assertInstanceOf('Horde_Ldap_Search', $res); 377 $this->assertEquals(1, $res->count()); 378 379 // Search using default filter for base-onelevel scope with sizelimit, 380 // should of course return more than one entry, but not more than 381 // sizelimit 382 $res = $ldap->search( 383 null, null, 384 array('scope' => 'one', 'sizelimit' => 1, 'attributes' => '1.1') 385 ); 386 $this->assertInstanceOf('Horde_Ldap_Search', $res); 387 $this->assertEquals(1, $res->count()); 388 // Sizelimit should be exceeded now. 389 $this->assertTrue($res->sizeLimitExceeded()); 390 391 // Bad filter. 392 try { 393 $res = $ldap->search(null, 'somebadfilter', 394 array('attributes' => '1.1')); 395 $this->fail('Horde_Ldap_Exception expected.'); 396 } catch (Horde_Ldap_Exception $e) {} 397 398 // Bad base. 399 try { 400 $res = $ldap->search('badbase', null, 401 array('attributes' => '1.1')); 402 $this->fail('Horde_Ldap_Exception expected.'); 403 } catch (Horde_Ldap_Exception $e) {} 404 405 // Nullresult. 406 $res = $ldap->search(null, '(cn=nevermatching_filter)', 407 array('scope' => 'base', 'attributes' => '1.1')); 408 $this->assertInstanceOf('Horde_Ldap_Search', $res); 409 $this->assertEquals(0, $res->count()); 410 } 411 412 /** 413 * Test exists(). 414 */ 415 public function testExists() 416 { 417 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 418 419 $dn = 'ou=Horde_Ldap_Test_exists,' . self::$ldapcfg['server']['basedn']; 420 421 // Testing not existing DN. 422 $this->assertFalse($ldap->exists($dn)); 423 424 // Passing an entry object (should work). exists() should return false, 425 // because we didn't add the test entry yet. 426 $ou1 = Horde_Ldap_Entry::createFresh( 427 $dn, 428 array('objectClass' => array('top', 'organizationalUnit')) 429 ); 430 431 $this->assertFalse($ldap->exists($dn)); 432 $this->assertFalse($ldap->exists($ou1)); 433 434 // Testing not existing DN. 435 $ldap->add($ou1); 436 $this->assertTrue($ldap->exists($dn)); 437 438 // Passing an float instead of a string. 439 try { 440 $ldap->exists(1.234); 441 $this->fail('Horde_Ldap_Exception expected.'); 442 } catch (Horde_Ldap_Exception $e) {} 443 444 // Testing multivalued RDNs. 445 $dn = 'ou=Horde_Ldap_Test_exists_2+l=somewhere,' . self::$ldapcfg['server']['basedn']; 446 $ou2 = Horde_Ldap_Entry::createFresh( 447 $dn, 448 array('objectClass' => array('top', 'organizationalUnit')) 449 ); 450 $this->assertFalse($ldap->exists($dn)); 451 $ldap->add($ou2); 452 $this->assertTrue($ldap->exists($dn)); 453 } 454 455 /** 456 * Test getEntry(). 457 */ 458 public function testGetEntry() 459 { 460 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 461 $dn = 'ou=Horde_Ldap_Test_getEntry,' . self::$ldapcfg['server']['basedn']; 462 $entry = Horde_Ldap_Entry::createFresh( 463 $dn, 464 array('objectClass' => array('top', 'organizationalUnit'), 465 'ou' => 'Horde_Ldap_Test_getEntry')); 466 $ldap->add($entry); 467 468 // Existing DN. 469 $this->assertInstanceOf('Horde_Ldap_Entry', $ldap->getEntry($dn)); 470 471 // Not existing DN. 472 try { 473 $ldap->getEntry('cn=notexistent,' . self::$ldapcfg['server']['basedn']); 474 $this->fail('Horde_Ldap_Exception expected.'); 475 } catch (Horde_Exception_NotFound $e) {} 476 } 477 478 /** 479 * Test move(). 480 */ 481 public function testMove() 482 { 483 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 484 485 // For Moving tests, we need some little tree again. 486 $base = self::$ldapcfg['server']['basedn']; 487 $testdn = 'ou=Horde_Ldap_Test_move,' . $base; 488 489 $ou = Horde_Ldap_Entry::createFresh( 490 $testdn, 491 array('objectClass' => array('top', 'organizationalUnit'), 492 'ou' => 'Horde_Ldap_Test_move')); 493 $ou_1 = Horde_Ldap_Entry::createFresh( 494 'ou=source,' . $testdn, 495 array('objectClass' => array('top', 'organizationalUnit'), 496 'ou' => 'source')); 497 $ou_1_l1 = Horde_Ldap_Entry::createFresh( 498 'l=moveitem,ou=source,' . $testdn, 499 array('objectClass' => array('top','locality'), 500 'l' => 'moveitem', 501 'description' => 'movetest')); 502 $ou_2 = Horde_Ldap_Entry::createFresh( 503 'ou=target,' . $testdn, 504 array('objectClass' => array('top', 'organizationalUnit'), 505 'ou' => 'target')); 506 $ou_3 = Horde_Ldap_Entry::createFresh( 507 'ou=target_otherdir,' . $testdn, 508 array('objectClass' => array('top','organizationalUnit'), 509 'ou' => 'target_otherdir')); 510 $ldap->add($ou); 511 $ldap->add($ou_1); 512 $ldap->add($ou_1_l1); 513 $ldap->add($ou_2); 514 $ldap->add($ou_3); 515 $this->assertTrue($ldap->exists($ou->dn())); 516 $this->assertTrue($ldap->exists($ou_1->dn())); 517 $this->assertTrue($ldap->exists($ou_1_l1->dn())); 518 $this->assertTrue($ldap->exists($ou_2->dn())); 519 $this->assertTrue($ldap->exists($ou_3->dn())); 520 // Tree established. 521 522 // Local rename. 523 $olddn = $ou_1_l1->currentDN(); 524 $ldap->move($ou_1_l1, str_replace('moveitem', 'move_item', $ou_1_l1->dn())); 525 $this->assertTrue($ldap->exists($ou_1_l1->dn())); 526 $this->assertFalse($ldap->exists($olddn)); 527 528 // Local move. 529 $olddn = $ou_1_l1->currentDN(); 530 $ldap->move($ou_1_l1, 'l=move_item,' . $ou_2->dn()); 531 $this->assertTrue($ldap->exists($ou_1_l1->dn())); 532 $this->assertFalse($ldap->exists($olddn)); 533 534 // Local move backward, with rename. Here we use the DN of the object, 535 // to test DN conversion. 536 // Note that this will outdate the object since it does not has 537 // knowledge about the move. 538 $olddn = $ou_1_l1->currentDN(); 539 $newdn = 'l=moveditem,' . $ou_2->dn(); 540 $ldap->move($olddn, $newdn); 541 $this->assertTrue($ldap->exists($newdn)); 542 $this->assertFalse($ldap->exists($olddn)); 543 // Refetch since the object's DN was outdated. 544 $ou_1_l1 = $ldap->getEntry($newdn); 545 546 // Fake-cross directory move using two separate links to the same 547 // directory. This other directory is represented by 548 // ou=target_otherdir. 549 $ldap2 = new Horde_Ldap(self::$ldapcfg['server']); 550 $olddn = $ou_1_l1->currentDN(); 551 $ldap->move($ou_1_l1, 'l=movedcrossdir,' . $ou_3->dn(), $ldap2); 552 $this->assertFalse($ldap->exists($olddn)); 553 $this->assertTrue($ldap2->exists($ou_1_l1->dn())); 554 555 // Try to move over an existing entry. 556 try { 557 $ldap->move($ou_2, $ou_3->dn(), $ldap2); 558 $this->fail('Horde_Ldap_Exception expected.'); 559 } catch (Horde_Ldap_Exception $e) {} 560 561 // Try cross directory move without providing an valid entry but a DN. 562 try { 563 $ldap->move($ou_1_l1->dn(), 'l=movedcrossdir2,'.$ou_2->dn(), $ldap2); 564 $this->fail('Horde_Ldap_Exception expected.'); 565 } catch (Horde_Ldap_Exception $e) {} 566 567 // Try passing an invalid entry object. 568 try { 569 $ldap->move($ldap, 'l=move_item,'.$ou_2->dn()); 570 $this->fail('Horde_Ldap_Exception expected.'); 571 } catch (Horde_Ldap_Exception $e) {} 572 573 // Try passing an invalid LDAP object. 574 try { 575 $ldap->move($ou_1_l1, 'l=move_item,'.$ou_2->dn(), $ou_1); 576 $this->fail('Horde_Ldap_Exception expected.'); 577 } catch (Horde_Ldap_Exception $e) {} 578 } 579 580 /** 581 * Test copy(). 582 */ 583 public function testCopy() 584 { 585 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 586 587 // Some testdata. 588 $base = self::$ldapcfg['server']['basedn']; 589 $ou1 = Horde_Ldap_Entry::createFresh( 590 'ou=Horde_Ldap_Test_pool,' . $base, 591 array('objectClass' => array('top','organizationalUnit'), 592 'ou' => 'Horde_Ldap_Test_copy')); 593 $ou2 = Horde_Ldap_Entry::createFresh( 594 'ou=Horde_Ldap_Test_tgt,' . $base, 595 array('objectClass' => array('top','organizationalUnit'), 596 'ou' => 'Horde_Ldap_Test_copy')); 597 $ldap->add($ou1); 598 $this->assertTrue($ldap->exists($ou1->dn())); 599 $ldap->add($ou2); 600 $this->assertTrue($ldap->exists($ou2->dn())); 601 602 $entry = Horde_Ldap_Entry::createFresh( 603 'l=cptest,' . $ou1->dn(), 604 array('objectClass' => array('top','locality'), 605 'l' => 'cptest')); 606 $ldap->add($entry); 607 $ldap->exists($entry->dn()); 608 609 // Copy over the entry to another tree with rename. 610 $entrycp = $ldap->copy($entry, 'l=test_copied,' . $ou2->dn()); 611 $this->assertInstanceOf('Horde_Ldap_Entry', $entrycp); 612 $this->assertNotEquals($entry->dn(), $entrycp->dn()); 613 $this->assertTrue($ldap->exists($entrycp->dn())); 614 615 // Copy same again (fails, entry exists). 616 try { 617 $entrycp_f = $ldap->copy($entry, 'l=test_copied,' . $ou2->dn()); 618 $this->fail('Horde_Ldap_Exception expected.'); 619 } catch (Horde_Ldap_Exception $e) {} 620 621 // Use only DNs to copy (fails). 622 try { 623 $entrycp = $ldap->copy($entry->dn(), 'l=test_copied2,' . $ou2->dn()); 624 $this->fail('Horde_Ldap_Exception expected.'); 625 } catch (Horde_Ldap_Exception $e) {} 626 } 627 628 /** 629 * Tests retrieval of root DSE object. 630 */ 631 public function testRootDSE() 632 { 633 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 634 $this->assertInstanceOf('Horde_Ldap_RootDse', $ldap->rootDSE()); 635 } 636 637 /** 638 * Tests retrieval of schema through LDAP object. 639 */ 640 public function testSchema() 641 { 642 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 643 $this->assertInstanceOf('Horde_Ldap_Schema', $ldap->schema()); 644 } 645 646 /** 647 * Test getLink(). 648 */ 649 public function testGetLink() 650 { 651 $ldap = new Horde_Ldap(self::$ldapcfg['server']); 652 $this->assertTrue(is_resource($ldap->getLink())); 653 } 654 655 public function testQuoteDN() 656 { 657 $this->assertEquals( 658 'cn=John Smith,dc=example,dc=com', 659 Horde_Ldap::quoteDN( 660 array( 661 array('cn', 'John Smith'), 662 array('dc', 'example'), 663 array('dc', 'com') 664 ) 665 ) 666 ); 667 $this->assertEquals( 668 'cn=John+sn=Smith+o=Acme Inc.,dc=example,dc=com', 669 Horde_Ldap::quoteDN( 670 array( 671 array( 672 array('cn', 'John'), 673 array('sn', 'Smith'), 674 array('o', 'Acme Inc.'), 675 ), 676 array('dc', 'example'), 677 array('dc', 'com') 678 ) 679 ) 680 ); 681 $this->assertEquals( 682 'cn=John+sn=Smith+o=Acme Inc.', 683 Horde_Ldap::quoteDN( 684 array( 685 array( 686 array('cn', 'John'), 687 array('sn', 'Smith'), 688 array('o', 'Acme Inc.'), 689 ), 690 ) 691 ) 692 ); 693 } 694} 695