Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * ldap tests. 19 * 20 * @package core 21 * @category phpunit 22 * @copyright Damyon Wiese, Iñaki Arenaza 2014 23 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 global $CFG; 29 require_once($CFG->libdir . '/ldaplib.php'); 30 31 class core_ldaplib_testcase extends advanced_testcase { 32 33 public function test_ldap_addslashes() { 34 // See http://tools.ietf.org/html/rfc4514#section-5.2 if you want 35 // to add additional tests. 36 37 $tests = array( 38 array ( 39 'test' => 'Simplest', 40 'expected' => 'Simplest', 41 ), 42 array ( 43 'test' => 'Simple case', 44 'expected' => 'Simple\\20case', 45 ), 46 array ( 47 'test' => 'Medium ‒ case', 48 'expected' => 'Medium\\20‒\\20case', 49 ), 50 array ( 51 'test' => '#Harder+case#', 52 'expected' => '\\23Harder\\2bcase\\23', 53 ), 54 array ( 55 'test' => ' Harder (and); harder case ', 56 'expected' => '\\20Harder\\20(and)\\3b\\20harder\\20case\\20', 57 ), 58 array ( 59 'test' => 'Really \\0 (hard) case!\\', 60 'expected' => 'Really\\20\\5c0\\20(hard)\\20case!\\5c', 61 ), 62 array ( 63 'test' => 'James "Jim" = Smith, III', 64 'expected' => 'James\\20\\22Jim\22\\20\\3d\\20Smith\\2c\\20III', 65 ), 66 array ( 67 'test' => ' <jsmith@example.com> ', 68 'expected' => '\\20\\20\\3cjsmith@example.com\\3e\\20', 69 ), 70 ); 71 72 73 foreach ($tests as $test) { 74 $this->assertSame($test['expected'], ldap_addslashes($test['test'])); 75 } 76 } 77 78 public function test_ldap_stripslashes() { 79 // See http://tools.ietf.org/html/rfc4514#section-5.2 if you want 80 // to add additional tests. 81 82 // IMPORTANT NOTICE: While ldap_addslashes() only produces one 83 // of the two defined ways of escaping/quoting (the ESC HEX 84 // HEX way defined in the grammar in Section 3 of RFC-4514) 85 // ldap_stripslashes() has to deal with both of them. So in 86 // addition to testing the same strings we test in 87 // test_ldap_stripslashes(), we need to also test strings 88 // using the second method. 89 90 $tests = array( 91 array ( 92 'test' => 'Simplest', 93 'expected' => 'Simplest', 94 ), 95 array ( 96 'test' => 'Simple\\20case', 97 'expected' => 'Simple case', 98 ), 99 array ( 100 'test' => 'Simple\\ case', 101 'expected' => 'Simple case', 102 ), 103 array ( 104 'test' => 'Simple\\ \\63\\61\\73\\65', 105 'expected' => 'Simple case', 106 ), 107 array ( 108 'test' => 'Medium\\ ‒\\ case', 109 'expected' => 'Medium ‒ case', 110 ), 111 array ( 112 'test' => 'Medium\\20‒\\20case', 113 'expected' => 'Medium ‒ case', 114 ), 115 array ( 116 'test' => 'Medium\\20\\E2\\80\\92\\20case', 117 'expected' => 'Medium ‒ case', 118 ), 119 array ( 120 'test' => '\\23Harder\\2bcase\\23', 121 'expected' => '#Harder+case#', 122 ), 123 array ( 124 'test' => '\\#Harder\\+case\\#', 125 'expected' => '#Harder+case#', 126 ), 127 array ( 128 'test' => '\\20Harder\\20(and)\\3b\\20harder\\20case\\20', 129 'expected' => ' Harder (and); harder case ', 130 ), 131 array ( 132 'test' => '\\ Harder\\ (and)\\;\\ harder\\ case\\ ', 133 'expected' => ' Harder (and); harder case ', 134 ), 135 array ( 136 'test' => 'Really\\20\\5c0\\20(hard)\\20case!\\5c', 137 'expected' => 'Really \\0 (hard) case!\\', 138 ), 139 array ( 140 'test' => 'Really\\ \\\\0\\ (hard)\\ case!\\\\', 141 'expected' => 'Really \\0 (hard) case!\\', 142 ), 143 array ( 144 'test' => 'James\\20\\22Jim\\22\\20\\3d\\20Smith\\2c\\20III', 145 'expected' => 'James "Jim" = Smith, III', 146 ), 147 array ( 148 'test' => 'James\\ \\"Jim\\" \\= Smith\\, III', 149 'expected' => 'James "Jim" = Smith, III', 150 ), 151 array ( 152 'test' => '\\20\\20\\3cjsmith@example.com\\3e\\20', 153 'expected' => ' <jsmith@example.com> ', 154 ), 155 array ( 156 'test' => '\\ \\<jsmith@example.com\\>\\ ', 157 'expected' => ' <jsmith@example.com> ', 158 ), 159 array ( 160 'test' => 'Lu\\C4\\8Di\\C4\\87', 161 'expected' => 'Lučić', 162 ), 163 ); 164 165 foreach ($tests as $test) { 166 $this->assertSame($test['expected'], ldap_stripslashes($test['test'])); 167 } 168 } 169 170 /** 171 * Tests for ldap_normalise_objectclass. 172 * 173 * @dataProvider ldap_normalise_objectclass_provider 174 * @param array $args Arguments passed to ldap_normalise_objectclass 175 * @param string $expected The expected objectclass filter 176 */ 177 public function test_ldap_normalise_objectclass($args, $expected) { 178 $this->assertEquals($expected, call_user_func_array('ldap_normalise_objectclass', $args)); 179 } 180 181 /** 182 * Data provider for the test_ldap_normalise_objectclass testcase. 183 * 184 * @return array of testcases. 185 */ 186 public function ldap_normalise_objectclass_provider() { 187 return array( 188 'Empty value' => array( 189 array(null), 190 '(objectClass=*)', 191 ), 192 'Empty value with different default' => array( 193 array(null, 'lion'), 194 '(objectClass=lion)', 195 ), 196 'Supplied unwrapped objectClass' => array( 197 array('objectClass=tiger'), 198 '(objectClass=tiger)', 199 ), 200 'Supplied string value' => array( 201 array('leopard'), 202 '(objectClass=leopard)', 203 ), 204 'Supplied complex' => array( 205 array('(&(objectClass=cheetah)(enabledMoodleUser=1))'), 206 '(&(objectClass=cheetah)(enabledMoodleUser=1))', 207 ), 208 ); 209 } 210 211 /** 212 * Tests for ldap_get_entries_moodle. 213 * 214 * NOTE: in order to execute this test you need to set up OpenLDAP server with core, 215 * cosine, nis and internet schemas and add configuration constants to 216 * config.php or phpunit.xml configuration file. The bind users *needs* 217 * permissions to create objects in the LDAP server, under the bind domain. 218 * 219 * define('TEST_LDAPLIB_HOST_URL', 'ldap://127.0.0.1'); 220 * define('TEST_LDAPLIB_BIND_DN', 'cn=someuser,dc=example,dc=local'); 221 * define('TEST_LDAPLIB_BIND_PW', 'somepassword'); 222 * define('TEST_LDAPLIB_DOMAIN', 'dc=example,dc=local'); 223 * 224 */ 225 public function test_ldap_get_entries_moodle() { 226 $this->resetAfterTest(); 227 228 if (!defined('TEST_LDAPLIB_HOST_URL') or !defined('TEST_LDAPLIB_BIND_DN') or 229 !defined('TEST_LDAPLIB_BIND_PW') or !defined('TEST_LDAPLIB_DOMAIN')) { 230 $this->markTestSkipped('External LDAP test server not configured.'); 231 } 232 233 // Make sure we can connect the server. 234 $debuginfo = ''; 235 if (!$connection = ldap_connect_moodle(TEST_LDAPLIB_HOST_URL, 3, 'rfc2307', TEST_LDAPLIB_BIND_DN, 236 TEST_LDAPLIB_BIND_PW, LDAP_DEREF_NEVER, $debuginfo, false)) { 237 $this->markTestSkipped('Cannot connect to LDAP test server: '.$debuginfo); 238 } 239 240 // Create new empty test container. 241 if (!($containerdn = $this->create_test_container($connection, 'moodletest'))) { 242 $this->markTestSkipped('Can not create test LDAP container.'); 243 } 244 245 // Add all the test objects. 246 $testobjects = $this->get_ldap_get_entries_moodle_test_objects(); 247 if (!$this->add_test_objects($connection, $containerdn, $testobjects)) { 248 $this->markTestSkipped('Can not create LDAP test objects.'); 249 } 250 251 // Now query about them and compare results. 252 foreach ($testobjects as $object) { 253 $dn = $this->get_object_dn($object, $containerdn); 254 $filter = $object['query']['filter']; 255 $attributes = $object['query']['attributes']; 256 257 $sr = ldap_read($connection, $dn, $filter, $attributes); 258 if (!$sr) { 259 $this->markTestSkipped('Cannot retrieve test objects from LDAP test server.'); 260 } 261 262 $entries = ldap_get_entries_moodle($connection, $sr); 263 $actual = array_keys($entries[0]); 264 $expected = $object['expected']; 265 266 // We need to sort both arrays to be able to compare them, as the LDAP server 267 // might return attributes in any order. 268 sort($expected); 269 sort($actual); 270 $this->assertEquals($expected, $actual); 271 } 272 273 // Clean up test objects and container. 274 $this->remove_test_objects($connection, $containerdn, $testobjects); 275 $this->remove_test_container($connection, $containerdn); 276 } 277 278 /** 279 * Provide the array of test objects for the ldap_get_entries_moodle test case. 280 * 281 * @return array of test objects 282 */ 283 protected function get_ldap_get_entries_moodle_test_objects() { 284 $testobjects = array( 285 // Test object 1. 286 array( 287 // Add/remove this object to LDAP directory? There are existing standard LDAP 288 // objects that we might want to test, but that we shouldn't add/remove ourselves. 289 'addremove' => true, 290 // Relative (to test container) or absolute distinguished name (DN). 291 'relativedn' => true, 292 // Distinguished name for this object (interpretation depends on 'relativedn'). 293 'dn' => 'cn=test1', 294 // Values to add to LDAP directory. 295 'values' => array( 296 'objectClass' => array('inetOrgPerson', 'organizationalPerson', 'person', 'posixAccount'), 297 'cn' => 'test1', // We don't care about the actual values, as long as they are unique. 298 'sn' => 'test1', 299 'givenName' => 'test1', 300 'uid' => 'test1', 301 'uidNumber' => '20001', // Start from 20000, then add test number. 302 'gidNumber' => '20001', // Start from 20000, then add test number. 303 'homeDirectory' => '/', 304 'userPassword' => '*', 305 ), 306 // Attributes to query the object for. 307 'query' => array( 308 'filter' => '(objectClass=posixAccount)', 309 'attributes' => array( 310 'cn', 311 'sn', 312 'givenName', 313 'uid', 314 'uidNumber', 315 'gidNumber', 316 'homeDirectory', 317 'userPassword' 318 ), 319 ), 320 // Expected values for the queried attributes' names. 321 'expected' => array( 322 'cn', 323 'sn', 324 'givenname', 325 'uid', 326 'uidnumber', 327 'gidnumber', 328 'homedirectory', 329 'userpassword' 330 ), 331 ), 332 // Test object 2. 333 array( 334 'addremove' => true, 335 'relativedn' => true, 336 'dn' => 'cn=group2', 337 'values' => array( 338 'objectClass' => array('top', 'posixGroup'), 339 'cn' => 'group2', // We don't care about the actual values, as long as they are unique. 340 'gidNumber' => '20002', // Start from 20000, then add test number. 341 'memberUid' => '20002', // Start from 20000, then add test number. 342 ), 343 'query' => array( 344 'filter' => '(objectClass=posixGroup)', 345 'attributes' => array( 346 'cn', 347 'gidNumber', 348 'memberUid' 349 ), 350 ), 351 'expected' => array( 352 'cn', 353 'gidnumber', 354 'memberuid' 355 ), 356 ), 357 // Test object 3. 358 array( 359 'addremove' => false, 360 'relativedn' => false, 361 'dn' => '', // To query the RootDSE, we must specify the empty string as the absolute DN. 362 'values' => array( 363 ), 364 'query' => array( 365 'filter' => '(objectClass=*)', 366 'attributes' => array( 367 'supportedControl', 368 'namingContexts' 369 ), 370 ), 371 'expected' => array( 372 'supportedcontrol', 373 'namingcontexts' 374 ), 375 ), 376 ); 377 378 return $testobjects; 379 } 380 381 /** 382 * Create a new container in the LDAP domain, to hold the test objects. The 383 * container is created as a domain component (dc) + organizational unit (ou) object. 384 * 385 * @param object $connection Valid LDAP connection 386 * @param string $container Name of the test container to create. 387 * 388 * @return string or false Distinguished name for the created container, or false on error. 389 */ 390 protected function create_test_container($connection, $container) { 391 $object = array(); 392 $object['objectClass'] = array('dcObject', 'organizationalUnit'); 393 $object['dc'] = $container; 394 $object['ou'] = $container; 395 $containerdn = 'dc='.$container.','.TEST_LDAPLIB_DOMAIN; 396 if (!ldap_add($connection, $containerdn, $object)) { 397 return false; 398 } 399 return $containerdn; 400 } 401 402 /** 403 * Remove the container in the LDAP domain root that holds the test objects. The container 404 * *must* be empty before trying to remove it. Otherwise this function fails. 405 * 406 * @param object $connection Valid LDAP connection 407 * @param string $containerdn The distinguished of the container to remove. 408 */ 409 protected function remove_test_container($connection, $containerdn) { 410 ldap_delete($connection, $containerdn); 411 } 412 413 /** 414 * Add the test objects to the test container. 415 * 416 * @param resource $connection Valid LDAP connection 417 * @param string $containerdn The distinguished name of the container for the created objects. 418 * @param array $testobjects Array of the tests objects to create. The structure of 419 * the array elements *must* follow the structure of the value returned 420 * by ldap_get_entries_moodle_test_objects() member function. 421 * 422 * @return boolean True on success, false otherwise. 423 */ 424 protected function add_test_objects($connection, $containerdn, $testobjects) { 425 foreach ($testobjects as $object) { 426 if ($object['addremove'] !== true) { 427 continue; 428 } 429 $dn = $this->get_object_dn($object, $containerdn); 430 $entry = $object['values']; 431 if (!ldap_add($connection, $dn, $entry)) { 432 return false; 433 } 434 } 435 return true; 436 } 437 438 /** 439 * Remove the test objects from the test container. 440 * 441 * @param resource $connection Valid LDAP connection 442 * @param string $containerdn The distinguished name of the container for the objects to remove. 443 * @param array $testobjects Array of the tests objects to create. The structure of 444 * the array elements *must* follow the structure of the value returned 445 * by ldap_get_entries_moodle_test_objects() member function. 446 * 447 */ 448 protected function remove_test_objects($connection, $containerdn, $testobjects) { 449 foreach ($testobjects as $object) { 450 if ($object['addremove'] !== true) { 451 continue; 452 } 453 $dn = $this->get_object_dn($object, $containerdn); 454 ldap_delete($connection, $dn); 455 } 456 } 457 458 /** 459 * Get the distinguished name (DN) for a given object. 460 * 461 * @param object $object The LDAP object to calculate the DN for. 462 * @param string $containerdn The DN of the container to use for objects with relative DNs. 463 * 464 * @return string The calculated DN. 465 */ 466 protected function get_object_dn($object, $containerdn) { 467 if ($object['relativedn']) { 468 $dn = $object['dn'].','.$containerdn; 469 } else { 470 $dn = $object['dn']; 471 } 472 return $dn; 473 } 474 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body